MNIST是一个开源手写数字(0-9共10个数字)图片数据集,格式为28×28大小的图片灰度值。有60000个训练数据和10000个测试数据。标记值为图片所写的数字。任务是用计算机程序来读取图片数据灰度值,判断所写的数字。就尝试用各种方法来解决这个问题。
导入需要的库
1 | import torch |
首先下载数据,用torchvision下载。
1 | # 下载并加载数据 |
因为还要使用sklearn里的一些算法,还需要numpy.array的数据,自己写了个转换程序,用的最笨的办法。
1 | # 将DataLoader数据转换为numpy数组 |
然后在主程序里调用。
1 | if __name__ == "__main__": |
这样数据就准备好了。在为sklearn准备的数据里把训练集和验证集又合并了,因为它可能有自己的划分方式。
先试试最简单的算法:随机瞎猜
1 | # 算法1:随机算法 |
结果当然是准确率10%左右了。
1 | 算法1:瞎猜 |
下面尝试机器学习算法。
逻辑回归
参考:
本来逻辑回归只能用于二分类问题,但可以用”one vs rest”方法(即下文模型定义中的”ovr”,将某类与其余类别做为两类,分别进行逻辑回归,取概率最大的分类作为预测结果。
1 | # 算法2:逻辑回归 |
结果:
1 | 算法2:逻辑回归 |
准确率达到91.69%,突破90%了。但时间也用了两分多钟。
朴素贝叶斯算法
参考:
1 | # 算法3:朴素贝叶斯 |
结果
1 | 算法3:朴素贝叶斯 |
准确率55.6%,不过时间只要1.8秒。
支持向量机
参考:
1 | # 算法4:支持向量机 |
结果
1 | 算法4:支持向量机 |
一开始照那篇文章里的用LinearSVC,结果提示有”段错误”,调了半天都不行。于是改用SVC,时间最长,二十多分钟,准确率也最高,近98%。
下面试试knn算法。
算法5:KNN算法
1 | KNN算法预测准确率:0.9688 __main__.KNN_Model的运行时间为 : 1802.849214115995 |
用时半小时,准确率目前第二高,96.8%。
接下来,随机森林。
1 | # 算法6:随机森林 |
结果
1 | 算法6:随机森林算法 |
准确率97%,训练时间两分钟。这个也比较好。
接下来用深度学习。
先用一般的神经网络。
参考:
1 | # 算法7:一般神经网络 |
这里有个坑:batch_size要设置了能整除数据总量,否则最后一个batch的数据量与之前的batch不一样,会报错。我调了好久才发现。
迭代20次,最后结果:
1 | 一般神经网络算法预测准确率:0.9258 |
时间很长,但最后结果还不如随机森林呢。
再来试试其它神经网络模型。
卷积神经网络以前只是知道个名称,没仔细了解过,写详细一点。
先看原理,看这两个视频: 1 2
全连接神经网络的缺点:网络层次越深,计算量越大,多个神经元输出作为下一级神经元输入时,形成多个复杂的嵌套关系。
卷积神经网络包括输入层(input layer),卷积层(convolutional layer),池化层(pooling layer)和输出层(全连接层+softmax layer)。
①总有至少1个卷积层,用以提取特征。
②卷积层级之间的神经元是局部连接和权值共享,这样的设计大大减少了权值的数量,加快了训练。
卷积层是压缩提纯的。卷积核在上一层滑动过程中做卷积运算(卷积核w与其所覆盖的区域的数据进行点积),结果映射到卷积层。
池化层对卷积层输出的特征图进一步特征抽样,池化层主要分为最大池化(max polling)和平均池化(average polling)。即选取池化区域的最大值/平均值作为池化层的输出。
最后输出层用softmax层使得所有输出的概率总和为1。
超参数设置
padding,保持边界信息。
stride步幅,卷积核滑动幅度,默认为1。
下面代码撸起来,参考这个项目:
1 | # 算法8:卷积神经网络 |
迭代20次,运行结果:
1 | ,随机算法,逻辑回归算法,朴素贝叶斯算法,支持向量机算法,随机森林算法,一般神经网络算法,卷积神经网络算法 |
卷积网络模型运行时间最长,准确率也最高。但似乎迭代两次跟迭代20次差别不大?
不管了,先这样吧,准确率也蛮高了。
现在进行运用,用程序识别新的手写输入数据。先造一个数据,自己写吧。写了100个。
这是原图。
这是用手机相机的文档模式拍的黑白图片。
下面就是怎么把图片转换成mnist的28×28灰度值格式的问题了。
先用这个在线工具(http://www.zuohaotu.com/cut-image.aspx)把图像分割成100份,然后人肉删除没分割好的图片,比如只有一半数字,或者两个数字分割到一张图片上的情况。
分割以后是这样的
文件名用“序号_标签”的形式命名,比如09_9.jpg。接下来就将图片转换成28×28的数据。
照这里:
1 | # 将图片文件转换为MNIST数据 |
根据文件名提取标签,原始图片是这样。
转换以后的数据画图是这样
差不多,还是能看出来的。
下面就开始用模型预测了。先选取准确率最高的机器学习模型随机森林(逻辑回归准确率更高,但随机森林也差不多而且要快得多)和准确率最高的深度学习模型卷积网络模型作为实际工作模型。先用mnist数据进行训练并保存模型。
1 | # 训练要用的模型并保存,一般只运行一次 |
基本跟前面一样,多了保持模型的步骤。
接下来就可以用保存的模型进行识别了,折腾了几天,主要是数据形状不对。
1 | # 实际运用模型来识别手写数据 |
运行结果:
1 | 随机森林算法实际识别准确率:0.10227272727272728 |
我晕,跟瞎猜差不多,虽然CNN要好一点。这就是运用机器学习的难点之一:测试时很好,运用时很差。由于在研究时用的是独立的测试数据测试的,所以首先怀疑是数据的问题。先换黑白图片看看。
1 | 随机森林算法实际识别准确率:0.12359550561797752 |
好一点了,尤其CNN模型,准确率提高了近一倍,不过还是很低。
人肉把一些不太好的图片删了,再看看。
1 | 随机森林算法实际识别准确率:0.07692307692307693 |
到33%了!哈哈。看来得重新整个手写数据试试。
又重新写了一份,110个数字,认真了一点,并且手动在电脑上截图,尽量把数字放到中央。
像这样:
数据转换以后画的图是这样:
还是一样的文件命名方法,再试一次。
1 | 随机森林算法实际识别准确率:0.16363636363636364 |
随机森林的准确率依然不高,但是卷积神经网络的准确率提高到90%了!哈哈,看来数据才是最重要的!
输出错误的情况看看:
1 | 实际数字为8,预测值为6 |
都是6跟8,9跟0,4跟8,9跟1混淆了。
那能不能再提高的?或者说让模型对数据不那么挑?看看别人做的吧。
找到一篇:
Yellapragada SS Bharadwaj, Rajaram P, Sriram V.P, et al. Effective Handwritten Digit Recognition using Deep Convolution Neural Network. International Journal of Advanced Trends in Computer Science and Engineering, Volume 9 No.2, March -April 2020. https://doi.org/10.30534/ijatcse/2020/66922020
作者声称模型对MNIST训练集的错误率达到低于0.1%,对真实手写数字的识别准确率达到98.5%。这就是我想要的,还有代码实现,可是用的是tensorflow,我没用过,尝试用pytorch实现一下看看吧。
论文里给的模型参数:
就按这个移植吧。
迭代次数过多会造成过拟合,因此当准确率达到98%时就停止迭代。
用torchsummary.summary输出了一下模型参数,发现跟论文的好像不对。
尝试了半天,终于改了跟论文上的模型参数一模一样了,再跑了试试。
1 | class improve_conv_net(nn.Module): |
尝试了半天,预测正确率84.5%……还不如我自己的。把学习率改小,到0.0001试试。正确率80%……
把学习率改回0.001,减少迭代次数(通过限定当验证集的准确率大于一定值——如98%——时停止迭代),结果:迭代了3次,预测准确率0.8727272727272727。
把学习率再调高10倍到0.01看看。准确率60%,改回去吧。
论文复现就到这儿吧。看来还是模型过拟合了。这是我第一次复现论文中的算法,也没想象中那么难。不是所有文章都是通篇的公式的。
最后,再来自己探索一下能不能再提高一点吧。主要是解决模型过拟合的问题。找了篇文章:
防止模型过拟合的方法之一是正则化(Regularization),其目的是要同时让经验风险和模型复杂度较小。
正则化的方法之一,是上面的提前结束迭代。下面试试另一个方法:Dropout。它属于模型集成的一种,在训练过程中随机丢弃一部分输入,对应的参数不再更新。
先在最先的位置增加nn.Dropout,概率20%
迭代了六次,识别准确率82.8%。
换到卷积操作之后,迭代四次,识别准确率86.4%。
概率增加到50%看看。准确率还是86.4%,迭代次数减少到4次。
尝试了几次,貌似都没啥用。下面试试参数正则化。
pytorch的optim里实现了L2正则化,先尝试这个,设置weight_decay参数即可。设为0.01,准确率85.5%。
还用改进前的模型跑一下新数据吧。
到这里,所有的改进似乎都失败了,还不如我改进以前的预测准确率高。就先到这吧,本文主要是尝试机器学习的运用过程。首先定义问题,考虑能否使用机器学习模型来解决。尝试各个模型,选择有效的,调参。对实际的新数据进行预测,评估结果。如果不满意,再调参或看看其他人是怎么做的。重复这个过程直到满意。这当中的难点,是对训练和测试数据有效的模型和参数,对实际数据未必有效,甚至效果很差。另外,数据的处理似乎比模型的选择以及调参对预测的结果影响更大。
我发文章的三个地方,欢迎大家在朋友圈等地方分享,欢迎点“在看”。
我的个人博客地址:https://zwdnet.github.io
我的知乎文章地址: https://www.zhihu.com/people/zhao-you-min/posts
我的微信个人订阅号:赵瑜敏的口腔医学学习园地