实践教程|用 C++ 从零开始实现CNN
导读
目前搭建卷积神经网络(CNN)一般直接用Pytorch、Tensorflow等深度学习框架,很简单。但如果是手写反向传播过程,情况就比BP网络复杂多了,因为不仅仅是矩阵相乘。本文详解了作者从零开始用c++实现CNN的过程,附详细代码介绍。
目前搭建卷积神经网络(CNN)一般直接用 Pytorch、Tensorflow 等深度学习框架,很简单。但如果是手写反向传播过程,情况就比 BP 网络复杂多了,因为不仅仅是矩阵相乘。
目标是,从零开始实现 CNN。
刚开始,本人搜网上的卷积神经网络反向推导的相关博客,发现了几个问题:
公式看的实在脑袋疼,不好理解,一大堆的 ,变量还特别多,最后自己实现才发现,卷积的时间复杂度还挺高,好几层 for; 卷积层的反向传播,从输出回传的梯度 ,求输入的梯度 存在一个权重矩阵 rot180 的操作,而且还需要对梯度 填充 padding 的问题,见卷积神经网络(CNN)反向传播算法 - 刘建平Pinard - 博客园:https://www.cnblogs.com/pinard/p/6494810.html(刘建平老师的博客强烈推荐),如下
这个例子没有错,但只是一种特殊情况,如果步长 stride 大于 1,就不仅仅是外围填充 0 了,还需要对 数 据之间也做填充 0,具体过程和转置卷积的前向过程一模一样。但即使真的可以这样做,写对了,做 padding 消耗也是很大的,假设 stride = 2,则大约有 的计算都是跟 0 做乘法,是无意义的计算,个人以为不可取,个人改用了其他思路。
后续一步步实现,遇到的也不仅仅这两个问题,一一克服,整个过程的关键就是——硬着头皮,老老实实写,别看公式。
这也是本人一直以来的夙愿,终于实现。整个过程中,又回顾了一些 C++ 的坑与优化技巧,对卷积的前向以及后向过程和如何搭建深度学习流程有了更清晰的认识,收获颇丰。
提纲
卷积神经网络(一)tensor 定义:https://zhuanlan.zhihu.com/p/463673933
卷积神经网络(二)从图像到 tensor:https://zhuanlan.zhihu.com/p/468161119
卷积神经网络(三)ReLU 层:https://zhuanlan.zhihu.com/p/468161821
卷积神经网络(四)池化层:https://zhuanlan.zhihu.com/p/468163843
卷积神经网络(五)卷积层:https://zhuanlan.zhihu.com/p/468164733
卷积神经网络(六)Linear 线性层:https://zhuanlan.zhihu.com/p/468165951
卷积神经网络(七)搭建 CNN 网络结构:https://zhuanlan.zhihu.com/p/469475509
卷积神经网络(八)训练 CNN:https://zhuanlan.zhihu.com/p/468177334
代码
https://github.com/hermosayhl/CNN
环境
Windows 11 >=C++17(TDM GCC 10.3.0:https://jmeubank.github.io/tdm-gcc/download/) OpenCV 4.5.2 构建工具 Cmake
数据集
采用的小型图像分类数据集,从 cat-dog-panda:https://www.kaggle.com/ashishsaxena2209/animal-image-datasetdog-cat-and-panda 数据集剔除 cat(cat 和 dog 相对比较难),然后又从 CUB-200 bird:http://www.vision.caltech.edu/visipedia/CUB-200.html 数据集中随机抽出 1000 张鸟类图像,凑成三分类的小型数据集。train : valid : test 比例 8:1:1。
网络模型
本人也不知道是什么网络结构,随便设计的(能跑就行),只有卷积层、最大池化层、ReLU 层、Softmax 层、Linear 全连接层,比 AlexNet 要简单,接受的输入大小是 224x224x3,输出 3 个值,经过 softmax 得到概率,损失函数是交叉熵,优化方法是 SGD 随机梯度下降,最终在测试集上大概可以达到 0.91 的准确率,不高,但至少跑通了。
后面虽然也写了 BatchNorm 层、DropOut 层,训练是没问题的,这俩的前向和反向传播都对,但 valid 和 test 阶段,过拟合了。。。按照网上诸多说法尝试,但都失败了,遗留问题。
后面还尝试了 Grad-CAM 可视化神经网络,实现跟论文里的细节些许不一样,例子如下,分类为 bird
往期精彩: