Modifier

Partager via


Activer l’inférence Machine Learning sur un appareil Azure IoT Edge

Azure IoT Edge
Azure IoT Hub

L’IA à la périphérie est l’un des scénarios de périphérie les plus populaires. Les implémentations de ce scénario incluent la classification d’images, la détection d’objets, l’analyse du corps, du visage et des mouvements, ainsi que la manipulation d’images. Ce guide d’architecture explique comment utiliser Azure IoT Edge pour prendre en charge ces scénarios.

Vous pouvez améliorer la précision de l’IA en mettant à jour le modèle d’IA mais, dans certains scénarios, l’environnement réseau des périphériques n’est pas favorable. Par exemple, dans les industries éolienne et pétrolière, l’équipement peut se trouver dans le désert ou dans l’océan.

Les jumeaux de module IoT Edge sont utilisés pour implémenter le modèle d’IA chargé dynamiquement. Les modules IoT Edge sont basés sur Docker. Une image d’un module de IoT Edge dans un environnement d’IA a généralement une taille d’au moins 1 Go. La mise à jour incrémentielle du modèle d’IA est donc importante dans un réseau à bande passante étroite. Cette considération est l’objet principal de cet article. L’idée est de créer un module d’IoT Edge AI qui peut charger des modèles de détection d’objets TensorFlow Lite ou Open Neural Network Exchange (ONNX). Vous pouvez également activer le module en tant qu’API web afin de pouvoir l’utiliser au profit d’autres applications ou modules.

La solution décrite dans cet article peut vous aider des manières suivantes :

  • Activez l’inférence d’IA sur les périphériques.
  • Réduisez le coût réseau du déploiement et de la mise à jour des modèles d’IA à la périphérie. La solution peut vous permettre ou permettre à vos clients d’économiser de l’argent, en particulier dans un environnement réseau à bande passante étroite.
  • Créez et gérez un référentiel de modèles d’IA dans le stockage local d’un appareil IoT Edge.
  • Obtenez un temps d’arrêt quasiment inexistant lorsque le périphérique bascule des modèles d’IA.

TensorFlow est une marque déposée de Google Inc. L’utilisation de cette marque n’implique aucun appui.

Architecture

Diagramme montrant une architecture qui prend en charge l’inférence Machine Learning.

Téléchargez un fichier Visio de cette architecture.

Dataflow

  1. Le modèle d’IA est chargé sur Stockage Blob Azure ou un service web. Le modèle peut être un modèle TensorFlow Lite ou ONNX pré-entraîné ou un modèle créé dans Azure Machine Learning. Le module IoT Edge peut accéder à ce modèle et le télécharger ultérieurement sur le périphérique. Si vous avez besoin d’une sécurité renforcée, envisagez d’utiliser des connexions de point de terminaison privé entre le Stockage Blob et le périphérique.
  2. Azure IoT Hub synchronise automatiquement les jumeaux de module d’appareil avec les informations de modèle d’IA. La synchronisation se produit même si IoT Edge est hors connexion. (Dans certains cas, les appareils IoT sont connectés à des réseaux à des heures planifiées, quotidiennes ou hebdomadaires pour économiser de l’énergie ou réduire le trafic réseau.)
  3. Le module chargeur surveille les mises à jour des jumeaux de module via l’API. Lorsqu’il détecte une mise à jour, il obtient le jeton SAP du modèle Machine Learning, puis télécharge le modèle d’IA.
    • Pour plus d’informations, consultez Créer un jeton SAP pour un conteneur ou un objet blob.
    • Vous pouvez utiliser la propriété ExpiresOn pour définir la date d’expiration des ressources. Si votre appareil est hors connexion pendant une longue période, vous pouvez prolonger le délai d’expiration.
  4. Le module chargeur enregistre le modèle d’IA dans le stockage local partagé du module IoT Edge. Vous devez configurer le stockage local partagé dans le fichier JSON du déploiement IoT Edge.
  5. Le module chargeur charge le modèle d’IA à partir d’un stockage local via l’API TensorFlow Lite ou ONNX.
  6. Le module chargeur démarre une API web qui reçoit la photo binaire via une requête POST et retourne les résultats dans un fichier JSON.

