2018 年 7 月

第 33 卷,第 7 期

机器学习 - 边缘状态下的机器学习与 loT 设备

通过James McCaffrey

想象一下,不太遥远的将来,您的智能流量交集设计器。智能交集具有四个视频摄像机连接到 Internet 的物联网 (IoT) 设备的小 CPU,类似于将 Raspberry Pi。摄像机将视频帧发送到 IoT 设备,它们使用机器学习 (ML) 图像识别模型分析,以及控制说明随后会发送到流量信号。一个小型的 IoT 设备连接到 Azure 云服务,其中记录信息和脱机分析。

这是在 edge 上的 IoT 设备上的机器学习示例。我使用的术语边缘设备来表示任何内容连接到云,其中云是指内容 (如 Microsoft Azure) 公司的远程服务器。在本文中,我将介绍两种方法,您可以设计机器学习边缘上。具体而言,我将介绍如何编写一个自定义模型和 IO 函数对于设备,以及如何使用 Microsoft Embedded 学习库 (格) 的工具集将优化的机器学习模型部署到边缘设备。自定义的 IO 方法目前,当我写本文中,将机器学习模型部署到 IoT 设备的最常见方法。前瞻性格方法。

即使您不使用 ML IoT 设备上,有为什么要阅读这篇文章的至少三个原因。首先,所涉及的设计原则通用化到其他软件开发方案。其次,它是很有可能,您将使用机器学习和 IoT 设备相对推出。第三,您可能只是发现此处所述的技术本身就非常吸引人。

为什么 ML 需要位于 IoT edge 上?为什么不只是执行所有处理在云中?IoT edge 上的设备可以是非常便宜,但通常具有有限的内存,有限处理功能和限制的电源。在许多情况下,尝试执行机器学习在云中处理有几个缺点。

延迟通常是很大的问题。在智能流量交集示例中,一小部分的第二个以上的延迟可能造成灾难性后果。尝试在云中执行机器学习的其他问题包括的可靠性 (无法预测且难以处理通常是网络连接中断),网络可用性 (例如,船在 sea 可能没有连接仅在时附属项是开销) 和隐私/安全 (时,例如,您在监视在医院患者。)

本文并不假定您有任何特定的背景或技能集,但假定您有一些常规软件开发经验。该演示本文 (使用 CNTK 库创建 ML 模型、 C 程序中模拟 IoT 代码和使用格模型的 Python 程序的 Python 程序) 中所述的程序是太长,无法在这里,但它们在向下随附的文件中可用加载。

什么是机器学习模型?

若要了解使用机器学习模型部署到边缘上的 IoT 设备的问题,必须了解究竟是什么的机器学习模型。机器学习模型非常粗略地讲,是接受的输入的数据、 进行预测并生成输出数据所需的所有信息。而不是尝试对此进行说明抽象,我将演示使用一个具体示例的想法。

看一看中的屏幕截图图 1和中的关系图图 2。两个图显示包含四个输入的节点、 五个隐藏的层处理节点和三个输出层节点的神经网络。输入的值为 (6,1、 3.1、 5.1、 1.1),输出值为 (0.0321,0.6458,0.3221)。图 1演示了如何开发和训练模型时。我使用 Visual Studio Code 中,但存在许多可选方案。

创建和定型神经网络模型
图 1 创建和定型神经网络模型

神经网络输入输出机制
图 2 神经网络输入输出机制

此特定示例包括预测鸢尾花使用表示花萼 (类似于叶的结构) 长度和宽度和花瓣长度和宽度的输入的值的种类。有三个可能物种的花: setosa,versicolor,virginica。输出值可以解释为概率 (请注意,它们总和为 1.0) 因此,第二个值,0.6458,是最大,因为该模型的预测是第二个种类,versicolor。

在中图 2,连接权重的节点表示的对每个行。权重是只是一个数值常量。如果节点都从零开始编制索引,从上到下,从 input[0] 到 hidden[0] 的权重是 0.2680 和从 hidden[4] 到 [0] 的输出的权重是 0.9381。

每个隐藏和输出节点都有一个小箭头,指向到的节点。这些被称为偏差。隐藏 [0] 的偏差是 0.1164 和输出 [0] 的偏差是-0.0466。

