Введение
Это короткое упражнение для классификации птиц у вашей кормушки с использованием Keras и Tensorflow в качестве серверной части. Это продолжение моего коллеги по работе, Робина Коула, проекта Hackster Motion Activated Image Capture and Classification of Birds. Эта система в основном использует чувствительную к движению веб-камеру USB, прикрепленную к кормушке для птиц за окном, и захватывает изображения при срабатывании. Однако возникает много ложных срабатываний, когда около половины захваченных изображений не являются птицами.
Я покажу пример самообучаемой модели глубокого обучения, в которой используются двумерные сверточные нейронные сети, чтобы предсказать, является ли изображение птицей или нет. При использовании карты Nvidia GTX 1070 обучение занимает около 10–15 минут. Изображения, сгенерированные системой, были переданы мне автором проекта, который вручную отсортировал изображения птиц по сравнению с отсутствием птиц для обучающего набора. Изображение птицы у кормушки показано ниже.
Проблема в том, что много ложноположительных изображений возникает, когда птиц нет, а вы видите изображения только неба или просто включения лампочки в комнате.
Увеличение данных
Сначала мы дополняем обучающие данные, чтобы увеличить количество обучающих наборов.
import os from keras.preprocessing.image import ImageDataGenerator fnames = [os.path.join('birds_vs_not_birds/training_set/birds', fname) for fname in os.listdir('birds_vs_not_birds/training_set/birds')] train_datagen = ImageDataGenerator(rescale = 1./255, shear_range = 0.2, zoom_range = 0.2, horizontal_flip = True) # We pick one image to "augment" img_path = fnames[249] # Read the image and resize it img = image.load_img(img_path, target_size=(150, 150)) # Convert it to a Numpy array with shape (150, 150, 3) x = image.img_to_array(img) # Reshape it to (1, 150, 150, 3) x = x.reshape((1,) + x.shape) # The .flow() command below generates batches of randomly transformed images. # It will loop indefinitely, so we need to `break` the loop at some point! i = 0 for batch in train_datagen.flow(x, batch_size=1): plt.figure(i) imgplot = plt.imshow(image.array_to_img(batch[0])) i += 1 if i % 4 == 0: break plt.show()
Построить модель
Теперь давайте построим модель. Мы будем делать три 2D CNN, используя функцию активации RELU и максимальное объединение по мере прохождения слоев.
# Initialising the CNN classifier = Sequential() # Step 1 - Convolution classifier.add(Conv2D(32, (3, 3), input_shape = (128, 128, 3), activation = 'relu')) # Step 2 - Pooling classifier.add(MaxPooling2D(pool_size = (2, 2))) # Adding a second convolutional layer classifier.add(Conv2D(32, (3, 3), activation = 'relu')) classifier.add(MaxPooling2D(pool_size = (2, 2))) # Adding a third convolutional layer classifier.add(Conv2D(64, (3, 3), activation = 'relu')) classifier.add(MaxPooling2D(pool_size = (2, 2))) # Step 4 - Flattening classifier.add(Flatten()) # Step 5 - Full connection classifier.add(Dense(units = 64, activation = 'relu')) classifier.add(Dropout(0.5)) classifier.add(Dense(units = 1, activation = 'sigmoid')) # Compiling the CNN classifier.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
Наконец, мы сглаживаем его и используем сигмовидную функцию активации. Для классификации мы будем использовать оптимизатор Адама, бинарную функцию кросс-энтропийных потерь и метрику точности.
Краткое описание модели здесь:
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 126, 126, 32) 896 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 63, 63, 32) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 61, 61, 32) 9248 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 30, 30, 32) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 28, 28, 64) 18496 _________________________________________________________________ max_pooling2d_3 (MaxPooling2 (None, 14, 14, 64) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 12544) 0 _________________________________________________________________ dense_1 (Dense) (None, 64) 802880 _________________________________________________________________ dropout_1 (Dropout) (None, 64) 0 _________________________________________________________________ dense_2 (Dense) (None, 1) 65 ================================================================= Total params: 831,585 Trainable params: 831,585 Non-trainable params: 0 _________________________________________________________________
Модель поезда
Теперь давайте обучим модель на тренировочном наборе. У нас есть 815 тренировочных изображений, 401 изображение птиц, 414 изображений нептиц.
from keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator(rescale = 1./255, shear_range = 0.2, zoom_range = 0.2, horizontal_flip = True) test_datagen = ImageDataGenerator(rescale = 1./255) training_set = train_datagen.flow_from_directory('birds_vs_not_birds/training_set', target_size = (128, 128), batch_size = 32, class_mode = 'binary') test_set = test_datagen.flow_from_directory('birds_vs_not_birds/test_set', target_size = (128, 128), batch_size = 32, class_mode = 'binary') history = classifier.fit_generator(training_set, steps_per_epoch = 8000/32, epochs = 90, validation_data = test_set, validation_steps = 2000/32, workers=12, max_q_size=100) #save the model classifier.save('my_model_fc__birds.hdf5')
Тестовая модель
Для тестирования обученной модели мы будем тестировать 100 изображений птиц и 100 изображений нептиц. Затем мы строим функцию потерь и точность.
import matplotlib.pyplot as plt loss = history.history['loss'] val_loss = history.history['val_loss'] epochs = range(len(loss)) plt.figure() plt.plot(epochs, loss, 'bo', label='Training loss') plt.plot(epochs, val_loss, 'b', label='Validation loss') plt.title('Training and validation loss') plt.legend() plt.show() test_set_2 = test_datagen.flow_from_directory('birds_vs_not_birds/test_set_2', target_size = (128, 128), batch_size = 32, class_mode = 'binary')
Как вы можете видеть, это сильно переоснащает из-за шума кривой потерь проверки. Это связано с тем, что обучающие и тестовые изображения очень похожи. Кормушка все время находится в одном и том же положении. Птица, приходящая покормиться, — это большое изменение по сравнению с нулевыми изображениями без птиц.
Давайте теперь попробуем модель на новых тестовых изображениях. Исходя из этого, мы проверим отдельные прогнозы, а также получим для этого общую точность. Этот второй тестовый набор содержит 175 изображений птиц и 325 изображений нептиц.
test_set_2 = test_datagen.flow_from_directory('birds_vs_not_birds/test_set_2', target_size = (128, 128), batch_size = 32, class_mode = 'binary')
Одиночные прогнозы
Чтобы проверить одиночные прогнозы, используйте приведенный ниже код для каждого изображения, чтобы увидеть прогноз обученной модели.
from keras.preprocessing import image import numpy as np #test_image = image.load_img('birds_vs_not_birds/test_set_2/bird/birds_test_2 (152).jpg', target_size = (128, 128)) test_image = image.load_img('birds_vs_not_birds/test_set_2/not_bird/not_bird_test_2 (115).jpg', target_size = (128, 128)) test_image = image.img_to_array(test_image) test_image = np.expand_dims(test_image, axis = 0) result = classifier.predict(test_image) training_set.class_indices if result[0][0] == 0: prediction = 'bird' else: prediction = 'not bird'
Оценить модель
Теперь давайте оценим обученную модель на всех новых тестовых данных.
scores = classifier.evaluate_generator(test_set_2,500) #500 testing images print("Accuracy = ", scores[1])
96% Точность. Неплохо для этой системы. Однако я не думаю, что это будет хорошо обобщаться на других изображениях с других кормушек для птиц с другой геометрией и расстоянием от веб-камеры.
Будущая работа
Можно использовать эту очень специфическую обученную модель, описанную в этой статье, и подключить ее к флешке Intel Movidius, используя эту обученную модель, которая захватывает изображения с веб-камеры. Веб-камера будет записывать только изображения птиц у кормушки, что исключает ложные срабатывания.
Во-вторых, методика, описанная в этой статье, является только бинарной, независимо от того, есть птица или нет. Его можно расширить до более сложной модели с несколькими классами, используя функцию активации softmax. Это можно тренировать на разных видах обычных птиц, посещающих сад, таким образом фактически определяя, какие виды птиц находятся у кормушки. Проблема на самом деле в том, что для этого нужны наземные данные. Так что, возможно, можно было бы использовать метод трансферного обучения, который уже классифицировал обычные виды птиц. Это можно расследовать. Кроме того, модель будет более общей и может быть применена к любой системе кормушки для птиц/веб-камеры. Метод, описанный в этой статье, сильно переобучает и поэтому применим только к этой системе. Его необходимо будет переобучить на новых обучающих данных, чтобы его можно было применять в других системах. Данные для обучения должны быть намного больше, используя изображения с различных фотографий кормушек для птиц, доступных в Интернете, таким образом обобщая, возможно, за счет более низкой точности.
использованная литература
Глубокое обучение с помощью Python, Франсуа Шолле, Мэннинг, 2018 г.
Захват изображения с активацией движения и классификация птиц, https://www.hackster.io/robin-cole/motion-activated-image-capture-and-classification-of-birds-6ef1ce