项目概述
本项目旨在完成对 AI 计算机视觉的入门学习,并在 Intel CPU 上完成以 ResNet50 为基础的神经网络的全部手写实现及性能优化。
本项目旨在通过从零手写 ResNet50 神经网络,深入理解计算机视觉与传统深度学习算法原理。内容涵盖传统图像处理(灰度、滤波、边缘检测)、AI 模型训练推理(MNIST),以及基于 Python 和 C++ 的 ResNet50 实现。重点展示了从基础功能到高性能优化的完整迭代过程,包括 AVX2 指令集加速、内存池管理、JIT 编译及多线程优化,最终将推理延时降低近 60 倍,适合希望深入底层算法与工程落地的开发者学习。

本项目旨在完成对 AI 计算机视觉的入门学习,并在 Intel CPU 上完成以 ResNet50 为基础的神经网络的全部手写实现及性能优化。
首先通过对一些经典的传统计算机视觉算法进行实操,理解计算机视觉的基本含义;随后以 ResNet50 神经网络为例子,系统地讲解一个 AI 模型的基础算法原理和相关背景知识。最后通过代码实战,从零手写 ResNet50 神经网络,完成任意一张图片的识别,以及神经网络模型的性能优化。
包含灰度图处理、RGB 转换、均值/高斯滤波器应用、利用 Canny 算子对图像进行边缘检测、利用大津算法(Otsu)对图像进行分割等小型实操项目。这些是理解数字图像处理基础的关键步骤。
以一个手写数字识别项目(MNIST)作为引子,帮助理解一个 AI 模型的训练和推理过程,建立对神经网络输入输出及损失函数的直观认识。
详细阐述和解析 ResNet50 中用到的算法和背景知识,包括卷积操作、池化层、激活函数、批量归一化(BatchNorm)以及残差结构(Residual Block)的设计思想。
使用 Python 和 C++ 两种语言完成 ResNet50 模型的从零手写。
目前网上有很多教程,在教你搭建神经网络的时候,基本都是基于 PyTorch 的 nn 模块或其他模块,用 nn.conv2d 就完成了卷积计算。对于想深究算法和学习算法的同学,或者一些初学者而言,即使按照教程将神经网络搭建出来了,或将图片推理出来了,依旧是云里雾里,不知其原理,始终浮于表面,心里学的也不踏实。
事实上,nn.conv2d 是将 conv2d 的算法实现给封装起来了,我们看不到它的实现机制,很难学到里面的实现细节,更别提如何在此基础上进行性能优化了(虽然该接口已经被优化过)。
于是便有了这个项目。最初仅仅是想自己动手完成一个简单的 ResNet50 的模型的手写。随后开始系统的写文章,结果越写越多,索性做了一个小册,通过小册的写作,激励我不断的维护和更新这个项目。该项目中的代码从 2023 年 4 月开始编写,陆续调试了很多次,所有代码都是我手动编写而成。目前项目中所有代码已经完全跑通,精度也很 OK,性能经过 5 个版本的迭代,也基本达到了不错的效果。
通过本项目,你可以一窥传统计算机视觉的经典算法,理解传统计算机视觉和基于深度学习的计算机视觉算法的联系和区别,深入理解 ResNet50 中用到的所有算法原型、算法背景原理、ResNet50 的思想、ResNet50 的网络结构、以及常见的神经网络优化方法。
你可以参考项目中的代码,真正运行一个 ResNet50 神经网络,完成一张或多张图片的推理。在项目的 new_version_with_notes 目录下是带有注释的版本,代码中关键的地方我会给出文字详解。如果你把项目代码和配套的文章都阅读一遍,完全实操一遍,我觉得入门 AI 视觉并不是难事,同时关于 ResNet50 这个经典模型,即使你是一个小白,完全实操一遍之后也可以出师了。
0_gray: 为灰度图相关代码1_RGB: 为灰度图与 RGB 转换相关代码2_mean_blur: 为均值滤波相关代码3_gaussian_blur: 为高斯滤波相关代码4_canny: 为 Canny 算法相关,用来完成图片的边缘检测5_dajin: 为大津算法相关,用来完成图片的分割6_mnist: 为一个经典的手写数字识别 AI 模型(神经网络),可以在笔记本(CPU)上进行模型的训练和推理practice: 为以 ResNet50 为基础的模型算法手写、模型搭建和相关的主目录,也是本项目从零手写 ResNet50 的主要目录,这里面又包含了:
model 目录:与开源模型相关的文件,包括模型参数的下载,参数的解析等。pics 目录:使用模型识别一张图片时,存放图片的目录。python 目录:利用 Python 语言手写的 ResNet50 项目。cpp 目录:利用 C++ 语言手写的 ResNet50 项目。其中,Python 目录和 C++ 目录互相独立。在 C++ 目录中,分别存在 1st 到 6th 共 6 个目录,为性能优化的迭代目录,6 个目录相互独立,可以独立运行任意目录中的代码,对比查看在迭代过程中,由于代码的优化带来的性能提升效果。
new_version_with_notes 目录:这是本仓库的一个新版本,包含上述所有代码,里面的目录结构复刻了上述结构。区别在于给代码添加了注释,并且优化了一些细节。建议第一次使用的同学直接使用 new_version_with_notes 目录下的代码。
使用 torchvision 从已经预训练好的模型中,将 ResNet50 每一层的权值保存到仓库中,所保存的权值文件会在后续被加载进来,参与卷积、全连接、BN 层的计算。
这里多说一些,在实际工业项目的模型部署中,神经网络的权值也是作为独立的数据被加载到 GPU/CPU 中完成计算的。而很多实际模型的性能瓶颈会是在权值加载部分。原因分析如下:
在 model 目录下,运行以下脚本,即可将参数保存到 model/resnet50_weight 中:
$ python3 resnet50_parser.py
在保存完权值后,利用 Python / C++ 语言,分别实现 Conv2d, BatchNorm, Relu, AvgPool, MaxPool, FullyConnect (MatMul) 等核心函数。
按照网络结构定义,将以上算法搭起来。手工搭建 ResNet50 的网络结构参考官方文档或相关论文。
代码实现完成后,意味着模型运行需要的基础算法和参数已经就位,下面读取一张本地图片,进行推理。
在基本功能实现完成后,开始着手进行性能优化。性能优化属于神经网络中的一大重点,下面分章节详细说明。
这部分是 Python 版本的性能优化。先看下本仓库如何使用 Python 代码。
$ pip3 install -r requirements.txt
国内用户可使用清华源加速:
$ pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
my_infer.py 中的获取图片的逻辑,将图片替换成你自己的图片,看能否正确的识别出来。
$ python3 my_infer.py
由于 Python 版本也基本没有调用三方库,以 Python 的语法来写卷积循环,其性能绝对差到惨不忍睹,实测发现用 Python 版本推理一张图片十分缓慢,主要是循环太多(但是为了展示算法的内部实现)。利用 np.dot(内积运算)代替卷积的乘累加循环。
这部分是 C++ 版本的性能优化,先看下本仓库如何使用 C++ 代码。
本仓库的 C++ 代码已经合入了几次优化提交,每次都是在前一次优化的基础上做的进一步优化,优化记录可以通过 cpp 目录下的文件名很方便的看出来。
1st 目录下存放的是第一版的 C++ 代码。2nd 目录下存放的是第二版的 C++ 代码,启用了 AVX2 指令集的优化,以及 -Ofast 编译选项。3rd 目录下存放的是第三版的 C++ 代码,利用类似内存池的方式,增加了权值提前加载的逻辑,仍保留了每一层结果输入输出的动态 malloc 过程。4th 目录下存放是第四版优化的 C++ 代码,删除了所有动态内存申请的操作,大幅提高性能。5th 目录下存放是第五版优化的 C++ 代码,利用 CodeGen 和 JIT 编译技术生成核心计算逻辑。6th 目录下存放是第六版优化的 C++ 代码,利用多线程优化卷积的运算,大幅提升性能。每个版本的目录下文件是独立的,不存在依赖,如果你想看两个版本间的代码改动,可以使用源码比较工具来查看。每个版本的目录下文件的编译过程是相同的。
C++ 版本编译依赖 OpenCV 库,用来进行图片的导入,功能与 Python 版本的 Pillow 类似。Linux 环境下,执行以下命令安装 OpenCV 库:
$ sudo apt-get install libopencv-dev python3-opencv libopencv-contrib-dev
cpp 目录下,运行 ./compile.sh 即可完成编译。
$ bash ./compile.sh
编译完成后,在当前目录下,生成名为 resnet 的可执行文件,直接执行该文件,会对仓库中保存的图片进行推理,并显示结果。
$ ./resnet
目录为 1st。
第一版没有考虑性能问题,仅仅是按照想法完成了功能,可想而知性能惨不忍睹,此版本性能数据:
性能数据和电脑性能有关,你可跑下试试,看看打印出来的 Latency 是多少。
目录为 2nd。
第二版在第一版的基础上,将卷积算法中的乘累加的循环运算,利用向量指令集做了并行化加速,采用的向量指令集为 AVX2。你可以通过以下命令查看你的 CPU 是否支持 AVX2 指令集:
$ cat /proc/cpuinfo
在显示的信息中如果存在 avx2 便是支持该指令集。此版本性能数据:
目录为 3rd。
第三版在第二版的基础上,消除了运算推理过程中针对权值参数动态 malloc 的过程,改为在推理之前,利用 std::map 管理一个类内存池的结构,推理之前将所有的权值参数全部加载进来,这一步优化在实际模型部署中是有现实意义的。模型参数的提前加载可以最大限度的减轻系统的 IO 压力,减少时延。此版本性能数据:
目录为 4th。
第四版在第三版的基础上,消除了运算推理过程中所有动态内存申请,以及与字符串相关的操作。此版本性能数据:
目录为 5th。
第五版在第四版的基础上,利用 CodeGen 技术生成核心计算逻辑,利用 JIT 编译完成编译过程。此版本性能数据:
目录为 6th。
第六版在第五版的基础上,利用多线程来优化了卷积计算,对 CO 维度进行了线程间的独立拆分,用满 CPU 线程数。此版本性能数据:
经过 6 个版本的优化,推理延时从 16923 ms 优化至 297 ms,提升了近 60 倍的性能。推理一张图片已经感觉不到卡顿,算是不错的效果。
model 目录,安装解析模型相关的依赖库。
$ pip3 install -r requirements.txt
python 目录,安装推理 ResNet50 需要的依赖库,主要是 NumPy 还有 Pillow 库,用来导入图片。
$ pip3 install -r requirements.txt
通过本项目,学习者不仅能够掌握 ResNet50 的具体实现细节,还能深刻理解深度学习模型在底层硬件上的运行机制。从 Python 的原生循环到 C++ 的 SIMD 指令集优化,再到内存管理和并发处理,这一系列的技术栈覆盖了现代高性能计算的核心要素。
对于希望从事 AI 工程化落地的开发者来说,理解模型推理的每一个环节至关重要。单纯的调用 API 往往无法解决生产环境中的延迟和吞吐问题。本项目的优化路径提供了一个清晰的参考:
建议读者在阅读本文档的同时,务必动手实践代码。尝试修改其中的优化策略,观察性能变化,这将极大地加深你对计算机视觉和系统性能优化的理解。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online