logo

Залишкові мережі (ResNet) – глибоке навчання

Після першої архітектури на основі CNN (AlexNet), яка виграла конкурс ImageNet 2012, кожна наступна архітектура-переможець використовує більше рівнів глибокої нейронної мережі, щоб зменшити рівень помилок. Це працює для меншої кількості шарів, але коли ми збільшуємо кількість шарів, виникає загальна проблема глибокого навчання, пов’язана з градієнтом, що зникає/розривається. Це призводить до того, що градієнт стає 0 або занадто великим. Таким чином, коли ми збільшуємо кількість шарів, рівень помилок навчання та тестування також зростає.

Порівняння 20-рівневої та 56-рівневої архітектури



На наведеному вище графіку ми можемо спостерігати, що 56-рівневий CNN дає більший рівень помилок як для навчального, так і для тестового набору даних, ніж 20-рівнева архітектура CNN. Проаналізувавши більше частоти помилок, автори змогли дійти висновку, що це спричинено зникаючим/вибухаючим градієнтом.
ResNet, який був запропонований у 2015 році дослідниками Microsoft Research, представив нову архітектуру під назвою Residual Network.

Залишкова мережа: Щоб вирішити проблему градієнта, що зникає/вибухає, ця архітектура представила концепцію під назвою «Залишкові блоки». У цій мережі ми використовуємо техніку під назвою пропуск з'єднань . З’єднання пропуску з’єднує активації шару з наступними шарами, пропускаючи деякі шари між ними. Це утворює залишковий блок. Resnets виготовляються шляхом складання цих залишкових блоків разом.
Підхід, що лежить в основі цієї мережі, полягає в тому, що замість того, щоб рівні вивчати базове відображення, ми дозволяємо мережі відповідати залишковому відображенню. Отже, замість H(x), скажімо, початкове відображення , нехай мережа підходить,

рядок до цілого
 F(x) := H(x) - x  which gives H(x) := F(x) + x .>

Пропустити (швидке) підключення



Перевага додавання цього типу пропуску підключення полягає в тому, що якщо будь-який рівень зашкодить продуктивності архітектури, він буде пропущений регуляризацією. Таким чином, це призводить до навчання дуже глибокої нейронної мережі без проблем, викликаних зникненням/вибухом градієнта. Автори статті експериментували на 100-1000 шарах набору даних CIFAR-10.
Існує подібний підхід, який називається магістральними мережами, ці мережі також використовують пропуск підключення. Подібно до LSTM, ці з’єднання пропуску також використовують параметричні ворота. Ці ворота визначають, скільки інформації проходить через з'єднання пропуску. Однак ця архітектура не забезпечила кращої точності, ніж архітектура ResNet.

Архітектура мережі: Ця мережа використовує 34-рівневу звичайну мережеву архітектуру, натхненну VGG-19, до якої потім додається швидке підключення. Потім ці швидкі з’єднання перетворюють архітектуру на залишкову мережу.

Архітектура ResNet -34



Реалізація: Використовуючи API Tensorflow і Keras, ми можемо розробляти архітектуру ResNet (включаючи залишкові блоки) з нуля. Нижче наведено реалізацію різної архітектури ResNet. Для цього ми використовуємо набір даних CIFAR-10. Цей набір даних містить 60 000 кольорових зображень 32×32 у 10 різних класах (літаки, автомобілі, птахи, коти, олені, собаки, жаби, коні, кораблі та вантажівки) тощо. Цей набір даних можна оцінити за k eras.datasets Функція API.

Крок 1: Спочатку ми імпортуємо модуль keras і його API. Ці API допомагають у створенні архітектури моделі ResNet.

код: Імпорт бібліотек

# Import Keras modules and its important APIs import keras from keras.layers import Dense, Conv2D, BatchNormalization, Activation from keras.layers import AveragePooling2D, Input, Flatten from keras.optimizers import Adam from keras.callbacks import ModelCheckpoint, LearningRateScheduler from keras.callbacks import ReduceLROnPlateau from keras.preprocessing.image import ImageDataGenerator from keras.regularizers import l2 from keras import backend as K from keras.models import Model from keras.datasets import cifar10 import numpy as np import os>

Крок 2: Тепер ми встановлюємо різні гіперпараметри, необхідні для архітектури ResNet. Ми також провели попередню обробку нашого набору даних, щоб підготувати його до навчання.

код: Налаштування тренувальних гіперпараметрів

python3




