După prima arhitectură bazată pe CNN (AlexNet) care a câștigat competiția ImageNet 2012, fiecare arhitectură câștigătoare ulterioară folosește mai multe straturi într-o rețea neuronală profundă pentru a reduce rata de eroare. Acest lucru funcționează pentru un număr mai mic de straturi, dar atunci când creștem numărul de straturi, există o problemă comună în învățarea profundă asociată cu cea numită gradient de dispariție/explozie. Acest lucru face ca gradientul să devină 0 sau prea mare. Astfel, atunci când creștem numărul de straturi, crește și rata erorilor de antrenament și de testare.
gimp înlocuiți culoarea

Comparație între arhitectura cu 20 de straturi și 56 de straturi
În graficul de mai sus, putem observa că un CNN cu 56 de straturi oferă o rată de eroare mai mare atât pentru setul de date de instruire, cât și pentru testare decât o arhitectură CNN cu 20 de straturi. După ce au analizat mai multe despre rata de eroare, autorii au putut ajunge la concluzia că aceasta este cauzată de gradient de dispariție/explozie.
ResNet, care a fost propus în 2015 de cercetătorii de la Microsoft Research, a introdus o nouă arhitectură numită Residual Network.
Rețea reziduală: Pentru a rezolva problema gradientului de dispariție/explozie, această arhitectură a introdus conceptul numit Blocuri reziduale. În această rețea, folosim o tehnică numită sări peste conexiuni . Conexiunea de ignorare conectează activările unui strat la straturi ulterioare, omitând unele straturi între ele. Aceasta formează un bloc rezidual. Rețelele sunt realizate prin stivuirea acestor blocuri reziduale împreună.
Abordarea din spatele acestei rețele este, în loc de straturi care învață maparea de bază, permitem rețelei să se potrivească cu maparea reziduală. Deci, în loc să spunem H(x), maparea inițială , lasă rețeaua să se potrivească,
F(x) := H(x) - x which gives H(x) := F(x) + x .>

Omite conexiunea (comandă rapidă).
Avantajul adăugării acestui tip de conexiune de ignorare este că, dacă orice strat dăunează performanței arhitecturii, atunci acesta va fi omis prin regularizare. Deci, acest lucru are ca rezultat antrenarea unei rețele neuronale foarte profunde, fără problemele cauzate de gradient de dispariție/explozie. Autorii lucrării au experimentat pe 100-1000 de straturi ale setului de date CIFAR-10.
Există o abordare similară numită rețele de autostrăzi, aceste rețele folosesc, de asemenea, conexiunea de ignorare. Similar cu LSTM, aceste conexiuni skip folosesc, de asemenea, porți parametrice. Aceste porți determină cât de multă informație trece prin conexiunea de ignorare. Cu toate acestea, această arhitectură nu a oferit o precizie mai bună decât arhitectura ResNet.
Arhitectura de rețea: Această rețea folosește o arhitectură de rețea simplă cu 34 de straturi, inspirată de VGG-19, la care se adaugă apoi conexiunea de comandă rapidă. Aceste conexiuni de scurtătură transformă apoi arhitectura într-o rețea reziduală.

Arhitectura ResNet -34
Implementare: Folosind Tensorflow și Keras API, putem proiecta arhitectura ResNet (inclusiv blocuri reziduale) de la zero. Mai jos este implementarea diferitelor arhitecturi ResNet. Pentru această implementare, folosim setul de date CIFAR-10. Acest set de date conține 60.000 de imagini color 32×32 în 10 clase diferite (avioane, mașini, păsări, pisici, căprioare, câini, broaște, cai, nave și camioane) etc. Acest set de date poate fi evaluat din k epoci.seturi de date Funcția API.
Pasul 1: În primul rând, importăm modulul keras și API-urile acestuia. Aceste API-uri ajută la construirea arhitecturii modelului ResNet.
Cod: Import de biblioteci
# 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>
Pasul 2: Acum, setăm diferiți hiper parametri care sunt necesari pentru arhitectura ResNet. De asemenea, am făcut o preprocesare pe setul nostru de date pentru a-l pregăti pentru instruire.
Cod: Setarea hiperparametrilor de antrenament
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)> |
>
>
Pasul 3: În acest pas, setăm rata de învățare în funcție de numărul de epoci. Odată cu numărul de epoci, rata de învățare trebuie să fie redusă pentru a asigura o învățare mai bună.
Cod: Setarea LR pentru diferite numere de epoci
python3
înlocuiește-le pe toate
# Setting LR for different number of Epochs> def> lr_schedule(epoch):> >lr>=> 1e>->3> >if> epoch>>>> >lr>*>=> 0.5e>->3> >elif> epoch>>>> >lr>*>=> 1e>->3> >elif> epoch>>>> >lr>*>=> 1e>->2> >elif> epoch>>>> >lr>*>=> 1e>->1> >print>(>'Learning rate: '>, lr)> >return> lr> |
>
>
Pasul 4: Definiți blocul de bază ResNet care poate fi utilizat pentru definirea arhitecturii ResNet V1 și V2.
Cod: Blocul de bază 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> |
>
>
Pasul 5: Definiți arhitectura ResNet V1 care se bazează pe blocul de construcție ResNet pe care l-am definit mai sus:
Cod: Arhitectura ResNet V1
python3
operatori în programarea python
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> |
>
>
Pasul 6: Definiți arhitectura ResNet V2 care se bazează pe blocul de construcție ResNet pe care l-am definit mai sus:
Cod: Arhitectura 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> |
>
>
Pasul 7: Codul de mai jos este folosit pentru a antrena și a testa arhitectura ResNet v1 și v2 pe care am definit-o mai sus:
Cod: Funcția principală
10 la puterea lui 6
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>])> |
>
>
Rezultate și concluzii:
Pe setul de date ImageNet, autorii folosesc un ResNet cu 152 de straturi, care este de 8 ori mai adânc decât VGG19, dar are totuși mai puțini parametri. Un ansamblu de aceste ResNet-uri a generat o eroare de doar 3,7% pe setul de testare ImageNet, rezultat care a câștigat competiția ILSVRC 2015. Pe setul de date de detectare a obiectelor COCO, acesta generează, de asemenea, o îmbunătățire relativă de 28% datorită reprezentării sale foarte profunde.

Rata de eroare pe arhitectura ResNet
- Rezultatul de mai sus arată că conexiunile cu comenzi rapide ar putea rezolva problema cauzată de creșterea straturilor, deoarece pe măsură ce creștem straturile de la 18 la 34, rata de eroare pe ImageNet Validation Set scade și ea spre deosebire de rețeaua simplă.

Rata de eroare top-1 și top-5 pe ImageNet Validation Set.
- Mai jos sunt rezultatele la ImageNet Test Set. The 3,57% Rata de eroare de top 5 a ResNet a fost cea mai scăzută și, prin urmare, arhitectura ResNet a fost pe primul loc în provocarea de clasificare ImageNet în 2015.
