2019 年 2 月

第 34 卷,第 2 期

[机器学习]

Edge 上的机器学习:Azure ML 和 Azure IOT Edge 集成

作者 Ted Way | 2019 年 2 月

Edge 被定义为,不是云的任何计算平台。它包括 Azure Stack(充当微型 Azure 的计算机群集)、功能强大的网关服务器,以及 Raspberry Pi 或传感器等更小的设备。如今,甚至冰箱和其他有足够处理能力的设备也可被视为 Edge 设备。面临的挑战是,如何将人工智能 (AI) 和机器学习 (ML) 应用于无法进入云的数据,无论是由于数据主权、隐私、带宽还是其他问题。

James McCaffrey 在 2018 年 7 月发行的《MSDN 杂志》中发表了一篇标题为“Edge 上的机器学习与 loT 设备”的文章 (msdn.com/magazine/mt847186),介绍了如何定型 ML 模型,并使用 Microsoft 嵌入式学习库 (ELL) 将优化后的模型部署到小型 Edge 设备。本文重点介绍了用于通过 Azure 机器学习来定型、管理和容器化模型的企业工具和服务。此外,还介绍了如何使用 Azure IoT Edge 配置数据管道,以利用此模型并将它部署到 Edge 设备。本文将举例逐步介绍。在示例中,可使用自定义视觉服务来定型使用 Azure IoT Edge 部署的模型。可使用 Azure ML 注册此模型的迭代。

雪豹方案

国际雪豹基金会 (snowleopard.org) 致力于“更好地了解濒临灭绝的雪豹,并通过与雪豹栖息地内的社区密切合作来保护雪豹”。 为此,基金会在 200 平方英里范围内设置相机,以跟踪此区域内有哪些雪豹。先部署相机,再在一段设定时间后从相机中收集数据。这些相机是运动感应型,可能会产生一些意外情况。例如,几年前,研究人员冲洗了一卷胶片,只发现在风中摇曳的树的照片。

使用数字相机,可以捕获更多图像,但即使这样,也只有 5% 的图像内有雪豹。研究人员可能需要花费超过 200 个小时来细查图片,才能找到内有雪豹的图片。能够更好地利用研究人员时间的做法是,将 ML 算法定型为识别雪豹。

ML 算法可以说成是进行“学习”,而不是进行显式编程。例如,若要创建根据各种因素(如卧室数、建筑面积和其他因素)预测房屋价格的应用,可以显式编程应用,具体做法是添加规则,或在已包含答案的某数据库中进行查找。如果用户输入的卧室数介于 2 和 4 之间,并且房屋有一定的建筑面积,你便会返回特定价格。

可使用非常简单的方程(而不是对响应进行编程),例如:

y = w1*x1 + w2*x2 + b

在此示例中,x1 表示卧室数,x2 表示建筑面积。根据大量房屋的的最后销售价格数据,可以通过找到最适合数据的 w1、w2 和 b 来“定型”模型。定型后的 ML 模型是静态的。也就是说,如果两次你都提供相同的输入,模型每次都会响应输出相同的内容。不过,可使用新数据定型算法并更新算法,从而继续改进算法,这实际上是在寻找 w1、w2 和 b 的新值。

深度学习是 ML 的一部分。我们使用跨多层包含多个神经元或节点的人工神经网络或深度神经网络 (DNN),而不是刚刚所述的简单线性方程。目标是模拟大脑的工作方式。每个神经元都根据数据(如图像的某些部分)执行计算;如果结果达到特定阈值,神经元便会激活。

然后,此输入会被馈送到下一层。例如,假设有一张手写数字的图片,并且有两个神经元。第一个神经元检查图片的上半部分,第二个神经元检查图片的下半部分。如果第一个神经元看到圆圈,它就会激活。如果第二个神经元看到竖线,它就会激活。对于数字 8,第一个神经元会激活,但第二个神经元不会激活。不过,对于数字 9,第一个神经元和第二个神经元都会激活。然后,可以根据此信息得出结论:数字为 9。

Azure 组件

在进一步探讨之前,请务必了解 Azure 和 ML 堆栈的组件。这些工作包括:

自定义视觉服务:此服务属于认知服务平台,可便于通过上传图像并手动标记图像来轻松定型 AI 模型。模型可以导出到多个平台(如 iOS (CoreML)、ONNX 或 TensorFlow),并能根据 Protocol Buffers (protobufs) 保存为 .pb 格式。Google 对 protobufs 的描述为,“它是一种中性语言、中性平台且可扩展的机制,可用于序列化结构化数据(例如,XML),但更小、更快且更简单”(developers.google.com/protocol-buffers)。

Azure 机器学习:此服务是用作云服务的端到端数据科学平台,可用于开发和部署 ML 模型。使用 Azure 机器学习服务,可以在生成、定型、部署和管理模型时跟踪模型,所有都是在云提供的大规模范围内进行。

