Category

VeoNews

Botpress et ses principaux concepts

By VeoNews

Les récentes évolutions dans le domaine de la compréhension du langage naturel permettent désormais aux machines de comprendre et de répondre aux messages écrits et vocaux de manière instantanée et sans interruption.

Aujourd’hui, le nombre d’utilisateurs d’applications de messagerie comme WhatsApp, Slack ou encore Skype montent en flèche. L’application Facebook Messenger à elle seule compte plus de 1,2 milliard d’utilisateurs par mois. Avec la multiplication des applications de messages, les chatbots virtuels qui imitent les conversations humaines pour résoudre diverses tâches sont de plus en plus demandés. Par exemple, le chatbot WeChat chinois peut déjà fixer des rendez-vous médicaux, appeler un taxi, envoyer de l’argent à des amis ou bien s’enregistrer pour un vol.

Est-ce que les chatbots ont aujourd’hui les ressources suffisantes pour imiter les humains ou s’agit-il simplement d’une nouvelle façon d’interagir plus naturellement avec les machines? Je vais ici présenter Botpress, une solution permettant de créer des chatbots simplement.

Botpress est une solution open-source et on-premise écrite en TypeScript et ayant pour ambition d’être le WordPress des chatbots avec une plate-forme qui facilite la construction de bots à l’aide de modules de code créés par la communauté.

Il s’agit d’une plate-forme complète livrée avec tous les outils nécessaires pour construire, déployer et gérer des bots de production. Botpress utilise notamment son propre moteur de compréhension du langage naturel (Natural Langage Understanding ou NLU) embarquée avec la solution, ce qui signifie qu’il n’y a aucun besoin d’effectuer des appels à un service web assurant ici un grand contrôle sur les données pour les créateurs de chatbots.

A cela s’ajoute un éditeur de flux visuel intuitif qui rend la solution très simple à utiliser, permettant aux personnes ayant un profil non-techniques de maintenir et de faire évoluer le bot facilement avec des parcours utilisateurs complexes.

Simple à utiliser, Botpress est cependant parfois compliqué à configurer. En effet, la documentation, bien que couvrant de nombreux aspects, n’est parfois pas mis à jour avec les évolutions (du moins lors de mon utilisation en août 2019). Or le produit étant assez récent, celui-ci évolue régulièrement.

La présence d’un débogueur de chat permet d’améliorer rapidement son bot et d’assurer une bonne compréhension sur les résultats du moteur de NLP et un contrôle fin. De plus, si les développeurs souhaitent intégrer des actions personnalisées comme effectuer une recherche dans une base de données ou un appel à un API, il suffit d’ajouter du code dans des dossiers spécifiques, par la suite facilement réutilisable, afin que celui-ci soit lancé au moment souhaité avec une gestion des événements par Botpress.

Depuis la version 12, la gestion du langage a été amélioré avec la possibilité de changer entre plusieurs langues de façon transparent. Les langues par défaut étant l’arabe, le français, l’anglais, le japonais et le portugais

Voici donc une présentation des concepts associés à Botpress afin de comprendre son mode de fonctionnement.

Dialog Engine

Botpress utilise ce que les développeurs appellent le moteur de dialogue pour gérer les conversations. Le moteur de dialogue est responsable de chaque interaction avec un bot. Il gère l’entrée utilisateur et la réponse du bot.

Vue d’ensemble

Le moteur de dialogue utilise des flux (Flows) qui représentent la logique conversationnelle globale d’un bot. Un flux est alors composé de nœuds (Nodes) qui exécutent une série d’instructions. Les instructions font partie du cycle de vie d’un nœud et peuvent exécuter des Actions. Une Action est un essentiellement un fichier javascript de code, écrit par le développeur, par Botpress ou par d’autres.

La création de bot se fait simplement à l’aide de l’interface utilisateurs. Il suffit d’ajouter des nœuds et de le relier entre eux. Au sein de ceux-ci, on définit des messages ou des actions à exécuter. Cette interface et la logique associée au bot sont sauvegardées au sein de fichier json de type *.ui.json et *.flow.json qu’il est possible de modifier manuellement.

Flows

Les bots plus complexes sont généralement décomposés en plusieurs flux plus petits au lieu d’un seul grand flux (par exemple un flux pour le traitement d’une demande de réservation et un autre flux pour une recherche d’informations). La raison de décomposer le bot en plusieurs flux est de faciliter la maintenabilité et la réutilisabilité.

Cycle de vie d’un flow

Un flux démarre toujours au startNode de son fichier *.flow.json. Le nœud de départ (startNode) indique le nom du nœud sur lequel le flux va commencer. Une fois le nœud sélectionné, le moteur de dialogue met en file d’attente les instructions du nœud actif. Ensuite, il traitera les instructions de façon séquentielle.

