0%

Pytorch之多层感知机

本节主要介绍了多层感知机、K-折交叉验证、欠拟合与过拟合以及权重衰退

多层感知机

  • 我们可以通过在网络中加入一个或多个隐藏层来克服线性模型的限制, 使其能处理更普遍的函数关系类型。

  • 要做到这一点,最简单的方法是将许多全连接层堆叠在一起,每一层都输出到上面的层,直到生成最后的输出

  • 我们可以把前$L−1$层看作表示,把最后一层看作线性预测器

  • 这种架构通常称为多层感知机(multilayer perceptron),通常缩写为MLP

    image-20230413192102040
  • 这个多层感知机有4个输入,3个输出,其隐藏层包含5个隐藏单元

  • 输入层不涉及任何计算,因此使用此网络产生输出只需要实现隐藏层和输出层的计算,因此,这个多层感知机中的层数为2

  • 注意,这两个层都是全连接的,每个输入都会影响隐藏层中的每个神经元, 而隐藏层中的每个神经元又会影响输出层中的每个神经元

  • 多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出

  • 常用的激活函数包括ReLU函数、sigmoid函数和tanh函数


多层感知机的简洁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import torch
from torch.utils import data
from torchvision import transforms
import torchvision
from torch import nn
import matplotlib.pyplot as plt

#读取数据集
trans = transforms.ToTensor() #通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
mnist_train = torchvision.datasets.FashionMNIST(
root="D:\App_Data_File\VScode_Project\Python\Pytorch\dataset\Fashion-MNIST", train=True, transform=trans, download=False)
mnist_test = torchvision.datasets.FashionMNIST(
root="D:\App_Data_File\VScode_Project\Python\Pytorch\dataset\Fashion-MNIST", train=False, transform=trans, download=False)

batch_size = 256
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True)
test_iter = data.DataLoader(mnist_test, batch_size, shuffle=False)

#模型参数
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)

#损失函数
loss = nn.CrossEntropyLoss(reduction='none')

#优化算法:使用学习率为0.1的小批量随机梯度下降作为优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

#训练模型
num_epochs = 10

train_loss = []
train_acc = []
test_loss = []
test_acc = []

for epoch in range(num_epochs):
net.train()
total_loss, total_correct = 0.0, 0.0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y)
trainer.zero_grad()
l.mean().backward()
trainer.step()

total_loss += l.sum()
total_correct += (y_hat.argmax(dim=1) == y).sum().float()

train_loss.append(total_loss.detach().numpy() / len(mnist_train))
train_acc.append(total_correct / len(mnist_train))

net.eval()
total_loss, total_correct = 0.0, 0.0
for X, y in test_iter:
y_hat = net(X)
l = loss(y_hat, y)

total_loss += l.sum()
total_correct += (y_hat.argmax(dim=1) == y).sum().float()

test_loss.append(total_loss.detach().numpy() / len(mnist_test))
test_acc.append(total_correct / len(mnist_test))

print(f"Epoch [{epoch + 1}/{num_epochs}], Train Loss: {train_loss[-1]:.4f}, Train Acc: {train_acc[-1]*100:.2f}%, Test Loss: {test_loss[-1]:.4f}, Test Acc: {test_acc[-1]*100:.2f}%")

plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(train_loss, label='Train Loss')
plt.plot(test_loss, label='Test Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(train_acc, label='Train Acc')
plt.plot(test_acc, label='Test Acc')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.show()

##从测试集中选中10个样本去测试训练好的模型,并可视化测试结果
def get_fashion_mnist_labels(labels): #@save
"""返回Fashion-MNIST数据集的文本标签"""
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
return text_labels[labels]

test_samples = data.DataLoader(mnist_test, batch_size=10, shuffle=True)
test_batch = next(iter(test_samples))
X_test, y_test = test_batch

# 预测并可视化结果
net.eval()
with torch.no_grad():
y_pred = net(X_test).argmax(dim=1)

fig2=plt.figure(figsize=(10, 3))
for i in range(10):
plt.subplot(2, 5, i+1)
plt.imshow(X_test[i, 0], cmap='CMRmap')
plt.title(f"Pred: {get_fashion_mnist_labels(y_pred[i])}, Actual: {get_fashion_mnist_labels(y_test[i])}")
plt.axis('off')
fig2.subplots_adjust(hspace=1.2, wspace=0.5)
plt.show()

结果如下:

1
2
3
4
5
6
7
8
9
10
Epoch [1/10], Train Loss: 1.0367, Train Acc: 64.71%, Test Loss: 0.6816, Test Acc: 76.38%
Epoch [2/10], Train Loss: 0.5972, Train Acc: 78.91%, Test Loss: 0.5632, Test Acc: 80.51%
Epoch [3/10], Train Loss: 0.5189, Train Acc: 81.77%, Test Loss: 0.5277, Test Acc: 81.49%
Epoch [4/10], Train Loss: 0.4791, Train Acc: 83.14%, Test Loss: 0.5051, Test Acc: 82.13%
Epoch [5/10], Train Loss: 0.4549, Train Acc: 83.95%, Test Loss: 0.4625, Test Acc: 83.73%
Epoch [6/10], Train Loss: 0.4315, Train Acc: 84.88%, Test Loss: 0.4773, Test Acc: 83.28%
Epoch [7/10], Train Loss: 0.4155, Train Acc: 85.26%, Test Loss: 0.4530, Test Acc: 83.56%
Epoch [8/10], Train Loss: 0.4036, Train Acc: 85.76%, Test Loss: 0.4420, Test Acc: 84.14%
Epoch [9/10], Train Loss: 0.3904, Train Acc: 86.21%, Test Loss: 0.4351, Test Acc: 84.05%
Epoch [10/10], Train Loss: 0.3824, Train Acc: 86.46%, Test Loss: 0.4199, Test Acc: 84.63%
image-20230419235927270

image-20230419235953900

image-20230420000011181


K-折交叉验证

  • 非大数据集上通常使用k-折交叉验证

  • 训练数据集:训练模型参数

  • 验证数据集:选择模型超参数

  • K-折交叉验证的算法:

    • 将训练数据分割成K块

    • For i = 1,…,K

      • 使用第i块作为验证数据集,其余的k-1块作为训练数据集
    • 报告K个验证集误差的平均

    • 常用K=5或10

    • 例如:若K=3

      块1 块2 块3
      i=1 val train train
      i=2 train val train
      i=3 train train val

欠拟合与过拟合

  • 模型容量:

    • 拟合各种函数的能力
    • 低容量的模型难以拟合训练数据
    • 高容量的模型可以记住所有的训练数据
  • 过拟合与欠拟合

    数据:简单 数据:复杂
    模型容量:低 正常 欠拟合
    模型容量:高 过拟合 正常
    • 过拟合:在训练集上表现好,但是在测试集上效果差
    • 欠拟合(高偏差):模型拟合不够,在训练集上表现效果差,没有充分的利用数据,预测的准确度低
    • 偏差:反映的是模型在样本上的输出与真实值之间的误差,即模型本身的精确度
    • 方差:反映的是模型每一次输出结果与模型输出期望之间的误差,即模型的稳定性
  • 模型复杂度对欠拟合和过拟合的影响

    image-20230624232454855
  • 如何防止过拟合与欠拟合:

    • 防止过拟合方法:
      • 补充数据集
      • 减少模型参数
      • Dropout
      • Earlystopping
      • 正则化或稀疏化
    • 防止欠拟合方法
      • 加大模型参数
      • 减少正则化参数
      • 更充分的训练

权重衰退($L_2$正则化)

  • 在训练参数化机器学习模型时, 权重衰减(weight decay)是最广泛使用的正则化的技术之一, 它通常也被称为$L_2$正则化

  • 使用均方范数作为硬性限制:

    • 通过限制参数值的选择范围来控制模型容量
      $$
      min[l(w,b)],,,subject, to, ||W||^2\leq\theta
      $$

    • 通常不限制偏移b

    • 小的$\theta$意味着更强的正则项

  • 使用均方范数作为柔性限制

    • 对于每一个$\theta$,都可以找到$\lambda$使得之前的目标函数等价于:
      $$
      min[l(w,b)+\frac\lambda2||w||^2]
      $$

    • 超参数$\lambda$控制了正则项的重要程度

      • $\lambda=0$:正则项无作用
      • $\lambda\rightarrow \infty$:意味着$\theta\rightarrow0$,$w\rightarrow0$
  • 参数更新法则:

    • 计算梯度:
      $$
      \frac{\partial (l(w,b)+\frac\lambda2||w||^2)}{\partial w}=\frac{\partial l(w,b)}{\partial w}+\lambda w
      $$

    • 时间t更新参数:
      $$
      w_{t+1}=w_t-\eta\frac{\partial loss}{\partial w_t}
      $$

      $$
      w_{t+1}=(1-\eta\lambda)w_t-\eta\frac{\partial l(w_t,b_t)}{\partial w_t}
      $$

    • 通常$\eta\lambda<1$,在深度学习中通常叫做权重衰退


Reference

欢迎来到ssy的世界