目录
最简单的深度⽹络称为多层感知机。多层感知机由多层神经元组成,每⼀层与它的上⼀层相连,从中接收输⼊;同时每⼀层也与它的下⼀层相连,影响当前层的神经元。
1.多层感知机
隐藏层
线性模型意味着输入和输出是线性相关的,有时数据中的自变量和响应变量没有明显的线性关系,这时可以通过在网络中加入一个或多个隐藏层来克服线性模型的限制。最简单的方法是将许多全连接层堆叠在一起。每一层都输出到上面的层,直到生成最后的输出。可以把前L-1层看作表示,最后一层看作预测器。这种架构成为多层感知机。(multilayer perceptron,MLP)
这个多层感知机有4个输⼊,3个输出,其隐藏层包含5个隐藏单元。输⼊层不涉及任何计算,因此使⽤此⽹络 产⽣输出只需要实现隐藏层和输出层的计算。因此,这个多层感知机中的层数为2。注意,这两个层都是全连接的。每个输⼊都会影响隐藏层中的每个神经元,⽽隐藏层中的每个神经元⼜会影响输出层中的每个神经元。
在仿射变换之后对每个隐藏单元应⽤⾮线性的 激活函数(activation function)σ。激活函数的输出(例如,σ(·))被称为活性值(activations)。⼀般来说,有了激活函数,就不可能再将我们的多层感知机退化成线性模型:
为了构建更通⽤的多层感知机,可以继续堆叠这样的隐藏层,例如H(1) = σ1(XW(1) + b (1))和H(2) = σ2(H(1)W(2) + b (2)),⼀层叠⼀层,从⽽产⽣更有表达能⼒的模型。
激活函数
激活函数(activation function)通过计算加权和并加上偏置来确定神经元是否应该被激活,它们将输⼊信号 转换为输出的可微运算。⼤多数激活函数都是⾮线性的。常见的激活函数有以下这些:
ReLU函数
ReLU提供了⼀种⾮常简单的⾮线性变换。给定元素x,ReLU函数被定义为该元素与0的最⼤值:
左边是ReLU函数的图像,右边是它的导数的图像。
ReLU函数通过将相应的活性值设为0,仅保留正元素并丢弃所有负元素。激活函数是分段线性的。当输⼊为负时,ReLU函数的导数为0,⽽当输⼊为正时,ReLU函数的导数为1。当输⼊值精确等于0时, ReLU函数不可导。此时默认使用左侧导数(即输入0时导数为0)。
使⽤ReLU的原因是,它求导表现得特别好:要么让参数消失,要么让参数通过。这使得优化表现得更好,并 且ReLU减轻了困扰以往神经⽹络的梯度消失问题。
ReLU的变体:参数化ReLU(Parameterized ReLU,pReLU),该变体为ReLU添加了⼀个线性项,因此即使参数是负的,某些信息仍然可以通过:
Sigmoid函数
对于⼀个定义域在R中的输⼊,sigmoid函数将输⼊变换为区间(0, 1)上的输出。它被称为挤压函数(squashing function):它将范围(-inf, inf)中的任意输⼊压缩到区间(0, 1)中的某个值:
上图左边是Sigmoid函数的图像,右边是它的导数的图像。当输⼊接近0时,sigmoid函数接近线性变换。
Sigmoid函数的导数如下:
当输⼊为0时,sigmoid函数的导数达到最⼤值0.25;⽽输⼊在任⼀ ⽅向上越远离0点时,导数越接近0。
tanh函数
与sigmoid函数类似,tanh(双曲正切)函数也能将其输⼊压缩转换到区间(-1, 1)上。tanh函数的公式如下:
上图左边是tanh函数的图像,右边是它的导数的图像。当输⼊在0附近时,tanh函数接近线性变换。函数的形状类似于sigmoid函数, 不同的是tanh函数关于坐标系原点中⼼对称。
tanh函数的导数是:
当输⼊接近0时,tanh函数的导数接近最⼤值1。与我们在sigmoid函数图像 中看到的类似,输⼊在任⼀⽅向上越远离0点,导数越接近0。
多层感知机的从0开始实现
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)
# 初始化模型参数
"""
Fashion-MNIST中的每个图像由 28 × 28 = 784个灰度像素值组成。所有图像共分为10个类别。忽
略像素之间的空间结构,我们可以将每个图像视为具有784个输⼊特征和10个类的简单分类数据集。⾸先,我
们将实现⼀个具有单隐藏层的多层感知机,它包含256个隐藏单元。注意,我们可以将这两个变量都视为超参
数。通常,我们选择2的若⼲次幂作为层的宽度。因为内存在硬件中的分配和寻址⽅式,这么做往往可以在计
算上更⾼效。
"""
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.randn(num_hiddens,requires_grad=True))
w2 = nn.Parameter(torch.randn(
num_hiddens,num_outputs,requires_grad=True)*0.01)
b2 = nn.Parameter(torch.randn(num_outputs,requires_grad=True))
params = [w1,b1,w2,b2]
# 激活函数
def relu(x):
a = torch.zeros_like(x)
return torch.max(x,a)
# 模型
def net(x):
x = x.reshape(-1,num_inputs)
H = relu(x@w1 + b1) # 这里@代表矩阵乘法
return (H@w2+b2)
# 损失函数
loss = nn.CrossEntropyLoss(reduction='none')
# 训练
num_epochs,lr =10, 0.1
updater = torch.optim.SGD(params,lr)
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,updater)
d2l.predict_ch3(net,test_iter)
d2l.plt.show()
多层感知机的简单实现
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)
train_iter,test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)