您可以将神经网络作为复杂的数学运算函数因为它只接受数值输入并生成数字输出。在 IoT 设备上的机器学习模型需要知道如何计算输出。针对中的神经网络图 2,第一步是计算隐藏节点的值。每个隐藏节点值是应用于的输入和关联的权重,再加上偏差乘积之和的双曲正切 (tanh) 函数。对于隐藏 [0] 计算为:

hidden[0] = tanh((6.1 * 0.2680) + (3.1 * 0.3954) +
                 (5.1 * -0.5503) + (1.1 * -0.3220) + 0.1164)
          = tanh(-0.1838)
          = -0.1817

隐藏的节点 [1] 到 [4] 的计算方式相似。tanh 函数称为隐藏激活函数。还有其他可以使用,如逻辑 sigmoid 函数和整流线性单元,可为不同的隐藏的节点值的激活函数。

隐藏的节点值计算出后下, 一步是计算输出节点的初始值。初步输出节点值是只需的隐藏的节点和关联的隐藏到输出权重,再加上偏差的乘积之和。换而言之,作为相同的计算使用但不支持的激活函数的隐藏节点。对于输出 [0] 的初步值计算为:

o_pre[0] = (-0.1817 * 0.7552) + (-0.0824 * -0.7297) +
           (-0.1190 * -0.6733) + (-0.9287 * 0.9367) +
           (-0.9081 * 0.9381) + (-0.0466)
         = -1.7654

以相同的方式进行计算输出节点 [1] 和 [2] 的值。计算出的输出节点后,最终输出节点值可以转换为使用 softmax 激活函数的概率。Softmax 函数按示例很好地解释。最终输出值的计算是:

sum = exp(o_pre[0]) + exp(o_pre[1]) + exp(o_pre[2])
    = 0.1711 + 3.4391 + 1.7153
    = 5.3255
output[0] = exp(o_pre[0]) / sum
          = 0.1711 / 5.3255 = 0.0321
output[1] = exp(o_pre[1]) / sum
          = 3.4391 / 5.3255 = 0.6458
output[2] = exp(o_pre[2]) / sum
          = 1.7153 / 5.3255 = 0.3221

对于隐藏节点没有备用输出节点激活函数,如 identity 函数。

总而言之,机器学习模型是接受的输入的数据并生成输出预测所需的所有信息。在神经网络的情况下此信息包括输入、 隐藏和输出节点的权重和偏置,值的数量和类型的隐藏和输出层节点上使用的激活函数。

好了,但其中的值的权重和偏置来自?它们是取决于为模型定型。培训使用已知输入的值和已知的更正的输出值,并且应用的优化算法,如反向传播到最小化计算得出的输出值之间的差异和已知的更正的输出值的数据集。

还有许多其他类型的机器学习模型,如决策树和朴素贝叶斯,但一般原则是相同的。使用 Microsoft CNTK 或 Google Keras/TensorFlow 等神经网络代码库时,训练机器学习模型的程序会将模型保存到磁盘。例如,CNTK 和 Keras 代码类似于:

mp = ".\\Models\\iris_nn.model"
model.save(mp, format=C.ModelFormat.CNTKv2)  # CNTK
model.save(".\\Models\\iris_model.h5")  # Keras

机器学习库也有函数来加载已保存的模型。例如,

mp = ".\\Models\\iris_nn.model"
model = C.ops.functions.Function.load(mp)  # CNTK
model = load_model(".\\Models\\iris_model.h5")  # Keras

大多数神经网络库有一种方法将只是模型的权重和偏置值保存到文件 (而不是整个模型)。

将标准的机器学习模型部署到 IoT 设备

中的图像图 1示范了定型机器学习模型的外观。Visual Studio Code 用作编辑器和 CNTK 于 v2.4 库的 Python 语言 API 接口。创建训练的机器学习模型可天或数周的努力,并且通常需要大量的处理能力和内存。因此,通常功能强大的计算机,通常使用一个或多个 Gpu 上执行模型训练。此外,如大小和复杂的神经网络会增加的权重数和偏置大大提高,并因此已保存的模型的文件大小也大大增加。

