亚马逊AWS官方博客

介绍 Gluon 这一易于使用的灵活深度学习编程接口

原文链接 | 今天,AWS 和 Microsoft 发布了一项新规范,该规范旨在为所有开发人员改进机器学习技术的速度、灵活性和可访问性,而无论他们选择什么样的深度学习框架。这种合作的第一项成果是全新的 Gluon 接口,这是 Apache MXNet 中的一个开源库,让所有技能级别的开发人员都能够设计深度学习模型的原型、构建该模型以及开展训练。该接口极大地简化了创建深度学习模型的过程,而不会影响到训练速度。

下面是 Gluon 的四大优势以及演示这些优势的代码示例:

(1) 简单、容易理解的代码

在 Gluon 中,您可以使用简单、清晰和简洁的代码定义神经网络。您将获得一整套即插即用的神经网络构建块,包括预定义的层、优化程序和初始化程序。利用这些构建块,您无需再纠缠于各种各样复杂的底层实施细节。下面的示例说明了如何使用短短几行代码定义一个简单的神经网络:

# 第一步是初始化您的模型
net = gluon.nn.Sequential()
# 接下来,定义您的模型架构
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu")) # 第一层 - 128 个节点
    net.add(gluon.nn.Dense(64, activation="relu")) # 第二层 - 64 个节点
    net.add(gluon.nn.Dense(num_outputs)) # 输出层

下表说明了神经网络的体系结构:

有关更多信息,请转到本教程,了解如何使用 Gluon 神经网络构建块来构建一个称为多层感知器 (MLP) 的简单神经网络。对于更高级的使用案例来说,从头开始编写神经网络的各个部分也是非常容易的。使用 Gluon,您可以在神经网络中混用和匹配预定义组件和自定义组件。

(2) 灵活的结构

训练神经网络模型会耗费大量计算资源,在某些情况下,可能需要几天甚至几周的时间。许多深度学习框架通过严格定义模型并将其与训练算法分离,减少了这一时间。这种僵化的方法会让复杂性大为增加,也使得调试工作变得非常困难。

Gluon 方法则另辟蹊径。它将训练算法和神经网络模型组合在一起,在开发过程中提供了灵活性,而不会牺牲性能。这种方法的核心是 Gluon trainer 方法,该方法用于对模型进行训练。 trainer 方法依托于 MXNet autograd 库,该库用于自动计算导数 (即梯度)。导数 是一种数学运算,用于测量变量的变化率。这是训练算法的必要输入。 autograd 库可以有效地实施这些数学运算,并且对于支持 Gluon 提供的灵活性至关重要。现在,您可以定义由一个简单的嵌套 for 循环构成的训练算法,只需结合使用 autogradtrainer即可。

epochs = 10

for e in range(epochs):
    for i, batch in enumerate(train_data):
        data = batch.data[0]
        label = batch.label[0]
        with autograd.record(): # 开始记录导数
            output = net(data) # 前向迭代
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])

这种灵活的结构使您的代码直观且易于调试,为建立更高级的模型奠定了基础。您可以在自己的神经网络中使用熟悉的本机 Python 语言结构,如 for 循环或 if 语句,或者将这些语言结构用作您算法的一部分。通过将模型和算法组合在一起,可以执行模型中的每一行代码,这样能够更容易地确定导致错误的特定代码行。

(3) 动态图表

在某些情况下,神经网络模型可能需要在训练过程中改变形状和大小。尤其是在馈送到神经网络中的数据输入是可变内容的情况下,这是非常必要的,这种情况在自然语言处理 (NLP) 中很常见,其中输入的每个句子都可能有不同的长度。使用 Gluon,神经网络定义可以是动态的,这意味着您可以随时使用所需的任何结构以及任何 Python 本机控制流来构建神经网络。

例如,利用这些动态神经网络结构,可以更轻松地构建树状结构的长短期记忆 (LSTM) 模型,该模型是 NLP 的一大进步,由 Kai Sheng Tai、Richard Socher 和 Chris Manning 在 2015 年提出。树结构 LSTM 是一个强大的模型,例如可以识别两个句子是否具有相同的含义。以下面的情况为例,其中两个句子本质上具有相同的含义:

  • “Michael threw the football in front of the player.”
  • “The ball was thrown short of the target by Michael.”

可以简单地将句子馈送到一个周期性神经网络 (一种流行的序列学习模型) 并进行分类。不过,树结构 LSTM 的独到之处在于,它认为我们经常在语言中遇到一些有关先前学到的知识的问题。例如,句子呈现出语法结构,并且我们有强大的工具从句子中提取这个结构。我们可以使用一个树结构的神经网络来组合这些单词,该神经网络的结构模仿了句子的已知语法树结构,如下图所示。

(斯坦福自然语言处理组)

这就要求为每个示例动态地构建不同的神经网络结构。这对于传统框架来说很难,但是 Gluon 可以处理它,而且不会有任何问题。在下面的代码段中,您可以看到如何在模型训练的每个前向迭代中引入一个循环,同时仍然受益于 autogradtrainer 带来的简化。这使得模型能够遍历句子的树结构,从而基于该结构进行学习。

def forward(self, F, inputs, tree):
    children_outputs = [self.forward(F, inputs, child)
                        for child in tree.children]
    #在模型定义和训练过程中,基于每个
    #输入句子的语法结构来构建神经网络
    …

(4) 高性能

