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

Компьютерное зрение прошло долгий путь за эти годы и совершило значительный скачок, когда телеканал CNN под названием AlexNet достиг высочайшего уровня качества маркировки изображений в конкурсе ImageNet.

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

Здесь я буду использовать разное количество блоков VGG вместе с некоторыми хорошо известными методами регуляризации, чтобы классифицировать объект из набора данных CIFAR-10 и сравнить результаты.

CIFAR-10 - это стандартный набор данных, используемый в компьютерном зрении и глубоком обучении. Набор данных в основном предназначался для исследований в области компьютерного зрения. Набор данных состоит из 60 000 цветных фотографий 32 * 32 пикселей объектов 10 классов, таких как самолет, автомобиль, птица и т. Д. Метки классов и соответствующие им стандартные целочисленные значения перечислены ниже:

  • 0: самолет
  • 1: автомобиль
  • 2: птица
  • 3: кошка
  • 4: олень
  • 5: собака
  • 6: лягушка
  • 7: лошадь
  • 8: корабль
  • 9: грузовик

Изображения в наборе данных - это очень маленькие изображения, намного меньше типичной фотографии. CIFAR-10 - это хорошо изученный набор данных, широко используемый для тестирования алгоритмов компьютерного зрения. Проблема эффективно «решена». Достичь точности классификации 80% относительно легко. Однако, используя сверточную нейронную сеть с глубоким обучением, мы можем получить точность классификации более 90% на тестовом наборе данных.

В приведенном ниже примере загружается набор данных CIFAR-10 из API Keras и показан пример.

#load the CIFAR-10 dataset and plot a sample image 
from tensorflow import keras 
from keras.datasets import cifar10 
import matplotlib.pyplot as plt 
#load dataset 
(trainX, trainY), (testX, testY) = cifar10.load_data()
#classes array for the 10 different classes of images in the dataset
classes = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']
#summarize the loaded dataset 
print('Train: X = %s, y = %s' %(trainX.shape, trainY.shape))
print('Test: X = %s, y = %s' %(testX.shape, testY.shape))
#plot a sample image 
plt.figure()
plt.imshow(trainX[0])
plt.grid(False)
plt.xlabel(classes[trainY[0].item()])
plt.show()

Мы видим, что в наборе поездов 50 000 примеров, а в наборе тестов 10 000 примеров. Позже мы разделим набор тестов на 2 разных подмножества, набор для проверки и набор тестов, которые мы будем использовать для проверки нашей модели во время и после обучения последовательно.

Train: X = (50000, 32, 32, 3), y = (50000, 1)
Test: X = (10000, 32, 32, 3), y = (10000, 1)

Образец изображения также строится из набора поездов.

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

После загрузки набора данных мы можем перейти к этапам предварительной обработки данных.

Мы знаем, что в наборе данных CIFAR-10 есть 10 классов и что классы представлены как уникальные целые числа. Поэтому мы можем использовать одно горячее кодирование для элемента класса каждого образца, преобразуя целое число в двоичный вектор из 10 элементов с 1 для индекса значения класса. Мы можем использовать служебную функцию to_categorical, предоставляемую Keras, для одного горячего кодирования.

#one hot encode the target 
trainY = keras.utils.to_categorical(trainY)
testY = keras.utils.to_categorical(testY)

Функция load_dataset () реализует эти шаги и может использоваться для загрузки данных и горячего кодирования элементов класса.

def load_dataset():
    #load dataset
    (trainX, trainY),(testX, testY) = cifar10.load_data()
    #one hot encode the target 
    trainY = keras.utils.to_categorical(trainY)
    testY = keras.utils.to_categorical(testY)
    return trainX, trainY, testX, testY

Мы знаем, что значения пикселей изображения в наборе данных представляют собой целое число без знака в диапазоне от 0 до 255. Нам необходимо нормализовать значения пикселей, например масштабируйте их до диапазона [0,1]. Это предполагает деление значения пикселя на максимальное значение.

train_norm = train_norm / 255.0
test_norm = test_norm / 255.0
valid_norm = valid_norm / 255.0

Функция normalize () реализует это:

