Распознавание изображений в последние годы продвинулось вперед благодаря доступности больших наборов данных и мощных графических процессоров, которые позволили обучать очень глубокие архитектуры. Симонян и др. Авторы VGG продемонстрировали, что, просто складывая больше слоев, мы можем повысить точность. До этого в 2009 Йошуа Бенжио в своей монографии Изучение глубинных архитектур для ИИ дал убедительный теоретический анализ эффективности глубоких архитектур.
В предыдущих сообщениях я продемонстрировал, как применять различные методы, включая пакетную нормализацию, отсев и увеличение данных, к сверточным нейтральным сетям. Можем ли мы построить более точные системы, просто складывая все больше и больше слоев слоев свертки-пакетной нормализации-повторения? В какой-то момент точность улучшится, но за пределами 25+ слоев точность скорее упадет.
Kaiming et al. 2015 впервые продемонстрировал проблему глубины и предложил замечательное решение, которое с тех пор позволило обучить более 2000 слоев! с возрастающей точностью.
В этом посте я расскажу об их технике и о том, как ее применять.
Во-первых, точность снижалась на многих слоях из-за исчезающих градиентов, поскольку слои уходят вглубь, градиенты становятся маленькими, что приводит к ухудшению производительности. Это не имеет ничего общего с переобучением, следовательно, бросившие школу не могут его спасти.
Конечным решением, разработанным Каймингом Хе и его коллегами из Microsoft Research Asia, было введение остаточных соединений. Это простой термин для описания соединения вывода предыдущих слоев с выводом новых слоев.
Предположим, у вас есть семиуровневая сеть. В остаточной настройке вы не только передадите выходные данные уровня 1 на уровень 2 и далее, но также добавите выходные данные уровня 1 к выходным данным уровня 2.
Обозначив каждый слой f (x)
В стандартной сети y = f (x)
Однако в остаточная сеть,
y = f (x) + x
Применяя этот принцип, авторы выиграли Imagenet 2015 и достигли новейших результатов по всем стандартным тестам компьютерного зрения. С тех пор идея была распространена на все другие области глубокого обучения, включая обработку речи и естественного языка.
Хватит базовой математики, давайте поработаем руками с кодами.
Стандартный двухслойный модуль показан ниже.
def Unit(x,filters): out = BatchNormalization()(x) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = BatchNormalization()(out) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) return out
Напомним, что в этом модуле мы передаем вход x, пропускаем его через пакетную нормализацию - relucon2d, и вывод берется через тот же стек.
Ниже представлен модуль resnet
def Unit(x,filters): res = x out = BatchNormalization()(x) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = BatchNormalization()(out) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = keras.layers.add([res,out]) return out
это выглядит очень похоже, но с одним существенным отличием: во-первых, мы сохраняем ссылку «res» на исходный ввод, а после прохождения через слои batchnorm-relu-conv добавляем вывод к остатку, для ясности это было сделано в линия
out = keras.layers.add([res,out])
Эта часть соответствует уравнению y = f (x) + x
Таким образом, мы можем построить реснет, объединив многие из этих модулей вместе.
Перед этим нам нужно немного изменить код, чтобы учесть пул.
def Unit(x,filters,pool=False): res = x if pool: x = MaxPooling2D(pool_size=(2, 2))(x) res = Conv2D(filters=filters,kernel_size=[1,1],strides=(2,2),padding="same")(res) out = BatchNormalization()(x) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = BatchNormalization()(out) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = keras.layers.add([res,out]) return out
Обратите внимание на что-то выше, когда мы объединяем, тогда размеры нашего вывода больше не будут соответствовать измерению нашего остатка, следовательно, мы не только применим объединение к входу, но остаток также будет преобразован с помощью последовательного преобразования 1 x 1. при этом фильтры будут такими же, как на выходе, а шаг 2 будет вдвое меньше размеров, как и при максимальном объединении.
Объяснив это, я представлю полный реснет.
def Unit(x,filters,pool=False): res = x if pool: x = MaxPooling2D(pool_size=(2, 2))(x) res = Conv2D(filters=filters,kernel_size=[1,1],strides=(2,2),padding="same")(res) out = BatchNormalization()(x) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = BatchNormalization()(out) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = keras.layers.add([res,out]) return out def MiniModel(input_shape): images = Input(input_shape) net = Conv2D(filters=32, kernel_size=[3, 3], strides=[1, 1], padding="same")(images) net = Unit(net,32) net = Unit(net,32) net = Unit(net,32) net = Unit(net,64,pool=True) net = Unit(net,64) net = Unit(net,64) net = Unit(net,128,pool=True) net = Unit(net,128) net = Unit(net,128) net = Unit(net, 256,pool=True) net = Unit(net, 256) net = Unit(net, 256) net = BatchNormalization()(net) net = Activation("relu")(net) net = Dropout(0.25)(net) net = AveragePooling2D(pool_size=(4,4))(net) net = Flatten()(net) net = Dense(units=10,activation="softmax")(net) model = Model(inputs=images,outputs=net) return model
Это исключает обучающий код, как вы можете видеть, ниже приведен обучающий код с эпохами, установленными на 50 эпох.
Вы можете запустить это бесплатно на GPU с Google Colab.
#import needed classes import keras from keras.datasets import cifar10 from keras.layers import Dense,Conv2D,MaxPooling2D,Flatten,AveragePooling2D,Dropout,BatchNormalization,Activation from keras.models import Model,Input from keras.optimizers import Adam from keras.callbacks import LearningRateScheduler from keras.callbacks import ModelCheckpoint from math import ceil import os from keras.preprocessing.image import ImageDataGenerator def Unit(x,filters,pool=False): res = x if pool: x = MaxPooling2D(pool_size=(2, 2))(x) res = Conv2D(filters=filters,kernel_size=[1,1],strides=(2,2),padding="same")(res) out = BatchNormalization()(x) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = BatchNormalization()(out) out = Activation("relu")(out) out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out) out = keras.layers.add([res,out]) return out #Define the model def MiniModel(input_shape): images = Input(input_shape) net = Conv2D(filters=32, kernel_size=[3, 3], strides=[1, 1], padding="same")(images) net = Unit(net,32) net = Unit(net,32) net = Unit(net,32) net = Unit(net,64,pool=True) net = Unit(net,64) net = Unit(net,64) net = Unit(net,128,pool=True) net = Unit(net,128) net = Unit(net,128) net = Unit(net, 256,pool=True) net = Unit(net, 256) net = Unit(net, 256) net = BatchNormalization()(net) net = Activation("relu")(net) net = Dropout(0.25)(net) net = AveragePooling2D(pool_size=(4,4))(net) net = Flatten()(net) net = Dense(units=10,activation="softmax")(net) model = Model(inputs=images,outputs=net) return model #load the cifar10 dataset (train_x, train_y) , (test_x, test_y) = cifar10.load_data() #normalize the data train_x = train_x.astype('float32') / 255 test_x = test_x.astype('float32') / 255 #Subtract the mean image from both train and test set train_x = train_x - train_x.mean() test_x = test_x - test_x.mean() #Divide by the standard deviation train_x = train_x / train_x.std(axis=0) test_x = test_x / test_x.std(axis=0) datagen = ImageDataGenerator(rotation_range=10, width_shift_range=5. / 32, height_shift_range=5. / 32, horizontal_flip=True) # Compute quantities required for featurewise normalization # (std, mean, and principal components if ZCA whitening is applied). datagen.fit(train_x) #Encode the labels to vectors train_y = keras.utils.to_categorical(train_y,10) test_y = keras.utils.to_categorical(test_y,10) #define a common unit input_shape = (32,32,3) model = MiniModel(input_shape) #Print a Summary of the model model.summary() #Specify the training components model.compile(optimizer=Adam(0.001),loss="categorical_crossentropy",metrics=["accuracy"]) epochs = 50 steps_per_epoch = ceil(50000/128) # Fit the model on the batches generated by datagen.flow(). model.fit_generator(datagen.flow(train_x, train_y, batch_size=128), validation_data=[test_x,test_y], epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4) #Evaluate the accuracy of the test dataset accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128) model.save("cifar10model.h5")
Надеюсь, вам это понравилось, будущие посты будут посвящены способам простого масштабирования остаточных сетей по ширине и глубине, а также способам регулировки скорости обучения.
Хорошо проведите время и не забудьте оставить несколько аплодисментов!
Вы всегда можете связаться со мной в твиттере через @johnolafenwa