9.2 KiB
Raport przygotowała: Kinga Molik
Raportowany okres: 10 maja - 17 maja 2020
Niniejszy raport poświęcony jest przekazaniu informacji na temat stanu mini-projektu indywidualnego w ramach projektu grupowego realizowanego na przedmiot Sztuczna Inteligencja w roku akademickim 2019/2020.
Tematem realizowanego projektu indywidualnego jest rozpoznawanie nominałów banknotów oraz kart płatniczych. Do rozwiązania problemu zostały wykorzystane sieci neuronowe, biblioteki:
- Keras (backend: Tensorflow),
- Pillow
Uczenie modelu
Dane wejściowe:
Do utworzenia modelu przygotowane zostały zdjęcia przedstawiające polskie banknoty oraz różne karty płatnicze. W folderach train i examine znajdują się foldery:
- 10,
- 20,
- 50,
- 100,
- 200,
- 500
Odpowiadające nominałom polskich banknotów (zdjęć jest odpowiednio: po 35 i po 10) oraz:
- cards,
w których umieszczone zostały zdjęcia kart bankowych (80 oraz 20).
Wszystkie zdjęcia są jednego typu (rozszerzenie .png), wszystkie są nasycone kolorami (nie ma zdjęć czarno-białych) oraz moją różne wielkości, co będzie normalizowane w kodzie.
Proces uczenia:
Inicjalizacja sieci neuronowej:
model = Sequential() model.add(Conv2D(32, (2, 2), input_shape=input_shape)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(32, (2, 2))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(64, (2, 2))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(64)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(7)) model.add(Activation('sigmoid'))
Conv2D
- warstwa do konwulsji obrazu w wiele obrazów - poprzez mnożenie ich przez macierz (2,2) .Activation('relu')
- funkcja aktywacji (negatywne wyniki są zerowane).MaxPooling2D
- funkcja zmiany rozdzielczości zdjęcia (raz dodana używana jest również przez kolejne warstwy).Flatten
- spłaszcza macierze do wektorów.\Dense(64)
- używana do łącznia całego modelu (jest ukrytą warstwą).\Dropout
- używana, by uniknąć nadmiernego dopasowania bazy danych.\Dense(7)
- warstwa wyjściowa zawierająca 7 neuronów (bo tyle mamy różnych kategorii) - decyduje o przynależności danego zdjęcia do kategorii.Activation('sigmoid')
- normalizuje wartości do przedziału [0,1] (ponieważ mamy tu do czynienia z prawdopodobieństwem).
Kompilacja modelu :
model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
Mamy 7 kategorii, które chcielibyśmy rozróżniać, dlatego zastosujemy funkcję straty categorical_crossentropy, do optymalizacji wykorzystamy rmsprop, a parametr metrics = ['accuracy'] pomaga nam sprawdzać dokładność nauki.
Generatory danych:
train_datagen = ImageDataGenerator( rotation_range=45, width_shift_range=0.3, height_shift_range=0.3, rescale=1./256, shear_range=0.25, zoom_range=0.1, horizontal_flip=True) examine_datagen = ImageDataGenerator(rescale=1. / 256) train_generator = train_datagen.flow_from_directory( train_data_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical') examine_generator = examine_datagen.flow_from_directory( examine_data_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical')
train_datagen - generator obrazów z bazy danych uczących. examine_datagen - generator obrazów z bazy danych sprawdzających. Generujemy obrazy, które są niewielką modyfikacją oryginalnych obrazów z folderów train i examine w celu zwiększenia ich ilości. Czym większa baza wiedzy, tymlepsze wyniki podczas sprawdzania. train_generator oraz examine_genertor zaczytują nam pliki z danych folderów (train, examine), normalizuje wielkość zdjęć (img_width, img_height = 256, 256), przypisujemy batch_size = 16 oraz class_mode='categorical', ponieważ mamy 7 różnych klas.
model.fit_generator( train_generator, steps_per_epoch=nb_train_samples // batch_size, epochs=epochs, validation_data=examine_generator, validation_steps=nb_examine_samples // batch_size) model.save_weights('model_payment.h5')
model.fit_generator - generujemy model danych z danymi: - train_generator - genetarator danych do nauki - steps_per_epoch - liczba kroków w danej epoce (obliczana przez podzielenie ilości danych uczących (290) przez batch_size (16)) - validation_data - generator danych do sprawdzania - validation_steps - liczna kroków podczas sprawdzania (ilość danych sprawdzających (80) przez batch_size(16))
' model_payment.h5 ' - plik, do którego zapisywane są wagi modelu.
Integracja z projektem
Na początku zostaje stworzony model sekwencyjny, na którym zostaje wywołana funkcja recognizeCash() oraz załadowany zostaje wcześniej stworzony model z wagami.
pln_classify = Sequential() recognizeCash(pln_classify) pln_classify.load_weights('Kinga/model_payment.h5')
Funkcja do rozpoznawania banknotów uruchomiona zostaje po wybraniu na ekranie startowym przycisku Rozpoznawanie banknotów. W funkcji payment() mamy podany przykładowy stan początkowy restauracji, czyli dane dotyczące klientów, tj. numer stolika, zamówione dania. Zakładamy, że każdy z klientów zakończył już konsumpcje oraz chciałby zapłacić. Każdy klient ma podany budżet (zakładamy tu, że budget >0 oznacza ilość posiadanej gotówki, a budget=0 oznacza chęć zapłaty kartą.) Agent powinien rozpoznać jakim banknotem zapłacił klient oraz wydać mu resztę. W przypadku zapłaty kartą, agent oznajmia, że restauracja nie przyjmuje płatności kartą. By to uschematyzować, została dodana funkcja w klasie Client:
def pay(self, image): load = Image.open(image) load.show()
Po podejściu do stolika, agent odczytuje cenę zamówionej przez klienta potrawy, zostaje wywołana funkcja decide() (z atrybutami client - dany klient, image - zdjęcie z bazy ćwiczeniowej, dish - potrawa klienta), w której zostaje wyświetlone zdjęcie z płatnością klienta oraz sprawdzamy w niej, do której klasy należy ów zdjęcie. Nasze sprawdzenie wykonujemy poprzez przesłanie znormalizowanego zdjęcia do modelu, który wcześniej zainicjowaliśmy.
def decide(client, image, dish): img_width, img_height = 256, 256 client.pay(image) test_image = load_img(image, target_size=(img_width, img_height)) test_image = img_to_array(test_image) test_image = test_image.reshape((1,) + test_image.shape) result = pln_model.predict(test_image) print(result) if result.tolist() == [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]: messagebox.showinfo("Rachunek", "Dziękuję, należy się reszta: " + str(10 - dish.price) + " zł.") return 0 elif result.tolist() == [[0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]]: messagebox.showinfo("Rachunek", "Dziękuję, należy się reszta: " + str(100 - dish.price) + " zł.") return 0 elif result.tolist() == [[0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0]]: messagebox.showinfo("Rachunek", "Dziękuję, należy się reszta: " + str(20 - dish.price) + " zł.") return 0 elif result.tolist() == [[0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]]: messagebox.showinfo("Rachunek", "Dziękuję, należy się reszta: " + str(200 - dish.price) + " zł.") return 0 elif result.tolist() == [[0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]]: messagebox.showinfo("Rachunek", "Dziękuję, należy się reszta: " + str(50 - dish.price) + " zł.") return 0 elif result.tolist() == [[0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]]: messagebox.showinfo("Rachunek", "Dziękuję, należy się reszta: " + str(500 - dish.price) + " zł.") return 0 else: messagebox.showinfo("Uwaga", "Niestety, nie przyjmujemy zapłaty kartą.") return 1
Zmienna result ma typ numpy.ndarray, dlatego przy użyciu jej w funkcji warunkowej if konwertujemy ją do listy. Nasze zdjęcie może przynależeć do jednej z 7 klas, gdzie 1.0 oznacza właśnie tą przynależność. Klasy zaczytywane są w kolejności alfabetycznej, czyli: 10, 100, 20, 200, 50, 500, cards. A więc lista: 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 oznacza, że na danym zdjęciu mamy banknot 20 złotowy. W zależności od zdjęcia, mamy komunikat o reszcie lub o braku możliwości płacenia kartą. Po przejściu przykładowo wyznaczonej trasy, aplikacja kończy swoje działanie.