多层感知机

作用: 在输入层与输出层之间添加隐藏层,并通过激活函数后输出,使网络可以学习复杂的非线性映射

多层感知机的简洁实现

导入torch

import torch
from torch import nn

与之前一样,创建神经网络
这里增加了一个Linear全连接层,和ReLU激活函数

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)# 均值mean默认0.标准差0.01
 
net.apply(init_weights);

与之前使用SoftMax进行图像分类相同的实现

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)# 训练

模型选择、欠拟合和过拟合

统计学习理论

风险是一个期望值,表示模型在特定任务中的预测误差

真实风险

是指在整个数据分布上的期望损失,无法直接计算

其中

  • l(f(x),y)为损失函数
  • P(x,y)是真实的分布,无法直接计算

经验风险

是指在训练数据集上的平均损失,可以直接计算
用经验风险来近似真实风险,通过优化经验风险来优化模型

结构风险

是指在经验风险的基础上添加正则项
用于控制模型的复杂度,提高正则化强度以降低模型复杂度,反之增加复杂度

其中

  • 为正则化强度
  • 为正则化项,例如范数

模型选择

验证集

原则上,在确定超参数之前不应使用测试集,否则可能导致测试集过拟合,并且难以发现.所以应该将数据分为三份:训练集,验证集,测试集;但实际上,由于数据过少,验证集和测试集的界限较为模糊.

K折交叉验证(K-fold Cross Validation)

在数据较少时,可以使用.
将数据分为训练集和测试集,然后将训练集分为K份;每次选择1份作为验证集,其余K-1份作为训练集,重复K次;将结果取平均来估计训练和验证误差

过拟合

是指模型在训练数据上表现很好,但在测试数据上表现很差, 因为模型学习了数据中的噪点或细节,导致无法有效的泛化到未见过的数据上
用于对抗过拟合的方法为正则化

模型复杂度

越复杂的模型,越容易过拟合.
例如在高阶多项式函数的例子中:

图:模型复杂度与对欠拟合和过拟合的影响

当数据样本包含了x的不同值时,令多项式阶数与样本数相同,函数可以完美拟合训练集.

数据集大小

数据越少,越容易过拟合

欠拟合

是指模型在训练数据和测试数据中表现都不好,可能是因为模型过于简单,特征选择不当,训练时间过短,超参数为调整好等原因导致的

权重衰减

权重衰减是最广泛使用的的正则化技术之一,通常称为L2正则化
损失函数:

为了惩罚权重w的大小,权重衰减在损失函数中添加了平方范数^[L2范数的平方]作为正则项

其中用于控制正则化强度
小批量随机梯度下降:

在损失函数这里加入正则项,并简化:

权重衰减的简洁实现

# 定义轮数,学习率
num_epochs, lr = 100, 0.003
 
# 创建神经网络
net = nn.Sequential(nn.Linear(num_inputs, 1))
# 正态分布初始化权重|偏置参数
# net.parameters()返回神经网络的所有可训练参数:[权重,偏置]
# param.data返回这些参数的直接数据,不包括计算图,梯度等
for param in net.parameters():
        param.data.normal_()
# 使用均方误差损失函数
loss = nn.MSELoss(reduction='none')
# 使用随机梯度下降优化器
# net[0]为神经网络的第一层,这里只对权重参数设置了衰减
trainer = torch.optim.SGD([
        {"params":net[0].weight,'weight_decay': wd},
        {"params":net[0].bias},
        ], lr=lr)
# 开始训练
for epoch in range(num_epochs):
		# 从迭代器获取数据
        for X, y in train_iter:
	        # 清除梯度
            trainer.zero_grad()
            # 计算损失
            l = loss(net(X), y)
            # 反向传播
            l.mean().backward()
            # 更新参数
            trainer.step()

暂退法(Dropout)

在计算后续层之前,向网络的每一层添加噪点,
在训练过程中,暂退法会根据一定概率p丢弃一些单元(将值设为0),达到降低模型复杂度,提高泛化能力的目的.
暂退法通过缩放被保留神经元的输出来使期望不变

这里期望
由于测试阶段没有使用dropout,会通过对输出乘以来使测试阶段的期望输出与训练阶段一致

暂退法的实现

只需在神经网络中添加Dropout层,该函数的唯一参数为概率p

# 以之前的图像分类为例
net = nn.Sequential(
		nn.Flatten(),
					
        nn.Linear(784, 256),
        nn.ReLU(),
        # 在第一个全连接层之后添加一个dropout层
        nn.Dropout(p1),
        
        nn.Linear(256, 256),
        nn.ReLU(),
        # 在第二个全连接层之后添加一个dropout层
        nn.Dropout(p2),
        
        nn.Linear(256, 10)
        
        )

前向传播|反向传播|计算图

前向传播

前向传播计算图

反向传播

训练神经网络