本项目旨在进行 resnet50 的算法原理学习和实战。
本项目会从零开始,不调用任何第三方库,手写 resnet50 这一神经网络中的所有算法,并且按照 resnet50 的网络结构搭建起来,最终完成一张图片的推理。
不调用任何第三方库,指的是核心算法和神经网络的搭建不会调用任何第三库。
目前很多教程手搭神经网络,基本都是基于 torch 的 nn 模块,用 nn.conv2d 就完成卷积的计算,但是卷积算法是如何实现的呢?被封装起来了,看不到,不能真正学到里面的实现细节,但是本项目,会把所有核心算法全部用 python 和 c++ 都手写一遍,然后手动搭建网络结构。
这也是进行本项目的初衷。查看从零手写resnet50开始啦。
通过本项目,你可以深入理解 resnet50 中用到的所有算法原型、算法的背景和原理、resent50 的思想、resnet50 的网络结构,并且你可以参考项目中给出的代码,真正运行一个 resnet50 神经网络,完成一张或多张图片的推理。
如果你把项目涉及的链接文章都阅读一遍,相信我,把这个项目写在简历上,关于 resnet50 的问题,你一点不会害怕。
大部分文章都是我自己写的,我基本都是用通俗易懂的语言来解析所有算法。
这个文档中,在适当的位置,我会给出文章链接,在下一节也会给出所有文章链接列表。
在阅读了文章之后,可以跟着项目中的代码进行练习,后面我会抽时间将代码解析写一写。
文章列表中展示的文章链接,皆为我的原创文章。分两个部分:原理解析和项目实战。
原理解析部分,是我对 resnet50 这一神经网络,用通俗易懂的语言,写的算法和原理的拆解,有助于帮助入门的小伙伴快速了解算法。
项目实战部分,是我在对本项目写代码、调试过程中,遇到的一些问题和总结,可以看作是项目完成的过程记录。
- 1 从像素说起
- 2 图像的色彩空间
- 3 初识卷积
- 4 卷积的核心,特征提取
- 5 残差结构
- 6 resent50的网络结构
- 7 激活函数
- 8 池化层
- 9 全连接是什么
- 10 彻底搞懂 softmax
- 11 总结篇:1.8w字解析resnet50的算法原理
- 1 准备从零开始手写 resnet50 了
- 2 权值参数保存
- 3 手写龟速卷积
- 4 利用 torch来debug,识别出了萨摩耶
- 5 我完全手写的算法和网络,识别出了虎猫
- 6 大提速,分分钟识别“十二生肖”
未完待续。
本项目实现的大致思路为:
- 使用 torchvision 从已经预训练好的模型中,将 resnet50 每一层的权值保存到仓库中,权值文件后续参与卷积、全连接、BN层的计算。
-
文章参考:权值参数保存
-
在仓库 model 目录下,运行以下脚本,即可将参数保存到 model/resnet50_weight 中。
python3 resnet50_parser.py
-
在保存完权值后。利用 python / C++ 语言,不借助第三方库,实现 my_conv2d, my_bn, my_relu, my_add, my_pool, my_fc 等核心函数。
-
按照 resent50的网络结构, 将以上算法(也就是每一层),手工搭建起来。
- 模型文件参考 model/resnet50.onnx.png 和 model/resnet50_structure.txt
- 手工搭建 resnet50 的网络结构参考 我手工搭建的模型
- 以上2,3步完成后,意味着模型运行需要的基础算法和参数已经就位,下面读取一张本地图片,进行推理。
- 读取一只猫的图片,参考获取图片
-
读取完图片,就开始推理,参考python/my_infer.py文件。正确推理出来是一只猫,本项目第一阶段(功能)即完成——我完全手写的算法和网络,识别出了虎猫
-
在功能实现完成后,下面开始性能优化:
- 优化 python 版本的算法实现:大提速,分分钟识别“十二生肖”
- 实现 C++ 版本:参考C++ 的实现
本项目实现了两个版本,python 版本和 C++ 版本,两者实现的思路大致相同,主要是为了方便不同技术栈的同学学习和交流。
如果你对 python 熟悉,可以查看根目录下的 python 目录中的文件。
如果你对 C++ 熟悉,可以查看根目录下的 cpp 目录中的文件。
- resnet50 的核心算法和手搭网络是用基础的 python 语言写的,有些十分基础的操作调用 numpy 库。
- 导入图片调用的 pillow 库,导入图片这种逻辑不属于从零手写 resnet50 核心算法的范畴,我也没时间去写类似的逻辑,直接用 pillow 库即可。
- 安装依赖,主要是上面两个库的依赖,在 python 目录下,执行
$ pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
- 推理
- 在 python 目录下,运行以下命令,完成推理。你可以修改 my_infer.py 中的获取图片的逻辑,将图片替换成你自己的图片,看能否正确的识别出来。
$ python3 my_infer.py
- c++ 版本的核心算法在 cpp/resnet.h 中,与之相对应的,cpp/resnet_avx2.h文件是使用 avx2 指令对一些向量乘累加操作做的向量优化,感兴趣的同学可以看看。
- 如果你的电脑不支持 avx2 指令,将 USE_AVX2_INST这个宏置为0即可。
- 如何查看电脑是否支持 avx2 指令?在 linux 环境下运行以下命令,在显示的信息中查看是否有 avx2 指令集即可。
$ cat /proc/cpuinfo
- 编译
- C++版本编译依赖 opencv 库,用来进行图片的导入,功能与 python 版本的 pillow 类似。执行以下命令安装 opencv 库:
$ sudo apt-get install libopencv-dev python3-opencv libopencv-contrib-dev
- cpp 目录下,运行 build.sh 即可完成编译,在当前目录下,生成 a.out 的可执行文件。
$ ./build.sh
- 推理
- 编译完成后,直接运行 a.out 即可完成推理。你可以修改 main.cc 中的 getFileName()函数来自定义需要出的图片。
$ a.out
由于手写的卷积算法,没有用到任何第三方模块加速计算,因此,一层卷积的计算时间较长,需要你能忍耐这个时间,忍耐的办法就是等。
我测试了一下第一层的卷积,大概1min左右计算完成。
如此算下来,整个网络有50个卷积层,还有其他计算,大概1个小时能完成那张猫的推理。
不过这都不是问题,我们在学习整个思路。等真的能花1个小时把猫推理出来之后,下一步的计划就是:怎么加速推理。
争取秒级的推理速度。
conv, bn, relu, add, maxpool, avgpool 的算法都已经开发完成,按照模型结构搭建起来了模型。
推理了一下,耗时40分钟,主要集中在卷积的循环计算上。这个后续优化。
分类结果还需要查找资料。
分类结果已经从网上下载下来了,放在了仓库中:imagenet_classes.txt, 共1000个分类。
另外,自己搭的神经网络推理完,发现识别错误,开始debug。
发现了一个地方:torch 中的模型权值是按照NCHW摆放的,而我手写的算法全部按照NHWC摆放的,因此在dump权值的时候,添加一个 transpose 操作,此bug已修复。
修复完成后,使用 torch 快速搭建了一个 resnet50模型,用用他来推理 pics 目录下的两种图片,均能正确识别出是虎猫和萨摩耶。
而我手写的模型识别不对,说明中间的计算环节有错误。于是开始debug。
逐层将 torch 的计算结果打印出来,和我手写的计算结果做对比,发现 resnet50.conv1, bn1, relu 的结果都能对的上。
第一个layer1,conv1->b1->conv2->bn1->conv3->bn3->relu的结果也能对的上, 其中单独的下采样 conv->bn 的结果也是对的。
但layer1最终的输出和我的layer1最终的输出存在差异,结果对不上,说明在处理残差结构的时候出了问题。
这个问题后面继续查找。先记录一下。
基本逻辑已经调通,可以出 v0.1版本了。
全部使用自己手写的算法和网络,已经try通整个流程,正确的识别出一只猫了。出猫了
备份跑通的v0.1版本,见v0.1 python版本代码备份。
最近没有更新文章,readme 也没更新,但是项目已经有了更大的进展。
- C++版本已经实现完成,并且功能正常,可以正确的推理出猫和萨摩耶了。
- C++版本性能比python版本快很多,目前的最佳性能,推理一张图片耗时 6000ms。主要包括:加载所有层的权值、预处理、推理计算的耗时。
- 目前启用了两种优化方法:
- 向量指令:使用 avx2 指令,替代标量的乘法和加法,主要优化卷积计算,对比 resnet.h和resent_avx2.h就可以看到优化差异。类似与 python 优化中使用 np.dot 优化乘累加,查看 np.dot 优化乘累加。
- -Ofast 优化:在编译脚本中,启用 g++ 的 -Ofast 优化,其优化的性能比 -O3 还要好。
- model 模型目录
- resnet50_weight 保存的权值文件
- requirements.txt 保存权值需要的依赖库
- resnet50.onnx.png resnet50 的可视化网络结构
- resnet50_structure.txt resnet50 的结构
- resnet50_parser.py 解析 resnet50,并且将每一层的权值参数保存下来的脚本
- 保存权值的依赖
- cd 到 model 目录,安装解析模型相关的依赖库。
$ pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
- python 推理依赖
- cd 到 python 目录,安装推理resnet50需要的依赖库,主要是 numpy 还有 Pillow 库,用来导入图片。
$ pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
- 如果你感兴趣,可以联系我,一起维护本仓库。
- 我个人感觉,本仓库的内容,包括原理解析和实战代码训练,对于了解整个 resnet50 是足够了。
- 本项目所有代码和所列的所有的文章,均为我个人原创,未经同意,请勿随意转载至任何平台,更不可用于商业目的,我已委托相关维权人士对我的原创文章和代码进行监督。
- 如果你有其他相关事宜,欢迎和我交流。