def normalize(train, test,valid): 
    #change the values from unsigned int to float
    train = train.astype('float32')
    test = test.astype('float32')
    valid = valid.astype('float32')
    #Normalize the pixel values by dividing by 255
    train_norm = train / 255.0
    test_norm = test/ 255.0
    valid_norm = valid / 255.0
    
    return train_norm, test_norm, valid_norm

Нам также необходимо определить набор для проверки нашей модели во время обучения. Функция validation_split () реализует это:

def validation_split(testX, testY, valid_X, valid_Y, v_split):
    index_of_validation = int(v_split * len(testX))
    valid_X.extend(testX[-index_of_validation:])
    valid_Y.extend(testY[-index_of_validation:])
    trainX = trainX[:-index_of_validation]
    trainY = trainY[:-index_of_validation]
    return testX, testY, np.asarray(valid_X), np.asarray(valid_Y)

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

Определите модель нейронной сети

После предварительной обработки магов нам нужен способ определения нашей модели нейронной сети. Здесь мы попробуем разные версии моделей VGG, чтобы проверить их точность на наборе данных CIFAR-10. Функцию define_model () можно вызвать для получения модели CNN.

#define the CNN model 
def define_model():
    #............
    #............
    return model

Оценить модель

После того, как модель была определена, нам нужно подобрать модель и оценить ее. Мы будем использовать набор поездов для обучения модели и набор проверки для расчета потерь и точности во время процесса обучения. Позже модель будет оценена после обучения на отдельном тестовом наборе.

# fit model 
history = model.fit(trainX,trainY, epochs = 50, batch_size = 64,    validation_data = (testX, testY), verbose = 1)

Как только модель подходит, мы можем оценить ее непосредственно на проверочном наборе.

# evaluate model
_, acc = model.evaluate(validX, validY, verbose=0)

Показать результаты

После того, как модель будет оценена, мы можем представить результаты.

Необходимо представить два ключевых аспекта: диагностику обучающего поведения модели во время обучения и оценку производительности модели.

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

Мы создадим единую фигуру с двумя частями графика, один для потерь, а другой для точности. Синие линии будут указывать на производительность модели в наборе обучающих данных, а оранжевые линии - на производительность на удерживаемом наборе тестовых данных. Функция summarize_diagnostics () ниже создает и показывает этот график с учетом собранных историй обучения. Сюжет сохраняется в файл, а именно в файл с тем же именем, что и сценарий, с расширением «png».

# plot diagnostic learning curves
def summarize_diagnostics(history):
    # plot loss
    plt.subplot(211)
    plt.title('Cross Entropy Loss')
    plt.plot(history.history['loss'], color='blue', label='train')
    plt.plot(history.history['val_loss'], color='orange',  label='test')
    # plot accuracy
    plt.subplot(212)
    plt.title('Classification Accuracy')
    plt.plot(history.history['accuracy'], color='blue', label='train')
    plt.plot(history.history['val_accuracy'], color='orange', label='test')
    plt.show()
    # save plot to file
    filename = sys.argv[0].split('/')[-1]
    plt.savefig(filename + '_plot.png')
    plt.close()

Мы также можем распечатать точность классификации модели как:

print(‘> %.3f’ % (acc * 100.0))

Теперь нам понадобится функция, которая будет управлять процессом обучения и тестирования. Функция test_model (), определенная ниже, выполняет эту операцию и может использоваться для запуска оценки данной модели:

# run all the defined functions for evaluating a model
def test_model():
 # load dataset
 train_X, train_Y, testX, testY = load_dataset()
 #get validation set 
 valid_X = []
 valid_Y = []
 trainX, trainY, validX, validY = validation_split(train_X, train_Y, valid_X, valid_Y,v_split=0.1)
 
 # normalize the data
 trainX, testX,validX = normalize(trainX, testX,validX)
 # define model
 model = define_model()
 # fit model
 history = model.fit(trainX, trainY, epochs=50, batch_size=64, validation_data=(validX, validY), verbose=1)
 # evaluate model
 _, acc = model.evaluate(testX, testY, verbose=0)
 print(‘> %.3f’ % (acc * 100.0))
 # learning curves
 summarize_diagnostics(history)