Le moteur de dialogue est basé sur les événements et n’est pas bloqué par défaut, ce qui signifie qu’un flux exécutera tout ce qu’il peut exécuter jusqu’à ce qu’il doive attendre.

Une fois le premier nœud traité, le moteur de dialogue passe au nœud suivant dans le flux jusqu’à la toute fin. Les nœuds ont également leur propre cycle de vie. Ce sont les nœuds qui font le travail le plus important dans un flux, celui-ci ne faisant que les orchestrer.

Exemple de flux

Nodes

Les nœuds sont les unités primaires de la logique conversationnelle du bot. Une conversation active (appelé « session ») a toujours un et un seul nœud actif. Un nœud passe généralement à un autre nœud ou à un autre flux. Si ce n’est pas le cas, la conversation est terminée. Le message suivant de l’utilisateur fera alors partie d’une session entièrement nouvelle.

Un nœud est séparé en trois étapes différentes : onEnter (A), onReceive (B) et onNext (C).

Cycle de vie d’un noeud

onEnter

onEnter est une liste d’instructions qui seront exécutées lorsque le nœud est saisi. Si plusieurs actions sont définies, elles seront toutes exécutées séquentiellement.

onReceive

onReceive est une liste d’instructions qui sera exécutée lorsque le nœud reçoit un message tout en étant le nœud actif. Dès qu’une action est définie, le nœud attend automatiquement la saisie utilisateur (nœud orange).

Lorsque cette propriété n’est pas utilisée, le nœud n’est pas bloquant (noir), ce qui signifie qu’il passe directement de onEnter à onNext.

Exemples de noeuds bloquants ou non

onNext

onNext (aussi appelé Transitions) n’est évalué qu’après l’exécution de onReceive ou onEnter et redirige vers une cible appelée une destination. Ça peut être :

  • Un nœud différent
  • Un flux différent
  • Le flux précédent
  • Lui-même (boucle de retour sur lui-même)
  • La fin de la conversation

Cas particuliers : Si aucune condition n’est définie, le comportement par défaut est que la conversation se termine. S’il y a des conditions définies mais qu’aucune ne correspond, rien ne se passe, c’est-à-dire que le nœud courant reste actif, et il s’écoule quand une condition correspond. Par défaut, onNext ne sera réessayé qu’après la réinvocation de onReceive.

Actions

Les actions sont essentiellement des fonctions côté serveur qui sont exécutées par le bot dans le cadre d’un flux conversationnel. Les actions permettent de faire beaucoup de choses :

  • Modifier l’état de la conversation
  • Envoyer des messages personnalisés à la conversation
  • Exécuter du code javascript arbitraire comme appeler une API ou stocker des données dans la base de données

Les Actions sont exécutées dans une machine virtuelle pour éviter un crash du serveur en cas d’erreur de script. Les scripts peuvent nécessiter n’importe quel module qui est chargé par Botpress par défaut (par exemple : lodash, axios, moment, nanoid, et autres).

Lorsqu’une action est invoquée par le gestionnaire de dialogue (DM), elle récupère les arguments suivants :

  • user : Inclut tous les attributs d’un utilisateur.
  • session : Inclut les variables conservées pendant toute la durée de la session.
  • temp : Contient des variables qui ne durent que pendant la durée du flux.
  • bot : Objet contenant des variables globales pour ce bot (identique pour tous les utilisateurs)
  • event : L’événement original (le plus récent) reçu de l’utilisateur au cours de la conversation.
  • args : Les arguments qui ont été passés à cette action depuis le Visual Flow Builder.
  • process : machine virtuelle « bac à sable » contenant certaines des variables d’environnements (à commencer par EXPOSED_)

Pour ajouter une action il suffit de sélectionner « Execute code » dans un nœud et de choisir l’action souhaitée.

Exemple d'ajout d'action

Hooks

Les « hooks » sont des actions exécutés lorsque des événements spécifiques se produisent. En effet, certains événements sont récurrents et il peut être utile de vouloir exécuter une action récurrente en fonction. Chaque fichier placé dans le dossier correspondant sera exécuté au moment de l’événement correspondant.

Ils sont définis globalement comme fichiers javascript dans le dossier data/global/hooks. Il est possible d’ajouter autant de fichiers que l’on veut, ils seront traités séquentiellement, par ordre alphabétique.

Attention, les hooks ne reçoivent pas automatiquement les mêmes arguments que les actions mais certains spécifiques à chaque évènement.

After Server Starts

Cet événement est appelé une fois que tous les modules et les bots sont chargés et que le bot est prêt à accepter les connexions entrantes.

