PyTorch, MNIST
ModelTrainResult
本文目标:理解代码,能够复现
更多细节指路⭐️代码参考原博客写得非常详细?
实际上识别手写数字是大二《人工智能》的一个实验,当时用的是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 torchfrom torch import nnfrom torch import optimimport torch.nn.functional as Fimport torchvision# 一般都要分批次进行训练,硬件算力非常有限train_batch_size = 64test_batch_size = 1000# MNIST数据集中图像的尺寸28*28img_size = 28def 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.001optimizer = 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多一点,精度好一点,一般来讲.