借助 Gluon 提供的灵活性,您可以轻松地对神经网络模型进行原型设计和试验。然后,当速度变得比灵活性更为重要时 (例如,当您准备好提供所有训练数据时),Gluon 接口使您能够轻松地缓存神经网络模型,以实现高性能和减少内存占用。当您完成原型设计并准备在更大的数据集上进行测试时,您只需要在设置神经网络过程中稍微调整即可。您不能使用 Sequential (如前所示) 来堆叠神经网络层,而必须使用 HybridSequential。它的功能与 Sequential相同,但可以让您调用经过优化的底层引擎来表示您的部分或全部模型架构。

net = nn.HybridSequential()
with net.name_scope():
    net.add(nn.Dense(128, activation="relu")) # 第一层 - 128 个节点
    net.add(nn.Dense(64, activation="relu")) # 第二层 - 64 个节点
    net.add(nn.Dense(10)) # 输出层

接下来,为了编译和优化 HybridSequential,我们可以调用 hybridize 方法:

net.hybridize()

现在,在训练您的模型时,您将能够获得与本机 MXNet 接口几乎相同的高性能,并实现几乎相同的内存节约。

Gluon 入门

要开始使用 Gluon,您可以按照安装最新版本的 MXNet 的以下简单步骤操作,或者可以在云中启动深度学习 Amazon 系统映像 (AMI)。接下来,我们将逐步了解如何使用我们之前讨论过的不同组件来构建和训练一个简单的两层神经网络,该神经网络称为多层感知器。我们建议使用 Python 3.3 或更高版本,并使用 Jupyter 笔记本实施此示例。

第一步,导入 MXNet 并获取 gluon 库以及其他必需的库 autogradndarray

import mxnet as mx
from mxnet import gluon, autograd, ndarray

接下来,获取数据并执行一些预处理。我们将导入常用的 MNIST 数据集,其中包括大量手写数字图像集合以及图像的正确标签。我们还将图片重新整理成数组,以便轻松处理并将数组转换为 MXNet 本机 NDArray 对象类。

# 使用本机 MXNet utils 函数导入 MNIST
data = mx.test_utils.get_mnist()

# 设置训练数据并重新整理图片
train_data = data['train_data'].reshape((-1, 784))
train_label = data['train_label']

# 设置测试数据并重新整理图片
test_data = data['test_data'].reshape((-1, 784))
test_label = data['test_label']

# 将数据转换为 NDArray
train_data_mx = mx.nd.array(train_data)
train_label_mx = mx.nd.array(train_label)
test_data_mx = mx.nd.array(test_data)
test_label_mx = mx.nd.array(test_label)

接下来,我们创建一个迭代器来保存训练数据。迭代器是一个非常有用的对象类,用于遍历大型数据集。在这之前,我们必须先设置批处理大小,它定义神经网络在训练算法的每次迭代期间将要处理的数据量 – 在本示例中为 32。

batch_size = 32
train_data = mx.io.NDArrayIter(train_data_mx, train_label_mx, batch_size,  
                                 shuffle=True)           

现在,我们已经准备好定义实际的神经网络。我们将创建两个层:第一层将有 128 个节点,第二层将有 64 个节点。它们都包含一个称为修正线性单元 (ReLU) 的激活函数。激活函数非常重要,因为它们使模型能够表示输入与输出之间的非线性关系。我们还需要设置输出层,其节点数与可能的输出总数相对应。在我们使用 MNIST 的示例中,只有 10 个可能的输出,因为图片只给出 10 个数字 (即 0 到 9)。

# 第一步是初始化您的模型
net = gluon.nn.Sequential()
# 接下来,定义您的模型架构
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu")) # 第一层 - 128 个节点
    net.add(gluon.nn.Dense(64, activation="relu")) # 第二层 - 64 个节点
    net.add(gluon.nn.Dense(10)) # 输出层

在开始模型训练过程之前,我们需要初始化模型的参数并设置损耗和模型优化程序函数。

# 我们从正态分布中所有模型参数的
# 随机值开始,标准偏差为 0.05
net.collect_params().initialize(mx.init.Normal(sigma=0.05))

# 我们选择使用 softmax 交叉熵损耗函数
# 来测量模型能够预测正确答案的程度
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()

# 我们选择使用随机梯度下降 (sgd) 训练算法,
# 并将学习速率超参数设置为 .1
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': .1})

现在可以定义模型训练算法了。对于每次迭代,有四个步骤:(1) 传入一批数据;(2) 计算由神经网络模型生成的输出与实际真实结果之间的差异 (即损耗);(3) 使用 autograd 计算模型参数与损耗影响有关的导数;(4) 使用 trainer 方法以减少损耗的方式优化参数。我们将 epochs 的数量设置为 10,这意味着我们将遍历整个训练数据集 10 次。

epochs = 10

for e in range(epochs):
    for i, batch in enumerate(train_data):
        data = batch.data[0]
        label = batch.label[0]
        with autograd.record(): # 开始记录导数
            output = net(data) # 前向迭代
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])

我们现在有一个经过训练的神经网络模型,下面让我们看看使用我们设定的测试数据的准确度。我们通过将预测值与实际值进行比较来计算准确度。

acc = mx.metric.Accuracy()# 初始化准确度指标
output = net(test_data_mx) # 通过神经网络运行测试数据
predictions = ndarray.argmax(output, axis=1) # 测试数据的预测
acc.update(preds=predictions, labels=test_label_mx) # 计算准确度
print(acc)

要详细地了解 Gluon 接口和深度学习,您可以参考一系列全面的教程,其中涵盖了从介绍性内容到深度学习再到如何实施先进的神经网络模型在内的所有内容。


作者简介

Vikram Madan 是 AWS 深度学习高级产品经理。他负责开发让深度学习引擎变得更易于使用的产品,工作重点是开源 Apache MXNet 引擎。在业余时间,他喜欢长跑以及看纪录片。