After Bot Mount

Cet événement est appelé chaque fois qu’un bot est monté, que ce soit au démarrage du serveur ou lors de l’ajout de nouveaux bots lors de l’exécution.

After Bot Unmount

Cet événement est appelé chaque fois qu’un bot est démonté. C’est généralement pour faire le nettoyage quand un bot est supprimé.

Before Incoming Middleware

Ce hook est appelé lorsqu’un événement est reçu, avant tout middleware. Il est possible de modifier les propriétés des événements. Il est souvent utilisé pour définir des flags pour sauter certains traitements, par exemple pour empêcher le module QNA de faire un traitement lorsqu’il s’agit d’une réponse rapide.

After Incoming Middleware

Ce hook est appelé juste après que tous les middlewares entrants aient traité l’événement, mais avant que le moteur de dialogue ne commence à le traiter. Cela signifie que l’on a accès à toutes les données nécessaires (y compris l’intention de NLU) pour effectuer un traitement spécial et décider de ce qui se passe avec l’événement.

Before Outgoing Middleware

Ce hook est appelé avant que les réponses du bot ne soient envoyées à l’utilisateur.

Before Session Timeout

Ce hook est appelé juste avant un timeout utilisateur sur un nœud.

Before Suggestions Election

Ce hook est appelé après le classement du moteur de décision, mais avant l’élection de suggestion. Il permet de surcharger le classement du moteur de décision en modifiant directement le event.suggestions.

Sources

Botpress website,  2019

Apache Spark pour les nuls

By VeoNews

En général, lorsque vous pensez à un « ordinateur », vous pensez à une machine posée sur votre bureau à la maison ou au travail. Cette machine fonctionne parfaitement bien pour regarder des films ou travailler avec un tableur. Cependant, comme de nombreux utilisateurs le constateront probablement à un moment donné, il y a certaines choses que votre ordinateur n’est pas assez puissant pour effectuer. Un domaine particulièrement difficile est le traitement des données. Les machines individuelles n’ont pas assez de puissance et de ressources pour effectuer des calculs sur d’énormes quantités d’informations (ou l’utilisateur peut ne pas avoir le temps d’attendre que le calcul soit terminé).

Un cluster, ou groupe de machines, met en commun les ressources de nombreuses machines, ce qui nous permet d’utiliser toutes les ressources cumulées comme si elles ne faisaient qu’une. Or, un groupe de machines seul n’est pas puissant, vous avez besoin d’un cadre pour coordonner le travail entre elles. Spark est un outil qui permet de gérer et de coordonner l’exécution de tâches sur des données à travers un groupe d’ordinateurs.

Spark (ou Apache Spark) est un framework open source de calcul distribué in-memory pour le traitement et l’analyse de données massives. Il s’agit d’un ensemble d’outils structurés selon une architecture définie. Ces composants font de Spark une plate-forme unificatrice riche en fonctionnalités : elle peut être utilisée pour de nombreuses tâches qui devaient auparavant être accomplies avec plusieurs frameworks différents.

Schéma des modules Spark

Le groupe de machines que Spark utilisera pour exécuter des tâches sera géré par un gestionnaire de groupe comme le gestionnaire de groupe autonome de Spark, YARN, ou Mesos. Les demandes Spark sont ensuite soumises à ces gestionnaires de clusters qui accorderont des ressources à la demande afin que celle-ci puisse être effectuer.

Installer Spark localement

Si vous souhaitez télécharger et exécuter Spark localement, la première étape consiste à vous assurer que vous disposez de Java installé sur votre machine, ainsi qu’une version Python si vous souhaitez utiliser
Python. Ensuite, visitez la page de téléchargement officielle du projet, sélectionnez le type de paquet « Pre-built for
Hadoop 2.7 and later », et cliquez sur « Download Spark ». Il permet de télécharger un fichier TAR compressé que vous devrez ensuite extraire.

Télécharger Spark pour un cluster Hadoop

Spark peut fonctionner localement sans aucun système de stockage distribué, tel qu’Apache Hadoop. Toutefois, si vous souhaitez connecter la version Spark de votre ordinateur portable à un cluster Hadoop, assurez-vous télécharger la bonne version de Spark pour cette version de Hadoop. Mais à ce stade, il suffit de lancer Spark sur votre ordinateur portable pour commencer.

PySpark

Il est possible d’installer Spark pour Python simplement avec pip install pyspark

Cependant son utilisation nécessite bien les JARs Spark. Ce package seul n’est pas destiné à remplacer tous les autres cas d’utilisation et n’est adaptée qu’à l’interaction avec un cluster existant (qu’il s’agisse de Spark autonome, de YARN ou de Mesos), c’est-à-dire qu’il ne contient pas les outils nécessaires à la mise en place de son propre cluster Spark autonome, ce que permet la version complète de Spark téléchargé précédemment.

