【pytorch+全连接层】mnist分类问题【尽可能的高准确率,99%以上】

46 篇文章 2 订阅
15 篇文章 1 订阅

要求

  1. 使用pytorch,使用全连接层,而不是用卷积层,要求有两层隐含层
  2. 尽可能提高准确率
  3. 本博客要求使用GPU,否则CPU可能需要半天时间才能运行出来结果
  4. 要求下载CSV格式的mnist数据(可以在我上一篇博客里面下载)

写在前面:运用各种方法来尽可能提高准确率,对理解深度学习及其各种技巧有很大好处。

注意:全连接层也是可以将准确率训练到99%以上的,不要以为只有卷积层才可以。毕竟mnist是一个简单的数据集。

在上一篇博客中,我用纯python写了mnist的分类,在小心的调试参数之后,终于能够达到96左右的准确率。
但是速度有点慢,不利于调参,所以改变到这个pytorch和CUDA环境方便调参,也为了巩固自己所学的pytorch知识。
如果没有先自己尝试写反向传播,建议看上一篇博客,会比较清楚的告诉你,怎么去反向传播的。直接进行pytorch编程,会对某些概念不理解。

结果展示,最终结果也可以达到99%以上吧。在这里插入图片描述

这里,我也尝试分析一下参数的影响吧,
条件:要注意这些条件,基于这些条件我们才好分析。如果你理解这些条件的意义最好。
1)使用了sigmoid激活函数,这个激活函数有饱和值,饱和之后梯度就比较小。
2)就只使用全批次梯度下降的方式,不使用momentum等。虽然这个也有助于提高收敛速度。
3)不使用任何正规化方法,如batch normalization,以及L2正则。
评价指标:损失曲线,正确率曲线
影响因素
1)隐含层数量
2)参数的初始化方式
3)输入值的统计值(均值和方差)
4)学习率

我们说说,设置的不合适会导致什么问题。
1)首先我们先说隐含层数量的影响。如果数量太大,我们知道 o u t = s i g m o i d ( w x + b ) out=sigmoid(wx+b) out=sigmoid(wx+b),在这里,wx+b就会比较大,(其他情况不变的情况下),因此就越有可能接近sigmoid函数的饱和值。这样在初期可能导致梯度较小,损失就难以降下去,要花很多时间在初始阶段,特别是学习率也比较小的时候。当然,隐层数量比较小,网络能力较差,这个大家都熟知,我就不分析了。
2)参数的初始化。第一,我们可以选择正态分布或者均匀分布。有两个方式需要注意:大小是否合适,是否是关于0对称的。如果初始化值比较大的话, o u t = s i g m o i d ( w x + b ) out=sigmoid(wx+b) out=sigmoid(wx+b),在这里,wx+b也就会比较大。也就越可能接近sigmoid的饱和区域,导致前期梯度较小。再来说是否关于0对称,或者说是否只有正值。假如使用均匀分布,且所有的值都大于0,你会碰到麻烦的。另外,你要使得参数的变化范围不能太小,假如你所有的参数都等于一个值或许也有很多麻烦。
3)实际上,我们需要联合参数的初始化和隐层数量,比如在同样的参数初始化环境下,不同隐层数量最好保证 w x + b wx+b wx+b的值都不会很大,常见的方法,可以将每一层参数除以每一个隐层的数量。
3)我这里不多解释对输入值需要进行怎样的处理了。
4)学习率设置不好的话,梯度的更新也可能比较大,可能导致刚开始损失不变。太大的话,导致后期一定的震荡。

改进版:增加BatchNorm1d.
结果瞬间好很多,可以看见batch normalization 对于提高训练速度有很大的帮助。
最终这个版本提供下载地址(jupyter版本,使用CUDA):下载地址
在这里插入图片描述

第一步:处理数据

import numpy as np
import csv


train = csv.reader(open('mnist_train.csv', 'r'))
train_content = []
for line in train:
    train_content.append(line)

test = csv.reader(open('mnist_test.csv', 'r'))
test_content = []
for line in test:
    test_content.append(line)
    
    
train_content = np.array(train_content, dtype=np.float32)
test_content = np.array(test_content, dtype=np.float32)

train_label = np.array(train_content[:, 0], dtype=np.int)
train_x = train_content[:,1 :]
test_label = np.array(test_content[:, 0], dtype=np.int)
test_x = test_content[:, 1:]

assert train_x.shape[1] == test_x.shape[1]
print('Number of input is %d' % train_x.shape[1])
num_input = train_x.shape[1]

train_x = (train_x - 255/2) / 255
test_x = (test_x - 255/2) / 255

import torch
train_x = torch.from_numpy(train_x)
train_label = torch.from_numpy(train_label)

第二步:建立模型框架

import torch
import torch.nn as nn
import torch.optim as optim
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'


class nn_XML(nn.Module):
    def __init__(self, num_in=784, num_out=10, hidden=[100, 30]):
        super().__init__()
        self.linear = nn.Sequential(nn.Linear(num_in, hidden[0], bias=True),
                                    nn.Sigmoid(),
                                    nn.Linear(hidden[0], hidden[1], bias=True),
                                    nn.Sigmoid(),
                                    nn.Linear(hidden[1], num_out, bias=True))
        
    def forward(self, input):
        out = self.linear(input)
        return out

def init_weight(m):
    name = m.__class__.__name__
    if name.find('Linear') != -1:
        m.weight.data.normal_(0.01, 0.1) # 0.01这个参数需要注意
        nn.init.constant_(m.bias.data, 0.0)

第三步:进行训练,训练前定义好优化器,损失函数

epoch = 50000
lr = 0.1
device = torch.device("cuda:0" if (torch.cuda.is_available()) else "cpu")

model = nn_XML().to(device)
model.apply(init_weight)
# print(model)
# print(device)

creation = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=lr)

for i in range(epoch):
    model.zero_grad()
    
    train_x = train_x.to(device)
    train_label = train_label.to(device)
    
    output = model(train_x)
    loss = creation(output, train_label) # 顺序不要反了,否则出错
    loss.backward()
    
    loss_ = loss.mean().item()
    
    optimizer.step()
    
    if i % 300 == 0:
        predict = torch.argmax(output, dim=1)
        assert predict.size() == train_label.size()
        sum = 0.0
        for j in range(predict.size()[0]):
            if int(predict[j]) == int(train_label[j]):
                sum += 1
        print('Epoch: {:d},   Loss: {:0.5f},   Acc: {:0.3f}%'.format(i, loss_, sum / len(predict) * 100))
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值