CNTK - Réseau neuronal convolutif

Dans ce chapitre, étudions comment construire un réseau neuronal convolutif (CNN) en CNTK.

introduction

Les réseaux de neurones convolutifs (CNN) sont également constitués de neurones, qui ont des poids et des biais apprenables. C'est pourquoi, de cette manière, ils sont comme des réseaux de neurones ordinaires (NN).

Si nous nous rappelons le fonctionnement des NN ordinaires, chaque neurone reçoit une ou plusieurs entrées, prend une somme pondérée et passe par une fonction d'activation pour produire la sortie finale. Ici, la question se pose que si les CNN et les NN ordinaires ont autant de similitudes, qu'est-ce qui rend ces deux réseaux différents l'un de l'autre?

Qu'est-ce qui les différencie est le traitement des données d'entrée et des types de couches? La structure des données d'entrée est ignorée dans NN ordinaire et toutes les données sont converties en tableau 1-D avant de les alimenter dans le réseau.

Mais, l'architecture du réseau de neurones convolutifs peut prendre en compte la structure 2D des images, les traiter et lui permettre d'extraire les propriétés spécifiques aux images. De plus, les CNN ont l'avantage d'avoir une ou plusieurs couches convolutionnelles et une couche de regroupement, qui sont les principaux éléments constitutifs des CNN.

Ces couches sont suivies par une ou plusieurs couches entièrement connectées comme dans les NN multicouches standard. Ainsi, on peut penser à CNN, comme un cas particulier de réseaux entièrement connectés.

Architecture de réseau de neurones convolutifs (CNN)

L'architecture de CNN est essentiellement une liste de couches qui transforme le volume de l'image en trois dimensions, c'est-à-dire la largeur, la hauteur et la profondeur, en un volume de sortie en trois dimensions. Un point important à noter ici est que chaque neurone de la couche actuelle est connecté à un petit patch de la sortie de la couche précédente, ce qui revient à superposer un filtre N * N sur l'image d'entrée.

Il utilise des filtres M, qui sont essentiellement des extracteurs de fonctionnalités qui extraient des fonctionnalités telles que les bords, les coins, etc. Voici les couches [INPUT-CONV-RELU-POOL-FC] qui sont utilisés pour construire des réseaux de neurones convolutionnels (CNN) -

  • INPUT- Comme son nom l'indique, ce calque contient les valeurs brutes des pixels. Les valeurs de pixel brutes désignent les données de l'image telles quelles. Par exemple, INPUT [64 × 64 × 3] est une image RVB à 3 canaux de largeur 64, hauteur 64 et profondeur 3.

  • CONV- Cette couche est l'un des blocs de construction des CNN car la plupart des calculs sont effectués dans cette couche. Exemple - si nous utilisons 6 filtres sur l'ENTREE [64 × 64 × 3] mentionnée ci-dessus, cela peut entraîner le volume [64 × 64 × 6].

  • RELU−Aussi appelé couche unitaire linéaire rectifiée, qui applique une fonction d'activation à la sortie de la couche précédente. D'une autre manière, une non-linéarité serait ajoutée au réseau par RELU.

  • POOL- Cette couche, c'est-à-dire la couche de regroupement, est un autre élément constitutif des CNN. La tâche principale de cette couche est le sous-échantillonnage, ce qui signifie qu'elle fonctionne indépendamment sur chaque tranche de l'entrée et la redimensionne spatialement.

  • FC- Il est appelé couche entièrement connectée ou plus précisément couche de sortie. Il est utilisé pour calculer le score de classe de sortie et la sortie résultante est un volume de taille 1 * 1 * L où L est le nombre correspondant au score de classe.

Le diagramme ci-dessous représente l'architecture typique des CNN -

Création de la structure CNN

Nous avons vu l'architecture et les bases de CNN, maintenant nous allons construire un réseau convolutif en utilisant CNTK. Ici, nous verrons d'abord comment assembler la structure du CNN, puis nous verrons comment entraîner les paramètres de celui-ci.

Enfin, nous verrons comment nous pouvons améliorer le réseau de neurones en modifiant sa structure avec différentes configurations de couches différentes. Nous allons utiliser le jeu de données d'image MNIST.

Alors, commençons par créer une structure CNN. Généralement, lorsque nous construisons un CNN pour reconnaître des motifs dans les images, nous faisons ce qui suit:

  • Nous utilisons une combinaison de couches de convolution et de mise en commun.

  • Une ou plusieurs couches cachées à la fin du réseau.

  • Enfin, nous terminons le réseau avec une couche softmax à des fins de classification.

À l'aide des étapes suivantes, nous pouvons construire la structure du réseau -

Step 1- Tout d'abord, nous devons importer les couches requises pour CNN.

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling

Step 2- Ensuite, nous devons importer les fonctions d'activation pour CNN.

from cntk.ops import log_softmax, relu

Step 3- Après cela, afin d'initialiser les couches convolutives plus tard, nous devons importer le glorot_uniform_initializer comme suit -

from cntk.initializer import glorot_uniform

Step 4- Ensuite, pour créer des variables d'entrée, importez le input_variablefonction. Et importerdefault_option , pour rendre la configuration de NN un peu plus facile.

from cntk import input_variable, default_options

Step 5- Maintenant, pour stocker les images d'entrée, créez un nouveau input_variable. Il contiendra trois canaux à savoir rouge, vert et bleu. Il aurait la taille de 28 par 28 pixels.

features = input_variable((3,28,28))

Step 6−Suivant, nous devons créer un autre input_variable pour stocker les étiquettes à prédire.

