本章视频地址 ,本章讲义地址
在介绍深度神经网络之前,我们需要了解神经网络训练的基础知识。在本章中,我们将介绍神经网络的整个训练过程,包括:定义简单的神经网络架构、数据处理、指定损失函数和如何训练模型。经典统计学习技术中的线性回归和 softmax 回归可以视为线性 神经网络。为了更容易学习,我们将从这些经典算法开始,向你介绍神经网络的基础知识。这些知识将为本书其他部分中更复杂的技术奠定基础。
线性回归 (linear regression)
线性回归 (linear regression) 基于几个简单的假设:
首先 ,假设自变量 x x x 和因变量 y y y 之间的关系是线性的,即 y y y 可以表示为 x x x 中元素的加权和,这里通常允许包含观测值的一些噪声;其次 ,我们假设任何噪声都比较正常,如噪声遵循正态分布。
线性模型
线性假设指目标可以表示为特征的加权和:y = w 1 x 1 + w 2 x 2 + w 3 x 3 + . . . + b y=w_1x_1+w_2x_2+w_3x_3 +...+ b y = w 1 x 1 + w 2 x 2 + w 3 x 3 + . . . + b
w i w_i w i 称为权重(weight),$b $称为偏置 (bias),或称为偏移量 (offset)、截距 (intercept)。权重决定了每个特征对我们预测值的影响。偏置是指当所有特征都取值为 0 时,预测值应该为多少。
对于特征集合X X X ,预测值 y ^ ∈ R n \hat y \in R_n y ^ ∈ R n 可以通过矩阵-向量 乘法表示为:y ^ = X w + b \hat y=Xw+b y ^ = X w + b
训练数据:X = [ x 1 , x 2 , . . . , x n ] T , y = [ y 1 , y 2 , . . . y n ] T X=[x_1,x_2,...,x_n]^T,y=[y_1,y_2,...y_n]^T X = [ x 1 , x 2 , . . . , x n ] T , y = [ y 1 , y 2 , . . . y n ] T
需要学习的参数:W = [ w 1 , w 2 , . . . , w n ] T , b W=[w_1,w_2,...,w_n]^T,b W = [ w 1 , w 2 , . . . , w n ] T , b
线性模型可以看作是单层神经网络
graph BT
o1
x1-->o1
x2-->o1
x3[...]-->o1
xd-->o1
1 2 3 def linreg (X, w, b ): """线性回归模型。""" return torch.matmul(X, w) + b
损失函数
定义损失 -> 平方损失:l ( y , y ′ ) = 1 2 ( y − y ^ ) 2 l(y,y')=\frac{1}{2}(y-\hat y)^2 l ( y , y ′ ) = 2 1 ( y − y ^ ) 2
损失函数 -> 在训练集的 n 个样本上的损失均值:L ( w , b ) = 1 n ∑ i = 1 n l ( i ) ( w , b ) = 1 2 n ∑ i = 1 n ( w ⊤ x ( i ) + b − y ( i ) ) 2 L(w,b)=\frac{1}{n}∑_{i=1}^{n}l^{(i)}(w,b)=\frac{1}{2n}∑_{i=1}^{n}(w^⊤x^{(i)}+b−y{(i)})^2 L ( w , b ) = n 1 ∑ i = 1 n l ( i ) ( w , b ) = 2 n 1 ∑ i = 1 n ( w ⊤ x ( i ) + b − y ( i ) ) 2
训练过程 -> 最小化损失来学习参数:w ∗ , b ∗ = a r g m i n w , b l ( X,y,w , b ) w^*,b^*=arg~min_{w,b} ~ l(\textbf{X,y,w},b) w ∗ , b ∗ = a r g m i n w , b l ( X,y,w , b )
1 2 3 def squared_loss (y_hat, y ): """均方损失。""" return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
解析解(显示解)
解析解指可以通过数学公式简单地表达出来的解。
将偏差加入权重,合并方法是在包含所有参数的矩阵中附加一列
损失是凸函数,所以梯度值为 0 的地方就是最优解。
我们的预测问题是最小化∥ y − X w ∥ 2 \|\mathbf{y} - \mathbf{X}\mathbf{w}\|^2 ∥ y − X w ∥ 2 。这在损失平面上只有一个临界点,这个临界点对应于整个区域的损失最小值。将损失关于 w \mathbf{w} w 的导数设为0,得到解析解(闭合形式):
w ∗ = ( X ⊤ X ) − 1 X ⊤ y \mathbf{w}^* = (\mathbf X^\top \mathbf X)^{-1}\mathbf X^\top \mathbf{y}
w ∗ = ( X ⊤ X ) − 1 X ⊤ y
像线性回归这样的简单问题存在解析解,但并不是所有的问题都存在解析解。解析解可以进行很好的数学分析,但解析解的限制很严格,导致它无法应用在深度学习里。
基础优化方法
当一个问题没有最优解时(NPC问题),应该使用什么方法来优化模型的预测结果(即学习参数)呢?
下面介绍几种基础的优化方法 😃
梯度下降
梯度下降 (gradient descent)方法几乎可以优化所有深度学习模型。它通过不断地在损失函数递减的方向 上更新参数 来降低误差。
优化过程
挑选(如,随机初始化)待学习参数的初始值 w 0 w_0 w 0
1 2 w = torch.normal(0 , 0.01 , size=(2 ,1 ), requires_grad=True ) b = torch.zeros(1 , requires_grad=True )
重复迭代参数 t=1,2,3…
w t = w t − 1 − η ∂ l ∂ w t − 1 w_t=w_{t-1}-\eta \frac{\partial l}{\partial w_t-1} w t = w t − 1 − η ∂ w t − 1 ∂ l
小批量随机梯度下降 :
梯度下降最简单的用法是计算损失函数(数据集中所有样本的损失均值)关于模型参数的导数(在这里也可以称为梯度)。但实际中的执行可能会非常慢 :因为在每一次更新参数之前,我们必须遍历整个数据集。因此,我们通常会在每次需要计算更新的时候随机抽取一小批样本 ,这种变体叫做小批量随机梯度下降 (minibatch stochastic gradient descent)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def data_iter (batch_size, features, labels ): num_examples = len (features) indices = list (range (num_examples)) random.shuffle(indices) for i in range (0 , num_examples, batch_size): batch_indices = torch.tensor( indices[i: min (i + batch_size, num_examples)]) yield features[batch_indices], labels[batch_indices] batch_size = 10 for X, y in data_iter(batch_size, features, labels): print(X, '\n' , y) break
1 2 3 4 5 6 7 8 def sgd (params, lr, batch_size ): """小批量随机梯度下降。""" with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size param.grad.zero_()
总结:
梯度下降通过不断沿着反梯度方向更新参数求解
小批量随机梯度下降是深度学习默认的求解算法
两个重要的超参数是批量大小和学习率
线性回归的简洁实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import numpy as npimport torchfrom torch.utils import datafrom d2l import torch as d2l"""生成数据集""" true_w = torch.tensor([2 , -3.4 ]) true_b = 4.2 features, labels = d2l.synthetic_data(true_w, true_b, 1000 ) """读取数据集""" def load_array (data_arrays, batch_size, is_train=True ): """构造一个PyTorch数据迭代器。""" dataset = data.TensorDataset(*data_arrays) return data.DataLoader(dataset, batch_size, shuffle=is_train) batch_size = 10 data_iter = load_array((features, labels), batch_size) next (iter (data_iter))"""1. 定义模型""" from torch import nnnet = nn.Sequential(nn.Linear(2 , 1 )) """2. 初始化模型参数""" net[0 ].weight.data.normal(0 ,0.01 ) net[0 ].bias.data.fill_(0 ) """3. 定义损失函数:均方误差 MSE""" loss = nn.MSELoss() """4. 定义优化算法:随机梯度下降 SGD""" trainer = torch.optim.SGD(net.parameters(), lr=0.03 ) """5. 训练""" num_epochs = 3 for epoch in range (num_epochs): for X, y in data_iter: l = loss(net(X) ,y) trainer.zero_grad() l.backward() trainer.step() l = loss(net(features), labels) print(f'epoch {epoch + 1 } , loss {l:f} ' ) """打印误差""" w = net[0 ].weight.data print('w的估计误差:' , true_w - w.reshape(true_w.shape)) b = net[0 ].bias.data print('b的估计误差:' , true_b - b)
小结
我们可以使用PyTorch的高级API更简洁地实现模型。
在PyTorch中,data
模块提供了数据处理工具,nn
模块定义了大量的神经网络层和常见损失函数。
我们可以通过_
结尾的方法将参数替换,从而初始化参数。
问题记录
损失函数为什么用平方损失而不是绝对插值?
二者没有绝对区别,平方损失更便于求导。
损失为什么要求平均?
不是绝对要求平均,若不除以 n,梯度值会过大;
求平均的好处在于,计算出的样本梯度大小不会受到样本数量的影响,这样调整学习率时会更方便。
随机梯度下降中的随机 指什么?
指随机采取一定量的样本。