TensorFlow, le nouveau framework pour démocratiser le Deep Learning

Quand Google dévoile une partie de son secret en novembre 2015 en rendant TensorFlow open source sous la licence Apache 2.0, un grand nombre de personnes s’y est naturellement mis. Sa popularité est justifiée d’une part par son origine et d’autre part par sa portabilité, sa flexibilité et une large communauté (plus de 10 000 commits et 3 000 repos en un an).

C’est quoi exactement TensorFlow ?

TensorFlow est un système d’apprentissage automatique qui est intégré à plusieurs produits du géant de Mountain View. Il se présente comme une bibliothèque dédiée au calcul numérique et en est à sa version 1.3.
Initialement, le but de TensorFlow était d’optimiser les calculs numériques complexes, mais aujourd’hui il est très connu pour résoudre des problèmes de Deep Learning. Toutefois, ce framework est assez général pour être utilisé à d’autres fins.
Les entreprises qui utilisent TensorFlow sont entre autres Google, DeepMind, OpenAI, Snapchat, Airbnb, Uber, Airbus, eBay, Dropbox. Aussi plusieurs projets sont menés sous ce framework. On peut citer le fameux Google Translate ou bien WaveNet de DeepMind qui génère des audios à partir de textes. Des radiologues utilisent aussi TensorFlow pour identifier des signes de Parkinson grâce aux analyses de scans.
Les deux composantes principales de TensorFlow sont : les Opérations et les Tensors.
L’idée est de créer un modèle, un ensemble d’Opérations, puis d’alimenter ce modèle avec les données ou Tensors. Ces Tensors vont couler dans le modèle comme un flux (Flow): d’où le nom TensorFlow.
Dans ce premier article, nous allons détailler les quatre étapes importantes dans la philosophie TensorFlow à savoir : les «Tensors», les «Operations », les «Sessions» et les «Graphs». Puis avant d’aborder les réseaux de neurones dans les prochains numéros, nous finirons cet article en créant un modèle de régression simple sous TensorFlow. Pour la suite de l’article, vous pouvez l’installer pour Java, Go, Python ou C.

Les différentes types de Tensors

– Un Tensor peut être vu comme un tableau de données (array) multidimensionnel à taille dynamique. Pour en créer un avec une valeur que l’on souhaite garder fixe, on utilise `tf.constant()`
– Par contre, si on souhaite pouvoir modifier la valeur d’un Tensor, on le définit avec `tf.Variable()`. Les Variables au sens de TensorFlow sont utiles quand on souhaite maintenir l’état d’une variable durant une première exécution puis le modifier pour de prochaines exécutions.
– `tf.placeholder()` permet de définir un placeholder. C’est simplement une Variable dont la valeur sera spécifiée plus tard lors du run d’un calcul. Le but de ces “espaces réservés” est de pouvoir définir une série d’opérations plus ou moins complexes. Puis d’alimenter ces placeholders plus tard lors du lancement de la Session grâce au “feed dictionary” nommé `feed_dict`.
Il est important de noter la possibilité de transformer les données existantes sous Python (`NumPy`, `Pandas`, … ) en tenseur par la méthode `tf.convert_to_tensor`.
Fig 1: Quelques exemples de structures de tenseurs[/caption]

Les «Operations»

En tant que bibliothèque de calculs numériques au même titre que `Numpy`, on peut effectuer plusieurs calculs sur TensorFlow comme le montrent les quelques exemples ci-dessous :
>>>

import tensorflow as tf
a=tf.constant([3,6])
b=tf.constant([2,2])
tf.add(a,b)
tf.add_n([a,b,b]) # a + b + b
tf.multiply(a,b)

Le tableau ci-dessous donne une idée non exhaustive, par groupe, des opérations possibles et imaginables via TensorFlow.

Groupe d’opérations sur les tenseurs

Les Sessions

Définissons et affichons une chaîne de caractères hello via TensorFlow.
>>>

hello = tf.constant('Unkipple welcomes you to TensorFlow')
print(hello)
hello

Sortie :

Tensor("Const_2:0", shape=(), dtype=string)

Comme vous pouvez voir, le tenseur constant hello n’a pas été exécuté. Pour exécuter une opération, il est primordial d’ouvrir une Session grâce à la méthode `Session.run()`. Ce système d’exécution permet d’éviter de retourner dans du pur Python à chaque étape et d’exécuter toutes les opérations d’un graphe en une seule fois dans un même backend optimisé.
>>>