# Setting Training Hyperparameters> batch_size>=> 32> # original ResNet paper uses batch_size = 128 for training> epochs>=> 200> data_augmentation>=> True> num_classes>=> 10> > # Data Preprocessing> subtract_pixel_mean>=> True> n>=> 3> > # Select ResNet Version> version>=> 1> > # Computed depth of> if> version>=>=> 1>:> >depth>=> n>*> 6> +> 2> elif> version>=>=> 2>:> >depth>=> n>*> 9> +> 2> > # Model name, depth and version> model_type>=> 'ResNet % dv % d'> %> (depth, version)> > # Load the CIFAR-10 data.> (x_train, y_train), (x_test, y_test)>=> cifar10.load_data()> > # Input image dimensions.> input_shape>=> x_train.shape[>1>:]> > # Normalize data.> x_train>=> x_train.astype(>'float32'>)>/> 255> x_test>=> x_test.astype(>'float32'>)>/> 255> > # If subtract pixel mean is enabled> if> subtract_pixel_mean:> >x_train_mean>=> np.mean(x_train, axis>=> 0>)> >x_train>->=> x_train_mean> >x_test>->=> x_train_mean> > # Print Training and Test Samples> print>(>'x_train shape:'>, x_train.shape)> print>(x_train.shape[>0>],>'train samples'>)> print>(x_test.shape[>0>],>'test samples'>)> print>(>'y_train shape:'>, y_train.shape)> > # Convert class vectors to binary class matrices.> y_train>=> keras.utils.to_categorical(y_train, num_classes)> y_test>=> keras.utils.to_categorical(y_test, num_classes)>

>

>

крок 3: На цьому кроці ми встановлюємо швидкість навчання відповідно до кількості епох. Зі збільшенням кількості епох швидкість навчання повинна бути зменшена, щоб забезпечити краще навчання.

код: Налаштування LR для різної кількості епох

python3


10 1 мільйон



# Setting LR for different number of Epochs> def> lr_schedule(epoch):> >lr>=> 1e>->3> >if> epoch>>180>:> >lr>*>=> 0.5e>->3> >elif> epoch>>160>:> >lr>*>=> 1e>->3> >elif> epoch>>120>:> >lr>*>=> 1e>->2> >elif> epoch>>80>:> >lr>*>=> 1e>->1> >print>(>'Learning rate: '>, lr)> >return> lr>

>

>

крок 4: Визначте базовий будівельний блок ResNet, який можна використовувати для визначення архітектури ResNet V1 і V2.

об’єкт java в json

код: Базовий будівельний блок ResNet

python3




# Basic ResNet Building Block> > > def> resnet_layer(inputs,> >num_filters>=>16>,> >kernel_size>=>3>,> >strides>=>1>,> >activation>=>'relu'>,> >batch_normalization>=>True>,> >conv>=>Conv2D(num_filters,> >kernel_size>=>kernel_size,> >strides>=>strides,> >padding>=>'same'>,> >kernel_initializer>=>'he_normal'>,> >kernel_regularizer>=>l2(>1e>->4>))> > >x>=>inputs> >if> conv_first:> >x>=> conv(x)> >if> batch_normalization:> >x>=> BatchNormalization()(x)> >if> activation>is> not> None>:> >x>=> Activation(activation)(x)> >else>:> >if> batch_normalization:> >x>=> BatchNormalization()(x)> >if> activation>is> not> None>:> >x>=> Activation(activation)(x)> >x>=> conv(x)> >return> x>

>

>

крок 5: Визначте архітектуру ResNet V1, яка базується на будівельному блоці ResNet, який ми визначили вище:

код: Архітектура ResNet V1

python3


рядок внутр



def> resnet_v1(input_shape, depth, num_classes>=>10>):> > >if> (depth>-> 2>)>%> 6> !>=> 0>:> >raise> ValueError(>'depth should be 6n + 2 (eg 20, 32, 44 in [a])'>)> ># Start model definition.> >num_filters>=> 16> >num_res_blocks>=> int>((depth>-> 2>)>/> 6>)> > >inputs>=> Input>(shape>=>input_shape)> >x>=> resnet_layer(inputs>=>inputs)> ># Instantiate the stack of residual units> >for> stack>in> range>(>3>):> >for> res_block>in> range>(num_res_blocks):> >strides>=> 1> >if> stack & gt> >0> and> res_block>=>=> 0>:># first layer but not first stack> >strides>=> 2> # downsample> >y>=> resnet_layer(inputs>=>x,> >num_filters>=>num_filters,> >strides>=>strides)> >y>=> resnet_layer(inputs>=>y,> >num_filters>=>num_filters,> >activation>=>None>)> >if> stack & gt> >0> and> res_block>=>=> 0>:># first layer but not first stack> ># linear projection residual shortcut connection to match> ># changed dims> >x>=> resnet_layer(inputs>=>x,> >num_filters>=>num_filters,> >kernel_size>=>1>,> >strides>=>strides,> >activation>=>None>,> >batch_normalization>=>False>)> >x>=> keras.layers.add([x, y])> >x>=> Activation(>'relu'>)(x)> >num_filters>*>=> 2> > ># Add classifier on top.> ># v1 does not use BN after last shortcut connection-ReLU> >x>=> AveragePooling2D(pool_size>=>8>)(x)> >y>=> Flatten()(x)> >outputs>=> Dense(num_classes,> >activation>=>'softmax'>,> >kernel_initializer>=>'he_normal'>)(y)> > ># Instantiate model.> >model>=> Model(inputs>=>inputs, outputs>=>outputs)> >return> model>

