1、基础知识
NDArray、NumPy的广播机制:数组维度不同,后缘维度的轴长(从末尾算起的维度)相同;(4,3)+(3,);(3,4,2)+(4,2) 2、数组维度相同,其中有个轴为1;(4,3)+(4,1):在1轴上广播扩展。
NDArray,NumPy的相互变换:
1 | P = np.ones((2,3)) |
自动求梯度(gradient)MXNET中使用autograd模块自动求梯度:
1 | x = nd.arange(4).reshape((4,1)) |
uniform:均匀分布采样;normal:正态分布采样;poisson:泊松分布采样。
2、线性回归
损失函数:平方函数,平方损失;在模型训练中,希望找到一组模型参数为w1,w2,b使得训练样本平均损失最小。
解析解:误差最小化问题的解刚好可用数学公式表达出来;大多数为数值解,只能利用优化算法有限次迭代模型参数,从而尽可能降低损失函数的值。
全连接层:又名稠密层,输出层中的神经元与输入层中的各个输入完全连接;
矢量计算比标量逐个相加更加省时间,故往往利用矢量矩阵运算来实现深度学习;
优化算法:小批量随机梯度下降:批量大小batch size,学习率 lr 均为超参数,为人为设定并非模型学习出来的,
调参:通过反复试错来寻找合适的超参数,
1 | def sgd(params,lr,batch_size): #sgd函数实现小批量随机梯度下降算法 |
在一个迭代周期epoch中,将完整遍历一遍data_iter函数,并对训练数据集中所有样本都使用一次。
Gluon简洁实现:
1、提供data包来读取数据:
2、提供大量预定义层,nn模块:neural networks:
1 | from mxnet.gluon import nn |
3、利用init模块来实现模型参数初始化的各种方法:
1 | from mxnet.gluon import init |
4、定义损失函数:利用loss模块:
1 | from mxnet.gluon import loss as gloss |
5、定义优化算法:创建Trainer实例,以sgd作为优化算法,用来迭代net实例所有通过add函数嵌套的层包含的全部参数,可通过collect_params函数获取。
1 | trainer = gluon.Trainer(net.collect_params(),'sgd',{'learning_rate':0.03}) |
6、训练模型:调用Trainer实例的step函数来迭代模型参数,按sgd的定义,在step中指明批量大小,从而对样本梯度求平均。
1 | num_epochs = 3 |
3、softmax回归
模型输出为图像类别这种离散值时,使用softmax回归,其输出单元从一个变成了多个,且引入了softmax运算使输出更加适合离散值的预测和训练。
sofemax回归模型:将输出特征与权重做线性叠加,输出值个数等于标签里的类别数。例:有4种特征(4个像素的图片)和3种输出动物类别,则权重包含12个标量(带下标w)、偏差包含3个标量(带下标b)。每个On计算都依赖所有输入,故为全连接层。
softmax计算:直接用最高On作为预测输出,有2个问题。1、输出值范围不定,难以直观判断;2、误差难以衡量。softmax运算符可以解决,即归一化,但softmax运算不改变预测类别输出。
$$
y1 = exp(O1)/exp(O1)+exp(O2)+exp(O3)
$$
交叉熵函数:使用更适合衡量分布差异的测量函数,只关心对正确类别的预测概率,
$$
H(yi,yi) = -Σ(yilogyi)
$$
交叉熵损失函数,最小化其等价于最大化训练数据集所有标签类别的联合预测概率。
$$
l(o) =1/n * Σ(yilogy*i)
$$
图像分类数据集:Fashion-MNIST
Gluon的DataLoader中可用多进程来加速数据读取;通过ToTensor实例将图像数据从unit8格式变换成32位浮点数格式,并除以255使得所有像素均在0至1之间。
Gluon简洁实现
1、导入模块并获取函数
1 | %matplotlib inline |
2、定义和初始化模型
添加输出为10的全连接层,并用均值为0、标准差为0.01的正态分布随机初始化模型的权重参数。
1 | net = nn.Sequential() |
3、同时定义softmax和交叉熵损失函数,使数值稳定性更好,使用Gluon提供的函数。
定义优化算法:使用学习率为0.1的小批量随机梯度下降算法。
1 | loss = gloss.SoftmaxCrossEntropyLoss() |
4、使用上一节定义的训练函数来训练模型:
1 | num_epochs = 5 |
4、多层感知机
深度学习主要关注多层模型,以多层感知机NLP(multilayer perceptron)为例。在单层网络的基础上引入了隐藏层hidden layer,但多个仿射线性变换叠加仍然是线性仿射,需引入非线性函数,该函数被称为激活函数
RELU函数:RELU(x) = max(x,0)
sigmoid函数:sigmoid(x) = 1/[1+exp(-x)]
sigmoid函数的导数:sigmoid(x)(1-sigmoid(x))
tanh(双曲正切)函数:[1- exp(-2x)]/[1+exp(-2x)]
tanh函数的导数:1 - [tanh(x)]^2
Gluon的简洁实现
1、导入包与模块,并定义模型,,多加一个全连接作为隐藏层,单元数为256,用RELU作为激活函数。
1 | import d2zlzh as d2l |
2、使用与softmax回归几乎相同的步骤来读取数据并训练模型,学习率为0.5
1 | batch_size = 256 #批量大小设置 |
5、模型选择与拟合问题
训练误差:模型在训练集上表现出来的误差;
泛化误差:任意一个测试数据样本上表现出的误差的期望;
使用验证数据集来进行模型选择:预留一部分在训练、测试数据集之外的数据来进行模型选择。K折交叉验证:将原始数据分成K个不重合的子数据集,做K次模型训练和验证,每一次用一个子数据集来验证模型,其他用于训练模型。最后对这K次结果分别求平均。
欠拟合:模型无法得到较低的训练误差。
过拟合:模型训练误差远小于其在测试集上误差;模型越复杂、训练集越小越容易过拟合。
6、权重衰减、丢弃法来处理过拟合
权重衰减:等价于L2范数正则化,通过为模型损失函数添加惩罚项,使学到的模型参数值较小。
L2惩罚项指的是:模型权重参数的每一个元素的平方和与一个正的常数的乘积。
1 | def l2_penalty(w): |
权重衰减Gluon简洁实现:
构造Trainer实例时通过wd参数来指定权重衰减超参数,默认下会对权重、偏差同时衰减。
1 | #对权重参数衰减,权重名称一般以weight结尾 |
丢弃法:隐藏单元有一定的概率P被丢弃掉,丢弃概率是丢弃法的超参数。具体而言,随机变量$为0和1的概率分别为P和1-P。
定义dropout函数,以drop_prob的概率丢弃NDArray输入X中的元素。
丢弃法Gluon简洁实现:
1 | net = nn.Sequential() |
7、反向传播
反向传播:指计算神经网络梯度的方法,依据链式法则,其梯度计算可能依据各变量的当前值,而这些变量的当前值是通过正向传播计算得到的。
正向传播的计算可能依赖于模型参数的当前值,而参数是在反向传播的梯度计算后通过优化算法迭代的。
模型参数初始化完成后,交替地进行正向传播和反向传播,并根据反向传播计算的梯度迭代模型参数。
梯度衰减、爆炸:由于层数过大时,输出呈幂次爆炸增长,故梯度爆炸或梯度消失。
8、深度学习计算原理细节
1、基于Block类的模型构建
Block类是nn模块里提供的一个模型构造类,继承Block类来构造多层感知机,重载init函数与forward函数,分别用于创建模型参数与定义前向计算。
1 | class MLP(nn.Block): #声明带有模型参数的层,声明2个全连接层 |
无需定义反向传播,系统将自动求梯度而生成反向传播所需的backward函数
实例化MLP类得到net,并传入输入数据X并做一次前向计算。
1 | X = nd.random.uniform(shape=(2,20)) |
2、构建一个继承于Block类的继承类
提供add函数来逐一添加串联的Block子类实例,而模型的前向计算就是将这些实例按顺序逐一计算。
1 | class MySequential(nn.Block): |
3、自定义初始化模型参数
对于Sequential类构造的神经网络,可通过方括号[]来访问网络的任一层。同时Sequential实例中含模型参数的层,可通过Block类的params属性来访问该层包含的参数。
共享模型参数:在利用Block类中的forward函数里多次调用一个层来计算。或者,在构造层时指定特定的参数,若不同层使用同一份参数,则它们会在前向、反向时均共享相同的参数。
延后初始化:只有将形状是(,)的输入X传进网络做前向计算net(X)时,系统才能推断出该层权重参数形状为(,),此时才能真正地开始初始化参数。
避免延后初始化:1、要对已初始化的模型重新初始化时,由于参数形状不会变化,能够立即重新初始化;2、创建层的时候指定了它的输入个数,故系统不需要额外信息来推测参数形状。
4、自定义层
5、读取与存储
将内存中训练好的模型参数存储在硬盘上,供后续读取使用。