Lancement des consoles interactives de Spark

Vous pouvez lancer un shell interactif dans Spark pour plusieurs langages de programmation différents. Voici quelques exemples en Python et Scala.

Notez que, lorsque vous démarrez Spark dans ce mode interactif, vous créez implicitement une SparkSession qui gère l’application Spark. Lorsque vous le lancez par le biais d’une application autonome, vous devez créer explicitement la SparkSession  dans votre code.

Lancement de la console Python

Vous aurez besoin de Python 2 ou 3 installé pour lancer la console Python. Depuis le dossier d’installation de Spark, exécutez le code suivant :
./bin/pyspark

Après avoir fait cela, tapez spark et appuyez sur Entrée. La SparkSession devrait alors être affiché, dont nous allons parlé plus tôt dans cet article.

Lancement de la console Scala

Pour lancer la console Scala, exécutez la commande suivante :
./bin/spark-shell

Après avoir fait cela, tapez spark et appuyez sur Entrée. Comme en Python, vous verrez la SparkSession s’afficher.

Fonctionnement de Spark

 

Les applications Spark se composent d’un pilote (« driver process ») et de plusieurs exécuteurs (« executor processes »). Il peut être configuré pour être lui-même l’exécuteur (local mode) ou en utiliser autant que nécessaire pour traiter l’application, Spark prenant en charge la mise à l’échelle automatique par une configuration d’un nombre minimum et maximum d’exécuteurs.

Schéma du fonctionnement logique de Spark

Le driver (parfois appelé « Spark Session ») distribue et planifie les tâches entre les différents exécuteurs qui les exécutent et permettent un traitement réparti. Il est le responsable de l’exécution du code sur les différentes machines.

Chaque exécuteur est un processus Java Virtual Machine (JVM) distinct dont il est possible de configurer le nombre de CPU et la quantité de mémoire qui lui est alloué. Une seule tâche peut traiter un fractionnement de données à la fois.

Lors de l’exécution de l’application, Spark crée des jobs, des stages et des tasks. Sans aller trop loin dans le détail, les jobs se composent de stages, et les stages se composent de tâches (voir le schéma ci-dessous). Les stages sont généralement exécutés séquentiellement, tandis que les tâches peuvent être exécutées en parallèle dans le cadre d’un seul stage.

Schéma de l'architecture logique d'exécution

Schéma de l’architecture logique d’exécution

Afin que Spark puisse distribuer les calculs, il lui faut donc des exécuteurs (c’est-à-dire des machines de calcul), ce que proposent des plateformes cloud comme Google Cloud Plateform (GCP) ou Amazon Web Services (AWS).

Deux points clés à comprendre sur les applications Spark à ce stade sont :

  • Spark emploie un gestionnaire de groupe (cluster manager) qui assure le suivi des ressources disponibles.
  • Le processus de pilotage (driver process) est responsable de l’exécution le programme à travers les exécuteurs pour accomplir une tâche donnée.

Les exécuteurs, pour la plupart, exécuteront toujours du code Spark. Cependant, le conducteur peut être « piloté » à partir d’un certain nombre de langues différentes par l’intermédiaire des API de différents langages.

Spark est implémenté via des API dans plusieurs langages de programmation, à savoir Python, Java, Scala , SQL et R. Les API Spark permettent d’exécuter le code Spark. Pour faire simple, Spark présente des « concepts » fondamentaux dans chaque langage; ces concepts sont ensuite traduits en code Spark exécuté sur des cluster de machines.

Le framework étant écrit en Scala, il s’agit du langage privilégié pour les nouvelles fonctionnalités et qui est le plus efficace à utiliser pour travailler avec Spark.

PySpark est l’implémentation de Spark pour Python contenant les différents composants de Spark.

Concepts principaux

SparkSession

L’application Spark est contrôlé grâce à un processus de pilotage (driver process) appelé SparkSession. Une instance de SparkSession est la façon dont Spark exécute les fonctions définis par l’utilisateur dans l’ensemble du cluster. Une SparkSession correspond toujours à une application Spark. En Scala et Python, la variable est disponible sous spark lorsque vous démarrez la
console.

En Scala, taper spark donne ça :

res0: org.apache.spark.sql.SparkSession = org.apache.spark.sql.SparkSession@...

Et en Phyton cela ressemble à ça :

<pyspark.sql.session.SparkSession at 0x7efda4c1ccd0>

Dataframes

Un DataFrame représente simplement un tableau de données avec des lignes et des colonnes. La liste qui définit les colonnes et les types à l’intérieur de ces colonnes est appelée le schéma.

