3 线性神经网络
3.1 线性回归
定义:利用数理统计中回归分析,来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法
线性回归要素
- 训练集/训练数据
- 输出数据
- 拟合的函数(或者称为假设或者模型)
- 训练数据的条目数
假设给定样本 ( x ( i ) , y ( i ) ) (\mathbf{x}^{(i)},\mathbf{y}^{(i)}) (x(i),y(i))
构造代价(误差、损失)函数: J ( θ ) = 1 2 Σ i = 1 m ( y ( i ) − h θ ( x ( i ) ) ) 2 J(\mathbf{\theta})={\frac{1}{2}}\Sigma_{i=1}^{m}\left(y^{(i)}-h_{\mathbf{\theta}}\big(\mathbf{x}^{(i)}\big)\right)^{2} J(θ)=21Σi=1m(y(i)−hθ(x(i)))2
目标: 找到超平面参数 θ \theta θ,求解 min θ J ( θ ) \operatorname*{min}_{\mathbf{\theta}}J(\mathbf{\theta}) minθJ(θ)
令 ∂ J ( θ ) ∂ θ = 0 \frac{\partial J(\mathbf{\theta})}{\partial\mathbf{\theta}}=0 ∂θ∂J(θ)=0
可得到 θ = ( X T X ) − 1 X T y \mathbf{\theta=\left(X^{\mathrm{T}}X\right)^{-1}X^{\mathrm{T}}y} θ=(XTX)−1XTy
3.2 线性二分类问题
定义: 线性分类器则透过特征的线性组合来做出分类决定,以达到
此种目的。简言之,样本通过直线(或超平面)可分
线性分类与线性回归差别:
- 输出意义不同:属于某类的概率<->回归具体值
- 参数意义不同:最佳分类直线<->最佳拟合直线
- 维度不同:前面的例子中,一个是一维的回归,一个是二维的分类
代价(误差)函数: J ( θ ) = 1 2 Σ i = 1 N ( y ( i ) − h θ ( x ( i ) ) ) 2 J(\mathbf{\theta})=\frac{1}{2}\Sigma_{i=1}^{N}\left(y^{(i)}-h_{\theta}(\mathbf{x}^{(i)})\right)^{2} J(θ)=21Σi=1N(y(i)−hθ(x(i)))2
其中 h θ ( x ( i ) ) = 1 1 + e − θ T X ( i ) h_{\mathbf{\theta}}(\mathbf{x}^{(i)})={\frac{1}{1+e^{-\mathbf{\theta}\mathbf{T}_{\mathbf{X}}(i)}}} hθ(x(i))=1+e−θTX(i)1
由于 J J J未非线性,采取迭代的方法 θ k + 1 = θ k + Δ θ k \mathbf{\theta}_{k+1}=\mathbf{\theta}_{k}+\Delta\mathbf{\theta}_{k} θk+1=θk+Δθk
采取: J ( θ k + 1 ) = J ( θ k ) + [ d J d θ ] T Δ θ k J(\mathbf{\theta}_{k+1})=J(\mathbf{\theta}_{k})+\left[\frac{dJ}{d\mathbf{\theta}}\right]^{\mathrm{T}}\Delta\mathbf{\theta}_{k} J(θk+1)=J(θk)+[dθdJ]TΔθk
必然有 J ( θ k + 1 ) ≤ J ( θ k ) J(\mathbf{\theta}_{k+1})\leq J(\mathbf{\theta}_{k}) J(θk+1)≤J(θk)
线性二分类的代码实践
%matplotlib inline
import random
import torch
from d2l import torch as d2l
使用下面的代码生成带有噪声的线性模型数据集
def synthetic_data(w, b, num_examples): #@save
X = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(X, w) + b
y += torch.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1))
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
print('features:', features[0],'\nlabel:', labels[0])
features: tensor([ 1.2964, -1.3842])
label: tensor([11.5007])
d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
下面的代码中定义一个data_iter函数,该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。每个小批量包含一组特征和标签。
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]
定义模型、损失函数、优化算法
def linreg(X, w, b): #@save
"""线性回归模型"""
return torch.matmul(X, w) + b
def squared_loss(y_hat, y): #@save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
def sgd(params, lr, batch_size): #@save
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
设计超参数进行训练
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
batch_size = 10
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。 l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()
sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
epoch 1, loss 0.029350
epoch 2, loss 0.000107
epoch 3, loss 0.000051
w的估计误差: tensor([-0.0001, -0.0004], grad_fn=<SubBackward0>)
b的估计误差: tensor([0.0009], grad_fn=<RsubBackward1>)
4 多层感知机
4.1 感知机原理
多层感知机由多层神经元组成,每一层与它的上一层相连,从中接收输入;同时每一层也与它的下一层相连,影响当前层的神经元。
4.1.1 隐藏层
线性模型可能会出错,过在网络中加入一个或多个隐藏层来克服线性模型的限制,将许多全连接层堆叠在一起。每一层都输出到上面的层,直到生成最后的输出。我们可以把前L−1层看作表示,把最后一层看作线性预测器。这种架构通常称为多层感知机(multilayerperceptron),通常缩写为MLP。使其能处理更普遍的函数关系类型。
通过激活函数可以避免多层感知机退化为线性模型
H = σ ( X W ( 1 ) + b ( 1 ) ) , O = H W ( 2 ) + b ( 2 ) . \begin{aligned}\mathbf{H}&=\sigma(\mathbf{X}\mathbf{W}^{(1)}+\mathbf{b}^{(1)}),\\\mathbf{O}&=\mathbf{H}\mathbf{W}^{(2)}+\mathbf{b}^{(2)}.\end{aligned} HO=σ(XW(1)+b(1)),=HW(2)+b(2).
4.1.2 激活函数
激活函数(activation function)通过计算加权和并加上偏置来确定神经元是否应该被激活,它们将输入信号转换为输出的可微运算。大多数激活函数都是非线性的。
常见的激活函数包括:
- ReLU函数
- sigmoid函数
- tanh函数
ReLU函数
ReLU ( x ) = max ( x , 0 ) . \operatorname{ReLU}(x)=\max(x,0). ReLU(x)=max(x,0).
x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = torch.relu(x)
d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5))
sigmoid函数
s i g m o i d ( x ) = 1 1 + exp ( − x ) . \mathrm{sigmoid}(x)=\frac{1}{1+\exp(-x)}. sigmoid(x)=1+exp(−x)1.
y = torch.sigmoid(x)
d2l.plot(x.detach(), y.detach(), 'x', 'sigmoid(x)', figsize=(5, 2.5))
tanh函数
tanh ( x ) = 1 − exp ( − 2 x ) 1 + exp ( − 2 x ) . \tanh(x)=\frac{1-\exp(-2x)}{1+\exp(-2x)}. tanh(x)=1+exp(−2x)1−exp(−2x).
y = torch.tanh(x)
d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5))
4.2 多层感知机的实现
通过调用API可以简介的实现多层感知机,下列示例使Fashion‐MNIST图像分类数据集进行展示
import torch
from torch import nn
from d2l import torch as d2l
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights);
batch_size, lr, num_epochs = 256, 0.1, 10
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=lr)
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
4.4 模型选择、欠拟合和过拟合
4.4.1 训练误差和泛化误差
- 训练误差(training error),模型在训练数据集上计算得到的误差。
- 泛化误差(generalization error),模型应用在同样从原始样本的分布中抽取的无限多数据样本时,模型误差的期望。
4.4.2 模型选择
验证集
将我们的数据分成三份,除了训练和测试数据集之外,还增加一个验证数据集(val‐
idation dataset),也叫验证集(validation set)。
K折交叉验证
当训练数据稀缺时,我们甚至可能无法提供足够的数据来构成一个合适的验证集。这个问题的一个流行的解决方案是采用K折交叉验证。这里,原始训练数据被分成K个不重叠的子集。然后执行K次模型训练和验证,每次在K − 1个子集上进行训练,并在剩余的一个子集(在该轮中没有用于训练的子集)上进行验证。最后,通过对K次实验的结果取平均来估计训练和验证误差。
4.4.3 欠拟合和过拟合
由于我们的训练和验证误差之间的泛化误差很小,用一个更复杂的模型可以降低训练误差。这种现象被称为欠拟合(underfitting)。
训练误差明显低于验证误差时要小心,这表明严重的过拟合(overfitting)。
常用技巧
- 权重衰减 (𝐿2正则化)
为防止过拟合和权值震荡,加入新的指标函数项: J ( w ) + λ 2 ∥ w ∥ 2 J(\mathbf{w})+\frac\lambda2\|\mathbf{w}\|^{2} J(w)+2λ∥w∥2,第二项约束了权值不能过大。 - Dropout(暂退)
在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。 - 模型初始化
简单的考虑,把所有权值在[-1,1]区间内按均值或高斯分布进行初始化。
BP算法基本思想
假设网络共有L层,(输入为第0层,输出为第L层)
y
=
a
L
y=aL
y=aL
二层神经网络的推导示例
第l层第i个神经元的输出为
a
i
=
f
(
w
[
2
]
⋅
a
[
1
]
)
=
f
(
∑
j
=
0
n
w
i
j
[
2
]
⋅
a
j
[
1
]
)
a_{i}=f(\mathbf{w}^{[2]}\cdot\mathbf{a}^{[1]})=f(\sum_{j=0}^{n}w_{ij}^{[2]}\cdot a_{j}^{[1]})
ai=f(w[2]⋅a[1])=f(∑j=0nwij[2]⋅aj[1])
选取 f f f函数为Log Sigmoid函数 σ = 1 1 + e − x \sigma=\frac{1}{1+e^{-x}} σ=1+e−x1
计算误差 e = y − y ^ = y − a \mathbf{e}=\mathbf{y}-\hat{\mathbf{y}}=\mathbf{y}-\mathbf{a} e=y−y^=y−a
Δ
w
k
=
−
α
d
J
d
w
\Delta\mathbf{w}_{k}=-\alpha\frac{dJ}{d\mathbf{w}}
Δwk=−αdwdJ
d
J
d
w
∣
w
=
w
k
\frac{dJ}{d\mathbf{w}}\mid\mathbf{w}=\mathbf{w}_k
dwdJ∣w=wk
根据链式求导法则
∂ J ∂ w i j [ 2 ] = ∂ J ∂ e i ⋅ ∂ e i ∂ a i ⋅ ∂ a i ν ∂ z i ⋅ ∂ z i ∂ w i j [ 2 ] \frac{\partial J}{\partial w_{ij}^{[2]}}=\frac{\partial J}{\partial e_{i}}\cdot\frac{\partial e_{i}}{\partial a_{i}}\cdot\frac{\partial a_{i}^{\nu}}{\partial z_{i}}\cdot\frac{\partial z_{i}}{\partial w_{ij}^{[2]}} ∂wij[2]∂J=∂ei∂J⋅∂ai∂ei⋅∂zi∂aiν⋅∂wij[2]∂zi
∂ J ∂ w i j [ 2 ] = − e i a i ( 1 − a i ) a j [ 1 ] \frac{\partial J}{\partial w_{ij}^{[2]}}=-e_{i}a_{i}(1-a_{i})a_{j}^{[1]} ∂wij[2]∂J=−eiai(1−ai)aj[1]
进一步有:
Δ w i j [ 2 ] ( k ) = − α ∂ J ∂ w i j [ 2 ] = α ⋅ a i ( 1 − a i ) e i ⋅ a j [ 1 ] \Delta w_{ij}^{[2]}(k)=-\alpha\frac{\partial J}{\partial w_{ij}^{[2]}}=\alpha\cdot a_{i}(1-a_{i})e_{i}\cdot a_{j}^{[1]} Δwij[2](k)=−α∂wij[2]∂J=α⋅ai(1−ai)ei⋅aj[1]
令 δ i [ 2 ] = a i ( 1 − a i ) e i \delta_{i}^{[2]}=a_{i}(1-a_{i})e_{i} δi[2]=ai(1−ai)ei,则有 Δ w i j [ 2 ] ( k ) = α δ i [ 2 ] ⋅ a j [ 1 ] \Delta w_{ij}^{[2]}(k)=\alpha\delta_{i}^{[2]}\cdot a_{j}^{[1]} Δwij[2](k)=αδi[2]⋅aj[1]
tial w_{ij}{[2]}}=-e_{i}a_{i}(1-a_{i})a_{j}{[1]}$
进一步有:
Δ w i j [ 2 ] ( k ) = − α ∂ J ∂ w i j [ 2 ] = α ⋅ a i ( 1 − a i ) e i ⋅ a j [ 1 ] \Delta w_{ij}^{[2]}(k)=-\alpha\frac{\partial J}{\partial w_{ij}^{[2]}}=\alpha\cdot a_{i}(1-a_{i})e_{i}\cdot a_{j}^{[1]} Δwij[2](k)=−α∂wij[2]∂J=α⋅ai(1−ai)ei⋅aj[1]
令 δ i [ 2 ] = a i ( 1 − a i ) e i \delta_{i}^{[2]}=a_{i}(1-a_{i})e_{i} δi[2]=ai(1−ai)ei,则有 Δ w i j [ 2 ] ( k ) = α δ i [ 2 ] ⋅ a j [ 1 ] \Delta w_{ij}^{[2]}(k)=\alpha\delta_{i}^{[2]}\cdot a_{j}^{[1]} Δwij[2](k)=αδi[2]⋅aj[1]