with tf.Session() as sess:
    print(sess.run(hello))

Sortie :

 Unkipple welcomes you to TensorFlow

Toutefois, c’est un système assez contraignant, surtout quand on travaille en mode interactif, par exemple en ligne de commande ou avec des notebooks. Dans ce cas précis, il peut être plus agréable d’utiliser une InteractiveSession. On n’a plus qu’à utiliser la méthode `.eval()` sans passer par la Session.
>>>

sess  =  tf.InteractiveSession()
a  =  tf.constant(5.0)
b  =  tf.constant(6.0)
c  =  a*b
print("La multiplication de a par b donne : %i" % c.eval())
sess.close()

Sortie :

   La multiplication de a par b donne : 30

L’exemple suivant montre l’utilisation d’une Session avec un placeholder :
>>>

# Créer un placeholder de type float 32-bit, vecteur à 3 éléments
a=tf.placeholder(tf.float32,shape=[3])
# Créer un vecteur constant à 3 éléments de type float 32-bit
b=tf.constant([5,5,5],tf.float32)
# Utiliser le placeholder comme une constante ou une variable
c=tf.add(a,b)   # On peut aussi faire simplement a+b
with tf.Session() as sess:
    print(sess.run(c,{a:[1,2,3]})) # le second paramètre de run met en valeur les placehoders

Sortie :

   [ 6.  7.  8.]

Le TensorBoard

TensorFlow nous permet de définir des opérations très complexes. TensorBoard est un formidable outil pour visualiser le graphe de ces opérations. Cet outil de TensorFlow permet aussi de debugger et d’optimiser les programmes. Notre prochain article lui sera exclusivement consacré.

Fig 2 : Exemple de visualisation de graphe via TensorBoard

La structure du code

En général, les étapes d’un code TensorFlow peuvent être divisées en deux parties :
– Construction et assemblage de toutes les opérations du graphe.
– Exécution des opérations sus-définies via une Session.

TensorFlow : Un premier modèle étape par étape

Dans la dernière partie de ce post, nous prenons, étape par étape, un exemple de régression linéaire simple avec TensorFlow. Une régression linéaire simple à pour but de prédire une variable continue Y grâce à une autre X via une relation linéaire de la forme Y = wX+b+e où w et b sont des coefficients à déterminer et e est le terme des erreurs.
Cela revient à faire passer une droite dans un nuage de points pour résumer au mieux ceux-ci. Autrement dit, cela revient à résoudre le problème suivant :

Fig 3: Cette figure représente la régression linéaire d’une variable X par la variable Y [1]
Dans notre exemple, le but est d’expliquer le nombre de vols (cambriolages) Y à partir du nombre de départs de feu dans la ville de Chicago (que nous nommerons par la variable X. Le jeu de données est disponible
Le jeu de données est disponible [2]
Phase 1: Construction du graphe
Etape 1: Charger les données depuis un fichier `.xls`
>>>

data_file = 'data/fire_theft.xls'
import xlrd
book = xlrd.open_workbook(data_file, encoding_override='utf-8')
sheet = book.sheet_by_index(0)
import numpy as np
data = np.asarray([sheet.row_values(i) for i in range(1, sheet.nrows)])
n=sheet.nrows - 1
import tensorflow as tf
n_samples = tf.constant(n,dtype=tf.float32)

Etape 2: Créer deux placeholders pour X (nombre de départs de feu) et Y (nombre de vols). Tous les deux de type float32
>>>

X = tf.placeholder(tf.float32, name='X')
Y = tf.placeholder(tf.float32, name='Y')

Etape 3: Créer les Variables poids w (pour weight) et biais b, et les initialiser à 0
>>>

w=tf.Variable(0.0,name='W')
b=tf.Variable(0.0,name='b')

Etape 4: Construction du graphe du modèle de régression simple
>>>

Y_predicted= tf.add(tf.multiply(X, w),b)

Etape 5: Utiliser le RMSE pour definir la fonction de coût
La qualité de l’ajustement est mesurée par plusieurs indicateurs. Dans notre cas, on fait le choix du RMSE (pour Root Mean Square Error) qui est défini par :

\hat y_t est la prédiction et y_t  l’observation. Cela représente la somme des longueurs (élevés au carré) des traits verticaux sur la fig 2.
>>>

loss=tf.sqrt(tf.reduce_mean(tf.square(tf.subtract(Y,Y_predicted))))

Etape 6: Utiliser la descente de gradient pour minimiser la fonction de coût avec un learning_rate à 0.001
La descente de gradient est une méthode itérative d’optimisation [3]. Ici, elle est utilisée pour minimiser les erreurs commises par notre droite. À chaque itération, on choisit la meilleure pente sur notre fonction de coût pour se diriger itération après itération vers le minimum de notre fonction de coût.
Notons que le learning_rate représente la vitesse à laquelle nous souhaitons terminer nos itération.

Fig 4 : Illustration de la descente de gradient.

>>>

optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001).minimize(loss)

