本文目标:理解代码,能够复现
更多细节指路⭐️代码参考原博客写得非常详细????实际上识别手写数字是大二《人工智能》的一个实验,当时用的是TensorFlow
对于这个数据集手动扩展训练数据的话,比如平移、旋转一个角度这样.
Neural Networks and Deep Learning.Michael Nielsen非常通俗易懂,以此为例带人入门神经网络.快看完了,真的对新手友好????
Model
# model.py
# 继承于nn.Module这个父类
class MnistNet(nn.Module):
# 初始化网络结构
def __init__(self):
super(MnistNet, self).__init__()
# 全连接
self.fc1 = nn.Linear(28 * 28 * 1, 28)
self.fc2 = nn.Linear(28, 10) # 尺寸,数据集的类别10
# 正向传播过程
def forward(self, x):
# 把三维的张量展平成一维向量
x = x.view(-1, 28 * 28 * 1)
x = self.fc1(x) # [batch_size,28]
x = F.relu(x) # [batch_size,28]
x = self.fc2(x) # [batch_size,10]
# return x
# return F.sigmoid(x)
return F.log_softmax(x, dim=-1) # 对softmax取对数
Train
import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
import torchvision
# 一般都要分批次进行训练,硬件算力非常有限
train_batch_size = 64
test_batch_size = 1000
# MNIST数据集中图像的尺寸28*28
img_size = 28
def get_dataloader(train=True):
assert isinstance(train, bool), "train 必须是bool类型"
# 准备数据集,其中0.1307,0.3081为MNIST数据的均值和标准差,这样操作能够对其进行标准化
# 因为MNIST只有一个通道(黑白图片),所以元组中只有一个值
# 第一次使用要将download设置为True才会自动下载数据集
# torchvision.datasets在先导入pytorch中的数据集
dataset = torchvision.datasets.MNIST('/data', train=train, download=False,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize((0.1307,), (0.3081,)), ]))
# 准备数据迭代器
batch_size = train_batch_size if train else test_batch_size
dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
return dataloader
# model.py
···
# 定义训练的网络模型
mnist_net = MnistNet()
# 优化器adam,学习率设为0.001
optimizer = optim.Adam(mnist_net.parameters(), lr=0.001)
# criterion = nn.NLLLoss()
# criterion = nn.CrossEntropyLoss()
train_loss_list = []
train_count_list = []
def train(epoch):
mode = True
mnist_net.train(mode=mode)
train_dataloader = get_dataloader(train=mode)
# loss_function = nn.CrossEntropyLoss()
print(len(train_dataloader.dataset))
print(len(train_dataloader))
# 遍历训练集
for idx, (data, target) in enumerate(train_dataloader):
# 清除历史梯度
optimizer.zero_grad()
# 正向传播
output = mnist_net(data)
# 计算损失
# loss = loss_function(output, target) #交叉熵损失
loss = F.nll_loss(output, target) # 对数似然损失
loss.backward() # 反向传播
optimizer.step() # 优化器更新参数
if idx % 10 == 0: # print every 10 mini-batches
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch+1, idx * len(data), len(train_dataloader.dataset),
100. * idx / len(train_dataloader), loss.item()))
train_loss_list.append(loss.item())
train_count_list.append(idx * train_batch_size + (epoch - 1) * len(train_dataloader))
# 保存训练得到的参数
# python中有一种存储方式,可以存储为.pkl文件,可以将python项目过程中用到的一些暂时变量、或者需要提取、暂存的字符串、列表、字典等数据保存起来
torch.save(mnist_net.state_dict(), "./mnist_net.pkl")
torch.save(optimizer.state_dict(), './mnist_optimizer.pkl')
# 测试
def test():
test_loss = 0
correct = 0
mnist_net.eval()
test_dataloader = get_dataloader(train=False)
with torch.no_grad(): # 冻结参数:
# 在以下步骤中不用计算每个节点的损失梯度,以防止过度占用内存
for data, target in test_dataloader:
output = mnist_net(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.data.max(1, keepdim=True)[1] # 获取最大值的位置,[batch_size,1]
correct += pred.eq(target.data.view_as(pred)).sum()
test_loss /= len(test_dataloader.dataset)
print('\nloss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
test_loss, correct, len(test_dataloader.dataset),
100. * correct / len(test_dataloader.dataset)))
if __name__ == '__main__':
test()
for i in range(10): # epoch自己根据需要调轮数
train(i)
test()
Result
原代码跑的epoch1的acc=93.26%
其实没训练过的时候,随便也有12%~13%
跑完acc有95.75%
但是《NNDL》上说精度能到99%,所以简单鼓捣一下
1、简单粗暴增加epoch次数
epoch = 10
acc=96.33%
epoch = 30
acc=96.33%
2、损失函数
这里学到的,在pytorch中有两种方式实现交叉熵损失
①loss = nn.CrossEntropyLoss(input,target)
②把softmax概率传入对数似然损失得到的损失函数:
#1. 对输出值计算softmax和取对数
output = F.log_softmax(x,dim=-1)
#2. 使用torch中带权损失
loss = F.nll_loss(output,target)
3、激活函数
把softmax换成sigmoid
不明白
更改超参数得到改进不是很随便的,但是简单任务来看,训练epoch多一点,精度好一点,一般来讲.