例如,4-5-3 在上一部分中所述的鸢尾花模型仅具有 (4 * 5) + 5 + (5 * 3) + 3 = 43 加的权重和偏差值。但该图像分类模型具有数以百万计的输入的像素值和数百个隐藏的处理节点可以有成百上千的数以百万计或甚至数十亿、 权重和偏差。请注意,所有的 43 个权重值和偏差的鸢尾花示例所示图 1:

[[ 0.2680 -0.3782 -0.3828  0.1143  0.1269]
 [ 0.3954 -0.4367 -0.4332  0.3880  0.3814]
 [-0.5503  0.6453  0.6394 -0.6454 -0.6300]
 [-0.322   0.4035  0.4163 -0.3074 -0.3112]]
 [ 0.1164 -0.1567 -0.1604  0.0810  0.0822]
[[ 0.7552 -0.0001 -0.7706]
 [-0.7297 -0.2048  0.9301]
 [-0.6733 -0.2512  0.9167]
 [ 0.9367 -0.4276 -0.5134]
 [ 0.9381 -0.3728 -0.5667]]
 [-0.0466  0.4528 -0.4062]

因此,假设您有一个训练的机器学习模型。你想要将模型部署到小型的弱,IoT 设备。最简单的解决方案是安装到的 IoT 设备上用来训练该模型的同一个神经网络库软件。然后可以将保存的训练的模型文件复制到的 IoT 设备,并编写代码来加载模型并进行预测。简单 !

遗憾的是,这种方法将仅适用于 IoT 设备是功能非常强大的相对较少情况下,可能是按一系列台式计算机或便携式计算机。此外,CNTK 和 Keras/TensorFlow 等神经网络库旨在快速而高效地定型模型,但一般情况下它们一定的设计并未以获得最佳性能时执行输入-输出使用训练的模型。简单地说,训练的机器学习模型部署到 IoT 设备上边缘的简单解决方案并不现实。

自定义代码解决方案

根据我的体验和与同事展开对话,将已训练的机器学习模型部署到 IoT 设备上边缘的最常见方法是在设备上编写 C/c + + 的自定义代码。思路是 C/c + + 在 IoT 设备上普遍可用,并且 C/c + + 通常既快速又 compact。中的演示程序图 3说明了这一概念。

模拟的 IoT 设备上的自定义 C/c + + IO 代码
图 3 模拟的 IoT 设备上的自定义 C/c + + IO 代码

演示程序使用 gcc C/c + + 工具来编译成可执行文件在目标设备上的文件 test.c 启动。在这里,目标设备是只是我的桌面 PC 但几乎每种 IoT/CPU 设备的 C/c + + 编译器。运行时,该演示程序显示的权重值和偏差的鸢尾花示例中,然后使用 (6.1、 3.1、 5.1、 1.1) 的输入的值和计算资源和显示 (0.0321,0.6458,0.3221) 的输出值。如果您比较图 3图 12、 输入、 权重和偏置,将看到和输出都相同 (取决于舍入误差)。   

演示程序 test.c 实现神经网络的输入输出过程。程序启动时通过设置结构数据结构,以包含每个层、 隐藏的值和输出层节点中的节点数和权重和偏差值:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>  // Has tanh()
typedef struct {
  int ni, nh, no;
  float *h_nodes, *o_nodes;  // No i_nodes
  float **ih_wts, **ho_wts;
  float *h_biases, *o_biases;
} nn_t;

程序定义的以下函数:

construct(): initialize the struct
free(): deallocate memory when done
set_weights(): assign values to weights and biases
softmax(): the softmax function
predict(): implements the NN IO mechanism
show_weights(): a display helper

关键的演示程序主函数中的代码行如下所示:

nn_t net;  // Neural net struct
construct(&net, 4, 5, 3);  // Instantiate the NN
float wts[43] = {  // specify the weights and biases
  0.2680, -0.3782, -0.3828, 0.1143, 0.1269,
. . .
 -0.0466, 0.4528, -0.4062 };
set_weights(&net, wts);  // Copy values into NN
float inpts[4] = { 6.1, 3.1, 5.1, 1.1 };  // Inputs
int shownodes = 0;  // Don’t show
float* probs = predict(net, inpts, shownodes);

