Введение

Это короткое упражнение для классификации птиц у вашей кормушки с использованием 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