经过上面的处理之后,反正我随便手写了几十个数,因为字不算丑,所以都识别对了,大家也可以试试。草书大师们先往后稍稍,让字好看的人先来
这一部分因为实际上和训练部分非常像,我们就不贴代码了,就在这里简单说一下思路:
定义模型:创建一个LeNet1989的对象
加载模型:将之前保存的模型使用load_state_dict()函数进行加载,这部分不清楚的查一下资料吧
读入数据并进行处理:将数据读进来,并且按照训练的时候的那种格式进行处理,注意一下上面提到的坑
送入模型获得结果:这个就和训练函数里面的with torch.no_grad()后面的部分基本一样
结果反思关于这篇论文的复现基本上就这些东西了,但是里面还是有一些东西有待思考和解决:
权重的初始化方法为什么是这样的,这样做合理吗?(事实上不是很合理,这部分可以参考Xavier初始化以及Hecaiming初始化的论文,因为我还没看,所以不好说什么)
激活函数选取这样的形式的原因是什么?(这部分我记得好像在LeNet-5的论文附录里有,到时候复现那篇文章的时候再细说好了)
输出的place coding没看懂,所以先拿one-hot凑合用的,如果有大佬知道这到底是啥的麻烦评论区指点一下
使用了一阶方法来进行参数更新,这一部分和原论文是不一致的,并且需要引入学习率这一超参数。虽然也训练出一个差不多的模型,损失函数和错误率基本收敛,但是这个收敛到底是因为真的收敛到了局部最优值附近,还是因为学习率有点大导致在一个对称区间反复横跳?(事实上学习率确实是有一点大,我们可以考虑使用那个学习率的调整部分,让学习率在训练后期下降到一个较小值)
每训练一个样本就进行一次参数更新,没有使用mini-batch。事实上mini-batch的思想有一点像参数估计,也就是使用样本均值来估计整体的期望(回忆一下梯度下降的公式形式,就是对所有样本的梯度取均值嘛),然而如果对每一个样本都进行参数更新,就相当于随机抽个样本用来取代期望,怎么想都不太合理嘛,虽然LeNet-5也是这么干的,但是从统计上讲这是不对的吖,这也是后面的大多数论文都使用mini-batch的原因吧,并且这也涉及到batch_size怎么选取的问题,反正挺麻烦的
训练的时候使用的SGD方法,但是实际上这个方法对于损失函数的“鞍点”以及“局部最优值”的表现会比较差,尤其是“鞍点”问题,这部分的原因大家可以去看一下斯坦福的CS231N,讲得还是比较清楚的,解决方案就是使用比如动量、Adam等优化的方法,不过这里因为是复现,所以就先这样用(而且Pytorch里面有现成的包,就把那个optimizer的部分改改就好咯)
关于那个“我就不告诉你”的那个部分没有实现,不过这一部分在后面的LeNet-5里面有说,所以等到那个时候再说好了
整体去看代码的话,其实大佬可能会对这个代码结构嗤之以鼻,因为很多可以复用的地方并没有复用,可以写到一块的地方非要分开,matplotlib画图既没有轴标题也没有图标题,连图例、颜色都没有。怎么说呢,这些东西要是真的写要发表的论文和代码,我肯定不这么写,我也是知道该怎么写的,只是我这个复现系列的博客的目的就是为了让和我一样转专业搞DL的小白萌新伙伴们能够很清晰的看到复现思路与代码结构,至于美观性和性能问题,emmmmm······大家懂了原理之后自己自然知道该怎么搞定咯。
到这里,这篇论文的复现就基本结束了。论文本身写的相当简单,但事实上复现的时候就会发现里面一大堆的坑······不过在复现过程中对每一部分的机理进行一些简单的思考,其实也是能有一些自己的理解与收获的,其实好多发文章的想法(idea)就是在复现论文的时候突发奇想,然后搞搞理论写写模型然后整出来的。我也还就是一个转专业刚刚入行的小白,看着桌子上摆的“待看论文”的小山······emmmmm,其实我内心是崩溃的TAT,总之先慢慢学习吧。那这篇就先这样,我们之后LeNet-5再见吧( ̄▽ ̄)