Глубокое обучение для распознавания изображений: сверточная нейронная сеть с Tensorflow и Keras

Создание нейронной сети с нуля с помощью Python

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

Чтобы показать, как создавать, обучать и прогнозировать с помощью нейронной сети, я буду использовать Tensorflow, который вы можете легко запустить на своем Jupyter Notebook после его установки с помощью pip.

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import numpy as np
import pydot_ng as pydot

Затем я буду использовать один из наборов данных, доступных в библиотеках Keras (пакет с открытым исходным кодом, который вы можете импортировать из Tensorflow), который содержит набор изображений, на которых будет обучаться наш алгоритм. Итак, давайте загрузим и ознакомимся с нашими данными:

from keras.datasets import cifar10

#I'm dividing my data into training and test set

(x_train, y_train), (x_test, y_test) = cifar10.load_data()

x_train.shape, x_test.shape, y_train.shape, y_test.shape

Как видите, в нашем обучающем наборе x у нас есть 50000 изображений, каждое размером 32 × 32 пикселя и с 3 каналами (то же самое для тестового набора x, но только с 10000 наблюдений). С другой стороны, наши наборы y представляют собой массивы чисел от 0 до 9, соответствующих нашим классам. Итак, мы можем начать с создания вектора соответствующих классов, который позже будет назначен нашим прогнозам. Кроме того, мы также можем установить еще две переменные:

  • эпохи: количество итераций для нашей нейронной сети во время обучения.
  • batch_size: количество образцов, которые мы хотим использовать для каждой эпохи.
batch_size=32
epochs=3
class_names = ["airplane","automobile","bird","cat","deer","dog","frog","horse","ship","truck"]

Так как я сначала хочу показать процесс очень интуитивно понятным образом, я буду работать с моими изображениями, изменяя их размер с 3-х канального до 1-канального изображения (то есть от цветного к черно-белому). Таким образом мы сможем визуализировать весь процесс свертки, объединения и полного соединения (я объясню эти три шага позже).

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

Итак, давайте посмотрим на это изображение вместе со сценарием ниже (который я собираюсь объяснять шаг за шагом). На рисунке я рассмотрел простую задачу: у нас есть алфавит из четырех букв - A, B, C и D - и нашему алгоритму предлагается распознать нашу входную букву (в нашем случае «C»). С другой стороны, алгоритм, встроенный в скрипт, обращается к нашему набору данных, поэтому выходной вектор будет иметь не четыре, а десять записей. Однако основной процесс тот же.

model=tf.keras.Sequential() model.add(tf.keras.layers.Conv2D(32,kernel_size=(3,3),activation='relu',input_shape=(32,32,1))) model.add(tf.keras.layers.MaxPooling2D(pool_size=(2,2))) model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(1024,activation='relu')) model.add(tf.keras.layers.Dense(10,activation='softmax'))

Давайте объясним каждый отрывок:

  • Свертка: изображение проверяется фильтром, который может сегментировать входное изображение на более мелкие части, возвращая так называемую карту функций (точнее, она возвращает столько карт функций, сколько использованные фильтры). Чтобы получить результат этой процедуры фильтрации, нам понадобится так называемая функция активации. Функции активации сопоставляют результирующие значения, а именно, от 0 до 1 или от -1 до 1 и так далее (в зависимости от функции). В нашем случае функция ReLU просто обнуляет любое отрицательное значение;
  • Объединение: основная цель этого шага - уменьшить размер наших карт функций по всей функции (в данном случае мы использовали функцию «max»: она возвращает наивысшее значение пикселя среди исследованных);
  • Полное соединение: цель слоя «Полное соединение» - использовать эти функции для классификации входного изображения по различным классам на основе набора обучающих данных. На этом этапе, после преобразования наших изображений в форме матриц в массивы чисел, мы снова применяем функцию активации, а затем получаем в качестве окончательного результата вектор вероятностей, равный вектору классов. Действительно, используемая нами функция активации, называемая softmax, преобразует входные данные в диапазон вероятностей. Диапазон будет от 0 до 1, а сумма всех вероятностей, естественно, будет равна единице. В нашем случае, поскольку это задача множественной классификации, эта функция возвращает вероятности каждого класса, и целевой класс будет иметь наибольшую вероятность.

Основная цель нашей CNN - произвести прогноз, максимально приближенный к реальному. Кроме того, после оценки алгоритм может учиться на своих прошлых путях, повторно взвешивая некоторые параметры и минимизируя ошибки. Эта операция называется обратным распространением.

Для реализации обратного распространения ошибки необходимы еще два элемента:

  • Функция потерь: измеряет согласованность модели. Он возвращает большие значения, если подогнанные значения далеки от фактических значений. Типичная функция потерь, широко используемая в моделях линейной регрессии, - это среднеквадратичные ошибки (MSE). В нашем случае мы будем использовать функцию категориальной кроссентропии.
  • Оптимизатор: чтобы минимизировать ошибки, необходимо изменить веса, и это можно сделать с помощью класса функций, называемых функциями оптимизации. Функции оптимизации обычно вычисляют частную производную функции потерь (называемую градиентом) по весам, а веса изменяются в направлении, противоположном вычисленному градиенту. Этот цикл повторяется до тех пор, пока мы не достигнем минимумов функции потерь. В нашем примере мы будем использовать Adam Optimizer.