Azure IoT 中心和 IoT Edge:Azure IoT 中心用于连接、监视和控制数十亿物联网 (IoT) 资产。Azure IoT Edge 是通过 IoT 中心远程管理的平台。借助此平台,可以将部分工作负载转移到 Edge,从而分析设备(而不是云)中的数据。设备可以花更少的时间将消息发送到云,并能自主快速地回应变更。

使用这些组件,国际雪豹基金会可以部署 30 个运动感应型相机,每个都连接到价格相对便宜的 PC。我们将使用 Azure IoT Edge 将雪豹模型部署到模拟设备之一的 PC。

Edge 上的图像分类

现在,将逐步介绍如何预配所需的 Azure 资源,并生成解决方案。以下部分将更详细地介绍预配内容以及代码中发生的一切。首先,需要满足以下先决条件:

• Azure 订阅。尚无帐户?请访问 azure.microsoft.com/free 来创建免费帐户。

• Visual Studio Code (code.visualstudio.com)。

• 用作 IoT Edge 设备的 Ubuntu 16.04 Linux x64 VM。

• Azure 资源,包括 Azure 机器学习工作区、Azure IoT 中心和 CustomVision.ai 帐户。

• 模型部署示例 (aka.ms/visedge)。

创建 Azure ML 工作区还会创建存储帐户和 Azure 容器注册表,可用于存储 Docker 映像以供部署到 IoT Edge 设备。首先,从 aka.ms/vscodetoolsforai 下载并安装 VS Code Tools for AI。安装后,单击左侧 VS Code 活动栏中的 Azure 符号。

现在,按 Ctrl+Shift+P 打开命令面板,并键入“Install Azure ML SDK”,从而安装 Azure ML SDK。 在集成终端窗口中,指定要使用的 Python 解释器,也可以按 Enter 来使用默认 Python 解释器。

接下来,单击 Visual Studio Code 活动栏中的 Azure 图标,以创建 Azure ML 工作区。此时,“Azure:机器学习”边栏显示。右键单击你的 Azure 订阅,并选择“创建工作区”。从随即显示的列表中选择现有资源组,或使用命令面板中的向导新建一个资源组。此示例使用的是“iot-camera-group”。

现在,在字段中为新工作区键入明确的唯一名称。按 Enter。此时,系统便会新建工作区。新工作区显示在树中的订阅名称下。

接下来,打开 VS Code,并单击左侧窗格中的“扩展”按钮 (Ctrl+Shift+X),从而创建 IoT 中心。搜索“Azure IoT 工具包”,并安装扩展。需要将 Linux PC 用作 Edge 设备。可使用 PC、Azure 或其他任何有权访问的位置上的 VM。

首先,单击左侧活动栏上的“资源管理器”按钮,并查找“Azure IoT 中心设备”条目。单击“更多操作”(三个点),再选择“创建 IoT 中心”。

现在,选择一个订阅,再选择前面创建的资源组(“iot-camera-group”)或新建一个。接下来,选择区域和层。可使用免费的 F1 层,但如果已有 F1 层 IoT 中心,便需要创建 S1 层。最后,为 IoT 中心命名(如“iot-camera-hub”)。

现在,是时候注册 IoT Edge 设备了。具体方法为将条目添加到 IoT 中心。这会生成要在 IoT Edge 设备(在此示例中,为 Linux VM)中输入的连接字符串,这样就能连接到 IoT 中心了。

为此,请先单击“Azure IoT 中心设备”上的“更多操作”菜单(三个点),再选择“创建 IoT Edge 设备”。 不要单击“创建设备”。 然后,输入 Edge 设备的名称(如“camera-pc-01”)。最后,将 connectionString 值复制到刚刚创建的条目中。需要在 Edge 设备中输入此信息。

未来可以依次单击“Azure IoT 中心设备”和“选择 IoT 中心”。 需要先登录 Azure,再依次选择预配了 IoT 中心的订阅和 IoT 中心,但可以查看与设备关联的所有连接字符串。

设置 IoT Edge 设备

模拟 Edge 设备的最简单方法是,使用预配置的 VM (aka.ms/edgevm)。VM 在启动后安装最新的 Azure IoT Edge 运行时和依赖项。预配 VM 后,登录 VM,并使用向 IoT Edge 注册的设备的连接字符串在命令行中运行以下命令(连接字符串是之前复制的 connectionString 值):

/etc/iotedge/configedge.sh "<your_iothub_edge_connection_string>"

若要将自己的 Ubuntu 16.04 x64 VM 设置为 Edge 设备,请查看以下信息:aka.ms/edgevm

图像分类解决方案

