Il est fortement recommandĂ© d’avoir Ă©tudiĂ© le tutoriel #1 avant celui-ci car on y trouve de nombreux Ă©lĂ©ments communs qui ne sont pas re-expliquĂ©s ici.

Cet article commente le tutoriel Tensorflow #2 de Magnus Erik Hvass Pedersen  : Convolutional Neural Network. Le code est ici et la vidéo ici (en anglais).

Les convolutions ne sont pas expliquées dans notre article. Elles sont supposées connues.

Pour ceux qui ne savent pas ce que c’est, lisez cet excellent article Comment les RĂ©seaux de neurones Ă  convolution fonctionnent, traduction de How do Convolutional Neural Networks work?

Cette autre vidĂ©o est aussi digne d’intĂ©rĂŞt (pour les dĂ©butants).

Lorsque le code est similaire au tutoriel #1 alors il n’est pas commentĂ© ici.

Flux

Le réseau implémenté est représenté ci-dessous :

Le logiciel proposĂ© n’est pas notre prĂ©fĂ©rĂ© car cette version n’utilise pas Keras, or avec Keras tout est beaucoup plus facile – par contre il a l’intĂ©rĂŞt d’entrer dans les dĂ©tails qui permettent de comprendre ce qui se passe derrière.

Première convolution

Pour chaque image, on applique 16 filtres diffĂ©rents (5*5), puis un ReLU (Rectified Linear Unit La fonction a pour valeurs Max(0,x)), puis un max-pooling (2*2) ce qui rĂ©duit la rĂ©solution de l’image Ă  14*14 (au lieu de 28*28)

Imports

