В предыдущем посте я говорил о классификации изображений с помощью нейронных сетей, но сегодня я хочу подробнее остановиться на этом. Итак, давайте посмотрим, как работает CNN и как вы реализуете его самостоятельно.

Данные

Прежде всего, давайте кратко рассмотрим данные, которые я использовал для этого проекта:

  • Мода MNIST: этот набор данных содержит 70 000 черно-белых изображений различных предметов одежды, от обуви и сумок до платьев и рубашек. Эти изображения имеют размеры 28x28 и отображаются в оттенках серого. (Данные можно найти здесь.)
  • Смайлики: содержит почти 2500 изображений нарисованных от руки смайликов, эти изображения имеют размер 32x32 5 различных типов смайликов. Этот набор данных был «сплющен», но его можно довольно легко снова «раздуть».

Си-Эн-Эн

Теперь вы, возможно, слышали о популярности CNN для обработки изображений или аудио, но вам также может быть интересно: «Что такое CNN вообще?»

CNN имеет 4 основных типа слоев:

  • Слои свертки: используются для извлечения характеристик из изображений (таких как границы, изменение цвета и т. д.) путем применения сверток.
  • Объединение слоев: используется для уменьшения количества характеристик, извлекаемых из сверточных слоев.
  • Слои ReLu: иногда используются для добавления нелинейности в сеть.
  • Полносвязные слои: MLP, используемый для классификации.

Первые три из этих слоев в основном используются для изучения признаков, а MLP используется для классификации (со слоем SoftMax в качестве выходного слоя). Вы можете задаться вопросом, зачем вообще использовать три слоя для того, что кажется предварительной обработкой данных. Дело в том, что эти три слоя действительнохороши для получения информации и ее обработки без потери важных частей — они способны определить, как каждый пиксель связан с другим и когда это важно сохранить, чтобы получить хороший прогноз. Другими словами, это позволяет извлекать масштабируемые функции для огромных объемов данных.

Свертка:

Теперь, как работает слой свертки? Ну, во-первых, этот слой загружается изображениями и их размерами. Итак, давайте сначала разделим наши данные на наборы для обучения и тестирования и посмотрим на их размеры:

(f_x_train, f_y_train), (f_x_test, f_y_test) = datasets.fashion_mnist.load_data()

f_x_train = f_x_train.astype("float32") / 255
f_x_test = f_x_test.astype("float32") / 255

f_x_train, f_x_val, f_y_train, f_y_val = train_test_split(f_x_train, f_y_train, test_size=0.15)
print(f_x_train.shape, f_x_val.shape, f_x_test.shape)
>>(51000, 28, 28) (9000, 28, 28) (10000, 28, 28)

Здесь, поскольку мы используем набор данных Fashion MNIST, мы видим, что это 28x28x1 (высота x ширина x канал RGB), поэтому они передаются в сеть следующим образом:

model = Sequential([
    Conv2D(64, kernel_size=(3, 3), activation='relu',padding='same', 
            input_shape=(28, 28, 1)), #image dimensions
    BatchNormalization(),
    Conv2D(64, kernel_size=(3, 3), activation='relu'),
    BatchNormalization(),
    MaxPooling2D(pool_size=(2, 2)), #Pooling Layer
    
    Flatten(), #Flattening to pass to Fully Connected Layer
    
    Dense(1024, activation='relu'),
    Dense(512, activation='relu'),
    
    Dense(10, activation='softmax') #Output Layer
])

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

Объединение:

Слои пула, как показано выше, уменьшают количество получаемых данных, вычисляя либо максимальные, либо средние значения части изображения. Это имеет двойную функциональность в случае Max Pooling, поскольку уменьшает объем работы (уменьшение размерности) и помогает с проблемами переобучения (удаление шума).

ReLu и полносвязные слои:

Наконец, данные выравниваются и передаются в MLP со слоями ReLu и выходным слоем Softmax. Давайте посмотрим на результат, который мы получили после компиляции модели и ее обучения:

#Compiling Model
adam = Adam(lr=0.001, decay=1e-6)
model.compile(optimizer=adam,
                loss='sparse_categorical_crossentropy',
                metrics=['accuracy'])
history = model.fit(f_x_train, f_y_train, validation_data=(f_x_val, f_y_val) , epochs=10, batch_size=32)
#Results
print("\nEvaluating...", flush=True)
print('Training data:', flush=True)
loss, acc = model.evaluate(f_x_train, f_y_train, verbose=1)
print("  Training : loss %.3f - acc %.3f" % (loss, acc))
print('Cross-validation data:', flush=True)
loss, acc = model.evaluate(f_x_val, f_y_val, verbose=1)
print("  Cross-val: loss %.3f - acc %.3f" % (loss, acc))
print('Test data:', flush=True)
loss, acc = model.evaluate(f_x_test, f_y_test, verbose=1)
print("  Testing  : loss %.3f - acc %.3f" % (loss, acc))
>>Evaluating...
>>Training data:
>>1594/1594 [==============================] - 12s 7ms/step - loss: >>0.0518 - accuracy: 0.9815
>>  Training : loss 0.052 - acc 0.981
>>Cross-validation data:
>>282/282 [==============================] - 2s 7ms/step - loss: >>0.3860 - accuracy: 0.9194
>>  Cross-val: loss 0.386 - acc 0.919
>>Test data:
>>313/313 [==============================] - 2s 8ms/step - loss: >>0.3981 - accuracy: 0.9189
>>  Testing  : loss 0.398 - acc 0.919

Полученные результаты:

Теперь давайте подробнее рассмотрим наши результаты:

Мода MNIST: 0,919

Смайликов: 0,964

В целом, результаты, которые мы получаем с CNN, поразительны! Они намного лучше, чем результат, полученный нами в предыдущем посте (кроме Fashion MNIST MLP). Вот результаты, полученные тогда:

LDA — мода MNIST: 0,8325 ; Смайлик: 0,6506

KNN — мода MNIST: 0,8326 ; Смайлик: 0,6532

MLP — мода MNIST: 0,9789; Эмоджи: 0,8329

Что примечательно, так это то, что классические методы, такие как LDA и KNN, кажется, прекрасно работают для базы данных Fashion, и здесь мы должны задать вопрос: действительно необходимо использовать глубокое обучение для этой проблемы? Однако для базы данных Emoji это очевидно — классические методы не достигли точности даже 0,7, и в этом случае я бы сказал, что подход глубокого обучения очень необходим.

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

Я оставлю исходник здесь, вы также сможете найти пример эмодзи: