W tym repozytorium zamieszczam wyniki mojego pierwszego projektu związanego z deep learningiem - model klasyfikujący zdrowe i niezdrowe jedzenie.
W projekcie skorzystałem z datasetu Food-101, wybierając z niego 20 klas, które później podzieliłem na zdrowe i niezdrowe:
- hamburgers 🍔
- donuts 🍔
- waffles 🍔
- pizza 🍔
- red_velvet_cake 🍔
- french_fries 🍔
- chocolate_mousse 🍔
- eggs_benedict 🍔
- chicken_wings 🍔
- hot_dog 🍔
- sushi 🍏
- curry 🍏
- greek_salad 🍏
- seaweed_salad 🍏
- grilled_salomon 🍏
- edamame 🍏
- guacamole 🍏
- falafel 🍏
- escargots 🍏
- lobster_bisque 🍏
Każda klasa zawiera po 1000 obrazków.
Tak skonstruowany dataset podzieliłem na validation data (po +/-100 obrazków na klasę) i na train data (po +/-900 obrazków na klasę).
Obrazy skalowałem do rozmiaru 128x128.
Przed podziałem na 2 główne kategorie, najpierw wytrenowałem model na domyślnych 20 kategoriach. Jako podstawę modelu wziąłem VGG16
model = applications.VGG16(include_top=False, weights='imagenet')
wygenerowałem bottle neck, który potem załadowałem do głównego modelu
bootle_neck.py
def trainTopModel():
train_data = np.load('bottleneck_features_train.npy')
train_labels = to_categorical(train_generator.classes, 20)
validation_data = np.load('bottleneck_features_validation.npy')
validation_labels =to_categorical(val_generator.classes, 20)
model = Sequential()
model.add(Flatten(input_shape=train_data.shape[1:]))
model.add(Dense(128, activation='relu'))
model.add(Dense(128, activation='relu'))
model.add(Dense(20, activation='softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(train_data, train_labels,
epochs=epochs,
batch_size=batch_size,
validation_data=(validation_data, validation_labels))
model.save_weights(top_model_weights_path)
main.py
base_model = applications.VGG16(weights='imagenet',include_top= False,input_tensor=input_tensor)
top_model = Sequential()
top_model.add(Flatten(input_shape=base_model.output_shape[1:]))
top_model.add(Dense(128, activation='relu'))
top_model.add(Dense(128, activation='relu'))
top_model.add(Dense(20, activation='softmax'))
top_model.load_weights(top_model_weights_path)
model = Model(input= base_model.input, output= top_model(base_model.output))
W modelu zafreezowałem pierwsze 15 warstw, tak aby zostawić ostatni conv block VGG16.
Ustaliłem 160 epochów i 16 batchów, optymalizator adagrad, po czym użyłem fit_generatora do wytrenowania modelu. Następnie go zapisałem.
Pierwsze wyniki przestawiały się tak:
po ewaluacji modelu val_accuracy wynosiło około 61%, a val_loss oscylowało nieco poniżej jedynki.
Po niesatysfakcjonujących wynikach ponownie wczytałem zapisany model, ustawiłem agresywniejsze data augmentation i spróbowałem jeszcze raz.
Zmieniłem również optymalizator na adam.
Kolejne wyniki wyglądały dużo lepiej:
val_accuracy na poziomie około 81% i val_loss na poziomie około 0.55%
Podzieliłem dataset na 2 główne klasy.
Do wcześniej stworzonego modelu dodałem nową warstę wyjścia i wytrenowałem na nim nowy model klasyfikujący obrazy jedzenia zdrowego i niezdrowego.
main.py
base_model = load_model('model_saved.h5')
ll = base_model.output
ll = Dense(20,activation="softmax")(ll)
model = Model(inputs=base_model.input,outputs=ll)
Wykresy:
ostatnie 20 epochów ucięte omyłkowo przez przybliżenie
Udało się podciągnąć val_accuracy do około 85% i val_loss do około 0.45%.
Myślę, że z większą liczbą sampli, rozdzielczością obrazów, lub innym podejściem do data augmentation udałoby się wyciągnąć więcej, ale jestem zadowolony :)