>

>

Крок 6: Визначте архітектуру ResNet V2, яка базується на будівельному блоці ResNet, який ми визначили вище:

код: Архітектура ResNet V2

python3




# ResNet V2 architecture> def> resnet_v2(input_shape, depth, num_classes>=>10>):> >if> (depth>-> 2>)>%> 9> !>=> 0>:> >raise> ValueError(>'depth should be 9n + 2 (eg 56 or 110 in [b])'>)> ># Start model definition.> >num_filters_in>=> 16> >num_res_blocks>=> int>((depth>-> 2>)>/> 9>)> > >inputs>=> Input>(shape>=>input_shape)> ># v2 performs Conv2D with BN-ReLU on input before splitting into 2 paths> >x>=> resnet_layer(inputs>=>inputs,> >num_filters>=>num_filters_in,> >conv_first>=>True>)> > ># Instantiate the stack of residual units> >for> stage>in> range>(>3>):> >for> res_block>in> range>(num_res_blocks):> >activation>=> 'relu'> >batch_normalization>=> True> >strides>=> 1> >if> stage>=>=> 0>:> >num_filters_out>=> num_filters_in>*> 4> >if> res_block>=>=> 0>:># first layer and first stage> >activation>=> None> >batch_normalization>=> False> >else>:> >num_filters_out>=> num_filters_in>*> 2> >if> res_block>=>=> 0>:># first layer but not first stage> >strides>=> 2> # downsample> > ># bottleneck residual unit> >y>=> resnet_layer(inputs>=>x,> >num_filters>=>num_filters_in,> >kernel_size>=>1>,> >strides>=>strides,> >activation>=>activation,> >batch_normalization>=>batch_normalization,> >conv_first>=>False>)> >y>=> resnet_layer(inputs>=>y,> >num_filters>=>num_filters_in,> >conv_first>=>False>)> >y>=> resnet_layer(inputs>=>y,> >num_filters>=>num_filters_out,> >kernel_size>=>1>,> >conv_first>=>False>)> >if> res_block>=>=> 0>:> ># linear projection residual shortcut connection to match> ># changed dims> >x>=> resnet_layer(inputs>=>x,> >num_filters>=>num_filters_out,> >kernel_size>=>1>,> >strides>=>strides,> >activation>=>None>,> >batch_normalization>=>False>)> >x>=> keras.layers.add([x, y])> > >num_filters_in>=> num_filters_out> > ># Add classifier on top.> ># v2 has BN-ReLU before Pooling> >x>=> BatchNormalization()(x)> >x>=> Activation(>'relu'>)(x)> >x>=> AveragePooling2D(pool_size>=>8>)(x)> >y>=> Flatten()(x)> >outputs>=> Dense(num_classes,> >activation>=>'softmax'>,> >kernel_initializer>=>'he_normal'>)(y)> > ># Instantiate model.> >model>=> Model(inputs>=>inputs, outputs>=>outputs)> >return> model>

>

отримати довжину масиву в c
>

Крок 7: Наведений нижче код використовується для навчання та тестування архітектури ResNet v1 і v2, яку ми визначили вище:

Код: Основна функція

python3




