logo

Réseaux résiduels (ResNet) – Deep Learning

Après la première architecture basée sur CNN (AlexNet) qui a remporté le concours ImageNet 2012, chaque architecture gagnante ultérieure utilise plus de couches dans un réseau neuronal profond pour réduire le taux d'erreur. Cela fonctionne pour un nombre inférieur de couches, mais lorsque nous augmentons le nombre de couches, il existe un problème courant dans l'apprentissage profond associé à celui appelé gradient de disparition/explosion. Cela fait que le dégradé devient 0 ou trop grand. Ainsi, lorsque nous augmentons le nombre de couches, le taux d’erreur de formation et de test augmente également.

Comparaison de l'architecture à 20 couches et à 56 couches



Dans le graphique ci-dessus, nous pouvons observer qu'un CNN à 56 couches donne un taux d'erreur plus élevé sur les ensembles de données de formation et de test qu'une architecture CNN à 20 couches. Après avoir analysé davantage le taux d'erreur, les auteurs ont pu conclure qu'il est dû à un gradient qui disparaît/explose.
ResNet, proposé en 2015 par des chercheurs de Microsoft Research, a introduit une nouvelle architecture appelée Residual Network.

Réseau résiduel : Afin de résoudre le problème du gradient de disparition/explosion, cette architecture a introduit le concept appelé blocs résiduels. Dans ce réseau, nous utilisons une technique appelée sauter les connexions . La connexion de saut connecte les activations d'une couche à d'autres couches en sautant certaines couches entre les deux. Cela forme un bloc résiduel. Les resnets sont créés en empilant ces blocs résiduels ensemble.
L'approche derrière ce réseau est qu'au lieu que les couches apprennent la cartographie sous-jacente, nous permettons au réseau de s'adapter à la cartographie résiduelle. Ainsi, au lieu de dire H(x), la cartographie initiale , laissez le réseau s'adapter,

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

Ignorer la connexion (raccourci)



L'avantage de l'ajout de ce type de connexion ignorée est que si une couche nuit aux performances de l'architecture, elle sera ignorée par régularisation. Ainsi, cela aboutit à la formation d’un réseau neuronal très profond sans les problèmes causés par la disparition/l’explosion du gradient. Les auteurs de l'article ont expérimenté sur 100 à 1 000 couches de l'ensemble de données CIFAR-10.
Il existe une approche similaire appelée réseaux autoroutiers, ces réseaux utilisent également des connexions sautées. Semblables au LSTM, ces connexions sautées utilisent également des portes paramétriques. Ces portes déterminent la quantité d'informations qui transitent par la connexion sautée. Cette architecture n'a cependant pas fourni une meilleure précision que l'architecture ResNet.

Architecture de réseau: Ce réseau utilise une architecture réseau simple à 34 couches inspirée de VGG-19 dans laquelle la connexion raccourci est ensuite ajoutée. Ces connexions raccourcies convertissent alors l’architecture en un réseau résiduel.

Architecture ResNet-34



Mise en œuvre: À l'aide des API Tensorflow et Keras, nous pouvons concevoir l'architecture ResNet (y compris les blocs résiduels) à partir de zéro. Vous trouverez ci-dessous la mise en œuvre de différentes architectures ResNet. Pour cette implémentation, nous utilisons l'ensemble de données CIFAR-10. Cet ensemble de données contient 60 000 images couleur 32 × 32 dans 10 classes différentes (avions, voitures, oiseaux, chats, cerfs, chiens, grenouilles, chevaux, navires et camions), etc. Cet ensemble de données peut être évalué à partir de k époques.datasets Fonction API.

Étape 1: Tout d'abord, nous importons le module keras et ses API. Ces API aident à construire l'architecture du modèle ResNet.

Code: Importation de bibliothèques

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

Étape 2: Maintenant, nous définissons différents hyper paramètres requis pour l'architecture ResNet. Nous avons également effectué un prétraitement sur notre ensemble de données pour le préparer à la formation.

commande d'installation npm

Code: Définition des hyperparamètres d'entraînement

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

>

>

Étape 3: Dans cette étape, nous définissons le taux d'apprentissage en fonction du nombre d'époques. Comme le nombre d'époques, le taux d'apprentissage doit être diminué pour assurer un meilleur apprentissage.

Code: Réglage de LR pour différents nombres d'époques

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>

>

>

Étape 4: Définissez le bloc de construction ResNet de base qui peut être utilisé pour définir l’architecture ResNet V1 et V2.

Code: Bloc de construction ResNet de base

python3


kajal aggarwal



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

>

>

Étape 5 : Définissez l'architecture ResNet V1 basée sur le bloc de construction ResNet que nous avons défini ci-dessus :

Code: Architecture ResNet V1

python3




nombre aléatoire en Java

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>

>

>

Étape 6 : Définissez l'architecture ResNet V2 basée sur le bloc de construction ResNet que nous avons défini ci-dessus :

Code: Architecture 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>

>

>

Étape 7 : Le code ci-dessous est utilisé pour entraîner et tester l'architecture ResNet v1 et v2 que nous avons définie ci-dessus :

Code : Fonction principale

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

>

>

Résultats et conclusion :
Sur l'ensemble de données ImageNet, les auteurs utilisent un ResNet à 152 couches, qui est 8 fois plus profond que VGG19 mais comporte toujours moins de paramètres. Un ensemble de ces ResNets a généré une erreur de seulement 3,7 % sur l'ensemble de test ImageNet, résultat qui a remporté le concours ILSVRC 2015. Sur le jeu de données de détection d'objets COCO, il génère également une amélioration relative de 28 % grâce à sa représentation très profonde.

obtenir une connexion

Taux d'erreur sur l'architecture ResNet

  • Le résultat ci-dessus montre que les connexions raccourcies seraient en mesure de résoudre le problème causé par l'augmentation du nombre de couches, car à mesure que nous augmentons les couches de 18 à 34, le taux d'erreur sur l'ensemble de validation ImageNet diminue également contrairement au réseau simple.

Taux d’erreur top-1 et top-5 sur l’ensemble de validation ImageNet.

  • Vous trouverez ci-dessous les résultats sur l'ensemble de tests ImageNet. Le 3,57% Le taux d'erreur du top 5 de ResNet était le plus bas et l'architecture ResNet est donc arrivée première dans le défi de classification ImageNet en 2015.