- 自动梯度
MXNet提供autograd模块来自动化求导过程。可对一般的命令式程序进行求导。MXNet的运行模式包括训练模式和预测模式。我们可以通过autograd.is_training()来判断运行模式。 - 看模块有哪些函数和方法
dir函数from mxnet import nd print(dir(nd.random))
- 查找特定函数和类的使用¶
help(nd.ones_like)
- assaclar()函数的意义
将向量X转换成标量,且向量X只能为一维含单个元素的向量X.asscalar()
-
数据集
样本(标签,特征) 特征 -> 标签
gluon读取数据:from mxnet.gluon import data as gdata batch_size = 10 # 将训练数据的特征和标签组合 dataset = gdata.ArrayDataset(features, labels) # 随机读取小批量 data_iter = gdata.DataLoader(dataset, batch_size, shuffle=True)
-
训练模型
当数据样本数为 𝑛,特征数为 𝑑时,线性回归的矢量计算表达式为: y=Xw+b
其中,y为n ✖️1的矩阵,X为数据样本特征,为n ✖️d的矩阵,权重w为d ✖️1,偏差b是一个数(广播机制)
自行代码实现 :def linreg(X, w, b): return nd.dot(X, w) + b
gluon接口实现:
from mxnet.gluon import nn #nn是神经网络的缩写 net = nn.Sequential() #Sequential可看成是串联各个层的容器 net.add(nn.Dense(1)) #线性回归是一个单层神经网络,且输出层中神经元和输入层中各个输入完全连接。 #故其输出层又叫做全连接层,gluon中用Dense来表示。 from mxnet import init #用于初始化模型参数,如线性回归模型中的权重和偏差 net.initialize(init.Normal(sigma=0.01)) #指定权重参数每个元素将在初始化时随机采样于均值为0、标准差为0.01的正态分布。偏差参数默认会初始化为零。
-
损失函数
评估预测值与真实值之间的误差,一般用平方损失。
代码:def squared_loss(y_hat, y): # 本函数已保存在d2lzh包中方便以后使用 return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
gulon代码:
from mxnet.gluon import loss as gloss #loss模块中有各种损失函数 loss = gloss.L2Loss() # 平方损失又称L2范数损失
-
优化函数
一般深度学习,没有解析解,只有数值解。故需用优化函数来降低损失函数的值。
一般采用小批量随机梯度下降(mini-batch stochastic gradient descent)。
算法步骤:
1. 先选取一组模型参数的初始值,如随机选取组成小批量β;
2. 求小批量中数据样本的平均损失有关模型参数的导数(梯度)
3. 用梯度结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。使每次迭代都可能降低损失函数的值。
传入参数:
学习率: 代表在每次优化中,能够学习的步长的大小
批量大小: 是小批量计算中的批量大小batch size
数学表达:
代码:def sgd(params, lr, batch_size): # 本函数已保存在d2lzh包中方便以后使用 for param in params: param[:] = param - lr * param.grad / batch_size
gluon代码:
from mxnet import gluon trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.03}) #指定学习率为0.03的小批量随机梯度下降(sgd)为优化算法。 #该优化算法将用来迭代net实例所有通过add函数嵌套的层所包含的全部参数。 #这些参数可以通过collect_params函数获取。
-
上述所有的数学表达
-
训练模型代码
lr = 0.03 num_epochs = 3 net = linreg loss = squared_loss for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期 # 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。X # 和y分别是小批量样本的特征和标签 for X, y in data_iter(batch_size, features, labels): with autograd.record(): l = loss(net(X, w, b), y) # l是有关小批量X和y的损失 l.backward() # 小批量的损失对模型参数求梯度 sgd([w, b], lr, batch_size) # 使用小批量随机梯度下降迭代模型参数 train_l = loss(net(features, w, b), labels) print('epoch %d, loss %f' % (epoch + 1, train_l.mean().asnumpy()))
-
gluon训练模型代码
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) #step函数用来迭代模型参数 l = loss(net(features), labels) print('epoch %d, loss: %f' % (epoch, l.mean().asnumpy())) dense = net[0] true_w, dense.weight.data() #获取权重 true_b, dense.bias.data() #获取偏置
-
回归模型
跟线性回归一样将输入特征与权重做线性叠加。与线性回归的一个主要不同在于,softmax回归的输出值个数等于标签里的类别数。
softmax运算:
将o1、o2等输出值b变成值为正且和为1的概率分布,即:
矢量表达式:
代码:#Y=softmax(XW+b) def net(X): return softmax(nd.dot(X.reshape((-1, num_inputs)), W) + b) def softmax(X): X_exp = X.exp() #e^x ,e的x次方 partition = X_exp.sum(axis=1, keepdims=True) return X_exp / partition # 这里应用了广播机制
-
交叉熵损失函数
对于softmax,平方损失函数过于严格,因为softmax只要预测正确即可。故采用交叉熵:
代码实现:def cross_entropy(y_hat, y): #输入分别是y的估计,和y return -nd.pick(y_hat, y).log() #从y中取出y的估计的这些位置,然后取log
-
计算准确率
def accuracy(y_hat, y): return (y_hat.argmax(axis=1) == y.astype('float32')).mean().asscalar() # y_hat 按行取最大值 == y的真实标签 求平均值
-
训练模型代码(有注释)
num_epochs, lr = 5, 0.1 # 本函数已保存在d2lzh包中方便以后使用 def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None, trainer=None): for epoch in range(num_epochs): #训练的次数 train_l_sum, train_acc_sum, n = 0.0, 0.0, 0 for X, y in train_iter: with autograd.record(): y_hat = net(X) l = loss(y_hat, y).sum() #反向传播求梯度 l.backward() #进行优化函数 if trainer is None: #假如没有传入优化函数 d2l.sgd(params, lr, batch_size) #那就用随机梯度下降 else: #传入了就用传入的优化函数进行优化迭代 trainer.step(batch_size) # “softmax回归的简洁实现”一节将用到 y = y.astype('float32') train_l_sum += l.asscalar() #损失的数量 train_acc_sum += (y_hat.argmax(axis=1) == y).sum().asscalar() #准确的数量 n += y.size test_acc = evaluate_accuracy(test_iter, net) #周期、损失、训练准确率、测试准确率 print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f' % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc)) train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr) #可以看出损失值是逐步减少的,训练准确率是比测试准确率要低的 #但是随着训练周期的增加,训练的参数进行了优化,故整体准确率在上升。 #训练集上的准确率是在一个epoch的过程中计算得到的,测试集上的准确率是在一个epoch结束后计算得到的,后者的模型参数更优
-
预测代码
for X, y in test_iter: break #从测试集中取出X,y数据 true_labels = d2l.get_fashion_mnist_labels(y.asnumpy()) #真实属性 pred_labels = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1).asnumpy()) #预测属性 titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)] d2l.show_fashion_mnist(X[0:9], titles[0:9])
-
隐藏层
多层感知机中的隐藏层和输出层都是全连接层。
对单隐藏层来说:
故这样设计隐藏层,和单层神经网络实质上是一样的。 -
激活函数
上述问题的根源在于全连接层只是对数据做仿射变换(affine transformation)。引入非线性变换,然后再作为下一个全连接层的输入,即可解决该问题。该非线性函数即为激活函数。
1.ReLU函数:ReLU(𝑥)=max(𝑥,0).
2.sigmoid函数:sigmoid(𝑥)=1/(1+exp(−𝑥))
保证元素值在0~1之间,当输入接近0时,接近线性变换。
3.tanh函数:tanh(𝑥)=(1−exp(−2𝑥))/(1+exp(−2𝑥))
4.激活函数的选择:
ReLu函数是一个通用的激活函数,目前在大多数情况下使用。但是,ReLU函数只能在隐藏层中使用。用于分类器时,sigmoid函数及其组合通常效果更好。由于梯度消失(范围只在0~1之间导致的)问题,有时要避免使用sigmoid和tanh函数。在神经网络层数较多的时候,最好使用ReLu函数,ReLu函数比较简单计算量少,而sigmoid和tanh函数计算量大很多。
综上,在选择激活函数的时候可以先选用ReLu函数如果效果不理想可以尝试其他激活函数 -
总结
多层感知机就是含有至少一个隐藏层的由全连接层组成的神经网络,且每个隐藏层的输出通过激活函数进行变换。
多层感知机的层数和各隐藏层中隐藏单元个数都是超参数(自己设定的参数)。
以单隐藏层为例并沿用本节之前定义的符号,多层感知机按以下方式计算输出:
𝐻=𝜙(𝑋𝑊ℎ+𝑏ℎ), 𝑂=𝐻𝑊𝑜+𝑏𝑜
其中H为隐藏层输出, 𝜙表示激活函数。
分类问题中,我们可以对输出 𝑂做softmax运算,并使用softmax回归中的交叉熵损失函数。
回归问题中,我们将输出层的输出个数设为1,并将输出 𝑂直接提供给线性回归中使用的平方损失函数。 -
代码
#定义模型 net = nn.Sequential() net.add(nn.Dense(256, activation='relu'), nn.Dense(10)) net.initialize(init.Normal(sigma=0.01))