Pour mettre à jour le modèle d’IA, vous pouvez charger la nouvelle version dans Stockage Blob et synchroniser à nouveau les jumeaux de module d’appareil pour une mise à jour incrémentielle. Il n’est pas nécessaire de mettre à jour l’ensemble des images du module IoT Edge.

Détails du scénario

Dans cette solution, un module IoT Edge est utilisé pour télécharger un modèle d’IA, puis activer l’inférence Machine Learning. Vous pouvez utiliser des modèles pré-entraînés TensorFlow Lite ou ONNX dans cette solution.

Les deux sections suivantes clarifient certains concepts relatifs aux modules d’inférence Machine Learning, à TensorFlow Lite et à ONNX.

TensorFlow Lite

  • Un fichier *.tflite est un modèle d’IA pré-entraîné. Vous pouvez en télécharger un à partir de TensorFlow.org. Il s’agit d’un modèle d’IA générique que vous pouvez utiliser dans des applications multi-plateformes comme iOS et Android. Pour plus d’informations sur les métadonnées et les champs associés (par exemple, labels.txt) consultez Lire les métadonnées des modèles.

  • Un modèle de détection d’objet est entraîné pour détecter la présence et l’emplacement de plusieurs classes d’objets. Par exemple, un modèle peut être entraîné avec des images qui contiennent différents fruits, ainsi qu’une étiquette qui spécifie le type de fruit qu’ils représentent (par exemple, pomme) et des données qui spécifient où chaque objet apparaît dans l’image.

    Lorsqu’une image est fournie au modèle, elle génère une liste des objets qu’elle détecte, l’emplacement d’un cadre englobant pour chaque objet et un score qui indique la fiabilité de la détection.

  • Si vous souhaitez créer ou personnaliser un modèle d’IA, consultez TensorFlow Lite Model Maker.

  • Vous pouvez obtenir plus de modèles de détection pré-entraînés gratuits, avec différentes caractéristiques de latence et de précision, sur Zoo de détections. Chaque modèle utilise les signatures d’entrée et de sortie indiquées dans les exemples de code suivants.

ONNX

ONNX est un format standard ouvert permettant de représenter des modèles Machine Learning. Il est pris en charge par une communauté de partenaires qui l’ont implémenté dans un grand nombre d’infrastructures et d’outils.

  • ONNX prend en charge les outils permettant de créer et de déployer des modèles et d’accomplir d’autres tâches. Pour plus d’informations, consultez Outils ONNX pris en charge.
  • Vous pouvez utiliser ONNX Runtime pour exécuter des modèles pré-entraînés ONNX. Pour plus d’informations sur les modèles pré-entraînés, consultez Zoo de modèles ONNX.
  • Pour ce scénario, vous pouvez utiliser un modèle de détection d’objets et de segmentation d’images : Tiny YOLO v3.

La communauté ONNX fournit des outils pour vous aider à créer et à déployer votre modèle Deep Learning.

Télécharger des modèles d’IA pré-entraînés

Pour télécharger des modèles d’IA entraînés, nous vous recommandons d’utiliser des jumeaux d’appareil pour recevoir des notifications lorsqu’un nouveau modèle est prêt. Même si l’appareil est hors connexion, le message peut être mis en cache dans IoT Hub jusqu’à ce que le périphérique soit de nouveau en ligne. Le message sera synchronisé automatiquement.

Voici un exemple de code Python qui enregistre des notifications pour les jumeaux d’appareil, puis télécharge le modèle d’IA dans un fichier ZIP. Il effectue également d’autres opérations sur le fichier téléchargé.

Le code suit cette procédure :

  1. Recevez la notification des jumeaux d’appareil. La notification inclut le nom du fichier, l’adresse de téléchargement du fichier et le jeton d’authentification MD5. (Dans le nom du fichier, vous pouvez inclure des informations de version, telles que 1.0.)
  2. Téléchargez le modèle d’IA sous forme de fichier ZIP dans le stockage local.
  3. Si vous le souhaitez, effectuez une somme de contrôle MD5. La vérification MD5 permet d’empêcher les fichiers ZIP qui ont été falsifiés lors de la transmission réseau.
  4. Décompressez le fichier ZIP et enregistrez-le localement.
  5. Envoyez une notification à IoT Hub ou un message de routage pour signaler que le nouveau modèle d’IA est prêt.
