logo

Residuele netwerken (ResNet) – Deep Learning

Na de eerste op CNN gebaseerde architectuur (AlexNet) die de ImageNet 2012-wedstrijd won, gebruikt elke volgende winnende architectuur meer lagen in een diep neuraal netwerk om het foutenpercentage te verminderen. Dit werkt voor een kleiner aantal lagen, maar als we het aantal lagen vergroten, is er een veelvoorkomend probleem bij diep leren dat verband houdt met het zogenaamde verdwijnen/exploderen-gradiënt. Hierdoor wordt de gradiënt 0 of te groot. Dus als we het aantal lagen vergroten, neemt ook het aantal trainings- en testfouten toe.

Vergelijking van architectuur met 20 lagen en 56 lagen



In de bovenstaande grafiek kunnen we zien dat een 56-laags CNN een hoger foutenpercentage geeft op zowel trainings- als testdatasets dan een 20-laags CNN-architectuur. Na meer analyse van het foutenpercentage konden de auteurs tot de conclusie komen dat dit wordt veroorzaakt door een verdwijnende/exploderende gradiënt.
ResNet, dat in 2015 werd voorgesteld door onderzoekers van Microsoft Research, introduceerde een nieuwe architectuur genaamd Residual Network.

Resterend netwerk: Om het probleem van de verdwijnende/exploderende gradiënt op te lossen, introduceerde deze architectuur het concept genaamd Residual Blocks. In dit netwerk gebruiken we een techniek genaamd verbindingen overslaan . De skip-verbinding verbindt activeringen van een laag met verdere lagen door enkele lagen daartussen over te slaan. Dit vormt een restblok. Resnets worden gemaakt door deze restblokken op elkaar te stapelen.
De aanpak achter dit netwerk is dat in plaats van dat lagen de onderliggende mapping leren, we het netwerk laten passen in de resterende mapping. Dus, in plaats van bijvoorbeeld H(x), initiële mapping , laat het netwerk passen,

 F(x) := H(x) - x  which gives H(x) := F(x) + x .>

Sla (snelkoppeling) verbinding over



Het voordeel van het toevoegen van dit type skip-verbinding is dat als een laag de prestaties van de architectuur schaadt, deze door regularisatie wordt overgeslagen. Dit resulteert dus in het trainen van een zeer diep neuraal netwerk zonder de problemen veroorzaakt door een verdwijnende/exploderende gradiënt. De auteurs van het artikel experimenteerden met 100 tot 1000 lagen van de CIFAR-10-dataset.
Er is een soortgelijke aanpak genaamd snelwegnetwerken, deze netwerken maken ook gebruik van skip-verbindingen. Net als bij LSTM gebruiken deze skip-verbindingen ook parametrische poorten. Deze poorten bepalen hoeveel informatie door de skip-verbinding gaat. Deze architectuur heeft echter geen betere nauwkeurigheid opgeleverd dan de ResNet-architectuur.

Netwerk architectuur: Dit netwerk maakt gebruik van een 34-laags eenvoudige netwerkarchitectuur geïnspireerd op VGG-19, waarin vervolgens de snelkoppeling wordt toegevoegd. Deze snelkoppelingen zetten de architectuur vervolgens om in een restnetwerk.

ResNet -34-architectuur



Implementatie: Met behulp van de Tensorflow en Keras API kunnen we de ResNet-architectuur (inclusief Residual Blocks) helemaal opnieuw ontwerpen. Hieronder vindt u de implementatie van verschillende ResNet-architectuur. Voor deze implementatie gebruiken we de CIFAR-10-dataset. Deze dataset bevat 60.000 32×32 kleurenafbeeldingen in 10 verschillende klassen (vliegtuigen, auto's, vogels, katten, herten, honden, kikkers, paarden, schepen en vrachtwagens), enz. Deze dataset kan worden beoordeeld vanaf k tijdperken.datasets API-functie.

Stap 1: Eerst importeren we de Keras-module en zijn API's. Deze API's helpen bij het bouwen van de architectuur van het ResNet-model.

tekenreeks vergelijken c#

Code: Bibliotheken importeren

# 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>

Stap 2: Nu stellen we verschillende hyperparameters in die vereist zijn voor de ResNet-architectuur. We hebben ook wat voorbewerking op onze dataset uitgevoerd om deze voor te bereiden op training.

Code: Trainingshyperparameters instellen

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)>

>

>

Stap 3: In deze stap stellen we de leersnelheid in op basis van het aantal tijdperken. Naarmate het aantal tijdperken toeneemt, moet de leersnelheid worden verlaagd om beter leren te garanderen.

Code: LR instellen voor verschillende aantallen tijdperken

python3




# 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>

>

>

Stap 4: Definieer de basis ResNet-bouwsteen die kan worden gebruikt voor het definiëren van de ResNet V1- en V2-architectuur.

Code: Basis ResNet-bouwsteen

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>

>

>

Stap 5: Definieer ResNet V1-architectuur die is gebaseerd op de ResNet-bouwsteen die we hierboven hebben gedefinieerd:

Code: ResNet V1-architectuur

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>

>

>

Stap 6: Definieer ResNet V2-architectuur die is gebaseerd op de ResNet-bouwsteen die we hierboven hebben gedefinieerd:

Code: ResNet V2-architectuur

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>

>

>

Stap 7: De onderstaande code wordt gebruikt om de ResNet v1- en v2-architectuur te trainen en te testen die we hierboven hebben gedefinieerd:

Code: Hoofdfunctie

python3


java voor lustypen



# 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>])>

>

>

Resultaten & Conclusie:
Op de ImageNet-dataset gebruiken de auteurs een ResNet met 152 lagen, dat 8 keer dieper is dan VGG19, maar nog steeds minder parameters heeft. Een ensemble van deze ResNets genereerde een fout van slechts 3,7% op de ImageNet-testset, het resultaat dat de ILSVRC 2015-wedstrijd won. Op de COCO-objectdetectiedataset genereert het ook een relatieve verbetering van 28% vanwege de zeer diepe representatie ervan.

Foutpercentage op ResNet-architectuur

  • Het bovenstaande resultaat laat zien dat snelkoppelingen het probleem zouden kunnen oplossen dat wordt veroorzaakt door het vergroten van de lagen, omdat als we de lagen verhogen van 18 naar 34, het foutenpercentage op ImageNet Validation Set ook afneemt, in tegenstelling tot het gewone netwerk.

top-1 en top-5 Foutpercentage op ImageNet-validatieset.

  • Hieronder vindt u de resultaten van de ImageNet Test Set. De 3,57% het top-5 foutenpercentage van ResNet was het laagst en daarom kwam de ResNet-architectuur op de eerste plaats in de ImageNet-classificatie-uitdaging in 2015.