前言
上一篇文章只利用NDArray和autograd来实现一个线性回归的训练,现在我们可以使用MXNet提供的Gluon接口更方便地实现线性回归的训练。(和上一篇文章类似的代码就不在给出注释了)
同样地,给了你许多满足y=w*x+b这一函数关系的x,y,通过训练,求出尽可能满足条件(误差尽可能小)的w和b。下面例子的函数为 y[i] = 2 * x[i][0] - 3.4 * x[i][1] + 4.2 + noise,即给出数据集x,y,求出w,b,使得w尽量接近[2,-3.4],b尽量接近4.2。
创建数据集
from mxnet import ndarray as nd
from mxnet import autograd
from mxnet import gluon #导入Gluon接口
num_inputs = 2
num_examples = 1000
true_w = [2,-3.4]
true_b = 4.2
x = nd.random_normal(shape=(num_examples,num_inputs))
y = true_w[0] * x[:,0] + true_w[1] * x[:,1] + true_b
y += 0.01 * nd.random_normal(shape=y.shape)
数据读取
此时不再需要data_iter()来进行数据的读取。
batch_size = 10
dataset = gluon.data.ArrayDataset(x,y)
data_iter = gluon.data.DataLoader(dataset,batch_size,shuffle=True) #shuffle表示是否随机打乱
for data,label in data_iter:
print(data,label)
break
[[-0.6128307 -0.13006628]
[ 0.17985249 1.8944763 ]
[-1.749226 0.12701914]
[ 1.1370468 -0.24184602]
[ 0.20744328 -0.5686463 ]
[ 0.8463716 -0.6305023 ]
[ 2.3007503 -0.89770544]
[-0.2647494 0.06295237]
[-0.9038635 0.6280921 ]
[-0.2878615 -0.9035178 ]]
<NDArray 10x2 @cpu(0)>
[ 3.4145176 -1.8604004 0.26332545 7.3125057 6.5659037 8.037142
11.842436 3.4656026 0.2390664 6.7074056 ]
<NDArray 10 @cpu(0)>
当不知道某个函数是如何定义时,在jupyter notebook中可以在函数后输入?和??来查询(不用加括号),比如:
gluon.data.ArrayDataset?
Init signature: gluon.data.ArrayDataset(*args)
Docstring:
A dataset that combines multiple dataset-like objects, e.g.
Datasets, lists, arrays, etc.
The i-th sample is defined as `(x1[i], x2[i], ...)`.
Parameters
----------
*args : one or more dataset-like objects
The data arrays.
File: c:\users\szw\miniconda3\envs\gluon\lib\site-packages\mxnet\gluon\data\dataset.py
Type: type
Subclasses:
gluon.data.ArrayDataset??
Init signature: gluon.data.ArrayDataset(*args)
Source:
class ArrayDataset(Dataset):
"""A dataset that combines multiple dataset-like objects, e.g.
Datasets, lists, arrays, etc.
The i-th sample is defined as `(x1[i], x2[i], ...)`.
Parameters
----------
*args : one or more dataset-like objects
The data arrays.
"""
def __init__(self, *args):
assert len(args) > 0, "Needs at least 1 arrays"
self._length = len(args[0])
self._data = []
for i, data in enumerate(args):
assert len(data) == self._length, \
"All arrays must have the same length; array[0] has length %d " \
"while array[%d] has %d." % (self._length, i+1, len(data))
if isinstance(data, ndarray.NDArray) and len(data.shape) == 1:
data = data.asnumpy()
self._data.append(data)
def __getitem__(self, idx):
if len(self._data) == 1:
return self._data[0][idx]
else:
return tuple(data[idx] for data in self._data)
def __len__(self):
return self._length
File: c:\users\szw\miniconda3\envs\gluon\lib\site-packages\mxnet\gluon\data\dataset.py
Type: type
Subclasses:
都是英文看得有点晕晕的hhh,我现阶段还是知道函数的大概作用后先多用起来(可以多print看看效果),等以后可以再查查具体文档。
定义模型
Gluon提供大量提前定制好的层,例如线性模型(单层网络,y=wx+b)就是使用对应的Dense层。
构建模型最简单的方法就是利用Sequential来把所有层串起来。Sequential实例可以看作是一个串联各个层的容器。在构造模型时,我们在该容器中依次添加层。当给定输入数据时,容器中的每一层将依次计算并将输出作为下一层的输入。
net = gluon.nn.Sequential() #定义一个空的模型(nn = neural network)
net.add(gluon.nn.Dense(1)) )#加入一个Dense层,这个1表示输出节点个数
net
Sequential(
(0): Dense(None -> 1, linear)
)
加入Dense层时,唯一必须要定义的参数就是输出节点的个数,在线性模型中是1,这里不需定义输入节点是多少,之后真正给数据的时候系统会自动赋值,这一设计为模型开发会带来便利。
初始化模型参数
在使用net前,我们需要初始化模型参数,如线性回归模型中的权重(weight)和偏差(bias)。
net.initialize() #使用默认随机初始化方法
损失函数
square_loss = gluon.loss.L2Loss() #平方误差函数,平方损失又称L2范数损失
优化:随机梯度下降
创建一个Trainer实例,并指定学习率为0.03的小批量随机梯度下降(sgd)为优化算法。该优化算法将用来迭代net实例所有通过add函数嵌套的层所包含的全部参数。
trainer = gluon.Trainer(net.collect_params(),'sgd',{'learning_rate':0.03})
#collect_params函数用于获取全部参数
训练
我们不再是调用SGD,而是用trainer.step()来更新模型,来迭代模型参数。
epochs = 5
batch_size = 10
for e in range(epochs):
total_loss = 0
for data,label in data_iter: #读一组数据块(一组数据块包括10组数据),data是x,label是y(真实)
with autograd.record(): #求导程序
output = net(data) #通过Dense得到output,即通过net中的参数得到预测的y
loss = square_loss(output,label) #利用预测y和真实y得到损失函数
loss.backward() #对损失函数求导
trainer.step(batch_size) #更新参数
total_loss += nd.sum(loss).asscalar()
print("Epoch %d , average loss: %f"%(e,total_loss/num_examples))
Epoch 0 , average loss: 2.819463
Epoch 1 , average loss: 0.007534
Epoch 2 , average loss: 0.000071
Epoch 3 , average loss: 0.000049
Epoch 4 , average loss: 0.000049
trainer.step(batch_size, ignore_stale_grad=False)
-----进行一个参数更新步骤。应该在autograd.backward() 后以及record()外使用。
-----batch_size : 处理数据的批量大小。
-----ignore_stale_grad : 如果为真,则忽略具有陈旧梯度的参数(在上一步之后没有被’ backward '更新的梯度)并跳过更新。
检验
dense = net[0] #从net获得需要的层,即第一层
true_w,dense.weight.data() #权重(weight)
([2, -3.4],
[[ 1.9996018 -3.39955 ]]
<NDArray 1x2 @cpu(0)>)
true_b, dense.bias.data()
(4.2,
[4.1994724]
<NDArray 1 @cpu(0)>)
总结
Gluon让我们能很简洁地实现模型,其中提供了数据处理的工具、定义好的神经网络层、损失函数、参数初始化方法等等,但直接上手可能会因为太简洁而觉得有些莫名,对一些函数的功能之类的不是很清楚,可以先手动试试搭个模型后再用Gluon。