С наступающим Новым годом! Форум программистов, компьютерный форум, киберфорум
Наши страницы
jvf
Войти
Регистрация
Восстановить пароль
Оценить эту запись

2018.05.25 Дообучение нейронной сети VGG16 для различения кошки

Запись от jvf размещена 25.05.2018 в 13:58

В прошлых блогах я рассказывал, что решил для развлечения написать две программы, в которых будут задействованы алгоритмы машинного обучения.

Первая -- программа автоматически будет делать скриншоты моего рабочего стола, а потом я буду пытаться как-то их изучать с помощью алгоритмов машинного обучения.

Вторая -- у меня есть веб-камера, она будет непрерывно установлена, и когда кто-то будет проходить мимо, то будет срабатывать детектор движения. Цель программы -- отличить кошку от меня и ложных срабатываний.

В этих программах нет большого практического смысла, я их пишу просто для развлечения.

Эти программы я уже написал. И первая, и вторая собирают статистику. Но пока данных очень мало, особенно по кошке, потому что она редко проходит рядом с камерой, а, соответственно, и данные набираются очень медленно.

Можно, конечно, ждать сто лет, прежде чем начать писать нейронную сеть, пока не наберу достаточных данных, но хочется действовать уже сейчас.

Поэтому вчера я написал программу: так как данных по кошке очень мало (ленивое животное предпочитает спать, а не ходить рядом с камерой), то решил попробовать дообучить нейронную сеть VGG16 на около 100 снимках.

Вот код. Импортируем необходимые библиотеки:

Python
1
2
3
4
5
6
7
8
9
10
from keras.preprocessing import image
from keras.models import Model
from keras.layers import Dense, GlobalAveragePooling2D
from keras import applications
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
from keras.models import Model
from keras.callbacks import EarlyStopping
utils - это мой файл для работы с каталогами, файлами и т.п.
Python
1
2
from utils import utils
myDirs = utils()
train_dir - это каталог, где лежат данные для обучения
validation_data_dir - где лежат данные для валидации
Python
1
2
3
4
train_data_dir = myDirs.KERAS_TRAIN_DIR
validation_data_dir = myDirs.KERAS_VAL_DIR
nb_train_samples = myDirs.number_images_in_folder(train_data_dir)
nb_validation_samples = myDirs.number_images_in_folder(validation_data_dir)
размер выборки делаем маленьким, потому что у меня слабый компьютер, и всё начнёт тормозить при большом размере.
Python
1
2
3
4
5
6
# Размер изображений
img_width, img_height = 150, 150
# Количество эпох
epochs = 5
# Размер выборки
batch_size = 8
Python
1
2
# Загружаем сеть VGG16 без части, которая отвечает за классификацию
base_model = applications.VGG16(weights='imagenet', include_top=False)
Python
1
2
3
4
5
6
# Добавляем слои классификации
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
# Выходной слой с двумя нейронами для классов "кошка" и "не кошка"
predictions = Dense(2, activation='softmax')(x)
Python
1
2
# Составляем сеть из двух частей
model = Model(inputs=base_model.input, outputs=predictions)
Python
1
2
3
4
# "Замораживаем" сверточные уровни сети VGG16
# Обучаем только вновь добавленные слои
for layer in base_model.layers:
    layer.trainable = False
Python
1
2
# Компилируем модель
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
Python
1
2
3
4
5
6
7
# Создаем генератор данных для обучения
datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = datagen.flow_from_directory(
    train_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode= 'categorical')
Python
1
2
3
4
5
6
# Создаем генератор данных для валидации
validation_generator = datagen.flow_from_directory(
    validation_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode= 'categorical')
Теперь основная вещь. У нас очень мало данных, мы боимся переобучения сети, поэтому используем функцию EarlyStopping. Она будет мониторить значение val_loss, и если она на какой-то эпохи не улучшится, то обучение прервётся.

Python
1
2
3
4
5
6
7
8
9
10
11
Early_Stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=3, verbose=1, mode='auto')
# Обучаем модель с помощью генератора
model.fit_generator(
    train_generator,
    steps_per_epoch=nb_train_samples,
    epochs=epochs,
    validation_data=validation_generator,
    validation_steps = nb_validation_samples,
    verbose=1,
    callbacks=[Early_Stopping]
    )
Вот наши результаты:

Start
Epoch 1/5
200/200 [==============================] - 541s 3s/step - loss: 0.1820 - acc: 0.9200 - val_loss: 0.4298 - val_acc: 0.8571
Epoch 2/5
200/200 [==============================] - 610s 3s/step - loss: 0.1115 - acc: 0.9487 - val_loss: 0.4841 - val_acc: 0.8776
Epoch 3/5
200/200 [==============================] - 622s 3s/step - loss: 0.0856 - acc: 0.9644 - val_loss: 0.4927 - val_acc: 0.8980
Epoch 4/5
200/200 [==============================] - 449s 2s/step - loss: 0.0623 - acc: 0.9744 - val_loss: 0.5442 - val_acc: 0.8571
Epoch 00004: early stopping

<keras.callbacks.History at 0x7fe24c865240>

Мы видим, что обучение прервалось на 4-ой эпохи, потому что значения val_vacc упали. Иными словами, нам хватит трёх эпох.

Python
1
2
3
4
5
6
7
8
9
10
11
print("Сохраняем сеть")
# Сохраняем сеть для последующего использования
# Генерируем описание модели в формате json
model_json = model.to_json()
json_file = open("my_vgg16_cat_dogs.json", "w")
# Записываем архитектуру сети в файл
json_file.write(model_json)
json_file.close()
# Записываем данные о весах в файл
model.save_weights("my_vgg16_cat_dogs.h5")
print("Сохранение сети завершено")
Теперь строим тестовый генератор: наша цель -- проверить результаты на тестовых данных.