Il est possible de considérer DataFrame comme une feuille de calcul avec des colonnes nommées. Cependant une feuille de calcul se trouve sur un ordinateur à un endroit précis, alors qu’un DataFrame Spark peut s’étaler sur des milliers d’ordinateurs. La raison pour laquelle les données sont placées sur plus d’un l’ordinateur est assez simple : soit les données sont trop volumineuses pour tenir sur une seule machine, soit il serait simplement
trop long d’effectuer des calculs sur ces données sur une seule machine.

Attention donc à ne pas confondre les DataFrames Spark et ceux de Python (avec pandas) et R qui, bien que le même concept et facilement convertible de l’un à l’autre, ne sont stockés que sur une machine et non plusieurs.

Par exemple, pour créer un simple DataFrame avec une colonne number contenant 1 000 lignes avec des valeurs de 0 à 999. Cette plage de nombres représente une collection distribuée.
// Scala
val myRange = spark.range(1000).toDF("number")

# Python
myRange = spark.range(1000).toDF("number")

Partitions

Pour permettre à chaque exécuteurs de travailler en parallèle, Spark décompose les données en morceaux appelés des partitions. Une partition est un ensemble de rangées qui se trouvent sur une machine physique du cluster.
Les partitions de DataFrame représentent la manière dont les données sont physiquement réparties dans le groupe de machines pendant l’exécution.

Si il n’y a qu’une seule partition, Spark aura un parallélisme de un, même si le cluster est composés de milliers d’exécuteurs. Si il y a plusieurs partitions mais un seul exécuteur, Spark aura toujours un parallélisme de un car il n’y a qu’une seule ressource de calcul.

Une chose importante à noter est qu’avec les DataFrames, les partitions s ne sont pas manipulés (pour la plupart) manuellement ou individuellement. Seul des transformations de haut niveau des données sont spécifiés et Spark détermine comment ce travail sera réellement exécuté sur le cluster. Ils existent cependant des API plus bas niveau tel que les Resilient Distributed Datasets (RDD) qui permettent cela.

Transformations

Dans Spark, les structures de données de base sont immuables, ce qui signifie qu’elles ne peuvent pas être modifiées après avoir été créé. Pour « changer » un DataFrame, il faut indiquer à Spark comment le modifier. Ce sont les transformations.

Par exemple, récupérons tous les nombres paires du DataFrame créé précédemment :

// Scala
val divisBy2 = myRange.where("number % 2 = 0")

# Python
divisBy2 = myRange.where("number % 2 = 0")

Notez que ces opérations ne renvoient aucun résultat. C’est parce que nous n’avons spécifié qu’une transformation abstraite, et Spark n’agira pas sur les transformations tant que nous n’aurons pas appelé une action (décrit plus bas). Il y a deux types de transformations : celles qui spécifient des dépendances étroites, et celles qui spécifient des dépendances larges.

Les transformations consistant en des dépendances étroites (appellées également transformations étroites ou narrow transformations) sont celles pour lesquelles chaque partition d’entrée ne contribuera qu’à une seule partition de sortie. Dans le code précédent, le where spécifie une dépendance étroite, où une seule partition contribue à au maximum une partition de sortie.

A l’inverse, une dépendance large (ou transformation large) aura des partitions d’entrée qui contribuent à de nombreuses partitions de sortie.

Spark effectue donc une lazy evalutation qui signifie que Spark attendra le tout dernier moment pour exécuter le graphique des instructions de calcul. Dans Spark, au lieu de modifier les données immédiatement lorsque une opération est définie, un plan de transformations à appliquer aux données sources est élaboré. En attendant le dernier moment pour exécuter le code (c’est-à-dire la prochaine action), Spark compile ce plan à partir du DataFrame brut à un plan physique qui fonctionnera aussi efficacement que possible dans l’ensemble du cluster.

Actions

Les transformations permettent de construire un plan de transformation logique. Pour déclencher le calcul, il faut utiliser une action. Une action demande à Spark de calculer un résultat à partir d’une série de transformations.

L’action la plus simple est le count, qui nous donne le nombre total d’enregistrements dans un DataFrame. Par exemple,
divisBy2.count()

La sortie du code précédent doit être 500. Bien sûr, le comptage n’est pas la seule action. Il y a trois types d’actions :

  • Actions de visualisation des données
  • Actions visant à collecter des données sur les objets natifs dans le langage de programmation respectif
  • Actions d’écriture sur les sources de données de sortie

En précisant cette action, nous avons lancé une tâche Spark qui gère la transformation de filtrage (une transformation étroite), puis une agrégation (une transformation large) qui effectue les comptages sur chaque partition, puis une collecte, qui renvoie notre résultat à un objet natif au langage correspondant.