不同之处在于,如果您知道确切的简单神经网络机器学习模型的工作原理,IO 进程不神奇。您可以非常轻松地实现基本的 IO。

使用自定义的 C/c + + IO 函数的主要优点是概念简单性。此外,您在非常低的级别 (程序集语言之上的抽象层实际上只是一个级别) 进行编码,因为生成的可执行代码通常会很小且运行非常快。此外,由于你有完全控制你 IO 的代码,可以使用所有类型的技巧来提高性能或减少内存占用量。例如,程序 test.c 使用 float 类型,但具体取决于该问题的情景,您可能能够使用自定义的 16 位定点数据类型。

使用自定义的 C/c + + IO 方法的主要缺点是技术变得越来越困难的已训练的机器学习模型增加的复杂性。例如,具有 tanh 和 softmax 激活的一个隐藏的层神经网络 IO 函数可以非常轻松地实现 — 当然采用只有大约一天的开发工作,具体取决于许多因素,一周。具有多个隐藏层的深度神经网络是某种程度上轻松处理 — 也许一周或两个工作。但实现的一个卷积神经网络 (CNN) 或长时间的 IO 功能,短期记忆 (LSTM) 循环神经网络是非常困难的通常需要更多个四个星期的开发工作。

我怀疑,作为使用 IoT 设备的增加,会创建实现由不同的神经网络库,如 CNTK 和 Keras/TensorFlow 创建机器学习模型的 IO 的开放源 C/c + + 库的工作。或者,如果没有足够的需求,神经网络库的开发人员可能会为 IoT 设备本身创建 C/c + + IO Api。如果有这样的库,编写自定义 IoT 设备的 IO 会变得相对简单。

Microsoft Embedded 的学习库

Microsoft Embedded 学习库 (格) 是旨在简化将机器学习模型部署到 IoT 设备上的边缘 (microsoft.github.io/ELL) 所需的开发工作量的前景广阔的开放源代码项目。左侧和右侧的说明了格的基本理念图 4

高级别和精细的格工作流进程
图 4 格工作流程,高级别和精细

在口头上,格系统接受由受支持的库,例如 CNTK 或支持的模型格式,如打开神经网络交换 (ONNX) 创建的机器学习模型。格系统使用输入的机器学习模型,并生成中间模型作为.ell 文件。然后,格系统使用中间.ell 模型文件来生成可执行代码的某种受支持的目标设备。将另一种方法,您可以将作为一种机器学习模型的交叉编译器格。

格的工作原理的更详细说明显示在右侧图 4,使用 iris 鲜花模型示例。过程开始编写名为 iris_nn.py 的 Python 程序的机器学习开发人员创建并保存名为 iris_cntk.model,这是专用的二进制格式中的预测模型。此过程所示图 1

好了命令行工具 cntk_import.py 然后用于创建一个中间 iris_cntk.ell 文件,其中以 JSON 格式存储。接下来,好了命令行工具 wrap.py 用于生成 C/c + + 源代码文件的目录 host\build。请注意,"host"意味着要采用从当前计算机设置,因此 \pi3\build 像是一种更常见方案。然后 cmake.exe C/c + + 编译器生成工具用于生成 Python 模块的可执行代码,其中包含名为 iris_cntk 原始的机器学习模型的逻辑。目标可能是 C/c + + 可执行文件或 C# 可执行文件或任何最适合的目标 IoT 设备。

Iris_cntk Python 模块可然后使用导入 (我的台式计算机),在目标设备上的 Python 程序 (use_iris_ell_model.py) 中所示图 5。请注意,输入的值 (6.1、 3.1、 5.1、 1.1) 和生成的格系统模型的输出值 (0.0321,0.6457,0.3221) 是在模型开发过程中生成的值相同 (图 1) 和生成的值自定义 C/c + + IO 函数 (图 3)。

模拟的 IoT 设备上使用格模型图 5 模拟的 IoT 设备上使用格模型

前导"(py36)"中的命令提示之前图 5指示我正在调用的 Conda 环境,我使用的 Python 版本 3.6,在我编写我的演示程序中格时所需的特殊 Python 设置中。

程序 use_iris_ell_model.py 的代码所示图 6。不同之处在于格已生成 Python 模块/包可以使用任何其他包/模块一样。