# Main function> if> version>=>=> 2>:> >model>=> resnet_v2(input_shape>=> input_shape, depth>=> depth)> else>:> >model>=> resnet_v1(input_shape>=> input_shape, depth>=> depth)> > model.>compile>(loss>=>'categorical_crossentropy'>,> >optimizer>=> Adam(learning_rate>=> lr_schedule(>0>)),> >metrics>=>[>'accuracy'>])> model.summary()> print>(model_type)> > # Prepare model saving directory.> save_dir>=> os.path.join(os.getcwd(),>'saved_models'>)> model_name>=> 'cifar10_% s_model.{epoch:03d}.h5'> %> model_type> if> not> os.path.isdir(save_dir):> >os.makedirs(save_dir)> filepath>=> os.path.join(save_dir, model_name)> > # Prepare callbacks for model saving and for learning rate adjustment.> checkpoint>=> ModelCheckpoint(filepath>=> filepath,> >monitor>=>'val_acc'>,> >verbose>=> 1>,> >save_best_only>=> True>)> > lr_scheduler>=> LearningRateScheduler(lr_schedule)> > lr_reducer>=> ReduceLROnPlateau(factor>=> np.sqrt(>0.1>),> >cooldown>=> 0>,> >patience>=> 5>,> >min_lr>=> 0.5e>->6>)> > callbacks>=> [checkpoint, lr_reducer, lr_scheduler]> > # Run training, with or without data augmentation.> if> not> data_augmentation:> >print>(>'Not using data augmentation.'>)> >model.fit(x_train, y_train,> >batch_size>=> batch_size,> >epochs>=> epochs,> >validation_data>=>(x_test, y_test),> >shuffle>=> True>,> >callbacks>=> callbacks)> else>:> >print>(>'Using real-time data augmentation.'>)> ># This will do preprocessing and realtime data augmentation:> >datagen>=> ImageDataGenerator(> ># set input mean to 0 over the dataset> >featurewise_center>=> False>,> ># set each sample mean to 0> >samplewise_center>=> False>,> ># divide inputs by std of dataset> >featurewise_std_normalization>=> False>,> ># divide each input by its std> >samplewise_std_normalization>=> False>,> ># apply ZCA whitening> >zca_whitening>=> False>,> ># epsilon for ZCA whitening> >zca_epsilon>=> 1e>->06>,> ># randomly rotate images in the range (deg 0 to 180)> >rotation_range>=> 0>,> ># randomly shift images horizontally> >width_shift_range>=> 0.1>,> ># randomly shift images vertically> >height_shift_range>=> 0.1>,> ># set range for random shear> >shear_range>=> 0.>,> ># set range for random zoom> >zoom_range>=> 0.>,> ># set range for random channel shifts> >channel_shift_range>=> 0.>,> ># set mode for filling points outside the input boundaries> >fill_mode>=>'nearest'>,> ># value used for fill_mode = 'constant'> >cval>=> 0.>,> ># randomly flip images> >horizontal_flip>=> True>,> ># randomly flip images> >vertical_flip>=> False>,> ># set rescaling factor (applied before any other transformation)> >rescale>=> None>,> ># set function that will be applied on each input> >preprocessing_function>=> None>,> ># image data format, either 'channels_first' or 'channels_last'> >data_format>=> None>,> ># fraction of images reserved for validation (strictly between 0 and 1)> >validation_split>=> 0.0>)> > ># Compute quantities required for featurewise normalization> ># (std, mean, and principal components if ZCA whitening is applied).> >datagen.fit(x_train)> > ># Fit the model on the batches generated by datagen.flow().> >model.fit_generator(datagen.flow(x_train, y_train, batch_size>=> batch_size),> >validation_data>=>(x_test, y_test),> >epochs>=> epochs, verbose>=> 1>, workers>=> 4>,> >callbacks>=> callbacks)> > # Score trained model.> scores>=> model.evaluate(x_test, y_test, verbose>=> 1>)> print>(>'Test loss:'>, scores[>0>])> print>(>'Test accuracy:'>, scores[>1>])>

>

>

Результати та висновок:
У наборі даних ImageNet автори використовують 152-шаровий ResNet, який у 8 разів глибший, ніж VGG19, але все ще має менше параметрів. Ансамбль цих ResNet генерував помилку лише в 3,7% на тестовому наборі ImageNet, результат, який переміг у конкурсі ILSVRC 2015. У наборі даних виявлення об’єктів COCO він також генерує відносне покращення на 28% завдяки дуже глибокому представленню.

Рівень помилок на архітектурі ResNet

  • Наведений вище результат показує, що швидкі підключення можуть вирішити проблему, спричинену збільшенням шарів, тому що зі збільшенням шарів з 18 до 34 частота помилок у ImageNet Validation Set також зменшується, на відміну від звичайної мережі.

топ-1 і топ-5 Частота помилок у наборі перевірки ImageNet.

  • Нижче наведено результати тестового набору ImageNet. The 3,57% Рівень помилок у топ-5 ResNet був найнижчим, і тому архітектура ResNet посіла перше місце в конкурсі класифікації ImageNet у 2015 році.