# define behavior for receiving a twin patch
async def twin_patch_handler(patch):
    try:
        print( "######## The data in the desired properties patch was: %s" % patch)
        if "FileName" in patch:
            FileName = patch["FileName"]
        if "DownloadUrl" in patch:
            DownloadUrl = patch["DownloadUrl"]
        if "ContentMD5" in patch:
            ContentMD5 = patch["ContentMD5"]
        FilePath = "/iotedge/storage/" + FileName

        # download AI model
        r = requests.get(DownloadUrl)
        print ("######## download AI Model Succeeded.")
        ffw = open(FilePath, 'wb')
        ffw.write(r.content)
        ffw.close()
        print ("######## AI Model File: " + FilePath)

        # MD5 checksum
        md5str = content_encoding(FilePath)
        if md5str == ContentMD5:
            print ( "######## New AI Model MD5 checksum succeeded")
            # decompressing the ZIP file
            unZipSrc = FilePath
            targeDir = "/iotedge/storage/"
            filenamenoext = get_filename_and_ext(unZipSrc)[0]
            targeDir = targeDir + filenamenoext
            unzip_file(unZipSrc,targeDir)

            # ONNX
            local_model_path = targeDir + "/tiny-yolov3-11.onnx"
            local_labelmap_path = targeDir + "/coco_classes.txt"

            # TensorFlow flite
            # local_model_path = targeDir + "/ssd_mobilenet_v1_1_metadata_1.tflite"
            # local_labelmap_path = targeDir + "/labelmap.txt"

            # message to module
            if client is not None:
                print ( "######## Send AI Model Info AS Routing Message")
                data = "{\"local_model_path\": \"%s\",\"local_labelmap_path\": \"%s\"}" % (filenamenoext+"/tiny-yolov3-11.onnx", filenamenoext+"/coco_classes.txt")
                await client.send_message_to_output(data, "DLModelOutput")
                # update the reported properties
                reported_properties = {"LatestAIModelFileName": FileName }
                print("######## Setting reported LatestAIModelName to {}".format(reported_properties["LatestAIModelFileName"]))
                await client.patch_twin_reported_properties(reported_properties)
        else:
            print ( "######## New AI Model MD5 checksum failed")

    except Exception as ex:
        print ( "Unexpected error in twin_patch_handler: %s" % ex )

Inférence

Une fois le modèle d’IA téléchargé, l’étape suivante consiste à utiliser le modèle sur le périphérique. Vous pouvez charger dynamiquement le modèle et effectuer la détection d’objets sur les périphériques. L’exemple de code suivant montre comment utiliser le modèle d’IA TensorFlow Lite pour détecter des objets sur des périphériques.

Le code suit cette procédure :

  1. Chargez dynamiquement le modèle d’IA TensorFlow Lite.
  2. Effectuez la normalisation des images.
  3. Détectez les objets.
  4. Calculez les scores de détection.
class InferenceProcedure():

    def detect_object(self, imgBytes):

        results = []
        try:
            model_full_path = AI_Model_Path.Get_Model_Path()
            if(model_full_path == ""):
                raise Exception ("PLEASE SET AI MODEL FIRST")
            if '.tflite' in model_full_path:
                interpreter = tf.lite.Interpreter(model_path=model_full_path)
                interpreter.allocate_tensors()
                input_details = interpreter.get_input_details()
                output_details = interpreter.get_output_details()
                input_shape = input_details[0]['shape']

                # bytes to numpy.ndarray
                im_arr = np.frombuffer(imgBytes, dtype=np.uint8)
                img = cv2.imdecode(im_arr, flags=cv2.IMREAD_COLOR)
                im_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                im_rgb = cv2.resize(im_rgb, (input_shape[1], input_shape[2]))
                input_data = np.expand_dims(im_rgb, axis=0)

                interpreter.set_tensor(input_details[0]['index'], input_data)
                interpreter.invoke()
                output_data = interpreter.get_tensor(output_details[0]['index'])
                detection_boxes = interpreter.get_tensor(output_details[0]['index'])
                detection_classes = interpreter.get_tensor(output_details[1]['index'])
                detection_scores = interpreter.get_tensor(output_details[2]['index'])
                num_boxes = interpreter.get_tensor(output_details[3]['index'])

                label_names = [line.rstrip('\n') for line in open(AI_Model_Path.Get_Labelmap_Path())]
                label_names = np.array(label_names)
                new_label_names = list(filter(lambda x : x != '???', label_names))

                for i in range(int(num_boxes[0])):
                    if detection_scores[0, i] > .5:
                        class_id = int(detection_classes[0, i])
                        class_name = new_label_names[class_id]
                        # top, left, bottom, right
                        results_json = "{'Class': '%s','Score': '%s','Location': '%s'}" % (class_name, detection_scores[0, i],detection_boxes[0, i])
                        results.append(results_json)
                        print(results_json)
        except Exception as e:
            print ( "detect_object unexpected error %s " % e )
            raise

        # return results
        return json.dumps(results)