图 6 在 Python 程序中使用格模型

# use_iris_ell_model.py
# Python 3.6
import numpy as np
import tutorial_helpers   # used to find package
import iris_cntk as m     # the ELL module/package
print("\nBegin use ELL model demo \n")
unknown = np.array([[6.1, 3.1, 5.1, 1.1]],
  dtype=np.float32)
np.set_printoptions(precision=4, suppress=True)
print("Input to ELL model: ")
print(unknown)
predicted = m.predict(unknown)
print("\nPrediction probabilities: ")
print(predicted)
print("\nEnd ELL demo \n"

格系统仍处于非常早期开发,但根据我的经验,系统已准备就绪,尝试使用,并且是稳定的有限的生产开发方案。

我希望您对关系图中的格过程的反应图 4 ,而这是其说明等,"哇 ! 这很多步骤 !" 至少,这就是我的反应。最后,我希望格系统成熟到可以在此生成的模型部署到 IoT 设备的代码行上的点:

source_model = ".\\iris_cntk.model"
target_model = ".\\iris_cortex_m4.model"
ell_generate(source_model, target_model)

但现在,如果你想要浏览格您必须能够使用几个步骤。幸运的是,本文大部分所基于的格 Web 站点的格教程是很好。若要开始使用格必须安装格在台式计算机上和安装包括生成 C/c + + 源代码,应该指出,没有 (尚) 格没有.msi 安装程序。

并不明显的格很酷的功能是它会执行在后台一定非常复杂的优化。例如,格团队探讨了如何压缩大的机器学习模型,包括 sparsification 和修剪技术,并替换具有 1 位数学的浮点数学。格团队还查看可用于代替神经网络,包括改进了的决策树和 k DNF 分类器的算法。

格网站上的教程很不错,但由于没有涉及的许多步骤,因此它们是有点长。我将简要草拟该过程,以便您可以了解的内容就像安装和使用格。请注意,我的命令不正确的语法;它们是高度简化,以保持主要观点的鲜明性。

安装好了系统如下所示:

x> (install several tools such as cmake and BLAS)
> git clone https://github.com/Microsoft/ELL.git
> cd ELL
> nuget.exe restore external/packages.config -PackagesDirectory external
> md build
> cd build
> cmake -G "Visual Studio 15 2017 Win64" ..
> cmake --build . --config Release
> cmake --build . --target _ELL_python --config Release

在口头上,您必须具有很多工具安装在开始之前,然后从 GitHub 拉取格源代码,然后生成格可执行工具和 Python 绑定使用 cmake。

创建格模型如下所示:

> python cntk_import.py iris_cntk.model
> python wrap.py iris_nn_cntk.ell --language python --target host
> cd host
> md build
> cd build
> cmake -G "Visual Studio 15 2017 Win64" .. && cmake --build . --config release

也就是说,使用格工具 cntk_import.py CNTK 模型文件创建.ell 文件。Wrap.py 用于生成大量的 C/c + + 特定于特定的目标 IoT 设备。并使用 cmake 生成封装原始定型的 ML 模型的行为的可执行文件。

总结

总而言之,机器学习模型是软件系统来接受输入,生成预测所需的所有信息。由于 IoT edge 上的设备通常需要非常快速且可靠的性能,因此,有时需要计算直接在设备上的机器学习预测。但是,IoT 设备通常是小型和弱,因此不能只需将复制到设备的功能强大的桌面计算机开发一个模型。标准方法是编写自定义 C/c + + 代码,但这种方法不会扩展到复杂的机器学习模型。一种新兴的方法是使用 ML 跨编译器,如 Microsoft Embedded 学习库。

当完全成熟且已发布时,格系统很可能会让开发比目前的难度大大降低边缘上的 IoT 设备的复杂机器学习模型。


Dr.James McCaffrey 供职于华盛顿地区雷蒙德市沃什湾的 Microsoft Research。他参与过多个 Microsoft 产品的工作,包括 Internet Explorer 和必应。Scripto可通过 jamccaff@microsoft.com 与 McCaffrey 取得联系。

衷心感谢以下 Microsoft 技术专家对本文的审阅:Byron Changuion、 Chuck Jacobs、 Chris Lee 和 Ricky Loynd


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