Académique Documents
Professionnel Documents
Culture Documents
Les architectures CNN typiques empilent quelques couches convolutives (chacune généralement suivi d'une
couche ReLU), puis une couche de regroupement(Pooling), puis encore quelques convolutions couches
(+ReLU), puis une autre couche de pooling, et ainsi de suite. L'image devient plus en plus petite à mesure
qu'elle progresse dans le réseau, mais elle devient aussi généralement plus en plus profonde, grâce aux
couches convolutives. Au départ de l'architecture CNN, un réseau de neurones feedforward régulier est
ajouté, composé de quelques couches entièrement connectées (+ ReLU), et la couche finale génère la
prédiction (par exemple, une couche softmax qui génère une classe estimée probabilités).
Voici comment mettre en place un CNN simple pour s'attaquer au Fashion MNIST base de données(Introduit
dans le premier chapitre):
model = keras.models.Sequential([
keras.layers.Conv2D(64, 7, activation="relu", padding="same", input_shape=[28,
28, 1]),
keras.layers.MaxPooling2D(2),
keras.layers.Conv2D(128, 3, activation="relu", padding="same"),
keras.layers.Conv2D(128, 3, activation="relu", padding="same"),
keras.layers.MaxPooling2D(2),
keras.layers.Conv2D(256, 3, activation="relu", padding="same"),
keras.layers.Conv2D(256, 3, activation="relu", padding="same"),
keras.layers.MaxPooling2D(2),
keras.layers.Flatten(),
keras.layers.Dense(128, activation="relu"),
keras.layers.Dropout(0.5),
keras.layers.Dense(64, activation="relu"),
keras.layers.Dropout(0.5),
keras.layers.Dense(10, activation="softmax")
])
Détail du modèle:
La première couche utilise 64 filtres assez gros (7×7) mais pas de foulée car les images d'entrée ne sont
pas très grandes. Il définit également input_shape=[28, 28, 1], car les images sont de 28 × 28 pixels,
Puis on répète deux fois la même structure : deux couches convolutives suivi d'une couche de pooling
maximale. Pour des images plus grandes, nous pourrions répéter cette structure plusieurs fois (le
(c'est initialement 64, puis 128, puis 256) : cela a du sens, car le nombre de caractéristiques de bas
niveau est souvent assez faible (par exemple, petits cercles, lignes horizontales), mais il existe de
nombreuses façons différentes pour les combiner en caractéristiques de niveau supérieur. C'est une
pratique courante de doubler le nombre de filtres après chaque couche de pooling : puisqu'un pooling
couche divise chaque dimension spatiale par un facteur de 2, nous pouvons nous permettre de
doubler le nombre de cartes d'entités dans la couche suivante sans craindre exploser le nombre de
paramètres, l'utilisation de la mémoire ou le calcul.
Vient ensuite le réseau entièrement connecté, composé de deux denses cachés couches et une couche
de sortie dense. A noter qu'il faut aplatir ses entrées, puisqu'un réseau dense attend un tableau 1D
d'entités pour chaque instance. Nous ajoutons également deux couches d'abandon(dropout), avec un
taux d'abandon de 50 % chacune pour réduire l'effet overfitting.
Au fil des années, des variantes de cette architecture fondamentale ont été développées, conduisant à des
avancées étonnantes dans le domaine. Dans ce chapitre, nous allons découvrir quelques modèles CNN le
plus connus.
4.1 LeNet-5
L'architecture LeNet-5 est peut-être l'architecture CNN la plus connue. Il a été créé par Yann LeCun en 1998
et a été largement utilisé pour la reconnaissance des chiffres manuscrits (MNIST). Il est composé des
couches affichées dans le tableau suivant:
In Input 1 32 × 32 - - -
Les images MNIST font 28 × 28 pixels, mais elles sont complétées par des zéros à 32 × 32 pixels et
normalisé avant d'être transmis au réseau. Le reste du réseau n'utilise aucun rembourrage(padding),
c'est pourquoi la taille continue à diminuer au fur et à mesure que l'image progresse sur le réseau.
Les couches de regroupement moyennes(Average Pooling) sont légèrement plus complexes que
d'habitude : chaque neurone calcule la moyenne de ses entrées, puis multiplie le résultat par un
coefficient apprenable (un par filtre) et ajoute un terme de biais apprenable (encore une fois, un par
vecteur poids, chaque neurone sort le carré de la distance euclidienne entre son vecteur d'entrée et
son vecteur de poids. Chaque sortie mesure à quel point l'image appartient à une classe de chiffres
particulière. La fonction de coût d'entropie croisée est maintenant préférée, car il pénalise beaucoup
plus les mauvaises prédictions, produisant des gradients plus importants et convergeant plus
rapidement. (Radial Basis Function)
lenet_5_model = keras.models.Sequential([
keras.layers.Conv2D(6, kernel_size=5, strides=1, activation='tanh',
input_shape=[32, 32, 1], padding='same'), #C1
keras.layers.AveragePooling2D(), #S2
keras.layers.Conv2D(16, kernel_size=5, strides=1, activation='tanh',
padding='valid'), #C3
keras.layers.AveragePooling2D(), #S4
keras.layers.Conv2D(120, kernel_size=5, strides=1, activation='tanh',
padding='valid'), #C5
keras.layers.Flatten(), #Flatten
keras.layers.Dense(84, activation='tanh'), #F6
keras.layers.Dense(10, activation='softmax') #Output layer
])
lenet_5_model.evaluate(test_x, test_y)
4.2 AlexNet
L'architecture AlexNet CNN a remporté le défi ImageNet ILSVRC 2012 en une grande marge : il a atteint un
taux d'erreur de 17 % parmi les cinq premiers, tandis que le deuxième meilleur atteint seulement 26 % ! Il a
été développé par Alex Krizhevsky (d'où le nom), Ilya Sutskever et Geoffrey Hinton. Il est similaire à LeNet-5,
mais beaucoup plus grand et plus profond, et c'était le premier à empiler des couches convolutives
directement au-dessus de les uns les autres, au lieu d'empiler une couche de pooling au-dessus de chaque
couche convolution.
Maps(Nb. of
Layer Type Size Kernel Stride Padding Activation
Kernel)
227 ×
In Input 3 (RGB) - - - -
227
55 × 11 ×
C1 Convolution 96 4 valid ReLU
55 11
27 ×
S2 Max pooling 96 3×3 2 valid -
27
27 ×
C3 Convolution 256 5×5 1 same ReLU
27
13 ×
S4 Max pooling 256 3×3 2 valid -
13
13 ×
C5 Convolution 384 3×3 1 same ReLU
13
13 ×
C6 Convolution 384 3×3 1 same ReLU
13
13 ×
C7 Convolution 256 3×3 1 same ReLU
13
Fully
F8 - 4,096 - - - ReLU
connected
Fully
F9 - 4,096 - - - ReLU
connected
Fully
Out - 1,000 - - - Softmax
connected
Pour réduire le overfitting, les auteurs ont utilisé deux techniques de régularisation. D'abord, ils ont
appliqué la méthode de dropout avec un taux d'abandon de 50 % aux sorties des couches F8 et F9.
Deuxièmement, ils ont effectué une méthode d'augmentation des données en décalant aléatoirement les
images d'apprentissage de divers décalages, en les retournant horizontalement et en modifiant les
conditions d'éclairage.
“
Note importante - Augmentation des données:
Par exemple, vous pouvez légèrement décaler, faire pivoter et redimensionner chaque image de
l'entraînement définie par différentes quantités et ajouter les images résultantes à l'entraînement.
Cela oblige le modèle à être plus tolérant
variations dans la position, l'orientation et la taille des objets dans les images. Pour un modèle plus
tolérant aux différentes conditions d'éclairage. En général, vous pouvez retournez également les
images horizontalement (à l'exception du texte et d'autres images asymétriques objets). En
combinant ces transformations, vous pouvez augmenter considérablement la taille de votre ensemble
d'entraînement.
4.2.1 Exercice: Implémenter AlexNet avec API Keras
AlexNet_model = keras.models.Sequential([
### Codes à Remplir ###
])
4.3 VGGNet
Le gagnant du défi ILSVRC 2014 était VGGNet, développé par Karen Simonyan et Andrew Zisserman du
Visual Geometry Group (VGG) laboratoire de recherche à l'Université d'Oxford. Il a une très simple et
classique architecture, avec 2 ou 3 couches convolutives et une couche de pooling, puis à nouveau 2 ou 3
couches convolutives et une couche de pooling, et ainsi de suite (atteignant un total de seulement 16 ou 19
couches convolutives, selon la variante VGG), plus sous réseaux final dense de 2 couches cachées et la
couche de sortie. Il n'utilisait que des filtres 3 × 3, mais de nombreux filtres.
La colonne E dans le tableau suivant est pour VGG19 (les autres colonnes sont pour d'autres variantes de
modèles VGG) :
tf.keras.applications.vgg19.VGG19(
include_top=False,
weights='imagenet',
input_tensor=None,
input_shape=None,
pooling=None,
classes=1000,
classifier_activation='softmax'
)
Paramètres Explication
4.3.1 Exercice: Implémenter VGG19 en utilisant les APIs par défaut (Dense, Conv2D,
MaxPooling etc.)
def VGG19(input_shape):
model = Sequential()
### Codes à Remplir ###
return model
4.4 ResNet
Kaiming He et al. a remporté le défi ILSVRC 2015 en utilisant un réseau résiduel (ou ResNet), qui a fourni un
taux d'erreur stupéfiant parmi les cinq premiers inférieur à 3,6 %. La variante gagnante utilisait un CNN
extrêmement profond composé de 152 couches (autres
les variantes avaient 34, 50 et 101 couches). Elle confirme la tendance générale : les modèles sont de plus
en plus profond, avec de moins en moins de paramètres. La clé pour être capable de former un réseau aussi
profond est d'utiliser des Skip Connections (connexions de saut, également appelées raccourcis
connexions) : le signal entrant dans une couche est également ajouté à la sortie d'une couche située un peu
plus haut dans le réseaux. Voyons pourquoi cela est utile.
Lors de l'entraînement d'un réseau de neurones, le but est de lui faire modéliser une fonction cible . Si
vous ajoutez l'entrée à la sortie du réseau (c'est-à-dire que vous ajoutez un saut connexion), alors le
réseau sera obligé de modéliser à plutôt que . C'est ce qu'on appelle l'apprentissage
résiduel(Residual Learning - ResNet).
Lorsque vous initialisez un réseau de neurones régulier, ses poids sont proches de zéro, donc le le réseau ne
produit que des valeurs proches de zéro. Si vous ajoutez un saut de connexion, en sortant, le réseau ne
produit qu'une copie de ses entrées. En d'autres termes, il a d'abord modélise la fonction identité. Si la
fonction cible est assez proche de l'identité fonction (ce qui est souvent le cas), cela accélérera
considérablement l'entraînement.
Bloc d'identité
De plus, si vous ajoutez de nombreuses saut connexions(Skip Connections), le réseau peut commencer à
effectuer des progrès même si plusieurs couches n'ont pas encore commencer à s'entraîner. Le réseau
résiduel profond peut être vu comme un empilement d'unités résiduelles (RUs), où chaque unité résiduelle
est un petit réseau de neurones avec un lien de saut.
Bloc Convolutif
Nous pouvons utiliser ce type de bloc lorsque les dimensions d'entrée et de sortie ne correspondent pas. La
différence avec le bloc d'identité est qu'il y a une couche CONV2D dans le chemin de raccourci.
Chaque l'unité résiduelle est composée de deux couches convolutives sans couche pooling. avec la
normalisation par lots (Batch Normalization) et l'activation ReLU, en utilisant des noyaux/filtres de 3 × 3 et
en préservant les dimensions spatiales (foulée 1, remplissage "même" / same padding).
4.4.2 Mise en œuvre du bloc d'identité
Pour construire une connexion de saut, on stocke d'abord la valeur initiale de X dans une variable et puis on
ajoute les couches normales et au final, on utilise Add()() pour construire le skip . C'est à dire ajouter le
shortcut(chemin de raccourci) et le chemin normal de votre réseau CNN.
X_shortcut = X
return X
Le code est assez simple mais il y a une chose importante à considérer - puisque X , X_shortcut ci-
dessus sont deux matrices, vous ne pouvez les ajouter que si elles ont la même forme. Donc, si les
opérations de convolution + norme de lot(batch normalisation) sont effectuées de manière à ce que la
forme de sortie soit la même, nous pouvons simplement les ajouter comme indiqué ci-dessus.
Dans le cas où x_shortcut passe par une couche de convolution choisie de telle sorte que la sortie de
celle-ci soit de la même dimension que la sortie du bloc de convolution, comme indiqué ci-dessus dans la
graphe Bloc Convolutif.
X_shortcut = X
# On ajoute une couche Conv2D dans le shortcut pour que la sortie soit de même
forme que chemin principal
X_shortcut = Conv2D(filters=F3, kernel_size=(1, 1), strides=(s, s),
padding='valid', name=conv_name_base + '1',
kernel_initializer=glorot_uniform(seed=0))(X_shortcut)
# On effectue également une étape de BatchNormalization
X_shortcut = BatchNormalization(axis=3, name=bn_name_base + '1')(X_shortcut)
X = Add()([X, X_shortcut])
X = Activation('relu')(X)
return X
Une chose importante à noter ici est que la connexion de saut(skip connection) est appliquée avant
l'activation RELU, comme indiqué dans le schéma ci-dessus. La recherche a montré que cela donne les
meilleurs résultats.
ResNet a de nombreuses variantes qui fonctionnent sur le même concept mais ont des nombres de couches
différents. Resnet50 est utilisé pour désigner la variante qui peut fonctionner avec 50 couches de réseau de
neurones. Dans ce programme, nous utiliserons cette variante pour illustrer l'implémentation d'un ResNet.
X_input = Input(input_shape)
X = ZeroPadding2D((3, 3))(X_input)
return model
Revenons maintenant sur le sujet que nous avons parlé dans la section 1.3.2: Réutiliser les couches pré-
entraînées/ Transfer Learning. Le réseau de neurones profond convolutif prend des jours à s'entraîner et son
entraînement nécessite de nombreuses ressources de calcul. Donc, pour surmonter cela, nous utilisons
l'apprentissage par transfert dans cette implémentation Keras de ResNet 50.
L'apprentissage par transfert est une technique par laquelle un modèle de réseau de neurone profond est
d'abord formé sur un problème similaire au problème à résoudre. Une ou plusieurs couches du modèle
formé sont ensuite utilisées dans un nouveau modèle formé sur le problème d'intérêt. En termes simples,
l'apprentissage par transfert fait référence à un processus dans lequel un modèle formé sur un problème est
utilisé d'une manière ou d'une autre sur un deuxième problème connexe.
headModel = base_model.output
headModel = Flatten()(headModel)
headModel=Dense(256, activation='relu',
name='fc1',kernel_initializer=glorot_uniform(seed=0))(headModel)
headModel=Dense(128, activation='relu',
name='fc2',kernel_initializer=glorot_uniform(seed=0))(headModel)
headModel = Dense( 1,activation='sigmoid',
name='fc3',kernel_initializer=glorot_uniform(seed=0))(headModel)
Enfin, créons le modèle qui prend l'entrée de la dernière couche de la couche d'entrée et les sorties de la
dernière couche du modèle de tête:
model = Model(inputs=base_model.input, outputs=headModel)