深度学习基础
线性回归的简洁实现
- 实践中,我们通常可以用更简洁的代码来实现同样的模型,我们将介绍如何使用 MXNet 提供的Gluon 接口更方便地实现线性回归的训练。
生成数据集
-
我们生成与之前相同的数据集,其中 features 是训练数据特征,labels 是标签。
from mxnet import autograd, nd num_inputs = 2 num_examples = 1000 true_w = [2, -3.4] true_b = 4.2 features = nd.random.normal(scale=1, shape=(num_examples, num_inputs)) labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b labels += nd.random.normal(scale=0.01, shape=labels.shape)
读取数据
-
Gluon 提供了 data 包来读取数据。
-
由于 data 常用作变量名,我们将导入的 data 模块用添加了 Gluon 首字母的假名 gdata 代替。
-
在每一次迭代中,我们将随机读取包含10个数据样本的小批量。
from mxnet.gluon import data as gdata batch_size = 10 # 将训练数据的特征和标签组合 dataset = gdata.ArrayDataset(features, labels) # 随机读取小批量 data_iter = gdata.DataLoader(dataset, batch_size, shuffle=True)
-
让我们读取并打印第一个小批量数据样本。
# 让我们读取并打印第一个小批量数据样本。 for X, y in data_iter: print(X, y) break """ Output: [[ 0.10669409 -1.615797 ] [-0.24337251 -0.55600816] [ 0.18799396 -0.45784158] [-1.599301 0.06591962] [ 1.5509241 0.63531184] [ 0.1757906 0.74375474] [ 0.3500547 0.5360521 ] [ 0.6038035 1.1042424 ] [ 1.0598046 0.943863 ] [-0.5385921 -0.8611333 ]] <NDArray 10x2 @cpu(0)> [9.915791 5.609725 6.1265273 0.779277 5.141447 2.036555 3.0958035 1.6462706 3.1178625 6.0557494] <NDArray 10 @cpu(0)> """
定义模型
-
在从零开始的实现中,我们需要定义模型参数,并使用它们一步步描述模型是怎样计算的。当模型结构变得更复杂时,这些步骤将变得更繁琐。
-
其实,Gluon 提供了大量预定义的层,这使我们只需关注使用哪些层来构造模型。
-
下面将介绍如何使用 Gluon 更简洁地定义线性回归:
-
首先,导入 nn 模块。
- 实际上,“nn” 是 neural networks(神经网络)的缩写。
- 顾名思义,该模块定义了大量神经网络的层。
-
我们先定义一个模型变量 net,它是一个 Sequential 实例。
- 在 Gluon 中,Sequential 实例可以看作是一个串联各个层的容器。
-
在构造模型时,我们在该容器中依次添加层。
- 当给定输入数据时,容器中的每一层将依次计算并将输出作为下一层的输入。
from mxnet.gluon import nn net = nn.Sequential()
-
线性回归的输出层又叫全连接层。
- 作为一个单层神经网络,线性回归输出层中的神经元和输入层中各个输入完全连接。
- 在 Gluon 中,全连接层是一个 Dense 实例。我们定义该层输出个数为1。
-
-
在 Gluon 中我们无须指定每一层输入的形状,例如线性回归的输入个数。当模型得到数据时,例如后面执行 net(X) 时,模型将自动推断出每一层的输入个数。
初始化模型参数
- 在使用 net 前,我们需要初始化模型参数,如线性回归模型中的权重和偏差。
-
我们从 MXNet 导入 init 模块。
- 该模块提供了模型参数初始化的各种方法。
- 这里的 init 是 initializer 的缩写形式。
-
我们通过 init.Normal(sigma=0.01) 指定权重参数每个元素将在初始化时随机采样于均值为0、标准差为0.01的正态分布,偏差参数默认会初始化为零。
from mxnet import init net.initialize(init.Normal(sigma=0.01))
-
定义损失函数
-
在 Gluon 中,loss 模块定义了各种损失函数。
-
我们用假名 gloss 代替导入的 loss 模块,并直接使用它提供的平方损失作为模型的损失函数。
from mxnet.gluon import loss as gloss loss = gloss.L2Loss() # 平方损失又称 L2 范数损失
定义优化算法
- 在导入 Gluon 后,我们创建一个 Trainer 实例,并指定学习率为0.03的小批量随机梯度下降(sgd)为优化算法。
-
该优化算法将用来迭代 net 实例所有通过 add 函数嵌套的层所包含的全部参数。
-
这些参数可以通过 collect_params 函数获取。
from mxnet import gluon trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.03})
-
训练模型
-
在使用 Gluon 训练模型时,我们通过调用 Trainer 实例的 step 函数来迭代模型参数。
-
由于变量 l l l 是长度为 batch_size 的一维 NDArray,执行 l . b a c k w a r d ( ) l.backward() l.backward() 等价于执行 l . s u m ( ) . b a c k w a r d ( ) l.sum().backward() l.sum().backward()。
-
按照小批量随机梯度下降的定义,我们在 step 函数中指明批量大小,从而对批量中样本梯度求平均。
# 训练模型 num_epochs = 3 for epoch in range(1, num_epochs + 1): for X, y in data_iter: with autograd.record(): l = loss(net(X), y) l.backward() trainer.step(batch_size) l = loss(net(features), labels) print('epoch %d, loss: %f' % (epoch, l.mean().asnumpy())) """ Output: epoch 1, loss: 0.035042 epoch 2, loss: 0.000129 epoch 3, loss: 0.000049 """ # 分别比较学到的模型参数和真实的模型参数 # 我们从 net 获得需要的层,并访问其权重(weight)和偏差(bias) dense = net[0] print(true_w, dense.weight.data()) print(true_b, dense.bias.data()) """ Output: [2, -3.4] [[ 1.999579 -3.399454]] <NDArray 1x2 @cpu(0)> 4.2 [4.199904] <NDArray 1 @cpu(0)> """