Habilitar a inferência de aprendizado de máquina em um dispositivo do Azure IoT Edge

Azure IoT Edge
Azure IoT Hub

A IA na borda é um dos cenários de borda mais populares. As implementações desse cenário incluem classificação de imagem, deteção de objetos, análise de corpo, rosto e gestos e manipulação de imagem. 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 o modelo de IA, 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 gêmeos de módulo 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 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 IA IoT Edge que possa carregar modelos de deteção de objetos 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:

  • Habilite a inferência de IA em dispositivos de borda.
  • Minimize 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 modelos de IA no armazenamento local de um dispositivo de borda IoT.
  • Alcance quase zero tempo de inatividade quando o dispositivo de borda muda de modelo de IA.

TensorFlow é uma marca comercial da Google Inc.

Arquitetura

Diagrama que mostra uma arquitetura que suporta inferência de aprendizado de máquina.

Transfira um ficheiro do Visio desta 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 TensorFlow Lite 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 melhor segurança, considere o uso de conexões de ponto de extremidade privado entre o Armazenamento de Blob 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 horários, diários ou semanais programados para economizar energia ou reduzir o tráfego de rede.)
  3. O módulo carregador monitora as atualizações dos gêmeos de módulo via API. Quando deteta uma atualização, obtém o token SAS do modelo de aprendizagem automática e, em seguida, transfere 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 seu dispositivo ficar offline por muito tempo, você pode 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 TensorFlow Lite 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 para o Blob Storage e sincronizar os gêmeos do módulo do dispositivo novamente para uma atualização incremental. Não há necessidade de atualizar toda a imagem do módulo IoT Edge.

Detalhes do cenário

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

As próximas duas seções esclarecem alguns conceitos sobre módulos de inferência de aprendizado de máquina, TensorFlow Lite e ONNX.

TensorFlow Lite

  • Um arquivo *.tflite é um modelo de IA pré-treinado. Você pode baixar um de TensorFlow.org. É um modelo genérico de IA que você pode usar em aplicativos multiplataforma, como iOS e Android. Para obter mais informações sobre metadados e campos associados (por exemplo, labels.txt) consulte Ler os metadados de modelos.

  • Um modelo de deteção de objetos é treinado para detetar 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 que deteta, o local de uma caixa delimitadora para cada objeto e uma pontuação que indica a confiança da deteção.

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

  • Você pode obter mais modelos de deteçã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

ONNX é um formato de padrão aberto para representar modelos de aprendizado de máquina. É apoiado por uma comunidade de parceiros que o implementaram em muitas estruturas e ferramentas.

  • O ONNX suporta ferramentas para criar e implantar modelos e para realizar outras tarefas. Para obter mais informações, consulte Ferramentas ONNX suportadas.
  • Você pode usar o ONNX Runtime para executar modelos pré-treinados ONNX. Para obter informações sobre modelos pré-treinados, consulte ONNX Model Zoo.
  • Para esse cenário, você pode usar um modelo de deteçã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.

A seguir está 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. Receba a 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 evitar 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 para o 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"

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

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 deteção de objetos em dispositivos de borda. O exemplo de código a seguir mostra como usar o modelo TensorFlow Lite AI para detetar objetos em dispositivos de borda.

O código executa estas tarefas:

  1. Carregue dinamicamente o modelo de IA do TensorFlow Lite.
  2. Execute a padronização de imagens.
  3. Detetar objetos.
  4. Calcular pontuações de deteçã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)

A seguir está a versão ONNX do código anterior. Os passos são basicamente os mesmos. A única diferença é como a pontuação de deteção é tratada, porque os parâmetros de saída e modelo Labelmap 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 seu dispositivo de borda IoT incorporar o código e os recursos anteriores, seu dispositivo de borda terá deteção de objeto de imagem AI e suporta 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.

Flask framework é 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 deteção e, em seguida, retornar os resultados em um formato JSON. Para obter mais informações, consulte Flask: Flask Tutorial no Visual Studio Code.

Contribuidores

Este artigo é mantido pela Microsoft. Foi originalmente escrito pelos seguintes contribuidores.

Autor principal:

  • Bo Wang - Brasil | Engenheiro de Software Sênior

Outros contribuidores:

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

Próximos passos