系列文章
李沐《动手学深度学习》预备知识 张量操作及数据处理
李沐《动手学深度学习》预备知识 线性代数及微积分
李沐《动手学深度学习》线性神经网络 线性回归
李沐《动手学深度学习》线性神经网络 softmax回归
目录
教材:李沐《动手学深度学习》
一、多层感知机
(一)隐藏层(提出原因、方式、多层感知机)
-
隐藏层的提出原因——线性模型可能会出错
线性意味着单调假设,但是现实生活中存在许多违反单调性的情况:- 根据体温预测死亡率:对体温高于37摄氏度的人来说,温度越高风险越大;对体温低于37摄氏度的人来说,温度越高风险就越低。
- 对猫和狗的图像进行分类:倒置图像后依然保留类别。
-
隐藏层的最简单方式:将许多全连接层堆叠在一起
多层感知机架构:将许多全连接层堆叠在一起,每一层都输出到上面的层,直到生成最后的输出。- 4个输入,3个输出,隐藏层包含5个隐藏单元;
- 输入层不涉及任何计算,只需要实现隐藏层和输出层的计算,因此层数为2;
- 该MLP的两个层都是全连接的,每个输入都会影响隐藏层中的每个神经元, 而隐藏层中的每个神经元又会影响输出层中的每个神经元;
- 多层感知机可以通过隐藏神经元,捕捉到输入之间复杂的相互作用。
-
多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。
(二)激活函数(ReLU、sigmoid、tanh)
- 激活函数的概念
- 作用:实现线性到非线性的飞跃;
- 激活函数通过计算加权和并加上偏置来确定神经元是否应该被激活, 它们将输入信号转换为输出的可微运算;
- 大多数激活函数都是非线性的。
- ReLU函数
- 修正线性单元(Rectified linear unit,ReLU)
- 给定元素
x
x
x,ReLU函数定义为该元素与0的最大值:
R e L U ( x ) = m a x ( x , 0 ) ReLU(x)=max(x,0) ReLU(x)=max(x,0) - ReLU函数是分段线性的,仅保留正元素丢弃负元素。
ReLU函数:
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))
ReLU函数的导数: 输入为负时,导数为0;输入为正时,导数为1。
y.backward(torch.ones_like(x), retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of relu', figsize=(5, 2.5))
ReLU求导后:要么让参数消失,要么让参数通过。 这使得优化表现得更好,并且ReLU减轻了困扰以往神经网络的梯度消失问题。
- sigmoid函数
- sigmoid函数将一个定义域在R中的输入变换为区间(0,1)上的输出;
- sigmoid也被称为挤压函数,将范围(-inf,inf)中的任意输入压缩到(0,1)中的某个值:
s i g m o i d ( x ) = 1 1 + e x p ( − x ) sigmoid(x)=\frac{1}{1+exp(-x)} sigmoid(x)=1+exp(−x)1 - sigmoid是一个平滑的、可微的阈值单元近似;
- 将输出视作二元分类问题的概率时,sigmoid被广泛用作输出单元上的激活函数;
- sigmoid在隐藏层中已经较少使用,在大部分时候被更简单、更容易训练的ReLU取代。
sigmoid函数:
y = torch.sigmoid(x)
d2l.plot(x.detach(), y.detach(), 'x', 'sigmoid(x)', figsize=(5, 2.5))
sigmoid函数的导数:
d
d
x
s
i
g
m
o
i
d
(
x
)
=
e
x
p
(
−
x
)
(
1
+
e
x
p
(
−
x
)
)
2
=
s
i
g
m
o
i
d
(
x
)
(
1
−
s
i
g
m
o
i
d
(
x
)
)
\frac{d}{dx}sigmoid(x)=\frac{exp(-x)}{({{1+exp(-x)}})^2}=sigmoid(x)(1-sigmoid(x))
dxdsigmoid(x)=(1+exp(−x))2exp(−x)=sigmoid(x)(1−sigmoid(x))
当输入为0时,sigmoid函数的导数达到最大值0.25,输入在任一方向越远离0点时,导数越接近0。
# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of sigmoid', figsize=(5, 2.5))
- tanh函数
- 与sigmoid函数类似, tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上:
t a n h ( x ) = 1 − e x p ( − 2 x ) 1 + e x p ( − 2 x ) tanh(x)=\frac{1-exp(-2x)}{1+exp(-2x)} tanh(x)=1+exp(−2x)1−exp(−2x) - 输入为0附近时,tanh函数接近线性变换;
- tanh函数形状类似于sigmoid函数,不同的是tanh函数关于坐标系原点中心对称。
- 与sigmoid函数类似, tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上:
tanh函数:
y = torch.tanh(x)
d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5))
tanh函数的导数:
d
d
x
t
a
n
h
(
x
)
=
1
−
t
a
n
h
2
(
x
)
\frac{d}{dx}tanh(x)=1-tanh^2(x)
dxdtanh(x)=1−tanh2(x)
当输入接近0时,tanh函数的导数接近最大值1,输入在任一方向越远离0点时,导数越接近0。
# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of tanh', figsize=(5, 2.5))
二、多层感知机的从零开始实现
(一)导入数据和相关库
导入相关库,设置批量大小为256,调用load_data_fashion_mnist函数获取数据集
import torch
from torch import nn
from d2l import torch as d2l
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
(二)初始化模型参数
- 用张量表示参数;
- 对每一层记录一个权重矩阵和一个偏置向量;
- 为损失关于这些参数的梯度分配内存。
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = nn.Parameter(torch.randn(
num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(
num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]
(三)模型构建
- ReLU激活函数
def relu(X):
a = torch.zeros_like(X)
return torch.max(X, a)
- 模型实现
隐藏层数目和隐藏单元数视为超参数,一般选择2的若干次幂为层的宽度, 因为内存在硬件中的分配和寻址方式,这么做往往可以在计算上更高效。
- 输入:将28x28=784的灰度像素图像展平
- 中间层:选择实现一个具有单隐藏层的多层感知机,包含256个隐藏单元
- 输出:10个类别
def net(X):
X = X.reshape((-1, num_inputs))
H = relu(X@W1 + b1) # 这里“@”代表矩阵乘法
return (H@W2 + b2)
(四)损失函数
在李沐《动手学深度学习》线性神经网络 softmax回归中实现了交叉熵损失函数,因此这里直接使用高级API中的内置函数计算softmax和交叉熵损失。
loss = nn.CrossEntropyLoss(reduction='none')
(五)训练
多层感知机的训练过程与softmax回归的训练过程完全相同。 可以直接调用d2l包的train_ch3函数, 将迭代周期数设置为10,并将学习率设置为0.1:
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
为了对学习到的模型进行评估,在测试数据上应用这个模型:
三、多层感知机的简洁实现
- 导入相关库
import torch
from torch import nn
from d2l import torch as d2l
- 模型构建
- 展平层:调整网络输入的形状
- 全连接层/线性层:输入784,输出256
- ReLU激活
- 全连接层/线性层:输入256,输出10
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)