Theano实现kaggle手写识别
前面写了使用theano来实现kaggle中的手写识别,分别用的是逻辑回归和多层感知机,这次加上卷积神经网络,将三者放在一块儿做个总结。下一步的工作是继续系统学习theano及其他深度学习工具,自己去实现更多模型和优化方法。
Logistic Regression
首先是逻辑回归,这里我用了个类似十折交叉验证的方法:将train.csv中的数据分成了十份,最后一份作为模型训练过程中的测试集,每次训练时选取前九份中的一份作为验证集,其余八份作为训练集,这样我们就可以得到同一个模型的九组参数设置。在预测阶段,前面得到的九组参数设置同时参与预测,具体做法是,对于同一组测试数据,我们分别用九组参数设置去预测,得到九个预测值,我们将这九个预测值中出现次数最多的那个label作为改组测试数据的最终预测值。
下面是代码。
|
|
下面是逻辑回归类,这个类来自Deep Learning Tutorials中的Logistic Regression,未作改动。
|
|
然后是模型训练函数,由Deep Learning Tutorials中的Logistic Regression中的模型训练函数改造而来,其中参数多了一个partion,用来指定train.csv中的哪一部分作为此次训练的验证集。
其中参数的选择我们会在实验结论部分给出。
|
|
下面是预测函数,该函数有两个参数,参数data_path表示测试集所在文件,参数has_label表示测试集中是否含有label,有label的话预测函数会计算出错误率,没有label的话函数会将测试结果保存在文件answer.csv中。
|
|
最后是主函数,如下所示,当然我们可以根据自己的实际需要去做调整。
|
|
结论
在参数的选择上,我们做了多组测试(该测试是选择train.csv中的前3/5座训练集,最后1/10做测试集,中间3/10做验证集得到的),最终选定了上面的参数,下面给出了一组对照数据。测试的机器是12年的,CPU为i3-2350M,主频为2。30GHZ,内存为6G,无GPU(机器太渣,没办法)。在写这篇博客时,我又试验了一下bacth_size大于1000的几种情况,等后续试验数据得出后,如果有更小的错误率,我会更新数据。
batch_size | learning_rate | epoches | seconds | epoches/sec | valid_err | test_err |
---|---|---|---|---|---|---|
300 | 0.13 | 126 | 2207 | 0.057 | 9.786 | 9.904 |
500 | 0.13 | 270 | 2736 | 0.097 | 10.064 | 9.700 |
800 | 0.13 | 162 | 1186 | 0.137 | 9.767 | 9.425 |
800 | 0.3 | 394 | 2922 | 0.135 | 9.98 | 9.875 |
800 | 0.03 | 202 | 1369 | 0.147 | 10.083 | 10.075 |
1000 | 0.13 | 201 | 1205 | 0.167 | 9.792 | 9.475 |
1400 | 0.13 | 408 | 2281 | 0.1789 | 9.6111 | 9.500 |
1600 | 0.13 | 334 | 1590 | 0.2101 | 9.089 | 8.4375 |
3200 | 0.13 | 714 | 1974 | 0.3622 | 9.083 | 8.5313 |
4200 | 0.13 | 834 | 2529 | 0.3297 | 9.2540 | 9.0714 |
单单针对MNIST数据集和逻辑回归模型,我们从上面这张表可以得出一些结论:batch_size越大,计算速度越快,精度慢慢有所提高;learning rate大的话epoches就会变大,也就是在整个train集上计算的次数多。
通过实验,我们得到9个模型在train.csv上的单独错误率分别为[8.352380952380953, 7.071428571428571, 7.304761904761905, 6.809523809523809, 7.311904761904762, 7.604761904761904, 7.607142857142857, 7.609523809523809, 7.228571428571429],平均错误率为7.4333333333333336,而综合9个模型去做预测的话在train.csv上的错误率为 7.433333,所以说,上面这种方法的效果不明显。
然后,我们又综合9个模型去预测test.csv中的数据,提交到kaggle上的正确率为0.91429,而9个模型分别单独去预测test.csv中的数据,得到的正确率分别为[0.91057, 0.90543, 0.90986, 0.91371, 0.91214](数据还没有全部拿到,kaggle一天只允许提交5次),可以看到十折交叉验证的方法提高了0.4%的准确率,可能是实验方法不恰当,没有想象的高。
另外之前在老电脑上没有N卡,只能用cpu来跑,跑一个逻辑回归模型大概要20多分钟,现在换了GTX960M,速度明显提升,上面的整个程序跑下来用了700秒,快了十多倍。
MLP
这里不再使用上面的数据读取函数,而是用pandas来读取csv文件,具体函数如下。我使用了train.csv中的前八份来做training set,第九份做validation set,第十份做test set。
|
|
下面定义了一个类,作为多层感知机的隐含层。
|
|
下面是多层感知机的类,具体如下:
|
|
然后是多层感知机的训练函数。
|
|
最后是预测函数 和主函数。
|
|
提交后的正确率为0.97486,如果将整个train.csv作为training set呢,最后的正确率达到0.97843,只是此时模型就过拟合了。
LeNet5
前面我们用逻辑回归和MLP实现了kaggle中的手写识别,下面我们用LeNet来实现之。读取数据的方式和上面一样,但是这里我不知道怎么用保存得到的模型去预测未标记的数据。因为前面的逻辑回归和MLP的模型都只有一层,很容易拿来去进行预测,关于这部分我会在写好预测函数后修改博客,现在先将预测部分写到训练函数的尾部。
LeNet的本质是卷积神经网络(Convolutional Neural Networks),其原理在前面的博客中有提到过,具体可阅读机器学习中使用的神经网络第五讲笔记,也可以阅读Deep Learning Tutorials中的Convolutional Neural Networks (LeNet)。下面给出LeNet的卷积池化层类:
|
|
下面是LeNet的训练函数主体,LeNet模型共4层:layer0层首先将2828的输入降维到(28-5+1)(28-5+1)=2424,这一步就是卷积化,然后再进行22的池化,最后layer0层的输出就变成了1212;layer1层首先将1212的输入降维到(12-5+1)(12-5+1)=88,再进行22的池化,最后layer1层的输出变成了44;layer2层是隐含层,layer3层是逻辑回归层。
|
|
注意,这里我不知道怎么用保存下来的模型去预测未标记的数据,所以这里没有写单独的预测函数,而是把预测的部分放在了训练函数的末尾。
kaggle的手写识别中,其训练数据共42000组,我这里将训练数据按照18:1:1的比例分给了训练集/交叉验证集和测试集。然后将learning rate定为0.03,batch size是20,epoch是80,最后得到的正确率为0.99086,排名是135/1037。当我把42000组数据全部用来训练的话,模型跑到第24个epoch时,错误率就到了0了,也就是已经完全过拟合了,然后用这时候的模型去预测未标记的那28000组数据,提交后正确率为0.99129,排名是118/1037。
下一步的工作是继续系统学习theano及其他深度学习工具,自己去实现更多模型和优化方法。另外,在新电脑上搭建环境用了十多天,反复出错,有时间我会整理一篇博客,方便以后查找。