Sources

The Data Scientist’s Guide to Apache Spark™, Databricks, 2017

Spark – The Definitive Guide – Big Data processing made simple, Bill Chambers and Matei Zaharia, 2018

Prise en main du Jetson Nano

By VeoNews

La carte Jetson Nano est un mini ordinateur pour le déploiement d’application d’Intelligence Artificielle dans des systèmes embarqués compacts à faible coût (109€) et basse consommation (5-10W).

En terme de spécifications techniques, la carte est équipée d’un GPU architecture NVIDIA Maxwell™ avec 128 cœurs NVIDIA CUDA® et un CPU quad-core ARM® Cortex®-A57 MPCore. La carte a une mémoire vive de 4Go 64-bit LPDDR4.

Il faut ajouter une microSD pour avoir une mémoire de stockage. Pour cela, il est nécessaire de flasher une image sur une microSD UHS-1 (non fourni). Considérez donc que la Nano n’est pas équipée d’une mémoire dédiée – la carte SD est tout ce que vous aurez. Assurez-vous donc d’en utiliser une qui soit de taille suffisante et rapide ! Ici les recommandations varient avec le tutoriel de base qui recommande 16Go minimum et le cours du Deep Learning Institute Nvidia qui en recommande une de 32Go. En fonction, l’image fera 13 ou 20Go dézippée, je recommande donc une carte de 32Go pour assurer les deux cas.

Jetson Nano est équipé de nombreux connecteurs notamment 4 ports USB (1x 3.0 et 3x 2.0), HDMI, Ethernet ou encore des connecteurs de caméra MIPI CSI-2 (compatible avec le module de caméra Raspberry Pi v2).

Il existe plusieurs façons d’alimenter la carte, soit par le connecteur coaxial 2.1mm 5V 4A (J25 sur le schéma ci-dessous) ou par le port microUSB 5V 2A (J28). On change de configuration à l’aide du pont de court-circuit (J48), la présence de celui-ci pour l’utilisation du connecteur axial et l’absence pour le microUSB.

Dans les deux cas, les câbles d’alimentation ne sont pas fournis et doivent être achetés séparément, on regrettera que l’un des deux ne vienne pas avec la carte ou du moins soit facilement achetable en complément.  Voici une liste des composants supportés par la Jetson Nano. Il est fortement recommandé d’avoir les deux types de câbles pour utiliser la carte de la meilleure façon, l’énergie en entrée micro USB étant insuffisante dans de nombreux cas.

En effet, par défaut l’état de consommation d’énergie est en mode 0 c’est-à-dire 10W uniquement pour la carte en elle-même. Dès qu’un périphérique est branché, par exemple sur un port USB, la carte ne fonctionne plus. Une solution est donc de changer l’état de consommation en mode 1 (5W) avec la commande suivante mais il y a une perte de performance. sudo nvpmodel -m 1 Le solution optimale est donc d’utiliser le câble coaxial pour alimenter la Jetson Nano.

Jetson Nano est livré avec la stack Nvidia via le JetPack SDK qui contient un OS, TensorRT, CUDA et autres fonctions. Cette carte est également compatible avec des framework de Deep Learning open source tel que Tensorflow, PyTorch, Caffe et mxnet.

Configuration

Attention, il s’agit ici d’étapes spécifiques au Jetson Nano, pour les autres cartes Jetson (AGX, TX2, …) la configuration se fait par le SDK Manager.

En premier lieu, il faut télécharger l’image NVIDIA DLI AI Jetson Nano SD Card Image du fait de sa taille (environ 7Go compressé). L’image est ici celle du cours du Deep Learning Institute de Nvidia et est donc différente de celle du tutoriel de base car vient avec des modules pré-installés.

Ensuite, il faut écrire l’image de JetCard sur la carte microSD.

Pour Linux, nous vous proposons deux méthodes, en graphique avec Etcher ou bien en lignes de commande.

Etcher

  1. Télécharger, installer et lancer Etcher
  2. Cliquer sur “Select Image” et choisissez l’image télécharger précédemment.
  3. Insérer la carte microSD (Etcher sélectionne automatiquement la carte microSD comme cible si il n’y a pas d’autres lecteurs externes)
  4. Cliquez sur « Flash » ! Votre système d’exploitation peut vous demander votre nom d’utilisateur et votre mot de passe avant de permettre à Etcher de continuer.
    Il faudra 10 à 15 minutes à Etcher pour écrire et valider l’image si votre carte microSD est connectée via USB3.

