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.
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é.
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 :
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 :
où est la prédiction et
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.
>>>
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.
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