Как только мы вызовем функцию test_model (), модель будет обучена и оценена.

Однако мы еще не определили правильную модель CNN, поэтому сценарий еще не может быть запущен.

Как разработать базовую модель

Базовая модель устанавливает минимальную производительность модели, с которой можно сравнивать все наши другие модели.

Мы будем использовать разные версии моделей VGG. Архитектура включает наложение сверточных слоев с небольшими фильтрами 3 * 3, за которыми следует максимальный уровень объединения. Вместе эти уровни образуют блок, и эти блоки могут повторяться, где количество фильтров в каждом блоке увеличивается с глубиной сети, например 32, 64, 128, 256 для первых четырех блоков модели. На сверточных слоях используется такое же заполнение, чтобы высота и ширина выходного объекта соответствовали входным.

Мы можем исследовать эту архитектуру в задаче CIFAR-10 и сравнить модель с этой архитектурой с 1, 2, 3 и 4 блоками.

Каждый уровень будет использовать функцию активации ReLU и инициализацию веса «he_uniform», что обычно является наилучшей практикой. Например, двухблочная архитектура в стиле VGG может быть определена в Keras следующим образом:

#example of a 2 block VGG architecture 
model = Sequential()
model.add(Conv2D(32,(3,3), activation = 'relu', kernel_initializer = 'he_uniform', padding = 'same', input_shape = (32,32,3)))
model.add(Conv2D(32,(3,3), activation = 'relu', kernel_initializer =  'he_uniform', padding = 'same'))
model.add(MaxPooling2D((2,2)))
model.add(Conv2D(64,(3,3), activation = 'relu', kernel_initializer = 'he_uniform', padding = 'same'))
model.add(Conv2D(64,(3,3), activation = 'relu', kernel_initializer = 'he_uniform', padding = 'same'))
model.add(MaxPooling2D((2,2)))
.....

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

#example output part of the model 
model.add(Flatten())
model.add(Dense(128, activation = 'relu', kernel_initializer =  'he_uniform'))
model.add(Dense(10, activation = 'softmax'))

Модель будет оптимизирована с использованием стохастического градиентного спуска. Мы будем использовать скорость обучения 0,001 и импульс 0,9. Модель оптимизирует категориальную функцию кросс-энтропийных потерь, необходимую для многоклассовой классификации, и будет контролировать точность классификации.

#compile model 
opt = SGD(lr = 0.001, momentum = 0.9)
model.compile(optimizer = opt, loss = ‘categorical_crossentropy’, metrics = [‘accuracy’])
return model

Теперь у нас достаточно элементов для определения наших базовых моделей в стиле VGG. У нас будут разные архитектуры моделей с модулями 1,2 и 3 VGG, что требует, чтобы мы определяли разные отдельные версии функций define_model, которые представлены ниже.

Мы по очереди рассмотрим каждую функцию define_model () и оценку результирующего теста.

1 блок VGG

Функция define_model () для одного блока VGG приведена ниже:

При запуске модели точность классификации печатается на наборе тестовых данных.

В этом случае мы видим, что модель достигла точности классификации около 67%.

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

2 БЛОКА VGG

Функция define_model () для двух блоков VGG показана ниже:

При запуске модели печатается точность классификации, которая немного лучше, чем у нашего блока 1-VGG с почти 71,5%, а также отображается потеря перекрестной энтропии и точность классификации в наборе обучающих данных и наборе тестовых данных.

Как и в блоке 1-VGG, модель переоснащается на обучающем наборе данных. Потери перекрестной энтропии в тестовом наборе данных уменьшаются примерно до 15 эпох, затем они начинают увеличиваться, тогда как потери в обучающем наборе непрерывно снижаются. График точности классификации также следует аналогичной тенденции.

3 БЛОКА VGG

Функция define_model () для 3 БЛОКОВ VGG показана ниже:

Опять же, модель улучшается, но не значительно. Точность классификации на проверочном наборе составляет чуть более 74%, в то время как графики перекрестных энтропийных потерь и точности классификации демонстрируют ту же тенденцию, что и для блоков 1 и 2 VGG.

4 БЛОКА VGG

Когда мы реализуем модель с 4 блоками VGG, не было значительного изменения точности модели по сравнению с моделью с 3 блоками VGG. Тем не менее, модель явно переоснащена по сравнению с моделью с 3 блоками VGG, о чем свидетельствует разница в кросс-энтропийных потерях на наборе данных для обучения и тестирования. Итак, мы будем использовать модель с 3 блоками VGG в качестве нашей базовой модели.

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

Регуляризация отсева

Согласно Википедии -
Термин выпадение относится к отсеву единиц (как скрытых, так и видимых) в нейронной сети.

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

Здесь мы будем использовать фиксированную вероятность выпадения, равную 0,2 после каждого блока VGG, что означает, что 20% узлов будут проигнорированы и только 80% будут сохранены.

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

Функция define_model () для 3 блоков VGG с выпадением 0,2 приведена ниже:

Когда модель обучена для 100 эпох, мы достигаем точности классификации около 81,5%, что значительно выше, чем наша базовая модель VGG из трех блоков. Изучая графики перекрестной энтропии и точности классификации, мы видим, что модель начинается с подгонки обучающего набора данных к более поздним эпохам. Точность классификации на проверочном наборе почти постоянна и составляет около 80%, в то время как точность обучающего набора непрерывно снижается.

Когда мы используем переменную вероятность выпадения 0,2, 0,3, 0,4 для последовательных блоков VGG соответственно и выпадение 0,5 на плотном слое, точность классификации на тестовом наборе увеличивается примерно до 83,5%. Также уменьшается чрезмерная подгонка модели к обучающей выборке.

Увеличение данных

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

Для дальнейшего развития нашей модели мы можем использовать дополнение данных к нашей существующей модели, содержащей 3 блока VGG с переменным отсевом.

Увеличение данных может быть реализовано в Keras с помощью класса ImageDataGenerator.

#create data generator
datagen = ImageDataGenerator(width_shift_range = 0.1, height_shift_range = 0.1, horizontal_flip = True)
#iterator
train = datagen.flow(trainX, trainY, batch_size = 64)

Мы можем передать итератор в функцию model.fit_generator () после определения количества пакетов за одну эпоху.

steps = int(trainX.shape[0] / 64)
#fit model
history = model.fit_generator(train, steps_per_epoch = steps, epochs = 200, validation_data=(validX, validY), verbose=1)

После обучения до 200 эпох мы видим, что точность тестового набора данных немного снизилась до 83%, но модель хорошо обобщает, о чем свидетельствует очень меньшая разница между точностью обучения и точностью проверки на обоих графиках. . Даже до 200 эпох потери кросс-энтропии постоянно уменьшаются. Дальнейшее обучение модели, возможно, до 400–500 эпох, безусловно, улучшит модель.

Пакетная нормализация

Затем мы добавляем пакетную нормализацию, надеясь стабилизировать сеть и сократить время обучения. Функция define_model () после добавления пакетной нормализации приведена ниже:

Когда мы обучаем модель для 400 эпох, мы получаем точность классификации на тестовом наборе данных 88%. Кроме того, модель довольно хорошо обобщает, как показано на графике кросс-энтропийных потерь и на последующих графиках точности классификации.

Окончательная реализация модели нейронной сети со всей регуляризацией представлена ​​ниже:

Заключение

Мы исследовали различные подходы к расширению блоков VGG для классификации изображений. Окончательная модель хорошо обучается, и даже после 400 эпох потери кросс-энтропии, кажется, уменьшаются. Мы все еще можем оптимизировать нашу модель, изменяя различные гиперпараметры, такие как скорость обучения, количество эпох и т. Д. Мы могли бы получить лучшую модель, изменив оптимизатор на что-то вроде Adam, RMSprop или Adagrad. CIFAR-10 - это решенная проблема, мы можем найти множество различных реализаций для этого набора данных в Интернете. Это всего лишь введение в сверточную нейронную сеть и несколько методов оптимизации, которые мы можем реализовать для повышения точности модели.