Lignes de commande

  1. Insérer la carte microSD puis taper une commande comme la suivante pour noter quel périphérique de disque lui a été attribué. dmesg | tail | awk '$3 == "sd" {print}'  Ou simplement df
  2. Taper ensuite simplement la commande suivante en prenant garde à spécifier le bon disque <x> pour écrire l’image sur la microSD. /usr/bin/unzip -p ~/Downloads/ainano_v1-1-1_20GB_200203.zip | sudo /bin/dd of=/dev/sd<x> bs=4M status=progress && sync
  3. Ne pas oublier d’éjecter proprement le disque avec la commande suivante. sudo eject /dev/sd<x>

Setup et lancement

Il existe deux modes d’utilisation du Jetson Nano : le mode head less  et le mode standalone.

Le mode headless est l’utilisation sans écran, clavier et souris depuis une machine hôte. La carte est alimentée par le câble coaxial et branchée à la machine hôte par le port microUSB. Attention à ne pas oublier de placer le pont J18. Il faut ensuite connecter la carte par USB à la machine hôte.

On peut alors accéder à un Jupyter Lab via l’adresse 192.168.55.1:8888. En cas d’échec, il peut être nécessaire de vérifier la configuration de sa machine hôte

Le mode indépendant (standalone) correspond quant à lui à une utilisation normal avec clavier, écran et souris. Un utilisateur dlinano est configuré par défaut avec pour mot de passe dlinano (attention le clavier est en qwerty! Et ce à chaque démarrage si la configuration du clavier n’est pas modifié). On accède alors une interface graphique Ubuntu classique.

Pour information, l’installation de certaines librairies python peuvent prendre beaucoup de temps car les images basées sur la distribution linux de Nvidia et l’architecture du processeur de la carte ne sont pas nécessairement disponibles. C’est pourquoi, lors des installations en utilisant python pip, ceux-ci sont compilés à partir des fichiers sources.

Utilisation de la caméra Raspberry Pi v2

Rien de plus simple, il suffit de la brancher sur le connecteur de caméra (les broches vers l’intérieur de la carte et la partie bleue vers l’extérieur) puis de lancer le notebook csi_camera.ipynb embarqué avec la distribution (dossier nvdli-nano). Ça fonctionne !

Nvidia propose une interface de caméra python facile à utiliser via Jetcam , déjà pré-installé sur le système qui permet de travailler avec diverses caméras USB et CSI.

Et l’IA dans tout ça ?

L’atout de ce mini ordinateur est la présence d’une carte graphique qui permet de faire du traitement d’images. Il s’agit principalement de faire l’inférence d’image et non de l’entraînement de modèles, ce qui serait trop lourd pour une carte comme celle-ci.

L’OS est livré avec des notebooks basiques directement utilisables (dans le dossier nvdli-nano) pour faire de la classification d’images (émotions, pouce levé ou non, …) ou de la régression (position des yeux, du nez, …). Mais également avec de nombreuses librairies tel que Pytorch ou Tensorflow pour exploiter les capacités de l’apprentissage profond.

Du fait de la puissance du Jetson Nano, les projets restent donc assez simples et permettent principalement de résoudre des problèmes d’inférences en embarqué.

Mon avis

Carte intéressante dans le même genre que le Raspberry Pi avec une meilleure capacité de traitement d’images pour mettre en place des projets IA embarqués à moindre coût. Facile d’utilisation pour les projets pré-installés mais quelques soucis quand il faut installer des nouveaux packages python qui doivent être compilés.

Application de génération de visages dans une image avec un GAN

By VeoNews

Mi-février, cinq étudiants de l’ESIR ont présenté leurs travaux en collaboration avec VeoNum. Dans le cadre de leur projet industriel, ils ont développé une application capable de détecter les visages présents sur des photos et regénérer ceux-ci à volonté.

En plus du côté ludique de l’application, celle-ci ouvre des possibilités en termes d’anonymisation de photo. Au lieu de flouter les visages des personnes ne souhaitant pas apparaître sur vos images, on peut imaginer les remplacer par des visages totalement fictifs.

Application VeoFace Reconstructor développée par les étudiants de l’ESIR

Le GAN dans tout ça ?

L’application utilise un GAN [1] (Generative Adversarial Network), c’est-à-dire un réseau de neurones artificiels capable de générer des données réalistes (ici, des visages). Ce réseau est décomposé en deux parties : un générateur et un discriminateur.

Durant sa phase d’apprentissage, les deux entités vont jouer l’une contre l’autre au jeu suivant. A partir d’un signal aléatoire, le générateur construit un jeu de données qu’il envoi ensuite au discriminateur. Ce dernier reçoit également un jeu de données réelles provenant d’une base d’apprentissage. Le but du discriminateur est de pouvoir différencier les données réelles des données envoyées par le générateur. A l’inverse, ce dernier doit être capable de tromper le discriminateur.

