Quand le Deep Learning permet l’apprentissage par renforcement des robots industriels – 4ième partie

Le second billet de notre série précisait les grands axes de notre architecture cible. Si l'on repart de l'architecture complète présentée à la fin de ce billet, intéressons-nous à présent à i) la mise en place d'une interface web d'évaluation du modèle sur Azure ainsi qu'ii) au format des échanges.

La mise en place d'une interface web (API REST) d'évaluation du modèle sur Azure

Dans le cadre du projet, nous souhaitions intégrer directement le modèle de prédiction au sein du robot. Ce modèle offrait l'avantage de ne pas faire transiter l'information entre plusieurs systèmes, résultant d'un gain de performance certain.

Cependant, le robot embarquant un environnement Windows Embedded 32 bits, il n'était pas possible d'utiliser CNTK dessus. Pour remédier à cela, et gagner en souplesse, au détriment de la performance, nous avons choisi d'intégrer l'interface d'évaluation au sein d'une API REST hébergée dans Azure.

L'interfaçage HTTP / REST

Afin de pouvoir évaluer notre modèle, nous nous sommes donc orienté vers une architecture micro-services, exposant deux point d'entrées vers l'API :

  • L'un pour recueillir les informations à propos du modèle de Deep Learning,
  • L'autre pour faire l'évaluation en fournissant l'image à évaluer.

Pour ce qui est du second point d'entrée, l'évaluation de l'image est réalisée via un appel « HTTP POST » contenant dans le corps de la requêtes les données binaires de l'image, lesquelles sont encodées en base 64 à des fins de transport.

L'un des principaux désavantages de cet encodage provient du fait que la base 64 augmente en moyenne de 30% le poids de l'information, cependant la compression du contenu proposée par HTTP permet d'en limiter l'impact.

L'optimisation de l'évaluation

Le second point qu'il était important de vérifier concerne le temps de réponse de l'API. En effet, il n'est pas envisageable d'obtenir des temps de réponse moyens de plus d'une seconde, et ce afin de conserver cet aspect interactif avec le robot.

Le modèle généré par CNTK et chargé par l'API pèse environ une centaine de mégaoctets, résultant d'un temps de chargement d'environ 2 secondes sur Azure. Ceci conduit au minimum 2 secondes à chaque requête, rien que pour charger le modèle de Deep Learning en mémoire et être à même de faire une évaluation.

Afin de remédier à ce problème, nous avons décidé d'utiliser un patron de conception (design patten) nommé « Object Pooling ». Ce dernier encourage à réutiliser des objets déjà instanciés en mémoire, ou pour lesquels l'initialisation prend du temps. Parmi les cas les plus récurrents d'utilisation de ce patron, on trouve les connexions aux bases de données distances, lesquels sont maintenues et réutilisées entre plusieurs transactions, dans le but d'en réduire la latence.

Nous appliquons ici exactement le même schéma. Ainsi, le modèle de prédiction est chargé en mémoire. Il est conservé entre plusieurs appels à l'API. Le nombre de modèles disponibles à un instant t pouvant être nul, la requête est mise en attente. Le temps d'évaluation est très largement inférieur au temps d'instanciation d'un nouvel évaluateur. Le bon dimensionnement du pool est donc nécessaire.

Quid du format des échanges ?

Toute interconnexion de systèmes hétérogènes entraine inévitablement la mise en place d'un format d'échange de données.

Dans cette optique, notre choix s'est porté logiquement sur le format JSON (JavaScript Object Notation) lequel propose plusieurs avantages :

  • Possibilité d'être lu et compris par un humain
  • Supporté par défaut par les différentes plateformes utilisées
  • La verbosité réduite comparé à des formats tels qu'XML (et un moindre outillage).

La structure des échanges avec l'interface web d'évaluation

Le système d'évaluation est basé sur l'utilisation du Framework CNTK (Computational Network ToolKit), l'outil de Deep Learning développé par Microsoft Research et disponible en Open Source sur le repo/la forge communautaire GitHub. A ce propos, le récent billet CNTK, quoi de neuf ? disponible sur ce même blog revient sur les évolutions en continu dont bénéficie ce Framework.

Nous exposons ce modèle pré-entrainé via un micro service REST hébergé dans Azure Web API.

Le modèle de Deep Learning que nous avons mis en place, utilise en entrée, des images BGR (à savoir le format OpenCV) de 224 x 224 pixels. Les images fournies à l'API doivent donc se conformer à ces spécifications. Les opérations prise en charge côté serveur sont : la décompression de l'image, ainsi que l'extraction des pixels dans le schéma attendu par CNTK.

Si vous souhaitez réutiliser le code que nous avons mis en place afin d'évaluer des images au travers de CNTK, sachez que celui-ci est disponible ici, sur le dépôt/la forge communautaire GitHub.