test_data_dir - папка, где лежат тестовые данные
nb_test_samples - количество файлов с тестовыми данными
Python
1
2
3
4
test_data_dir = myDirs.KERAS_TEST_DIR
#validation_data_dir = myDirs.KERAS_VAL_DIR
nb_test_samples = myDirs.number_images_in_folder(test_data_dir)
#nb_validation_samples = myDirs.number_images_in_folder(validation_data_dir)
Python
1
2
3
4
5
test_generator = datagen.flow_from_directory(
    test_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode= 'categorical')
Python
1
2
scores = model.evaluate_generator(test_generator, nb_test_samples  // batch_size)
print("Аккуратность на тестовых данных: %.2f%%" % (scores[1]*100))
Аккуратность на тестовых данных: 87.50%%

Что мы видим?

Аккуратность на данных на обучении равна 0.9744, а на валидации и на тесте -- 85 и 87 процентов. Возможно, всё равно наша сеть переобучена, и имеет смысл сократить число нейронов на выходном слое.

Один из критериев переобучения -- когда результаты на обучающей выборе значительно лучше, чем на тестовой. У нас разница в 10 процентов, поэтому, возможно, мы имеем переобучение, и в следующей раз стоит попробовать запустить сеть с меньшим числом нейронов на выходном слое.

Интересное видео о машинном обучении:
Сверточные нейронные сети | Глубокие нейронные сети на Python
https://www.youtube.com/watch?v=52U4BG0ENiM



Интересные мысли из видео:

1. В свёрточных нейронных сетях меньше количество параметров по сравнению с обычными.

2. Изображение представляется в виде плоского массива, теряется информация о топологии сети

3. Чтобы избавиться от этих проблем, были предложены другие сети под название "свёрточные нейронные сети", они используются сейчас для распознавания изображений, видео.

4. Операция свёртка -- это когда мы берём один участок изображения. Затем у нас есть некая свёртка -- массив данных. Им перемножаем две таблички, затем складываем полученные значения. Затем к этому значению может применять некоторая функция активации.

5. Было придумано много ядер свёртки, например, размытие, выделение границ, повышение чёткости и т.п.

6. В нейронных сетях ядра свёртки определяются автоматически в процессе обучения

7. Ещё один принцип в свёрточных нейронных сетях -- уменьшение их размерности (subsampling). Мы берём некоторую часть изображения, и находим в этой части максимальный элемент. Затем получаем новую табличку с максимальными элементами.

8. Свёрточные нейронные сети -- это сети, где чередуются слои свёртки (convolutions) и подвыборки (subsampling).

9. В каждом слое используется не обязательно одно ядро свёртки. Их может быть несколько. Набор слоёв нейронов, каждый их которых использует разные ядра свёртки для поиска разных признаков, называется картой признаков.

10. В конце свёрточной нейронной сети находится обычные слои, так называемся Full connection. Последний слой -- выходной.

Интересные тексты о машинном обучении:
https://www.kaggle.com/yassineghouza...as-0-997-top-6

Здесь рассказывается о том, как понять, нет ли у нас переобучения нашей сети.

Мы строим графики "Training loss", "validation loss", "Training accuracy", "Validation accuracy". На одном графике размещаем линии с "Training loss" и "validation loss", а на втором графике -- "Training accuracy" и "Validation accuracy"
Python
1
2
3
4
5
6
7
8
9
# Plot the loss and accuracy curves for training and validation 
fig, ax = plt.subplots(2,1)
ax[0].plot(history.history['loss'], color='b', label="Training loss")
ax[0].plot(history.history['val_loss'], color='r', label="validation loss",axes =ax[0])
legend = ax[0].legend(loc='best', shadow=True)
 
ax[1].plot(history.history['acc'], color='b', label="Training accuracy")
ax[1].plot(history.history['val_acc'], color='r',label="Validation accuracy")
legend = ax[1].legend(loc='best', shadow=True)
Если красная кривая Validation accuracy будет выше в большой части графика кривой Training accuracy, то "это означет, -- пишет автор, -- что наша модель не переобучена на обучающей выборке".

Соответственно, мне тоже нужно будет построить этот график для моей модели и посмотреть, будет ли мой график похож на график автора (и вообще что за график я получу):

Согласно тексту автор графики двух кривых, если модель подобрана верно, должен быть приблизительно таким:

http://img1.imagilive.com/0717/mnist_099671_train_val_loss_acc.png

В приложении к этому тексту находится изображения, которые собирает мой детектор кошки: мы видим не всю кошку, а только то, что попало в детектор движения. Плюс мы немного расширяем детектор движения, чтобы изображения были побольше.

Вот мы и получили около 250 подобных снимков. Все они маленькие, все они немного размытые, но всё равно, думаю, что с ними будет возможно что-то сделать.
Изображения
 
Размещено в Без категории
Просмотров 504 Комментарии 2
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Цитата:
    У нас очень мало данных
    Добавление зеркально отражённых снимков легко и просто удвоит объём данных.
    Запись от VTsaregorodtsev размещена 25.05.2018 в 20:42 VTsaregorodtsev на форуме
  2. Старый комментарий
    Спасибо за идею, отличная мысль.
    Запись от jvf размещена 26.05.2018 в 15:33 jvf вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru