INTELLIGENCE ARTIFICIELLE

Exécuter des algorithmes de Deep Learning de moins de 5Mo sous Windows ou Linux

9 min

Ces dernières années, les modèles de Deep Learning se sont avérés surperformer les techniques de Machine Learning à l’état de l’art et d’autres approches plus traditionnelles dans de nombreux domaines dont, notablement, la vision par ordinateur (computer vision). Un nouveau défi est né à mesure que progressent les cas d’usage du Deep Learning et ses performances grandissantes : appliquer le Deep Learning directement sur les terminaux finaux (edge devices). Les algorithmes de Deep Learning tournent généralement sur des serveurs, qui traitent des requêtes des terminaux et y répondent au moyen des sorties de l’algorithme. Cette interaction terminal-serveur implique une grande latence pour l’obtention des résultats, puisqu’elle nécessite des allers-retours dans leurs communications. Pour plusieurs cas d’usage – par exemple, les véhicules autonomes – ce délai n’est pas acceptable. Pour que les données soient traitées rapidement, les algorithmes de Deep Learning peuvent être appliqués directement sur les terminaux.

Cette solution apporte son lot de problèmes, au nombre desquels la taille des librairies et des modèles requis pour faire fonctionner des algorithmes de Deep Learning complexes. Chez HarfangLab, nous avons abordé cette problématique au prisme de deux principes : 1) utiliser des librairies réduites qui ne contiennent que le code dont nous avons besoin, et 2) réduire la taille du modèle tout en conservant ses performances. Dans notre cas, nous avons dû créer tflite-runtime (version réduite de tensorflow pour la prédiction) pour les plateformes souhaitées (Windows 32 et 64 bits), réduire la taille de numpy en supprimant du code non nécessaire, et diviser la taille de notre modèle par 10.

Voici comment nous avons accompli ces étapes et comment nous parvenons désormais à faire fonctionner des réseaux de neurones de Deep Learning complexes sur des machines Windows et Linux, avec des wheels Python qui pèsent moins de 5 Mo. Les différentes wheels sur-mesure que nous avons construites et qui sont requises pour faire fonctionner ces algorithmes seront aussi disponibles depuis les dépôts suivants.

Faire fonctionner du Deep Learning avec de dépendances réduites

Tensorflow et tensorflow-lite

Utilisation de tensorflow et tensorflow-lite

Tensorflow est l’une des principales librairies de Deep Learning fournissant un framework simple et efficace pour mettre en œuvre et entraîner des modèles perfectionnés. Une fois le modèle entraîné avec ce framework, il présente le sérieux avantage de permettre de simplement le compresser en un modèle tensorflow-lite. L’inférence peut donc fonctionner en n’utilisant que ce modèle et l’interpréteur tensorflow-lite (tflite-runtime), en laissant de côté toutes les dépendances non nécessaires qui viendraient avec les bibliothèques tensorflow et tensorflow-lite. Aussi, en utilisant tflite-runtime, nous évitons la bibliothèque tensorflow (1,2 Go) et la bibliothèque tensorflow-lite (50 MB). Au lieu de cela, nous récupérons seulement l’interpréteur qui pèse 1 Mo pour charger et lancer le moteur en inférence.

Installer et utiliser tflite-runtime est très simple. Il est possible de l’installer via une wheel avec pip install tflite-runtime. Malheureusement, il n’existe pas de wheel Windows 64-bits pour tensorflow après la version 2.5.0 (et aucune pour Windows 32-bits). Il existe quelques lignes directrices sur la manière de les construire, publiées par tensorflow : https://www.tensorflow.org/lite/guide/build_cmake_pip and https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/tools/pip_package

Ces wheels pour tensorflow 2.7.0 sont disponibles depuis notre dépôt GitHub. Voici quelques conseils supplémentaires pour les construire.

Construire les wheels

Pour Linux, vous trouverez les wheels tflite-runtime sur PyPI.

Pour construire tflite-runtime pour Windows x86 et Windows x64, en plus d’une bonne dose de courage, il vous faudra une machine virtuelle Windows x64 (ou votre ordinateur s’il est en Windows x64). Le constructeur CMake officiel doit être utilisé pour cette tâche. Pour lancer la construction, vous devez avoir installé Visual Studio Build Tools 2019 et Python 64-bits (python -V et python3 –V ne devrait pas produire d’erreurs) et Python 32-bits (qui doit être dans la même version que celle choisie pour Python 64-bits). Il vous faut aussi installer numpy, wheel et pybind11.

Voici la commande pour x64 et x86 : tensorflow/lite/tools/pip_package/build_pip_package_with_cmake.sh windows

Nous avons rencontré de nombreux problèmes avec cette commande. Voici comment nous avons modifié les lignes critiques du fichier thetensorflow/lite/tools/pip_package/build_pip_package_with_cmake.sh :

Copy