现在,是时候生成图像分类解决方案了。克隆 Git 示例 (aka.ms/visedge)。我们将使用 Linux x64 PC 作为 Edge 计算机。请遵循以下的步骤:

• 使用容器注册表的值更新 .env 文件,并确保 Docker 引擎有权访问它。

• 将 deployment.template.json 文件重命名为 deployment.template.RPI.json。

• 将 deployment.template.test-amd64.json 文件重命名为 deployment.template.json。

• 右键单击文件 deployment.template.json,并选择“生成 IoT Edge 解决方案”,从而生成整个解决方案(这可能需要一段时间才能完成,尤其是生成 numpy 和 pillow)。

• 右键单击 config/deployment.json 文件,为 IoT Edge 设备选择“创建部署”,并选择目标设备,从而将解决方案部署到设备。

• 在 VS Code IoT Edge 扩展中右键单击设备,并选择“开始监视 D2C 消息”,从而监视发送到云中的消息。

对于 x64 版解决方案,执行上面这些步骤会部署以下两个模块:

相机捕获:此模块捕获来自 USB 相机的视频流,并将这些帧发送到图像分类模块。结果发送到 edgeHub,它可以将结果作为消息中继到 IoT 中心。

自定义视觉:此 Web 服务通过 HTTP 在本地运行,接收图像并对图像进行分类。它是使用自定义视觉 (customvision.ai) 网站生成。解决方案中的此模型对图像进行分类,以确定其中包含的是苹果还是香蕉。

“相机捕获”模块可使用 Azure IoT Edge SDK 将消息发送到 edgeHub。可以生成自己的模块作为 Docker 容器,并使用 SDK 与 edgeHub 通信。

对于自定义视觉服务,可以使用解决方案中的现有模型,也可以转到 customvision.ai 定型你自己的模型。若要创建雪豹分类器,请将雪豹图片上传到自定义视觉服务。还可以上传可能会在雪豹栖息地发现的其他动物的图片。只需标记这些图像,并单击“定型”按钮,即可定型模型。模型和标签文件分别导出为 .pb 和 .txt 文件,可用于替换解决方案中的内容。

自定义视觉使用迁移学习,此学习需要使用现有深度神经网络,并从图像中提取功能。然后,它使用数据(在此示例中,为上传的图像和分配给图像的标记)定型分类器。

为了阐明迁移学习,假设要训练嗅弹犬(例如,德国牧羊犬)。如果你训练 2 岁的德国牧羊犬,它对炸弹一无所知。但它有强大的嗅觉,因此确实有检测炸弹的“基础设施”。可以这样开始训练德国牧羊犬:先让它嗅一嗅炸弹,并告诉它这是炸弹,再让它闻另一种味道(如花的味道)并告诉它这不是炸弹。使用各种类型的炸弹和其他味道重复此训练过程,经过几个月的训练并喂食大量狗粮后,你自己都已成为嗅弹犬了。训练德国牧羊犬并不困难。“生成”德国牧羊犬才是最难的部分。

ResNet 50、Inception v3、YOLO 和 VGG 等 DNN 本质上相当于德国牧羊犬。它们是接收图像并从图像中提取功能的复杂 AI 模型,工作方式与德国牧羊犬通过气味提取信号大致相同。就像房屋的卧室数和建筑面积是可用于预测价格的功能一样,提取的功能会被发送到分类器。此分类器是要定型的内容,然后它便能预测图像中是否有雪豹。分类器可以是简单逻辑回归、支持向量机或其他任何分类器。迁移学习的优势是,可使用 DNN 提取功能,从而生成可用于非常准确地分类图像的实用功能。

图 1 展示了接收图像并使用 AI 模型预测图像所含内容的代码。

图 1:预测代码

from urllib.request import urlopen

import tensorflow as tf

from PIL import Image
import numpy as np
# import scipy
# from scipy import misc
import sys
import os  

filename = 'model.pb'
labels_filename = 'labels.txt'

network_input_size = 227
mean_values_b_g_r = (0,0,0)

size = (256, 256)
output_layer = 'loss:0'
input_node = 'Placeholder:0'

graph_def = tf.GraphDef()
labels = []

def initialize():
  print('Loading model...',end=''),
  with tf.gfile.FastGFile(filename, 'rb') as f:
    graph_def.ParseFromString(f.read())
    tf.import_graph_def(graph_def, name='')
  print('Success!')
  print('Loading labels...', end='')
  with open(labels_filename, 'rt') as lf:
    for l in lf:
      l = l[:-1]
      labels.append(l)
  print(len(labels), 'found. Success!')
  
def crop_center(img,cropx,cropy):
  y,x,z = img.shape
  startx = x//2-(cropx//2)
  starty = y//2-(cropy//2)
  print('crop_center: ', x, 'x', y, 'to', cropx, 'x', cropy)
  return img[starty:starty+cropy,startx:startx+cropx]