model.compile(loss='categorical_crossentropy', optimizer=tf.keras.optimizers.Adam(lr=0.001,decay=1e-6), metrics=['accuracy'])

Обратите внимание, что аргумент «метрики», как функция потерь, позволяет измерить производительность модели. Однако это не влияет на процесс обучения (в отличие от функции потерь).

Теперь мы можем обучить нашу модель и проверить ее на нашем тестовом наборе (всегда преобразовывая ее в одноканальный набор данных):

#I'm dividing my train and test set by 255, since I want to normalize the value of each pixel (ranging from 0 to 255)
model.fit(np.resize(x_train, (50000,32,32,1))/255.0,tf.keras.utils.to_categorical(y_train),
         batch_size=batch_size,
         shuffle=True,
         epochs=epochs,
         validation_data=(np.resize(x_test, (10000,32,32,1))/255.0,tf.keras.utils.to_categorical(y_test))
         )

Пожалуйста, простите эту модель за то, что она такая плохая (точность достигла досадного значения 10,74%), но я попросил только 3 итерации и уменьшил количество каналов. Действительно, цель этого первого подхода - просто визуализировать процесс.

keras.utils.plot_model(model,show_shapes=True)

Сначала мы применяем наши фильтры 3 × 3 к нашим входным изображениям. Эта операция возвращает 32 карты объектов размером 30 × 30 пикселей каждая. Затем мы уменьшаем размеры наших изображений с 30 × 30 до 15 × 15, используя размер фильтра пула 2 × 2 (это означает, что наш фильтр пула будет проверять четыре пикселя за раз, возвращая только один пиксель, равный максимальному значению. оценки).

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

plt.imshow(x_train[12]) 
plt.title(class_names[y_train[12:13][0][0]])

Теперь давайте снова обучим нашу нейронную сеть на трехканальных изображениях (добавив некоторые изменения):

model.add(tf.keras.layers.Conv2D(32,kernel_size=(3,3),activation='relu',input_shape=(32,32,3))) model.add(tf.keras.layers.MaxPooling2D(pool_size=(2,2))) 
#I'm adding two Dropout layers to prevent overfitting model.add(tf.keras.layers.Dropout(0.25)) model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(1024,activation='relu')) model.add(tf.keras.layers.Dropout(0.5)) model.add(tf.keras.layers.Dense(10,activation='softmax'))

Давайте скомпилируем его, учитывая все вышеизложенные соображения о функции потерь и оптимизаторе:

model.compile(loss='categorical_crossentropy', optimizer=tf.keras.optimizers.Adam(lr=0.001,decay=1e-6), metrics=['accuracy']) 
model.fit(x_train/255.0,tf.keras.utils.to_categorical(y_train), batch_size=batch_size, shuffle=True, epochs=epochs, validation_data=(x_test/255.0,tf.keras.utils.to_categorical(y_test)) )

Теперь мы можем оценить его в нашем проверочном тесте (и сделать на нем прогнозы):

predictions=model.predict(x_test) 
scores = model.evaluate(x_test / 255.0, tf.keras.utils.to_categorical(y_test))

Отлично, точность увеличена до 63,87%. Сравним некоторые прогнозы:

#I'm defining a function that plot my predicted image, with true #label as title 
def plot_pred(i,predictions_array,true_label,img):
    predictions_array,true_label,img=predictions_array[i],true_label[i:i+1],img[i]
    plt.grid(False)
    plt.title(class_names[true_label[0][0]])
    plt.xticks([])
    plt.yticks([])
    
    plt.imshow(img)
 
#I'm defining a function that plot my prediction vector, showing #whether my
#predicted value is correct (in blue) or incorrect (in red)

def plot_bar(i,predictions_array,true_label):
    predictions_array, true_label = predictions_array[i], true_label[i]
    plt.grid(False)
    plt.yticks([])
    plt.xticks(np.arange(10),class_names,rotation=40)
    
    thisplot=plt.bar(range(10),predictions_array, color='grey')
    plt.ylim([0,1])
    predicted_label=np.argmax(predictions_array)
    
    
    if predicted_label==true_label:
        color='blue'
    else:
        color='red'
    
    thisplot[predicted_label].set_color(color)

#plotting both the images

plt.figure(figsize=(15,6))
plt.subplot(1,2,1)
plot_pred(10, predictions, y_test, x_test)
plt.subplot(1,2,2)
plot_bar(10, predictions,  y_test)
plt.show()
plt.imshow(img)

Теперь давайте посмотрим, что произойдет, если прогноз неверен:

plt.figure(figsize=(15,6)) 
plt.subplot(1,2,1) 
plot_pred(20, predictions, y_test, x_test) 
plt.subplot(1,2,2) 
plot_bar(20, predictions, y_test) 
plt.show()

Хорошая особенность Tensorflow и Keras заключается в том, что вы можете создать свою «самодельную» CNN, изменяя количество / тип слоев в зависимости от доступных данных. Затем ваша модель сама по себе продолжит обратное распространение, чтобы настроить свой параметр и минимизировать ошибку.

Что ж, CNN, которую мы создали здесь, проста, а объем данных невелик, тем не менее, мы смогли создать хороший классификатор, и мы могли бы захотеть реализовать его с новыми уровнями.

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

Источник: http://scs.ryerson.ca/~aharley/vis/conv/flat.html

Ссылки:

Первоначально опубликовано на http://datasciencechalktalk.com 10 июля 2019 г.