Editar

Compartilhar via


Habilite a inferência de machine learning em um dispositivo IoT Edge

Azure IoT Edge
Hub IoT do Azure

A IA na borda é um dos cenários de borda mais populares. As implementações desse cenário incluem classificação de imagens, detecção de objetos, análise de corpo, rosto e gestos e manipulação de imagens. Este guia de arquitetura descreve como usar o Azure IoT Edge para dar suporte a esses cenários.

Você pode melhorar a precisão da IA atualizando seus modelos, mas em alguns cenários o ambiente de rede do dispositivo de borda não é bom. Por exemplo, nas indústrias de energia eólica e petróleo, os equipamentos podem estar localizados no deserto ou no oceano.

Os módulos gêmeos do IoT Edge são usados para implementar o modelo de IA carregado dinamicamente. Os módulos do IoT Edge são baseados no Docker. Uma imagem para um módulo do IoT Edge em um ambiente de IA normalmente tem um tamanho de pelo menos 1 GB, portanto, atualizar incrementalmente o modelo de IA é importante em uma rede de largura de banda estreita. Essa consideração é o foco principal deste artigo. A ideia é criar um módulo de IoT Edge AI que possa carregar modelos de detecção de objetos LiteRT (anteriormente TensorFlow Lite) ou Open Neural Network Exchange (ONNX). Você também pode habilitar o módulo como uma API da Web para que possa usá-lo para beneficiar outros aplicativos ou módulos.

A solução descrita neste artigo pode ajudá-lo das seguintes maneiras:

  • Habilitar a inferência de IA em dispositivos de borda.
  • Minimizar o custo de rede da implantação e atualização de modelos de IA na borda. A solução pode economizar dinheiro para você ou seus clientes, especialmente em um ambiente de rede de largura de banda estreita.
  • Crie e gerencie um repositório de modelo de IA no armazenamento local de um dispositivo de IoT Edge.
  • Reduza o tempo de inatividade a quase zero quando o dispositivo de borda trocar de modelo de IA.

TensorFlow e LiteRT são marcas comerciais da Google Inc. O uso dessa marca não implica em nenhum endosso.

Arquitetura

Diagrama que mostra uma arquitetura que dá suporte à inferência de machine learning.

Baixe um Arquivo Visio dessa arquitetura.

Fluxo de dados

  1. O modelo de IA é carregado no Armazenamento de Blobs do Azure ou em um serviço Web. O modelo pode ser um modelo LiteRT ou ONNX pré-treinado ou um modelo criado no Azure Machine Learning. O módulo IoT Edge pode acessar esse modelo e baixá-lo para o dispositivo de borda mais tarde. Se você precisar de mais segurança, considere o uso de conexões de ponto de extremidade privadas entre o Armazenamento de Blobs e o dispositivo de borda.
  2. O Hub IoT do Azure sincroniza gêmeos de módulo de dispositivo automaticamente com informações de modelo de IA. A sincronização ocorre mesmo que o IoT Edge esteja offline. (Em alguns casos, os dispositivos IoT são conectados a redes em horários programados de hora em hora, diariamente ou semanalmente para economizar energia ou reduzir o tráfego de rede.)
  3. O módulo carregador monitora as atualizações dos gêmeos do módulo via API. Quando ele detecta uma atualização, ele obtém o token SAS do modelo de machine learning e, em seguida, baixa o modelo de IA.
    • Para obter mais informações, consulte Criar token SAS para um contêiner ou blob.
    • Você pode usar a propriedade ExpiresOn para definir a data de expiração dos recursos. Se o dispositivo ficar offline por muito tempo, você poderá estender o tempo de expiração.
  4. O módulo carregador salva o modelo de IA no armazenamento local compartilhado do módulo IoT Edge. Você precisa configurar o armazenamento local compartilhado no arquivo JSON de implantação do IoT Edge.
  5. O módulo carregador carrega o modelo de IA do armazenamento local por meio da API LiteRT ou ONNX.
  6. O módulo carregador inicia uma API da Web que recebe a foto binária via solicitação POST e retorna os resultados em um arquivo JSON.

Para atualizar o modelo de IA, você pode carregar a nova versão no Armazenamento de Blobs e sincronizar os gêmeos do módulo do dispositivo novamente para fazer uma atualização incremental. Não há necessidade de atualizar toda a imagem do módulo do IoT Edge.

Detalhes do cenário

Nesta solução, um módulo do IoT Edge é usado para baixar um modelo de IA e, em seguida, habilitar a inferência de machine learning. Você pode usar modelos LiteRT ou ONNX pré-treinados nesta solução.

LiteRT

  • Um arquivo .tflite é um modelo de IA pré-treinado. Você pode baixá-lo em TensorFlow.org. É um modelo genérico de IA que você pode usar em aplicativos multiplataforma como iOS e Android. O LiteRT é compatível com modelos de TensorFlow, PyTorch, JAX e Keras. Para obter mais informações sobre metadados e campos associados (por exemplo, labels.txt) consulte Ler os metadados de modelos.

  • Um modelo de detecção de objeto é treinado para detectar a presença e a localização de várias classes de objetos. Por exemplo, um modelo pode ser treinado com imagens que contêm vários pedaços de fruta, juntamente com um rótulo que especifica a classe de fruta que eles representam (por exemplo, maçã) e dados que especificam onde cada objeto aparece na imagem.

    Quando uma imagem é fornecida ao modelo, ela gera uma lista dos objetos detectados, o local de uma caixa delimitadora para cada objeto e uma pontuação que indica a confiança da detecção.

  • Se você quiser criar ou ajustar um modelo de IA, consulte LiteRT Model Maker.

  • Você pode obter mais modelos de detecção pré-treinados gratuitos, com várias características de latência e precisão, no Detection Zoo. Cada modelo usa as assinaturas de entrada e saída mostradas nos exemplos de código a seguir.

ONNX

O ONNX é um formato de modelo aberto que representa modelos de machine learning. Ele é apoiado por uma comunidade de parceiros que o implementaram em muitas estruturas e ferramentas.

  • O ONNX suporta ferramentas para construir e implantar modelos e para realizar outras tarefas. Para obter mais informações, consulte as ferramentas ONNX com suporte.
  • Você pode usar o ONNX Runtime para executar modelos pré-treinados do ONNX. Para obter informações sobre modelos pré-treinados, consulte ONNX Model Zoo.
  • Para esse cenário, você pode usar um modelo de detecção de objeto e segmentação de imagem: Tiny YOLOv3.

A comunidade ONNX fornece ferramentas para ajudá-lo a criar e implantar seu modelo de aprendizado profundo.

Baixar modelos de IA treinados

Para baixar modelos de IA treinados, recomendamos que você use gêmeos de dispositivo para receber notificações quando um novo modelo estiver pronto. Mesmo que o dispositivo esteja offline, a mensagem pode ser armazenada em cache no Hub IoT até que o dispositivo de borda volte a ficar online. A mensagem será sincronizada automaticamente.

Este é um exemplo de código Python que registra notificações para os gêmeos do dispositivo e, em seguida, baixa o modelo de IA em um arquivo ZIP. Ele também executa outras operações no arquivo baixado.

O código executa estas tarefas:

  1. Recebimento de notificação de gêmeos do dispositivo. A notificação inclui o nome do arquivo, o endereço de download do arquivo e o token de autenticação MD5. (No nome do arquivo, você pode incluir informações de versão, como 1.0.)
  2. Baixe o modelo de IA como um arquivo ZIP para o armazenamento local.
  3. Opcionalmente, execute a soma de verificação MD5. A verificação MD5 ajuda a impedir arquivos ZIP que foram adulterados durante a transmissão de rede.
  4. Descompacte o arquivo ZIP e salve-o localmente.
  5. Envie uma notificação ao Hub IoT ou uma mensagem de roteamento para informar que o novo modelo de IA está pronto.
# 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"

            # LiteRT
            # 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 )

Inferência

Depois que o modelo de IA é baixado, o próximo passo é usar o modelo no dispositivo de borda. Você pode carregar dinamicamente o modelo e executar a detecção de objetos em dispositivos de borda. O exemplo de código a seguir mostra como usar o modelo de AI LiteRT para detectar objetos em dispositivos de borda.

O código executa estas tarefas:

  1. Carregue dinamicamente o modelo de IA do LiteRT.
  2. Realizar padronização de imagem.
  3. Detectar objetos.
  4. Calcular as pontuações de detecção.
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)

Esta é a versão ONNX do código anterior. As etapas são praticamente as mesmas. A única diferença é como a pontuação de detecção é tratada, porque o Labelmap e os parâmetros de saída do modelo são diferentes.

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)

Se o dispositivo de borda IoT incorporar o código e os recursos anteriores, ele terá detecção de objeto de imagem de IA e oferece suporte à atualização dinâmica de modelos de IA. Se você quiser que o módulo de borda forneça funcionalidade de IA para outros aplicativos ou módulos por meio de uma API da Web, você pode criar uma API da Web em seu módulo.

A estrutura Flask é um exemplo de uma ferramenta que você pode usar para criar rapidamente uma API. Você pode receber imagens como dados binários, usar um modelo de IA para detecção e retornar os resultados em um formato JSON. Para obter mais informações, consulte Flask: tutorial do Flask no Visual Studio Code.

Colaboradores

Esse artigo é mantido pela Microsoft. Ele foi originalmente escrito pelos colaboradores a seguir.

Autor principal:

  • Bo Wang | Engenheiro de software sênior

Outro colaborador:

Para ver perfis não públicos do LinkedIn, entre no LinkedIn.

Próximas etapas