Google Cloud Platform regorge de reconnaissance d’images, de vidéos ou même de textes. On peut aussi penser héberger sa solution TensorFlow sur un serveur et y faire appel au besoin.
Mais la qualité du réseau est fluctuante. On se retrouve régulièrement dans des situations où le réseau est insuffisant voir manquant. On peut vouloir aussi être plus rapide qu’un échange avec un serveur, ou même être indépendant de tous serveurs. C’est dans ces problématiques qu’intégrer TensorFlow directement sur le device est une solution.
Google propose deux solutions de Machine Learning sur mobile : TensorFlow Mobile et son nouveau petit frère, TensorFlow Lite.
TensorFlow 1.0 a été annoncé le 15 février 2017. Il contenait sa version mobile : TensorFlow Mobile. Cette version est complète ; on peut y utiliser toutes les fonctionnalités disponibles dans TensorFlow.
TensorFlow Lite est arrivé le 14 novembre 2017. Il est considéré comme une évolution de TensorFlow Mobile. Considéré comme plus léger et plus rapide que ce dernier, il n’est néanmoins qu’en preview et ne dispose donc pas de toutes les fonctionnalités de TensorFlow.
Google préconise d’utiliser TensorFlow Mobile en production tant que TensorFlow Lite est en preview. Sur le Github du projet TensorFlow, la version Lite est toujours considérée comme une contribution.
Pour comprendre ces deux versions, il existe dans le codeLab TensorFlow for Poet 2 deux versions de la même application de reconnaissance d’images. L’une utilise TensorFlow Mobile, l’autre TensorFlow Lite. Dans les deux cas, le modèle est entraîné préalablement sur desktop (apprentissage), avant d’être intégré à l’application mobile (où il sera uniquement prédictif). Il est ici question de dépecer le code pour en extraire uniquement les principes de base, et ainsi comparer leur facilité d’utilisation.
Il suffit de tirer la librairie TensorFlow Android directement du jcenter:
repositories { jcenter() }
dependencies { compile 'org.tensorflow:tensorflow-android:1.4.0' }
Tensorflow Lite est encore en preview. Il faut donc le tirer depuis Bintray:
android { [...]
aaptOptions { noCompress "tflite" noCompress "lite" } } repositories { maven { url 'https://google.bintray.com/tensorflow' } }
dependencies { compile 'org.tensorflow:tensorflow-lite:0.1.1' }
La propriété noCompress du bloc aaptOptions précise que les extensions tflite et lite ne doivent pas être stockées compréssées dans l’APK.
L'intégration est simple d'un côté comme de l'autre, même si TensorFlow Lite est encore en preview.
TensorFlow Mobile nécessite un modèle au format Protocol Buffer. Ce modèle est donc d’abord entraîné sur desktop et ensuite intégré dans les assets du projet Android.
Ici, on utilise l’objet TensorFlowInferenceInterface. On l’initialise en lui donnant le nom du modèle, présent dans les assets (nommé graph.pb dans cet exemple) :
val modelFileName = "file:///android_asset/graph.pb" inferenceInterface = TensorFlowInferenceInterface(assetManager, modelFileName)
Avant de lancer la prédiction, il faut “nourrir” notre objet TensorFlowInferenceInterface avec l’image que l’on veut analyser. Le bitmap de l’image est donc déjà transformé en tableau de pixels avant d’être transféré dans un tableau de float (imgValues):
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); for (int i = 0; i < pixels.length; ++i) { final int val = pixels[i]; imgValues[i * 3 + 0] = (((val >> 16) & 0xFF) - imageMean) / imageStd; imgValues[i * 3 + 1] = (((val >> 8) & 0xFF) - imageMean) / imageStd; imgValues[i * 3 + 2] = ((val & 0xFF) - imageMean) / imageStd; }
Le tableau de float (imgValues) “nourrit” ensuite l’objet TensorFlowInferenceInterface:
inferenceInterface.feed(inputName, imgValues, 1, size, size, 3);
La commande run lance la prédiction de l’image contenue dans imgValues grâce au modèle graph.pb:
inferenceInterface.run(outputNames);
Enfin, grâce à la commande fetch, les probabilités de chaque possibilité contenue dans le modèle sont injectées dans le tableau de float outputs.
inferenceInterface.fetch(outputName, outputs);
Il suffit après de comparer les résultats avec le tableau de labels (chargé depuis label.txt), et en extraire, par exemple, la solution la plus probable :
labelList .mapIndexed { id, label -> Pair(label, outputs[id]) } .maxBy { it.second }?.first ?: "Unknown"
A la différence de TensorFlow Mobile, le modèle ingéré par TensorFlow Lite doit être en Flat Buffer. Un convertisseur est proposé pour transformer un modèle produit en Protocol Buffer en Flat Buffer.
Le classificateur TensorFlow Lite est appelé Interpreter. On le crée en lui injectant le modèle, mais à la différence de TensorFlow Mobile, il faut charger dans un ByteBuffer avant (lu depuis graph.lite) :
val modelFile = activity.assets.openFd("graph.lite").let { FileInputStream(it.fileDescriptor).channel.map( FileChannel.MapMode.READ_ONLY, it.startOffset, it.declaredLength ) } tflite = Interpreter(modelFile);
Ensuite, la détermination du résultat de TensorFlow Lite se fait en une seule commande. On transforme le bitmap en tableau de pixels avant de l’injecter dans un ByteBuffer. L’Interpreter prend alors ce ByteBuffer en premier paramètre et un tableau de float vide (ouputs) en second paramètre. La commande run remplit le tableau outputs avec les probabilités de chaque label (contenu dans label.txt).
val newBitmap = Bitmap.createScaledBitmap(bitmap, SIZE_X, SIZE_Y, false) newBitmap.getPixels(pixels, 0, newBitmap.width, 0, 0, newBitmap.width, newBitmap.height) pixels.forEach { pixel -> imgData.putFloat(((pixel shr 16 and 0xFF) - mean) / std) imgData.putFloat(((pixel shr 8 and 0xFF) - mean) / std) imgData.putFloat(((pixel and 0xFF) - mean) / std) } tflite.run(imgData, outputs)
Finalement, on fait concorder le tableau de labels avec le tableau de probabilités outputs. Ici, on récupère le label le plus probable :
labelList .mapIndexed { id, label -> Pair(label, outputs[0][id]) } .maxBy { it.second }?.first ?: "Unknown"
Les deux versions de TensorFlow pour Android sont plutôt simples à utiliser. Mais la victoire peut revenir à TensorFlow Lite pour deux raisons :
Les deux versions de l’application de reconnaissance d’images ont été testées en cold start sur un Sony Xperia sous Android 7.1.1
TensorFlow Mobile | TensorFlow Lite | |
Taille APK | 23,3 Mo | 8,7 Mo |
Temps de prédiction | environ 175 ms | environ 135 ms |
Consommation CPU | environ 30% | environ 30% |
Consommation Mémoire | Varie entre 69 et 80 Mb | Varie entre 51 et 61 Mb |
Les résultats sont largement en faveur de TensorFlow Lite avec un APK beaucoup plus léger et un temps de prédiction plus rapide et moins coûteux.
TensorFlow Lite est, comme promis par Google, plus performant et facile d’utilisation. Néanmoins, on peut regretter qu’il soit encore aujourd’hui en preview, et considéré comme une contribution de la librairie TensorFlow. Il est donc encore difficile d’imaginer TensorFlow Lite en production. Mais TensorFlow Mobile reste très gourmand pour la batterie d’un smartphone.
TensorFlow, et plus généralement le Machine Learning, offre de nombreuses possibilités. On pourrait imaginer personnaliser à l’extrême une application à son utilisateur, ou de façon plus raisonnable, prédire ses choix mineurs. Mais les exemples mobiles présentés par Google sont essentiellement de la reconnaissance d’image.
Dans un environnement offline ou d’interactions rapides avec l’utilisateur, embarquer TensorFlow directement sur le device peut apporter une nouvelle dimension à nos applications. Les use cases sont encore peu nombreux mais nous avons la conviction que des problématiques liées à l’intelligence de nos smartphones vont se développer d’ici peu.
Cet article était une introduction à l’utilisation de TensorFlow en tant que développeuse Android. Dans une seconde partie, je vous présenterai l’application de TensorFlow Mobile ou Lite dans un use case réel.