(voir  tutoriel #1)

Les imports sont ceux du tutoriel #1 auxquels on ajoute :

import time
from datetime import timedelta
import math

parce qu’on veut mesurer le temps d’exĂ©cution et qu’on a des racines carrĂ©es Ă  calculer.

Configuration du réseau

# Convolutional Layer 1.
filter_size1 = 5          # Convolution filters are 5 x 5 pixels.
num_filters1 = 16         # There are 16 of these filters.

# Convolutional Layer 2.
filter_size2 = 5          # Convolution filters are 5 x 5 pixels.
num_filters2 = 36         # There are 36 of these filters.

# Fully-connected layer.
fc_size = 128             # Number of neurons in fully-connected layer.

Les paramètres du réseau sont ceux indiqués sur le schéma (plus haut)

 Lecture des données

(voir  tutoriel #1)

La seule différence est :

# Number of colour channels for the images: 1 channel for gray-scale.
num_channels = data.num_channels

parce qu’on pourrait avoir des images sur plusieurs channels (en couleurs)

Affichage des images

(voir  tutoriel #1)

Tensorflow

Variables

def new_weights(shape):
    return tf.Variable(tf.truncated_normal(shape, stddev=0.05))

def new_biases(length):
    return tf.Variable(tf.constant(0.05, shape=[length]))

Dans le tutoriel #1, les poids et les biais sont initialisĂ©s de la façon suivante (Ă  vrai dire l’initialisation est faite après, il s’agit ici seulement d’une dĂ©claration du graphe)

  • weights = tf.Variable(tf.zeros([img_size_flat, num_classes]))
  • biases = tf.Variable(tf.zeros([num_classes]))

tf.truncated_normal(shape, stddev=0.05) produira un tenseur 1D de la forme shape avec une dĂ©viation standard de 0.05 par rapport Ă  la distribution normale, tandis que tf.constant(0.05, shape=[length]) crĂ©era comme le nom l’indique un tenseur de constantes.

Pourquoi choisir diffĂ©rentes façons d’initialiser les poids. Lire ici Ă  ce sujet.

Ce qui est sĂ»r, c’est qu’il ne faut pas initialiser les poids Ă  zĂ©ro car ce n’est acceptable que pour un modèle linĂ©aire (ce qui ne posait pas problème dans le tutoriel #1). Ce qui est sĂ»r aussi c’est qu’on peut initialiser les biais Ă  zĂ©ro, quelque soit le modèle.

Reste la question d’une initialisation avec une distribution normale. Il semblerait, d’après les experts, que ce ne soit pas la meilleure solution, notamment si on utilise ReLU comme fonction d’activation.

Il faudra revenir sur ce choix.

CrĂ©ation d’une couche de convolution

def new_conv_layer(input,              # The previous layer.
                   num_input_channels, # Num. channels in prev. layer.
                   filter_size,        # Width and height of each filter.
                   num_filters,        # Number of filters.
                   use_pooling=True):  # Use 2x2 max-pooling.

    # Shape of the filter-weights for the convolution.
    # This format is determined by the TensorFlow API.
    shape = [filter_size, filter_size, num_input_channels, num_filters]

    # Create new weights aka. filters with the given shape.
    weights = new_weights(shape=shape)

    # Create new biases, one for each filter.
    biases = new_biases(length=num_filters)

    # Create the TensorFlow operation for convolution.
    # Note the strides are set to 1 in all dimensions.
    # The first and last stride must always be 1,
    # because the first is for the image-number and
    # the last is for the input-channel.
    # But e.g. strides=[1, 2, 2, 1] would mean that the filter
    # is moved 2 pixels across the x- and y-axis of the image.
    # The padding is set to 'SAME' which means the input image
    # is padded with zeroes so the size of the output is the same.
    layer = tf.nn.conv2d(input=input,
                         filter=weights,
                         strides=[1, 1, 1, 1],
                         padding='SAME')

    # Add the biases to the results of the convolution.
    # A bias-value is added to each filter-channel.
    layer += biases

    # Use pooling to down-sample the image resolution?
    if use_pooling:
        # This is 2x2 max-pooling, which means that we
        # consider 2x2 windows and select the largest value
        # in each window. Then we move 2 pixels to the next window.
        layer = tf.nn.max_pool(value=layer,
                               ksize=[1, 2, 2, 1],
                               strides=[1, 2, 2, 1],
                               padding='SAME')

    # Rectified Linear Unit (ReLU).
    # It calculates max(x, 0) for each input pixel x.
    # This adds some non-linearity to the formula and allows us
    # to learn more complicated functions.
    layer = tf.nn.relu(layer)

    # Note that ReLU is normally executed before the pooling,
    # but since relu(max_pool(x)) == max_pool(relu(x)) we can
    # save 75% of the relu-operations by max-pooling first.

    # We return both the resulting layer and the filter-weights
    # because we will plot the weights later.
    return layer, weights

Le code parle de lui mĂŞme si vous connaissez les convolutions.

stride

strides=[1, 1, 1, 1] c’est Ă  dire dĂ©calage de 1 partout (le minimum).

Pour rappel :

The amount by which the filter shifts is the stride

Vous vous demandez pourquoi un vecteur de 4 Ă©lĂ©ments. L’explication est donnĂ©e dans les commentaires du code :

The first and last stride must always be 1,
# because the first is for the image-number and
# the last is for the input-channel.

padding

padding=’SAME’. Pour une explication du padding par Tf, voir ici.

pooling

On en a déjà parlé plus haut, voir ici.

reLu

Idem, voir ici.

Fully connected layer

Pour la création de cette couche entièrement connectée, on crée un outil qui redimensionne (2D) les tensors (4D) flatten_layer puis on applique layer = tf.matmul(input, weights) + biases avec ou sans reLu. Rien de très différent du tutoriel #1.

Accuracy

Le code qui suit est analogue Ă  celui du tutoriel #1. Il n’est pas commentĂ©.

Après 10000 itérations on obtient accuracy = 98,8% ! cet qui est nettement mieux que les 91,9% de la fois précédente.

Seulement, ce code est compliquĂ©, bavard, laborieux. On peut faire plus simple avec Keras. C’est ce que nous montrons avec le tutoriel #3.

 

 

 

 

 

 

 

 

 

 

 

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *