Включение вывода машинного обучения на устройстве Azure IoT Edge

Azure IoT Edge
Центр Интернета вещей Azure

ИИ на границе является одним из самых популярных пограничных сценариев. Реализация этого сценария включает классификацию изображений, обнаружение объектов, тело, лицо и анализ жестов, а также манипуляцию с изображениями. В этом руководстве по архитектуре описывается, как использовать Azure IoT Edge для поддержки этих сценариев.

Вы можете повысить точность ИИ, обновив модель ИИ, но в некоторых сценариях сетевая среда пограничных устройств не хороша. Например, в отрасли ветровой энергии и нефти оборудование может находиться в пустыне или океане.

Двойники модулей IoT Edge используются для реализации динамически загруженной модели ИИ. Модули IoT Edge основаны на Docker. Изображение модуля IoT Edge в среде ИИ обычно имеет размер не менее 1 ГБ, поэтому добавочное обновление модели ИИ важно в сети с узкой пропускной способностью. Это основное внимание в этой статье. Идея заключается в создании модуля искусственного интеллекта IoT Edge, который может загружать модели обнаружения объектов LiteRT (ранее TensorFlow Lite) или Open Neural Network Exchange (ONNX). Вы также можете включить модуль как веб-API, чтобы использовать его для преимущества других приложений или модулей.

Решение, описанное в этой статье, поможет вам следующим образом:

  • Включение вывода искусственного интеллекта на пограничных устройствах.
  • Свести к минимуму затраты на развертывание и обновление моделей искусственного интеллекта на пограничном сервере. Решение может сэкономить деньги для вас или клиентов, особенно в сетевой среде с узкой пропускной способностью.
  • Создайте репозиторий моделей ИИ и управляйте ими в локальном хранилище устройства IoT Edge.
  • Достигает почти нулевого простоя, когда пограничное устройство переключает модели искусственного интеллекта.

TensorFlow и LiteRT являются товарным знаком Google Inc. Никакое подтверждение не подразумевается использованием этого знака.

Архитектура

Схема, на котором показана архитектура, поддерживающая вывод машинного обучения.

Скачайте файл Visio для этой архитектуры.

Поток данных

  1. Модель ИИ отправляется в Хранилище BLOB-объектов Azure или веб-службу. Модель может быть предварительно обученной моделью LiteRT или ONNX или моделью, созданной в Машинное обучение Azure. Модуль IoT Edge может получить доступ к этой модели и скачать его на пограничное устройство позже. Если вам нужна более эффективная безопасность, рассмотрите возможность использования подключений к частной конечной точке между хранилищем BLOB-объектов и пограничным устройством.
  2. Центр Интернета вещей Azure автоматически синхронизирует двойники модулей устройства с сведениями о модели ИИ. Синхронизация возникает, даже если IoT Edge отключена. (В некоторых случаях устройства Интернета вещей подключены к сетям по расписанию, ежедневно или еженедельно для экономии питания или уменьшения сетевого трафика.)
  3. Модуль загрузчика отслеживает обновления двойников модулей через API. При обнаружении обновления он получает маркер SAS модели машинного обучения, а затем загружает модель ИИ.
  4. Модуль загрузчика сохраняет модель ИИ в общем локальном хранилище модуля IoT Edge. Необходимо настроить общее локальное хранилище в JSON-файле развертывания IoT Edge.
  5. Модуль загрузчика загружает модель ИИ из локального хранилища с помощью API LiteRT или ONNX.
  6. Модуль загрузчика запускает веб-API, который получает двоичную фотографию с помощью запроса POST и возвращает результаты в JSON-файле.

Чтобы обновить модель ИИ, вы можете отправить новую версию в хранилище BLOB-объектов и синхронизировать двойники модуля устройства еще раз для добавочного обновления. Нет необходимости обновлять весь образ модуля IoT Edge.

Подробности сценария

В этом решении модуль IoT Edge используется для скачивания модели ИИ, а затем включения вывода машинного обучения. В этом решении можно использовать предварительно обученные модели LiteRT или ONNX.

LiteRT

  • Файл .tflite — это предварительно обученная модель ИИ. Вы можете скачать один из TensorFlow.org. Это универсальная модель ИИ, которую можно использовать в кроссплатформенных приложениях, таких как iOS и Android. LiteRT поддерживает модели из TensorFlow, PyTorch, JAX и Keras. Дополнительные сведения о метаданных и связанных полях (например, labels.txt) см. в разделе "Чтение метаданных из моделей".

  • Модель обнаружения объектов обучена обнаруживать наличие и расположение нескольких классов объектов. Например, модель может быть обучена изображениями, содержащими различные куски фруктов, а также метку, указывающую класс фруктов, которые они представляют (например, apple) и данные, указывающие, где каждый объект отображается на изображении.

    При предоставлении изображения модели выводится список объектов, обнаруженных им, расположение ограничивающего прямоугольника для каждого объекта и оценка, указывающая достоверность обнаружения.

  • Если вы хотите создать или настроить модель ИИ, см. статью LiteRT Model Maker.

  • Вы можете получить более бесплатные предварительно обученные модели обнаружения с различными характеристиками задержки и точности в зоопарке обнаружения. Каждая модель использует входные и выходные сигнатуры, показанные в следующих примерах кода.

ONNX

ONNX — это открытый стандартный формат для представления моделей машинного обучения. Она поддерживается сообществом партнеров, которые реализовали его во многих платформах и средствах.

  • ONNX поддерживает средства для создания и развертывания моделей и выполнения других задач. Дополнительные сведения см. в разделе "Поддерживаемые средства ONNX".
  • Среду выполнения ONNX можно использовать для запуска предварительно обученных моделей ONNX. Сведения о предварительно обученных моделях см . в зоопарке моделей ONNX.
  • Для этого сценария можно использовать модель обнаружения объектов и сегментации изображений: Tiny YOLOv3.

Сообщество ONNX предоставляет средства для создания и развертывания модели глубокого обучения.

Скачивание обученных моделей ИИ

Чтобы скачать обученные модели ИИ, рекомендуется использовать двойники устройств для получения уведомлений о готовности новой модели. Даже если устройство находится в автономном режиме, сообщение может быть кэшировано в Центр Интернета вещей до тех пор, пока пограничное устройство не вернется в сеть. Сообщение будет синхронизировано автоматически.

Ниже приведен пример кода Python, который регистрирует уведомления для двойников устройств, а затем загружает модель ИИ в ZIP-файл. Он также выполняет дальнейшие операции с скачанным файлом.

Код выполняет следующие задачи:

  1. Получение уведомления двойников устройства. Уведомление включает имя файла, адрес скачивания файла и маркер проверки подлинности MD5. (В имени файла можно включить сведения о версии, например 1.0.)
  2. Скачайте модель ИИ в виде ZIP-файла в локальное хранилище.
  3. При необходимости выполните контрольную сумму MD5. Проверка MD5 помогает предотвратить изменение ZIP-файлов во время сетевой передачи.
  4. Распакуйте ZIP-файл и сохраните его локально.
  5. Отправьте уведомление в Центр Интернета вещей или сообщение маршрутизации, чтобы сообщить о готовности новой модели ИИ.
# 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 )

Вывод

После скачивания модели ИИ следующий шаг — использовать модель на пограничном устройстве. Вы можете динамически загружать модель и выполнять обнаружение объектов на пограничных устройствах. В следующем примере кода показано, как использовать модель искусственного интеллекта LiteRT для обнаружения объектов на пограничных устройствах.

Код выполняет следующие задачи:

  1. Динамически загружайте модель искусственного интеллекта LiteRT.
  2. Выполнение стандартизации изображений.
  3. Обнаружение объектов.
  4. Оценки обнаружения вычислений.
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)

Ниже приведена версия ONNX предыдущего кода. Действия в основном одинаковы. Единственное различие заключается в том, как обрабатывается оценка обнаружения, так как Labelmap выходные параметры модели отличаются.

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)

Если устройство IoT edge включает предыдущий код и функции, пограничное устройство имеет обнаружение объектов ИИ и поддерживает динамическое обновление моделей ИИ. Если вы хотите, чтобы пограничный модуль предоставлял функции искусственного интеллекта другим приложениям или модулям через веб-API, вы можете создать веб-API в модуле.

Платформа Flask — это один из примеров инструмента, который можно использовать для быстрого создания API. Изображения можно получать в виде двоичных данных, использовать модель ИИ для обнаружения, а затем возвращать результаты в формате JSON. Дополнительные сведения см. в руководстве flask: Flask в Visual Studio Code.

Соавторы

Эта статья поддерживается корпорацией Майкрософт. Первоначально он был написан следующими участниками.

Автор субъекта:

  • Бо Ван | Старший инженер по программному обеспечению

Другой участник:

Чтобы просмотреть недоступные профили LinkedIn, войдите в LinkedIn.

Следующие шаги