PYTHON_LIB=$(${PYTHON} -c "import distutils.sysconfig as sysconfig; print(sysconfig.PREFIX + '\libs')")
PYTHON_INCLUDE=$(${PYTHON} -c "from sysconfig import get_paths as gp; print(gp()['include'])")
PYBIND11_INCLUDE=$(${PYTHON} -c "import pybind11; print (pybind11.get_include())")
NUMPY_INCLUDE=$(${PYTHON} -c "import numpy; print (numpy.get_include())")
BUILD_FLAGS=${BUILD_FLAGS:-"-I${PYTHON_INCLUDE} -I${PYBIND11_INCLUDE} -I${NUMPY_INCLUDE} /EHsc"}
cmake -A Win32 -DCMAKE_C_FLAGS="${BUILD_FLAGS}" -DCMAKE_CXX_FLAGS="${BUILD_FLAGS}" -DCMAKE_SHARED_LINKER_FLAGS="/libpath:$PYTHON_LIB" "${TENSORFLOW_LITE_DIR}" -DTFLITE_ENABLE_XNNPACK=OFF -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=True
cmake --build . --verbose -j ${BUILD_NUM_JOBS} -t _pywrap_tensorflow_interpreter_wrapper --config Release
cd "${BUILD_DIR}"
cp "${BUILD_DIR}/cmake_build/Release/_pywrap_tensorflow_interpreter_wrapper.dll ${BUILD_DIR}/tflite_runtime/_pywrap_tensorflow_interpreter_wrapper${LIBRARY_EXTENSION}"

Numpy, taille des wheels et des dépendances

Pour x86, nous avons ajouté -A Win32 pour préciser que la construction doit être pour Windows 32-bit.

Maintenant que les wheels tflite-runtime sont construites, nous pouvons aisément les installer sur une machine Linux ou Windows et faire fonctionner un modèle avec cette wheel de 1 Mo. Toutefois, la wheel tflite-runtime requiert numpy comme dépendance. Selon votre machine, numpy devrait représenter entre 10 et 16 Mo. Il est possible d’optimiser la taille des dépendances requises pour faire fonctionner le modèle en réduisant la taille de numpy. Pour ce faire, vous pouvez construire numpy avec des exigences précises et réduire manuellement la taille de la wheel, afin de ne conserver que les fonctionnalités essentielles. Pour Windows x86, Windows x64, et Linux, suivez les étapes ci-dessous :

  • CFLAGS="-g0 -Wl,--strip-all -I/usr/include:/usr/local/include -L/usr/lib:/usr/local/lib" pip install --cache-dir . --compile --global-option=build_ext --global-option="-j 4" numpy
  • Construire la wheel et lancer wheel unpack name-of-wheel.whl
  • Trouver et supprimer le fichier RECORD dans le dossier extrait
  • Supprimer aléatoirement du dossier extrait quelques fichiers ou répertoires qui ne semblent pas nécessaires (numpy fournit des tests, de la documentation, des données, etc. qui augmentent grandement sa taille)
  • Packager le fichier à nouveau avec wheel pack name-of-unpacked-folder
  • Installer la wheel avec pip
  • Installer tflite-runtime
  • Vérifier que votre modèle effectue une inférence sans échouer
  • Supprimer d’autres fichiers tant que votre modèle peut toujours faire fonctionner l’inférence

Avec cette méthode manuelle, nous avons pu faire passer la taille de la wheel numpy-1.22.3 à moins de 3 Mo.

Réduire la taille des modèles de Deep Learning

Après les étapes précédentes, les dépendances nécessaires à l’exécution des algorithmes de Deep Learning ne devraient plus représenter que 4 Mo environ. Le seul objet manquant pour exécuter un algorithme est le modèle lui-même. La taille du fichier tflite contenant le modèle augmente rapidement avec le nombre de paramètres du modèle. Voici les techniques que nous avons utilisées pour réduire la taille nécessaire à l’enregistrement du modèle.

Quantification du modèle

Un moyen simple et courant de réduire la taille d’un modèle est la quantification post-entraînement. Cette méthode consiste à réduire le nombre de bits utilisés par les paramètres et opérations du modèle. En général, les paramètres du modèle sont encodés sur 32 bits dans tensorflow, mais ils peuvent être réduits à des flottants de 16 bits avec peu de pertes de performance. De manière plus radicale, les flottants en 32 bits peuvent aussi être convertis en entiers de 8 bits avec un risque de perte de performance plus important. D’autres variations de ces techniques sont disponibles et faciles à mettre en œuvre avec tensorflow-lite, comme décrit dans la documentation : https://www.tensorflow.org/lite/performance/post_training_quantization#dynamic_range_quantization.

Les couches denses et GlobalAveraging

Dans de nombreux réseaux de neurones (en particulier dans les réseaux de neurones convolutifs), les couches denses situées à l’extrémité du réseau et produisant les prédictions représentent plus de 80 % des paramètres du modèle. Ces couches critiques peuvent être nécessaires à la discrimination des entrées de la tâche, bien que les couches convolutives précédentes soient souvent suffisantes. Le simple fait de supprimer partiellement les couches denses (ou de les remplacer entièrement par une couche de global average pooling) peut réduire considérablement la taille du modèle sans trop modifier ses prédictions.

Le Pruning

Cette dernière méthode consiste à supprimer les neurones et synapses qui contribuent le moins aux outputs du modèle. Cette méthode est rapide à mettre en œuvre avec tensorflow, mais elle nécessite plus d’expérimentations et de réglages fins que les deux précédentes méthodes présentées pour optimiser le rapport taille/performance.

Faire fonctionner des algorithmes de Deep Learning de moins de 5 Mo sur des terminaux

En construisant l’interpréteur tensorflow-lite, en supprimant les parties inutiles de numpy, en personnalisant nos modèles et en limitant leur taille grâce à la quantification et au pruning, nous exécutons des algorithmes de Deep Learning sur des machines sous Windows et Linux, avec des wheels Python de moins de 5 Mo !