def predict_url(imageUrl):
  print('Predicting from url: ',imageUrl)
  with urlopen(imageUrl) as testImage:
    # image = scipy.misc.imread(testImage)
    image = Image.open(testImage)
    return predict_image(image)

def predict_image(image):
  print('Predicting image')
  tf.reset_default_graph()
  tf.import_graph_def(graph_def, name='')
  
  with tf.Session() as sess:
    prob_tensor = sess.graph.get_tensor_by_name(output_layer)

    # w = image.shape[0]
    # h = image.shape[1]
    w, h = image.size
    print('Image size',w,'x',h)

    # scaling
    if w > h:
      new_size = (int((float(size[1]) / h) * w), size[1], 3)
    else:
      new_size = (size[0], int((float(size[0]) / w) * h), 3)

    # resize
    if  not (new_size[0] == w and new_size[0] == h):
      print('Resizing to', new_size[0],'x',new_size[1])
      #augmented_image = scipy.misc.imresize(image, new_size)
      augmented_image = np.asarray(image.resize((new_size[0], new_size[1])))
    else:
      augmented_image = np.asarray(image)

    # crop center
    try:
      augmented_image = crop_center(augmented_image, network_input_size, 
        network_input_size)
    except:
      return 'error: crop_center'

    augmented_image = augmented_image.astype(float)

    # RGB -> BGR
    red, green, blue = tf.split(axis=2, num_or_size_splits=3, value=augmented_image)

    image_normalized = tf.concat(axis=2, values=[
      blue - mean_values_b_g_r[0],
      green - mean_values_b_g_r[1],
      red - mean_values_b_g_r[2],
    ])

    image_normalized = image_normalized.eval()
    image_normalized = np.expand_dims(image_normalized, axis=0)

    predictions, = sess.run(prob_tensor, {input_node: image_normalized})

    result = []
    idx = 0
    for p in predictions:
      truncated_probablity = np.float64(round(p,8))
      if (truncated_probablity > 1e-8):
        result.append({'Tag': labels[idx], 'Probability': truncated_probablity })
      idx += 1
    print('Results: ', str(result))
    return result

Azure 机器学习

可使用自定义视觉来创建 AI 模型,但若要定型多个模型或与数据科学家团队协作,该怎么办?数据科学工作流中缺少软件开发的严谨性,尤其是源代码管理方面。Azure ML 将所有这些与数据准备、试验、模型管理和操作化整合在一起。

对于此示例,若要使用自定义视觉进行各种试验,可以注册 .pb 模型和其他文件,这样就能跟踪它们了。代码如下所示:

from azureml.core.model import Model
model = Model.register(model_path = "custom-vision-model/",
                       model_name = "model.pb",
                       tags = {"area": "vision", "type": "classification", 
                         "version": "1.0"},
                       description = "Snow leopard classification model",
                       workspace = ws)

若要查看已注册的所有模型,请输入以下代码:

models = Model.list(workspace=ws)
for m in models:
  print("Name:", m.name,"\tVersion:", m.version, "\tDescription:", 
  m.description, m.tags)

若要定型你自己的模型,请查看 Azure 机器学习示例笔记本 (github.com/Azure/MachineLearningNotebooks)。可以定型和注册模型,并使用模型创建 Docker 映像。Docker 映像可以部署到云中的 Azure 容器实例或 Azure Kubernetes 服务。REST API 在执行数据评分时要调用的容器中进行公开。Docker 映像还包含 Azure IoT Edge SDK,它使用 Edge 设备上运行的 IoT Edge 中转消息。这样可以创建容器,并将容器部署到云或 Edge。

总结

生成企业解决方案时,需要使用大量组件,先使用自定义视觉来定型模型,再使用 Azure ML 来管理模型(并定型其他类型的模型),最后使用 Azure IoT Edge 来部署模型。设置这些服务虽然会多花一些时间,但有以下优势:轻松定型新模型,并在多个 Edge 设备上更新这些模型。


Ted Wa**** 是 Azure ML 团队的高级项目经理,致力于加速发展云中的 AI 和专用硬件上的 Edge。他凭借根据肺部 CT 扫描预测疾病是否为恶性的“面向放射科医生的拼写检查”计算机辅助诊断系统,取得了密歇根大学的博士学位。

Emmanuel Bertrand 是 Azure IoT Edge 团队的高级项目经理,他致力于方便用户通过 Azure 市场轻松部署 IoT 模块。Bertrand 是作为客户和用户体验专家加入了 Microsoft。在此之前,他已使用传统工具对多个工业公司的流程进行了数字化。

衷心感谢以下技术专家对本文的审阅:Rakesh Kelkar、Chipalo Street


在 MSDN 杂志论坛讨论这篇文章