Task4:多层感知机
1.感知机
感知机(perceptron)是二类分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别。感知机的取值为+1和-1。感知机对应于输入空间中将实例划分为正负两类的分离超平面,属于判别模型。
感知机学习旨在求出将训练数据进行线性划分的分离超平面,为此,导入基于误分类的损失函数,利用梯度下降法对损失函数进行极小化,求得感知机模型。多层感知机(MLP)是一种前向结构的人工神经网络,包含输入层、输出层及多个隐藏层
2.激活函数
激活函数(activation function)通过计算加权和并加上偏置来确定神经元是否应该被激活, 它们将输入信号转换为输出的可微运算。 大多数激活函数都是非线性的。
2.1 ReLU函数
ReLU函数被定义为该元素与的最大值:
ReLU(x)=max(x,0).
x = np.arange(-8.0, 8.0, 0.1)
x.attach_grad()
with autograd.record():
y = npx.relu(x)
d2l.plot(x, y, 'x', 'relu(x)', figsize=(5, 2.5))
当输入为负时,ReLU函数的导数为0,而当输入为正时,ReLU函数的导数为1。
2.2 Sigmoid函数
对于一个定义域在中的输入, sigmoid函数将输入变换为区间(0, 1)上的输出。 因此,sigmoid通常称为挤压函数(squashing function): 它将范围(-inf, inf)中的任意输入压缩到区间(0, 1)中的某个值:
sigmoid
(
x
)
=
1
1
+
exp
(
−
x
)
.
\operatorname{sigmoid}(x) = \frac{1}{1 + \exp(-x)}.
sigmoid(x)=1+exp(−x)1.
with autograd.record():
y = npx.sigmoid(x)
d2l.plot(x, y, 'x', 'sigmoid(x)', figsize=(5, 2.5))
2.3 tanh函数
当输入在0附近时,tanh函数接近线性变换。 函数的形状类似于sigmoid函数, 不同的是tanh函数关于坐标系原点中心对称。
tanh
(
x
)
=
1
−
exp
(
−
2
x
)
1
+
exp
(
−
2
x
)
.
\operatorname{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)}.
tanh(x)=1+exp(−2x)1−exp(−2x).
with autograd.record():
y = np.tanh(x)
d2l.plot(x, y, 'x', 'tanh(x)', figsize=(5, 2.5))
3.多层感知机从零实现
#导入数据
from mxnet import gluon, np, npx
from d2l import mxnet as d2l
npx.set_np()
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
#初始化模型参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = np.random.normal(scale=0.01, size=(num_inputs, num_hiddens))
b1 = np.zeros(num_hiddens)
W2 = np.random.normal(scale=0.01, size=(num_hiddens, num_outputs))
b2 = np.zeros(num_outputs)
params = [W1, b1, W2, b2]
for param in params:
param.attach_grad()
#模型定义
def net(X):
X = X.reshape((-1, num_inputs))
H = relu(np.dot(X, W1) + b1)
return np.dot(H, W2) + b2
#损失函数
loss = gluon.loss.SoftmaxCrossEntropyLoss()
#模型训练
num_epochs, lr = 10, 0.1
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs,
lambda batch_size: d2l.sgd(params, lr, batch_size))
#对测试数据进行预测
d2l.predict_ch3(net, test_iter)
4.多层感知机简洁实现
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))
# 此处即使不初始化参数,torch也会自己初始化
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)
5.模型选择
只有当模型真正发现了一种泛化模式时,才会作出有效的预测。
将模型在训练数据上拟合的比在潜在分布中更接近的现象称为过拟合(overfitting), 用于对抗过拟合的技术称为正则化(regularization)。
可调整参数的数量。当可调整参数的数量(有时称为自由度)很大时,模型往往更容易过拟合。
参数采用的值。当权重的取值范围较大时,模型可能更容易过拟合。
训练样本的数量。即使模型很简单,也很容易过拟合只包含一两个样本的数据集。而过拟合一个有数百万个样本的数据集则需要一个极其灵活的模型。
6.权重衰减
权重衰减等价于 L 2 范数正则化(regularization)。正则化通过为模型损失函数添加惩罚项使学出的模型参数值较小,是应对过拟合的常用手段。权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制,这可能对过拟合有效。实际场景中,有时也在惩罚项中添加偏差元素的平方和。
L
(
w
,
b
)
+
λ
2
∥
w
∥
2
L(\mathbf{w}, b) + \frac{\lambda}{2} \|\mathbf{w}\|^2
L(w,b)+2λ∥w∥2
7.暂退法(Dropout)
2014年,斯里瓦斯塔瓦等人 就如何将输入随机噪声应用于网络的内部层提出了一个想法: 在训练过程中,他们建议在计算后续层之前向网络的每一层注入噪声。 因为当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性。这个想法被称为暂退法(dropout)。 暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。 这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃一些神经元。 在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。
from mxnet import autograd, gluon, init, np, npx
from mxnet.gluon import nn
from d2l import mxnet as d2l
npx.set_np()
def dropout_layer(X, dropout):
assert 0 <= dropout <= 1
# 在本情况中,所有元素都被丢弃
if dropout == 1:
return np.zeros_like(X)
# 在本情况中,所有元素都被保留
if dropout == 0:
return X
mask = np.random.uniform(0, 1, X.shape) > dropout
return mask.astype(np.float32) * X / (1.0 - dropout)
8.梯度消失和梯度爆炸
在神经网络中,梯度容易受到数值下溢问题的影响. 当将太多的概率乘在一起时,这些问题经常会出现。 在处理概率时,一个常见的技巧是切换到对数空间, 即将数值表示的压力从尾数转移到指数。 最初,矩阵可能具有各种各样的特征值。 他们可能很小,也可能很大; 他们的乘积可能非常大,也可能非常小。不稳定梯度带来的风险不止在于数值表示; 不稳定梯度也威胁到我们优化算法的稳定性。 我们可能面临一些问题。 要么是梯度爆炸(gradient exploding)问题: 参数更新过大,破坏了模型的稳定收敛; 要么是梯度消失(gradient vanishing)问题: 参数更新过小,在每次更新时几乎不会移动,导致模型无法学习。
#梯度消失
%matplotlib inline
from d2l import mxnet as d2l
from mxnet import autograd, np, npx
npx.set_np()
x = np.arange(-8.0, 8.0, 0.1)
x.attach_grad()
with autograd.record():
y = npx.sigmoid(x)
y.backward()
d2l.plot(x, [y, x.grad], legend=['sigmoid', 'gradient'], figsize=(4.5, 2.5))
#梯度爆炸
M = np.random.normal(size=(4, 4))
print('一个矩阵 \n', M)
for i in range(100):
M = np.dot(M, np.random.normal(size=(4, 4)))
print('乘以100个矩阵后\n', M)