跟着上面的博主教程走完,可以看到你的代码成功运行,测试集准确率大概不到50%,进一步需要自己做了。
那个博主训练集只有10000个样本,所以直接去官网下载cifar-10数据集吧,我下载的是matlab版本的数据,文件后缀是.mat,然后torch读取此类型文件应该是要装一个包,就是下面代码中的‘matio’,至于如何安装,链接在此https://github.com/soumith/matio-ffi.torch/blob/master/README.md,感谢这位的教程帮了我忙。下面代码是load数据部分,手段可能很蠢(没啥经验,请原谅),但运行没毛病,这一点放心。
require 'nn'
require 'paths'
require 'optim'
--载入cifar 10,注意labels为0-9,而不是1-10,另外训练50000组,测试10000组
local matio=require'matio'
source1= matio.load('/Users/xuhy/Downloads/cifar-10-batches-mat/data_batch_1.mat')--训练部分1
a1=source1.data:reshape(10000,3,32,32);
b1=source1.labels;
source2= matio.load('/Users/xuhy/Downloads/cifar-10-batches-mat/data_batch_2.mat')--训练部分2
a2=source2.data:reshape(10000,3,32,32);
b2=source2.labels;
source3= matio.load('/Users/xuhy/Downloads/cifar-10-batches-mat/data_batch_3.mat')--训练部分3
a3=source3.data:reshape(10000,3,32,32);
b3=source3.labels;
source4= matio.load('/Users/xuhy/Downloads/cifar-10-batches-mat/data_batch_4.mat')--训练部分4
a4=source4.data:reshape(10000,3,32,32);
b4=source4.labels;
source5= matio.load('/Users/xuhy/Downloads/cifar-10-batches-mat/data_batch_5.mat')--训练部分5
a5=source5.data:reshape(10000,3,32,32);
b5=source5.labels;
--测试部分
source6=matio.load('/Users/xuhy/Downloads/cifar-10-batches-mat/test_batch.mat')--测试部分数据
a6=source6.data:reshape(10000,3,32,32);
b6=source6.labels;
--再次注意labels为0-9,至此数据载入完成
这个地方自己可以输出原始data看看结构,没记错的话原始应该为10000*1024的tensor,这里reshape成了10000*3*32*32,就是RGB三通道。
然后需要将数据进行拼接,因为训练集被分成了5个部分,另外要注意,labels的数据是0~9而不是1~10,可以自己输出测试下,这关系到后面的loss function,所以特意给了自己标注。
自己可以手动输出几组看下数据类型,这过程会熟练对tensor的操作,在上面load和reshape之后,进行拼接,形成trainset和dataset。
train_data=torch.cat(a1,a2,1);
train_data=torch.cat(train_data,a3,1);
train_data=torch.cat(train_data,a4,1);
train_data=torch.cat(train_data,a5,1);--得到train_data,Tensor(50000,3,32,32)
train_labels=torch.cat(b1,b2,1);
train_labels=torch.cat(train_labels,b3,1);
train_labels=torch.cat(train_labels,b4,1);
train_labels=torch.cat(train_labels,b5,1);--得到train_labels(50000,1),取值0~9
test_data=a6;--test_data,Tensor(10000,3,32,32)
test_labels=b6;--test_labels,(10000,1),取值0~9
--构建trainset和testset,暂时没做validationset,后面如果做,训练集用40000个即可
trainset={
size=50000,
data=(train_data:double()):clone(),--没加深拷贝之前,老是在第一个epoch上就内存占满卡死,
label=train_labels:clone()--同上,这个可能和lua的内存调用有关,传递的是引用不是数值,直接clone过来就OK了?
}
testset={
size=10000,
data=(test_data:double()):clone(),--同上,蜜汁尴尬啊,
label=test_labels:clone()--同上
}
这里有两个值得注意的点:(1)使用cat进行拼接,参数取“1”、“2”应该是表示列方向拼接、行方向拼接,总之这里可以自己体会下,将两个10000*3*32*32的tensor拼接为20000*3*32*32的tensor应该怎么做;(2)另外就是这个深拷贝clone的现象,我也觉得很奇怪,因为时而出现内存炸了的情况有时候又没有,这里需要大神指点下了,就是去掉这个clone部分,在第一个epoch就死了,偶尔呢又没事,具体原因我也没摸清楚。至此训练集、测试集准备完成。
一开始使用的网络是最开始提及的教程系列的网络,后来在看到http://www.cnblogs.com/neopenx/p/4480701.html后对将网络简化为3个卷积层和3个池化层,最后展开softmax,去掉了全连接层,而且第一个采用maxpooling,第二个和第三个采用overlapped average pooling,效果可以好到75%准确率测试集。
但是有个问题,就是读者可以自己试下会看到结果对learning rate等参数初始化敏感,而且对输入数据做了标准化处理。
后续加上batch normalization结构后,去掉数据标准化过程(教程中有,自己走一遍教程的应该还记得),会发现结果对初始参数不怎么敏感,而且收敛速度变快了,很快就达到了测试集75%准确率,大概二三十个epoch?有点忘了。大家可以对比下有无BN层的情况。
关于BN层的理解和使用,链接我忘记了,自己百度去吧,如果没记错参数分别为(通道数、初始γ、初始β、true表示带缩放和偏置)?
-- 神经网络结构
net=nn.Sequential()
net:add(nn.SpatialConvolution(3,32,3,3))
net:add(nn.SpatialBatchNormalization(32,1e-5,0.1,true))
net:add(nn.ReLU())
net:add(nn.SpatialMaxPooling(2,2,2,2))
net:add(nn.SpatialConvolution(32,32,4,4))
net:add(nn.SpatialBatchNormalization(32,1e-5,0.1,true))
net:add(nn.ReLU())
net:add(nn.SpatialAveragePooling(2,2,1,1))
net:add(nn.SpatialConvolution(32,64,5,5))
net:add(nn.SpatialBatchNormalization(64,1e-5,0.1,true))
net:add(nn.ReLU())
net:add(nn.SpatialAveragePooling(2,2,1,1))
net:add(nn.View(64*6*6))
net:add(nn.Linear(64*6*6,10))
net:add(nn.LogSoftMax())
criterion=nn.ClassNLLCriterion()
分类问题,这里用NLL作为loss function。
本文开头的推荐教程系列使用了简单的sgd训练,熟悉深度学习的同学都知道,比较常用的是mini batch sgd,这个地方推荐一个链接http://blog.csdn.net/u012749168/article/details/52684794,本人就是在这之上调整了一下的,细心的同学可以发现其中的区别,我认为原来的代码是有一些问题的(?),比如求size这里原来的代码是-t,我这里是-(t-1),剩下的不同点,认真看了的都能发现。
另外这个链接给出了GPU也就是加上cuda的模式,直接跑会出现问题,因为链接中少了一个:cuda,没有对indices进行:cuda处理。
PS:我的和链接中的微微有点不同,但本质上是一致的,另外我这个版本在CPU跑了几次没有问题,之前没有加BN层时在GPU跑了几次,代码也应该没啥问题吧。
sgd_params={
learningRate=1.0,
weightDecay=5e-4,
momentum=0.5
}
x,dl_dx=net:getParameters()
step=function(batch_size)
local current_loss=0
local count=0
local shuffle=torch.randperm(trainset.size)--打乱生成1-50000的随机组数
batch_size=batch_size or 200
for t=1,trainset.size,batch_size do
print(t)
local size=math.min(t+batch_size-1,trainset.size)-(t-1)
local inputs=torch.Tensor(size,3,32,32)
local targets=torch.Tensor(size)
for i=1,size do--生成batchsize样本
local input=trainset.data[shuffle[i+t-1]]
local target=trainset.label[shuffle[i+t-1]]
inputs[i]=input
targets[i]=target+1----------------
end
local feval=function(x_new)
if x ~=x_new then x:copy(x_new) end
dl_dx:zero()--梯度归零
local loss=criterion:forward(net:forward(inputs),targets)--求loss
net:backward(inputs,criterion:backward(net.output,targets))--两次backward,一次求梯度,一次更新权值
return loss,dl_dx
end
_, fs=optim.sgd(feval,x,sgd_params)--fs是一个loss function数值的table
count=count+1
current_loss=current_loss+fs[1]
end
return current_loss/count
end
eval=function(dataset,batch_size)
local count=0
batch_size=batch_size or 200
for i=1,dataset.size,batch_size do
local size=math.min(i+batch_size-1,dataset.size)-(i-1)
local inputs=dataset.data[{{i,i+size-1}}]
local targets=dataset.label[{{i,i+size-1}}]:long():add(1)------------------
local outputs=net:forward(inputs)--用计算好的模型计算输出
local _,indices=torch.max(outputs,2)--?
local guessed_right=indices:eq(targets):sum()
count=count+guessed_right
end
return count/dataset.size
end
max_iters=66
do
for i=1,max_iters do
local loss=step()
print(string.format( 'Epoch:%d Current loss:%4f', i,loss))
local accuracy=eval(trainset)
print(string.format( 'Accuracy on the trainset:%4f',accuracy))
end
end
--测试集数据标准化
testset.data=testset.data:double()
这里解释一下几个函数:(1)step()这个函数,输入是batchsize,默认为200,我在GPU上跑的时候,选择的是128,也就是在下面的循环里是local loss=step(128),step()这个函数就是进行一次全样本也就是1个epoch训练,并返回loss值;(2)eval(dataset)这函数很直观了,评价一个dataset的accuracy。
PS:这里给个建议,代码最好不要复制过去使用,如果像我一样比较小白,一句话一句话自己敲进去,遇到不明白的函数或者表达,百度之然后翻阅下博客论文什么的,消除这个疑问,然后继续,当敲到最后一句代码且没什么疑问的时候,你会发现学到了不少东西,动手动脑思考,这也是给我自己的要求了。
因为后续可能短时间没办法继续更新这部分了,本来后面的打算是有两个,一是对数据进行处理,包括拉伸旋转剪切等扩充数据集看下能把准确率提高多少,二是采用vgg、resnet等各种大佬结构练下。
有机会再说,先这样吧。