其他博客:
多层感知机概述:https://www.cnblogs.com/somedayLi/p/12313804.html
多层感知机简洁实现:https:////www.cnblogs.com/somedayLi/p/12359420.html
下⾯实现⼀个多层感知机。⾸先导⼊实现所需的包或模块。
# 导包
import torch
import numpy as np
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
1. 获取和读取数据:
这⾥继续使⽤Fashion-MNIST数据集。我们将使⽤多层感知机对图像进⾏分类。
# 1.获取和读取数据
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
2. 定义模型参数
Fashion-MNIST数据集中图像形状为(28 imes 28),类别数为 10 。本节中我们依然使⽤⻓度为 (28 imes 28 = 784) 的向量表示每⼀张图像。因此,输⼊个数为784,输出个数为10。实验中,我们设超参数隐藏单元个数为256。
# 2 定义模型参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256
w1 = torch.tensor(np.random.normal(0, 0.01, (num_inputs, num_hiddens)),dtype=torch.float)
b1 = torch.zeros(num_hiddens, dtype=torch.float)
w2 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens, num_outputs)), dtype=torch.float)
b2 = torch.zeros(num_outputs, dtype=torch.float)
params = [w1, b1, w2, b2]
for param in params:
param.requires_grad_(requires_grad=True)
3. 定义激活函数
使⽤基础的 max
函数来实现ReLU,⽽⾮直接调⽤ relu
函数。
# 3 定义激活函数
def relu(x):
return torch.max(input=x, other=torch.tensor(0.0))
4. 定义模型
同softmax回归⼀样,我们通过 view
函数将每张原始图像改成⻓度为 num_inputs
的向量。然后实现概述中的多层感知机的计算表达式。
# 4 定义模型
def net(x):
x = x.view((-1, num_inputs))
H = relu(torch.matmul(x,w1) + b1)
return torch.matmul(H, w2) + b2
5. 定义损失函数
为了得到更好的数值稳定性,我们直接使⽤PyTorch提供的包括softmax运算和交叉熵损失计算的函数。
# 5 定义损失函数
loss = torch.nn.CrossEntropyLoss()
6. 训练模型
训练多层感知机的步骤和3.6节中训练softmax回归的步骤没什么区别。我们直接调⽤ d2lzh_pytorch
包中的 train_ch3
函数,它的实现已经在3.6节⾥介绍过。我们在这⾥设超参数迭代周期数为5,学习率为100.0。
注:由于原书的mxnet中的 SoftmaxCrossEntropyLoss 在反向传播的时候相对于沿batch维求和了,⽽PyTorch默认的是求平均,所以⽤PyTorch计算得到的loss⽐mxnet⼩很多(⼤概是maxnet计算得到的1/batch_size这个量级),所以反向传播得到的梯度也⼩很多,所以为了得到差不多的学习效果,我们把学习率调得成原书的约batch_size倍,原书的学习率为0.5,这⾥设置成100.0。(之所以这么⼤,应该是因为d2lzh_pytorch⾥⾯的sgd函数在更新的时候除以了batch_size,其实PyTorch在计算loss的时候已经除过⼀次了,sgd这⾥应该不⽤除了)
# 6 训练模型
num_epochs, lr = 5, 100.0
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params, lr)
这一步稍稍费时一点,花了大概1min左右。
运行结果:
epoch 1, loss 0.0031, train acc 0.713, test acc 0.815
epoch 2, loss 0.0019, train acc 0.826, test acc 0.817
epoch 3, loss 0.0017, train acc 0.843, test acc 0.754
epoch 4, loss 0.0015, train acc 0.857, test acc 0.799
epoch 5, loss 0.0014, train acc 0.866, test acc 0.860
从上述结果中,即可看出不同损失函数下的准确率的变化,随着训练周期越长,损失函数值减小、准确率更高。
对第6步训练模型稍作修改,输出运行时间。修改后的代码:
# 6 训练模型
import time
num_epochs, lr = 5, 100.0
start = time.time()
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params, lr)
print(time.time() - start)
第二次运行训练模型训练结果:
epoch 1, loss 0.0014, train acc 0.870, test acc 0.860
epoch 2, loss 0.0013, train acc 0.876, test acc 0.833
epoch 3, loss 0.0013, train acc 0.880, test acc 0.841
epoch 4, loss 0.0012, train acc 0.885, test acc 0.844
epoch 5, loss 0.0012, train acc 0.887, test acc 0.841
132.66817021369934
第三次运行结果
epoch 1, loss 0.0012, train acc 0.890, test acc 0.872
epoch 2, loss 0.0011, train acc 0.894, test acc 0.808
epoch 3, loss 0.0011, train acc 0.894, test acc 0.870
epoch 4, loss 0.0011, train acc 0.897, test acc 0.869
epoch 5, loss 0.0010, train acc 0.900, test acc 0.873
118.6175651550293
我做了一个尝试,将学习周期设为10、20,继续运行:
- 将学习周期设为10,运行结果:
epoch 1, loss 0.0010, train acc 0.901, test acc 0.876
epoch 2, loss 0.0010, train acc 0.904, test acc 0.857
epoch 3, loss 0.0010, train acc 0.906, test acc 0.854
epoch 4, loss 0.0010, train acc 0.907, test acc 0.862
epoch 5, loss 0.0010, train acc 0.909, test acc 0.826
epoch 6, loss 0.0009, train acc 0.911, test acc 0.865
epoch 7, loss 0.0009, train acc 0.912, test acc 0.884
epoch 8, loss 0.0009, train acc 0.914, test acc 0.884
epoch 9, loss 0.0009, train acc 0.916, test acc 0.881
epoch 10, loss 0.0009, train acc 0.916, test acc 0.856
234.6531686782837
- 将学习周期设为20,运行结果:
epoch 1, loss 0.0009, train acc 0.917, test acc 0.871
epoch 2, loss 0.0009, train acc 0.918, test acc 0.875
epoch 3, loss 0.0008, train acc 0.922, test acc 0.867
epoch 4, loss 0.0008, train acc 0.923, test acc 0.873
epoch 5, loss 0.0008, train acc 0.924, test acc 0.885
epoch 6, loss 0.0008, train acc 0.925, test acc 0.886
epoch 7, loss 0.0008, train acc 0.926, test acc 0.885
epoch 8, loss 0.0008, train acc 0.926, test acc 0.883
epoch 9, loss 0.0008, train acc 0.929, test acc 0.893
epoch 10, loss 0.0007, train acc 0.929, test acc 0.872
epoch 11, loss 0.0007, train acc 0.930, test acc 0.871
epoch 12, loss 0.0007, train acc 0.932, test acc 0.882
epoch 13, loss 0.0007, train acc 0.933, test acc 0.887
epoch 14, loss 0.0007, train acc 0.934, test acc 0.867
epoch 15, loss 0.0007, train acc 0.935, test acc 0.885
epoch 16, loss 0.0007, train acc 0.936, test acc 0.885
epoch 17, loss 0.0007, train acc 0.937, test acc 0.880
epoch 18, loss 0.0007, train acc 0.937, test acc 0.854
epoch 19, loss 0.0007, train acc 0.939, test acc 0.893
epoch 20, loss 0.0006, train acc 0.939, test acc 0.886
490.9914619922638
不过,这样训练下去,因为是全连接层,也很可能导致过拟合,关于防止过拟合,在后续的博客中将持续更新。