Po pirmosios CNN pagrįstos architektūros (AlexNet), kuri laimėjo „ImageNet 2012“ konkursą, kiekviena paskesnė laimėjusi architektūra naudoja daugiau sluoksnių giliame neuroniniame tinkle, kad sumažintų klaidų dažnį. Tai veikia esant mažesniam sluoksnių skaičiui, bet kai padidiname sluoksnių skaičių, iškyla dažna gilaus mokymosi problema, susijusi su vadinamuoju nykstančiu / sprogstančiu gradientu. Dėl to gradientas tampa 0 arba per didelis. Taigi, kai mes padidiname sluoksnių skaičių, mokymo ir bandymo klaidų lygis taip pat didėja.

20 sluoksnių ir 56 sluoksnių architektūros palyginimas
Aukščiau pateiktame diagramoje galime pastebėti, kad 56 sluoksnių CNN suteikia daugiau klaidų tiek mokymo, tiek testavimo duomenų rinkinyje nei 20 sluoksnių CNN architektūra. Išsamiau išanalizavę klaidų lygį, autoriai galėjo padaryti išvadą, kad tai sukelia nykstantis / sprogstantis gradientas.
„ResNet“, kurį 2015 m. pasiūlė „Microsoft Research“ mokslininkai, pristatė naują architektūrą, pavadintą „Residual Network“.
Likęs tinklas: Siekiant išspręsti nykstančio/sprogstančio gradiento problemą, ši architektūra pristatė koncepciją, vadinamą likutiniais blokais. Šiame tinkle naudojame techniką, vadinamą praleisti ryšius . Praleidimo jungtis sujungia sluoksnio aktyvacijas su kitais sluoksniais praleidžiant kai kuriuos sluoksnius tarp jų. Taip susidaro liekamasis blokas. Resnetai gaminami sukraunant šiuos likusius blokus.
Šio tinklo principas yra tas, kad vietoj to, kad sluoksniai mokytųsi pagrindinio atvaizdavimo, mes leidžiame tinklui pritaikyti likutinį atvaizdavimą. Taigi, vietoj sakyti H(x), pradinis atvaizdavimas , tegul tinka tinklas,
F(x) := H(x) - x which gives H(x) := F(x) + x .>

Praleisti (sparčiųjų klavišų) ryšį
Šio tipo praleidimo jungties pridėjimo pranašumas yra tas, kad jei kuris nors sluoksnis pakenks architektūros veikimui, jis bus praleistas sureguliavus. Taigi, tai sukuria labai gilų neuroninį tinklą be problemų, kurias sukelia nykstantis / sprogstantis gradientas. Straipsnio autoriai eksperimentavo su 100–1000 CIFAR-10 duomenų rinkinio sluoksnių.
Yra panašus metodas, vadinamas greitkelių tinklais, šie tinklai taip pat naudoja praleidimo ryšį. Panašiai kaip LSTM, šios praleidžiamosios jungtys taip pat naudoja parametrinius vartus. Šie vartai nustato, kiek informacijos praeina per praleidimo ryšį. Tačiau ši architektūra neužtikrina geresnio tikslumo nei ResNet architektūra.
Tinklo architektūra: Šiame tinkle naudojama 34 sluoksnių paprastoji tinklo architektūra, įkvėpta VGG-19, kurioje pridedamas spartusis ryšys. Šios nuorodos jungtys paverčia architektūrą į likutinį tinklą.

ResNet -34 architektūra
Įgyvendinimas: Naudodami Tensorflow ir Keras API, galime sukurti ResNet architektūrą (įskaitant likusius blokus) nuo nulio. Žemiau pateikiamas skirtingos ResNet architektūros įgyvendinimas. Šiam įgyvendinimui naudojame CIFAR-10 duomenų rinkinį. Šiame duomenų rinkinyje yra 60 000 32 × 32 spalvotų vaizdų iš 10 skirtingų klasių (lėktuvai, automobiliai, paukščiai, katės, elniai, šunys, varlės, arkliai, laivai ir sunkvežimiai) ir kt. Šį duomenų rinkinį galima įvertinti iš k. eras.duomenų rinkiniai API funkcija.
1 žingsnis: Pirmiausia importuojame keras modulį ir jo API. Šios API padeda kurti ResNet modelio architektūrą.
Kodas: Bibliotekų importavimas
# 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 žingsnis: Dabar nustatome skirtingus hiperparametrus, reikalingus ResNet architektūrai. Taip pat atlikome išankstinį duomenų rinkinio apdorojimą, kad paruoštume jį mokymui.
Kodas: Treniruočių hiperparametrų nustatymas
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 veiksmas: Šiame žingsnyje mes nustatome mokymosi greitį pagal epochų skaičių. Didėjant epochų skaičiui, mokymosi greitis turi būti sumažintas, kad būtų užtikrintas geresnis mokymasis.
Kodas: LR nustatymas skirtingiems epochų skaičiams
Python3
# 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> |
konvertuoti char į eilutę
>
>
4 veiksmas: Apibrėžkite pagrindinį ResNet kūrimo bloką, kuris gali būti naudojamas ResNet V1 ir V2 architektūrai apibrėžti.
Kodas: Pagrindinis „ResNet“ kūrimo blokas
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 veiksmas: Apibrėžkite ResNet V1 architektūrą, pagrįstą aukščiau apibrėžtu ResNet kūrimo bloku:
Kodas: ResNet V1 architektūra
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> |
sujungimai ir sujungimų tipai
>
>
6 veiksmas: Apibrėžkite ResNet V2 architektūrą, pagrįstą aukščiau apibrėžtu ResNet kūrimo bloku:
Kodas: ResNet V2 architektūra
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> |
>
>
7 veiksmas: Toliau pateiktas kodas naudojamas treniruoti ir išbandyti ResNet v1 ir v2 architektūrą, kurią apibrėžėme aukščiau:
Kodas: Pagrindinė funkcija
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>])> |
>
>
Rezultatai ir išvados:
„ImageNet“ duomenų rinkinyje autoriai naudoja 152 sluoksnių „ResNet“, kuris yra 8 kartus gilesnis nei VGG19, bet vis tiek turi mažiau parametrų. Šių „ResNets“ rinkinys sugeneravo tik 3,7% paklaidą „ImageNet“ bandymo rinkinyje, kuris laimėjo ILSVRC 2015 konkursą. COCO objektų aptikimo duomenų rinkinyje jis taip pat sukuria 28 % santykinį patobulinimą dėl labai gilaus atvaizdavimo.

„ResNet Architecture“ klaidų dažnis
- Aukščiau pateiktas rezultatas rodo, kad sparčiųjų klavišų jungtys galėtų išspręsti problemą, kurią sukelia sluoksnių padidinimas, nes padidinus sluoksnius nuo 18 iki 34, „ImageNet“ patvirtinimo rinkinio klaidų lygis taip pat mažėja, skirtingai nei paprastame tinkle.

Top-1 ir Top-5 Klaidų dažnis „ImageNet“ patvirtinimo rinkinyje.
- Žemiau pateikiami „ImageNet“ testų rinkinio rezultatai. The 3,57 % 5 geriausių „ResNet“ klaidų lygis buvo mažiausias, todėl „ResNet“ architektūra užėmė pirmąją vietą „ImageNet“ klasifikavimo iššūkyje 2015 m.