Voici la version ONNX du code précédent. Les étapes sont généralement les mêmes. La seule différence réside dans la façon dont le score de détection est géré, car Labelmap et les paramètres de sortie du modèle sont différents.

class InferenceProcedure():

    def letterbox_image(self, image, size):
        '''resize image with unchanged aspect ratio using padding'''
        iw, ih = image.size
        w, h = size
        scale = min(w/iw, h/ih)
        nw = int(iw*scale)
        nh = int(ih*scale)

        image = image.resize((nw,nh), Image.BICUBIC)
        new_image = Image.new('RGB', size, (128,128,128))
        new_image.paste(image, ((w-nw)//2, (h-nh)//2))
        return new_image

    def preprocess(self, img):
        model_image_size = (416, 416)
        boxed_image = self.letterbox_image(img, tuple(reversed(model_image_size)))
        image_data = np.array(boxed_image, dtype='float32')
        image_data /= 255.
        image_data = np.transpose(image_data, [2, 0, 1])
        image_data = np.expand_dims(image_data, 0)
        return image_data

    def detect_object(self, imgBytes):
        results = []
        try:
            model_full_path = AI_Model_Path.Get_Model_Path()
            if(model_full_path == ""):
                raise Exception ("PLEASE SET AI MODEL FIRST")
            if '.onnx' in model_full_path:

                # input
                image_data = self.preprocess(imgBytes)
                image_size = np.array([imgBytes.size[1], imgBytes.size[0]], dtype=np.float32).reshape(1, 2)

                labels_file = open(AI_Model_Path.Get_Labelmap_Path())
                labels = labels_file.read().split("\n")

                # Loading ONNX model
                print("loading Tiny YOLO...")
                start_time = time.time()
                sess = rt.InferenceSession(model_full_path)
                print("loaded after", time.time() - start_time, "s")

                input_name00 = sess.get_inputs()[0].name
                input_name01 = sess.get_inputs()[1].name
                pred = sess.run(None, {input_name00: image_data,input_name01:image_size})

                boxes = pred[0]
                scores = pred[1]
                indices = pred[2]

                results = []
                out_boxes, out_scores, out_classes = [], [], []
                for idx_ in indices[0]:
                    out_classes.append(idx_[1])
                    out_scores.append(scores[tuple(idx_)])
                    idx_1 = (idx_[0], idx_[2])
                    out_boxes.append(boxes[idx_1])
                    results_json = "{'Class': '%s','Score': '%s','Location': '%s'}" % (labels[idx_[1]], scores[tuple(idx_)],boxes[idx_1])
                    results.append(results_json)
                    print(results_json)

        except Exception as e:
            print ( "detect_object unexpected error %s " % e )
            raise

        # return results
        return json.dumps(results)

Si votre appareil IoT Edge intègre le code et les fonctionnalités précédents, votre périphérique dispose de la détection d’objets image d’IA et prend en charge la mise à jour dynamique des modèles d’IA. Si vous souhaitez que le module à la périphérie fournisse des fonctionnalités d’IA à d’autres applications ou modules via une API web, vous pouvez créer une API web dans votre module.

L’infrastructure Flask est un exemple d’outil que vous pouvez utiliser pour créer rapidement une API. Vous pouvez recevoir des images en tant que données binaires, utiliser un modèle d’IA pour la détection, puis retourner les résultats dans un format JSON. Pour plus d’informations, consultez Flask : Tutoriel Flask dans Visual Studio Code.

Contributeurs

Cet article est géré par Microsoft. Il a été écrit à l’origine par les contributeurs suivants.

Auteur principal :

  • Bo Wang | Ingénieur informatique senior

Autre contributeur :

Pour afficher les profils LinkedIn non publics, connectez-vous à LinkedIn.

Étapes suivantes