微软与亚马逊携手发布Gluon库:一个易于使用的编程接口,用于灵活的深度学习
2017年10月14日 由 yining 发表
875474
0
10月12日,微软和亚马逊又达成一致,携手发布Gluon深度学习库。意在提高所有开发者的机器学习技术的速度、灵活性和可访问性,而不考虑他们选择的深度学习框架。这项合作的第一个结果是产生了新的Gluon接口,它是Apache MXNet的一个开源码库,能够允许所有级别的开发人员对深度学习模型进行原型化(prototype)、构建和训练。这个接口极大地简化了创建深度学习模型的过程,同时又不会牺牲训练速度。
下面是Gluon的四个主要优势和代码示例:
(1)简单、易于理解的代码
在Gluon中,你可以使用简单、清晰和简洁的代码来定义神经网络。你可以得到一套完整的“即插即用”的神经网络构建块,包括预定义的层、优化器和初始化器。这些抽象的概念远离了许多复杂的底层实现细节。下面的例子展示了如何用几行代码定义一个简单的神经网络:
net = gluon.nn.Sequential()
with net.name_scope():
net.add(gluon.nn.Dense(128, activation="relu"))
net.add(gluon.nn.Dense(64, activation="relu"))
net.add(gluon.nn.Dense(num_outputs))
下图展示了神经网络的结构:
想了解更多信息,这有一个“学习如何构建一个名为“多层感知器(MLP)”,并使用Gluon神经网络构建块的简单神经网络”的教程。教程地址:
http://thestraightdope.mxnet.io/chapter03_deep-neural-networks/mlp-gluon.html
对于更高级的用例,从scratch开始编写神经网络的部分也是很容易的。Gluon允许你在你的神经网络中混合和匹配预定义和自定义组件。
scratch地址:
http://gluon.mxnet.io/chapter03_deep-neural-networks/mlp-scratch.html
(2)灵活的结构
训练神经网络模型在计算上是非常集中的,在某些情况下,可能需要几天甚至几周的时间。许多深度学习框架通过严格定义模型并将其与训练算法分离开来,从而减少了这段时间。这种严格的方法增加了很多复杂性,也使调试变得困难。
Gluon的方法是不同的。它将训练算法和神经网络模型结合在一起,从而在不牺牲性能的前提下为开发过程提供了灵活性。这种方法的核心是Gluon训练器(trainer)方法,它被用来训练模型。训练器方法依赖于MXNet autograd库,它用于自动计算导数(即:梯度)。导数是一个用来测量变量变化速率的数学计算。它是训练算法的必要输入。autograd库可以有效地实现这些数学计算,并且对Gluon提供的灵活性至关重要。现在你可以定义一个训练算法,通过合并autograd和训练器,该算法包含了一个简单的嵌套循环。
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语句,或者作为算法的一部分。通过将模型和算法组合在一起,就可以执行模型中的每一行代码,从而更容易地识别出导致一个bug的特定代码行。
(3)动态图
在某些情况下,神经网络模型可能需要在训练过程中改变形状和大小。当输入到神经网络的数据输入是可变的时,这是尤其必要的,这在自然语言处理(NLP)中很常见。因为在自然语言处理中,每个句子的输入都可以是不同的长度。使用Gluon时,神经网络定义可以是动态的,这意味着你可以动态地构建它,使用任何你想要的结构,并使用Python的任何本机控制流。
例如,这些动态的神经网络结构使构建一个树状的长短期记忆(LSTM)模型变得更容易。树状LSTMs是一种强大的模型,它可以识别两个句子是否具有相同的含义。在下面的例子中,两个句子从本质上来说都有相同的意思:
- “迈克尔把足球传到球员面前。”
- “这个球被迈克尔传递。”
可以通过一个递归神经网络(一个流行的序列学习模型)来馈送这些句子,并进行分类。然而,树状LSTMs的主要观点是,我们经常在语言方面遇到先验知识的问题。例如,句子有语法结构,我们有强大的工具可以从句子中提取出这个结构。我们可以用一个树形结构的神经网络来组合单词,它的结构模仿了句子中已知的语法的树状结构,如下图所示。
图:斯坦福自然语言处理小组
这就需要为每个例子构建一个不同的神经网络结构。使用传统框架很难,但是Gluon可以解决这个问题。在下面的代码片断中,你可以看到如何在模型训练的每个向前迭代中合并一个循环,并且仍然从autograd和训练器的简化中受益。这使模型能够轻松地排查一个句子的树状结构,从而基于该结构学习。
def forward(self, F, inputs, tree):
children_outputs = [self.forward(F, inputs, child)
for child in tree.children]
…
(4)高性能
有了Gluon提供的灵活性,你就可以很容易地原型化并尝试使用神经网络模型。然后,当速度变得比灵活性更重要时(如:当你准备好输入所有的训练数据时),Gluon接口使你能够轻松地缓存神经网络模型,以实现高性能和减少内存占用。这只需要一个小的调整,当你在完成你的原型化并准备在一个更大的数据集上测试它时,你就建立了你的神经网络。你必须使用HybridSequential,而不是使用Sequential(如前面所示)来堆栈神经网络层。它的功能与Sequential相同,但是它让你调用底层的优化引擎来表达你的模型的部分或全部架构。
net = nn.HybridSequential()
with net.name_scope():
net.add(nn.Dense(128, activation="relu"))
net.add(nn.Dense(64, activation="relu"))
net.add(nn.Dense(10))
接下来,为了编译和优化HybridSequential,我们可以调用它的hybridize方法:
net.hybridize()
现在,当你对模型进行训练时,你将能够获得与本机MXNet接口相同的高性能,并且减少内存使用。
开始使用Gluon
在开始使用Gluon时,你可以遵循简单的步骤来安装MXNet的最新版本,或者你可以在云上启动深度学习Amazon Machine Image(AMI)。接下来,我们将介绍如何使用我们之前讨论过的不同组件来构建和训练一个简单的双层神经网络,它被称为多层感知器。我们建议使用Python 3.3或更高的版本,并使用Jupyter Notebook实现这个示例。
MXNet安装地址:
https://mxnet.incubator.apache.org/get_started/install.html
深度学习Amazon Machine Image(AMI)地址:
https://amazonaws-china.com/blogs/ai/get-started-with-deep-learning-using-the-aws-deep-learning-ami/
Jupyter Notebook安装地址:
http://jupyter.readthedocs.io/en/latest/install.html
首先,导入MXNet并获取gluon库,另外还需要
autograd库和ndarray库。
import mxnet as mx
from mxnet import gluon, autograd, ndarray
然后获取数据并对其进行一些预处理。我们将导入常用的MNIST数据集,其中包括大量的手写数字图像和图像的正确标签。我们还将这些图片重新组合到一个数组(array)中,以方便处理并将数组转换为MXNet本地NDArray对象类。
MNIST数据集地址:
http://yann.lecun.com/exdb/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']
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)
接下来,我们创建一个迭代器来保存训练数据。迭代器是一个有用的对象类,它遍历于大型数据集中。在此之前,我们必须首先设置批处理大小(batch size),它定义了在训练算法的每次迭代中神经网络将处理的数据量。在本例中批处理大小为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"))
net.add(gluon.nn.Dense(64, activation="relu"))
net.add(gluon.nn.Dense(10))
在启动模型训练过程之前,我们需要初始化模型的参数并设置损失和模型优化器函数。
net.collect_params().initialize(mx.init.Normal(sigma=0.05))
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': .1})
现在是定义模型训练算法的时候了。对于每个迭代,有四个步骤:(1)在一批数据中传递;(2)计算神经网络模型产生的输出与实际之间的差值,即:损失(loss);(3)利用autograd计算模型参数的导数,并考虑其对损失的影响;(4)使用训练器方法优化参数以减少损失。我们把epoch的数量设置为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接口和深度学习的知识,你可以参考以下这一系列的教程,这些教程涵盖了从入门到深度学习到如何实现最先进的神经网络模型的所有内容。
教程地址:
http://gluon.mxnet.io/