Schéma de l’entrainement d’un GAN.

Ce jeu répété de nombreuses fois va pousser le générateur à adapter ses paramètres pour produire des données plus réalistes afin que le discriminateur les confonde avec les données d’apprentissage. Dans notre cas, les étudiants ont utilisé la base de données publique CelebA [2] comportant plus de 200 000 images de visages de célébrités. Après de nombreuses itérations, le générateur arrive à produire des images tels que celles-ci :

Intégrer le GAN à l’application

Une fois le GAN entrainé, les étudiants ont pu intégrer le générateur à leur application en utilisant le framework Tensorflow Lite.

L’application se charge de trouver les visages dans l’une de vos photos (prise avec la caméra ou parmi votre galerie). Vous pouvez ensuite préciser les visages que vous souhaitez regénérer. Là, le générateur produit un visage à partir d’un signal aléatoire et le propose à l’utilisateur à la place du visage détecté.

Exemple d’utilisation de l’application

Améliorations possibles

Cette première application n’est cependant pas parfaite. Il est clair que le visage généré aléatoirement et plaqué directement sur l’image peut présenter de nombreuses incohérences. Par exemple :

Pour y faire face, les étudiants ont proposé d’appliquer une technique de morphing pour placer le visage généré dans l’image. L’idée étant de faire correspondre au mieux la position du visage réel avec celui regénéré.

Exemple de morphing entre G. W. Bush et A. Schwarzenegger (https://en.wikipedia.org/wiki/Morphing)

Cependant, par manque de temps cette dernière fonctionnalité n’a pas pu être implémentée correctement sur l’application Android.

Une autre possibilité testée à Veonum consiste à laisser au GAN le soin d’insérer au mieux le visage généré dans l’image. Pour cela, nous donnons le contexte du visage (c.-à-d. son contour) comme information à l’entrée du générateur en plus du signal aléatoire. Le discriminateur reçoit quant à lui l’image du visage généré fusionné avec le contexte. Ainsi le générateur doit apprendre à rendre le visage le plus réaliste possible dans son contexte.

Les résultats obtenus ne sont pas encore parfaits mais il est clair que cette fois, le contexte est bien pris en compte dans la génération du visage.

Les étudiants de L’ESIR

  • Gatien Gaumerais, étudiant de 3 année en IN
  • Adrien Gegout, étudiant de 3 année en SI
  • Tugdual Le Pen, étudiant de 3 année en IN
  • Paul le Tannou, étudiant de 3 année en IN
  • Simon Moisan, étudiant de 3 année en SI

Liens

[1] Ian J. Goodfellow, et al. « Generative adversarial nets. » Advances in neural information processing systems. 2014. (http://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf)

[2] Ziwei Liu, et al. « Deep Learning Face Attributes in the Wild  » Proceedings of International Conference on Computer Vision. 2015. (http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html)

Apprentissage automatique et réseaux de neurones

By VeoNews

Résumé de l’article :

Aujourd’hui l’apprentissage automatique fait partie de notre quotidien. Elle permet d’identifier vos
amis sur les photos, de comprendre vos requêtes vocales faites à votre assistant personnel. Dans cet
article, nous allons voir les différentes formes d’apprentissage automatique et nous présenterons un
cas concret d’apprentissage de modèle basé sur les réseaux de neurones.

<cliquer ici pour télécharger le pdf>

VeoNum lance son offre IA (Intelligence Artificielle) VeoBrain

By VeoNews

VeoNum lance 3 nouvelles offres de service autour de l’IA (Intelligence Artificielle). N’hésitez pas à nous contacter.

VeoBrain Online : une offre pour du deep learning en continu au plus près des données.

VeoBrain Community : une offre pour du deep learning collaboratif.

VeoBrain Secure : une offre pour du deep learning robuste et sécurisé.

#IA #DeepLearning #DNN

VeoNum lance son logiciel de montage video OpenVeo-Prod

By VeoNews

VeoNum lance son logiciel de production video OpenVeo Prod.

Ce logiciel (en mode SAAS) permet de monter ses vidéos au travers une interface web en appliquant des formules prédéfinies facilitant le montage.

Le logiciel est ainsi accessible sans apprentissage préalable.

Si vous avez besoin d’un formule particulière de montage vidéo, VeoNum est a même de vous la développer sur mesure.

Exemple de Formules:

  • Formule 1: Ajout vidéo intro/outro + logo + titre sur une vidéo
  • Formule 2: Montage de 2 vidéos avec gestion de filtres de positionnement des 2 vidéos tout du long du montage