Il existe beaucoup d’optimiseurs sous TensorFlow dont :
– tf.train.GradientDescentOptimizer
– tf.train.AdagradOptimizer
– tf.train.AdamOptimizer
– …
Phase 2 : L’entraînement du modèle
On recherche les meilleures valeurs de w et b qui minimise notre fonction de coût.
Pour cela, on itère 1000 fois sur l’exécution de l’optimiseur. Toutes les 50 itérations, on affiche la valeur de la fonction de coût et les résultats du modèle à cette étape.
>>>

display_step = 50
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(1000):
        for x, y in data:
            sess.run(optimizer, feed_dict={X: x, Y: y})
        if (epoch+1) % display_step == 0:
            _, c = sess.run([optimizer, loss], feed_dict={X: x, Y:y})
            print "Epoch:", '%04d' % (epoch+1), "cost=", c, \
                "W=", sess.run(w), "b=", sess.run(b)
    print "Optimization Finished!"
    training_cost = sess.run(loss, feed_dict={X: x, Y: y})
    print "Training cost=", training_cost, "W=", sess.run(w), "b=", sess.run(b), '\n'

Sortie :

    Epoch: 0050 cost= 8.97787 W= 1.89358 b= 0.956991
    Epoch: 0100 cost= 8.06919 W= 1.90475 b= 1.81203
    Epoch: 0150 cost= 7.48993 W= 1.84773 b= 2.66499
    Epoch: 0200 cost= 6.58134 W= 1.85891 b= 3.51993
    Epoch: 0250 cost= 6.00211 W= 1.80189 b= 4.37287
    Epoch: 0300 cost= 5.12279 W= 1.81447 b= 5.19181
    Epoch: 0350 cost= 4.68153 W= 1.76245 b= 5.88276
    Epoch: 0400 cost= 4.05922 W= 1.75023 b= 6.56371
    Epoch: 0450 cost= 3.43692 W= 1.73801 b= 7.24466
    Epoch: 0500 cost= 3.12123 W= 1.674 b= 7.86761
    Epoch: 0550 cost= 2.57061 W= 1.66638 b= 8.45479
    Epoch: 0600 cost= 2.09304 W= 1.64437 b= 9.03802
    Epoch: 0650 cost= 1.88439 W= 1.59176 b= 9.49921
    Epoch: 0700 cost= 1.98742 W= 1.47755 b= 9.94439
    Epoch: 0750 cost= 2.09045 W= 1.36334 b= 10.3896
    Epoch: 0800 cost= 1.9766 W= 1.29473 b= 10.8327
    Epoch: 0850 cost= 1.74765 W= 1.26052 b= 11.2259
    Epoch: 0900 cost= 1.52896 W= 1.23751 b= 11.555
    Epoch: 0950 cost= 1.31027 W= 1.2145 b= 11.8842
    Epoch: 1000 cost= 1.09158 W= 1.19149 b= 12.2133
    Optimization Finished!
    Training cost= 1.06754 W= 1.19149 b= 12.2133
   

On peut observer la décroissance de la fonction de coût au fil des itérations successives.

Fig 5 : Nuage de point et meilleure droite de régression

Existe-t-il vraiment une relation entre le nombre de vols et les départs de feu à Chicago ? Si oui, cette relation est-elle linéaire ? Est-ce que ce modèle peut être généralisé sur de nouvelles données ? Et le sur-apprentissage dans tout ça? Voici quelques unes des questions qu’on peut se poser.
À l’épisode suivant, on verra comment avec TensorBoard on peut suivre l’évolution de la fonction de coût à chaque itération entre autres.
Références:
[1] Introduction to Statistical Learning with Applications in R, Trevor Hastie et al.
[2] Source du jeu de données
[3] Descente de gradient

Auteur/Autrice

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

%d blogueurs aiment cette page :