labels = input_variable(10)

Step 7- Maintenant, nous devons créer le default_optionpour le NN. Et nous devons utiliser leglorot_uniform comme fonction d'initialisation.

with default_options(initialization=glorot_uniform, activation=relu):

Step 8- Ensuite, afin de définir la structure du NN, nous devons créer un nouveau Sequential ensemble de calques.

Step 9- Nous devons maintenant ajouter un Convolutional2D couche avec un filter_shape de 5 et un strides réglage de 1, dans le Sequentialensemble de calques. Activez également le remplissage afin que l'image soit remplie pour conserver les dimensions d'origine.

model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),

Step 10- Il est maintenant temps d'ajouter un MaxPooling couche avec filter_shape de 2, et un strides réglage de 2 pour compresser l'image de moitié.

MaxPooling(filter_shape=(2,2), strides=(2,2)),

Step 11- Maintenant, comme nous l'avons fait à l'étape 9, nous devons ajouter un autre Convolutional2D couche avec un filter_shape de 5 et un stridesréglage de 1, utilisez 16 filtres. Activez également le remplissage, de sorte que la taille de l'image produite par la couche de regroupement précédente soit conservée.

Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),

Step 12- Maintenant, comme nous l'avons fait à l'étape 10, ajoutez un autre MaxPooling couche avec un filter_shape de 3 et un strides réglage de 3 pour réduire l'image à un tiers.

MaxPooling(filter_shape=(3,3), strides=(3,3)),

Step 13- Enfin, ajoutez une couche dense avec dix neurones pour les 10 classes possibles, que le réseau peut prédire. Pour transformer le réseau en modèle de classification, utilisez unlog_siftmax fonction d'activation.

Dense(10, activation=log_softmax)
])

Exemple complet de création de structure CNN

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)

Formation CNN avec des images

Comme nous avons créé la structure du réseau, il est temps de former le réseau. Mais avant de commencer la formation de notre réseau, nous devons configurer des sources de minibatch, car la formation d'un NN qui fonctionne avec des images nécessite plus de mémoire que la plupart des ordinateurs.

Nous avons déjà créé des sources de minibatch dans les sections précédentes. Voici le code Python pour configurer deux sources de minibatch -

Comme nous avons le create_datasource fonction, nous pouvons maintenant créer deux sources de données distinctes (formation et test) pour entraîner le modèle.

train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)

Maintenant que nous avons préparé les images, nous pouvons commencer la formation de notre NN. Comme nous l'avons fait dans les sections précédentes, nous pouvons utiliser la méthode de train sur la fonction de perte pour lancer la formation. Voici le code pour cela -

from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)

Avec l'aide du code précédent, nous avons configuré la perte et l'apprenant pour le NN. Le code suivant entraînera et validera le NN−

from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   labels: train_datasource.streams.labels
}
loss.train(train_datasource,
     max_epochs=10,
     minibatch_size=64,
     epoch_size=60000,
        parameter_learners=[learner],
     model_inputs_to_streams=input_map,
     callbacks=[progress_writer, test_config])

Exemple d'implémentation complet

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
   mapping_file = os.path.join(folder, 'mapping.bin')
   image_transforms = []
   if train:
    image_transforms += [
     xforms.crop(crop_type='randomside', side_ratio=0.8),
     xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
   stream_definitions = StreamDefs(
   features=StreamDef(field='image', transforms=image_transforms),
    labels=StreamDef(field='label', shape=10)
)
   deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
   loss = cross_entropy_with_softmax(output, targets)
   metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   labels: train_datasource.streams.labels
}
loss.train(train_datasource,
     max_epochs=10,
     minibatch_size=64,
     epoch_size=60000,
        parameter_learners=[learner],
     model_inputs_to_streams=input_map,
     callbacks=[progress_writer, test_config])

Production

-------------------------------------------------------------------
average  since  average  since  examples
loss     last   metric   last
------------------------------------------------------
Learning rate per minibatch: 0.2
142      142      0.922   0.922    64
1.35e+06 1.51e+07 0.896   0.883    192
[………]

Transformations d'image

Comme nous l'avons vu, il est difficile de former les NN utilisés pour la reconnaissance d'images et, ils nécessitent également beaucoup de données pour s'entraîner. Un autre problème est que, ils ont tendance à surajuster sur les images utilisées pendant l'entraînement. Voyons avec un exemple, lorsque nous avons des photos de visages en position verticale, notre modèle aura du mal à reconnaître les visages qui sont tournés dans une autre direction.

Afin de surmonter ce problème, nous pouvons utiliser l'augmentation d'image et CNTK prend en charge des transformations spécifiques, lors de la création de sources minibatch pour les images. Nous pouvons utiliser plusieurs transformations comme suit -

  • Nous pouvons recadrer aléatoirement des images utilisées pour l'entraînement avec seulement quelques lignes de code.

  • Nous pouvons également utiliser une échelle et une couleur.

Voyons, à l'aide du code Python suivant, comment nous pouvons changer la liste des transformations en incluant une transformation de rognage dans la fonction utilisée pour créer la source de minibatch plus tôt.

import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
   mapping_file = os.path.join(folder, 'mapping.bin')
   image_transforms = []
   if train:
   image_transforms += [
     xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
   stream_definitions = StreamDefs(
   features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
   deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)

À l'aide du code ci-dessus, nous pouvons améliorer la fonction pour inclure un ensemble de transformations d'image, de sorte que, lorsque nous nous entraînerons, nous pouvons recadrer l'image de manière aléatoire, afin d'obtenir plus de variations de l'image.