要求
- 使用pytorch,使用全连接层,而不是用卷积层,要求有两层隐含层
- 尽可能提高准确率
- 本博客要求使用GPU,否则CPU可能需要半天时间才能运行出来结果
- 要求下载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))