Les données envoyées à l'api sont décrites dans le schéma suivant :

 {
  "$schema": "https://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "imageB64": {
      "type": "string"
    }
  },
  "required": [
    "imageB64"
  ]
}

Ce message est très basique, et permet uniquement de transporter les données binaires – interprétées en Base64 – vers le serveur d'évaluation.

Une fois les différentes opérations d'extractions et d'évaluation opérées, le serveur renvoie, en cas de succès, un message de la forme :

 {
  "$schema": "https://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "ExecutionMilli": {
      "type": "integer"
    },
    "Bottle": {
      "type": "number"
    },
    "Can": {
      "type": "number"
    },
    "Cup": {
      "type": "integer"
    }
  },
  "required": [
    "ExecutionMilli",
    "Bottle",
    "Can",
    "Cup"
  ]
}

Ce message comporte plusieurs informations :

  • Une valeur permettant de monitorer le temps nécessaire à l'évaluation du modèle (pour un réseau de neurones, on parle de l'étape de « feed-forward »).
  • La probabilité que l'image en entrée soit une bouteille.
  • La probabilité que l'image en entrée soit une canette.
  • La probabilité que l'image en entrée soit un gobelet.

A noter que chaque évaluation faite par notre API est stockée dans un compte de stockage Azure afin d'être réutiliser par la suite, lors des apprentissages futurs.

La priorité, là encore, était de conserver une structure simple et concise nous permettant de récupérer les différentes informations nécessaires à l'utilisation du service.

La structure des échanges entre Azure IoT Hub, le robot et la Gateway

Les échanges entre Azure IoT Hub, le robot et la Gateway partagent un format générique commun en JSON nous permettant d'avoir une certaine souplesse dans l'utilisation et l'implémentation, tout en conservant dans le même temps une topologie simple.

Il utilise pour cela :

  • Un identifiant « action », permettant de désigner la portée du message (à quel service le message est adressé).
  • Un référentiel temporel, permettant de s'assurer de la validité de l'action.
  • Un champ « parameters », sous forme de dictionnaire, permettant de renseigner les différents paramètres nécessaires relatifs à l'action définie.
 PROPERTIES 
{
  "$schema": "https://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "action": {
      "type": "string"
    },
    "time": {
      "type": "integer"
    },
    "params": {
      "type": "object",
      "properties": {}
    }
  },
  "required": [
    "action",
    "time"
  ]
}

La structure des échanges avec l'API d'acquisition d'images

Le périphérique d'acquisition d'images est exposé au travers d'une API REST en réponse à une requête HTTP GET. Cette requête doit fournir deux paramètres obligatoires, la largeur et la hauteur de l'image à retourner.

Les données de l'acquisition (données binaires), sont renvoyées par l'API sous la forme d'une chaine de caractère en base 64, augmentant ainsi la taille de ces dernières d'environ 30%.

Afin d'optimiser légèrement le trafic des données, nous avons activé la compression des réponses pour les données de type « application/json », résultant d'un poids total environ 15% inférieur à la taille initiale de l'image.

La structure du message renvoyée par l'API est la suivante :

  • Un champ temporel, permettant d'assurer la validité du message
  • Un champ « device », indiquant quel périphérique a été utilisé pour réaliser l'acquisition
  • Deux champs « width » et « height », indiquant le format de l'image retournée
  • Un champ « imageB64 », contenant les données binaires en Base64 de l'image.
 {
  "$schema": "https://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "time": {
      "type": "integer"
    },
    "device": {
      "type": "string"
    },
    "width": {
      "type": "integer"
    },
    "height": {
      "type": "integer"
    },
    "imageB64": {
      "type": "string"
    }
  },
  "required": [
    "time",
    "device",
    "width",
    "height",
    "imageB64"
  ]
}

Quelques conclusions préliminaires

Comme vous pouvez le constater, l'architecture ainsi mise en œuvre utilise un nombre de système et de briques logicielles complémentaires, adressant chacun/chacune un point précis du cahier des charges.

Les différents développements étant intervenus au niveau de l'interfaçage avec le monde extérieur pour l'évaluation du modèle, notamment au travers de l'API REST, sont d'autant plus de briques logiciels qu'il est nécessaire de prendre en compte.

La conception des différentes API s'est fait de manière à encourager l'utilisation de micro-services et de fournir des interfaces simples permettant aux personnes de Kuka et Microsoft d'en comprendre aisément le fonctionnement, ainsi que les interactions.

Le diagramme ci-dessous reprend les différents points énoncés plus hauts, et fournit une vue macroscopique de l'architecture globale du projet, ainsi que de l'utilisation des données.

Figure 2 : Architecture globale proposée et mise en œuvre dans le cadre de l'étude

Ceci conclut ce billet. Rendez-vous au prochain et dernier épisode de cette série ! ;-)