当前位置: 首页 > news >正文

卷积怎么实现?手写 CNN 才让我真正搞懂 im2col

今天上传了新的章节:卷积神经网络(6)卷积层。这是到目前为止计算编程难度最大的一个章节,值得总结一下。


卷积的概念很好理解:一个小卷积核在图像上滑动,每次对覆盖区域做加权求和。

这个"滑动"用代码怎么实现?

最朴素的写法是几层嵌套循环:遍历每张图,遍历每个输出位置,遍历卷积核的每个元素...

光是看这几层循环,性能就已经没救了。

PyTorch 帮我们把这一切都藏起来了,nn.Conv2d一行搞定。直到开始动手手搓 CNN,才不得不去探究背后的实现技巧。

主流的解决方案就是im2col(图转列)


im2col 的核心思想

im2col 的思路非常直接:把卷积操作变成矩阵乘法

卷积的每一个输出值,都是"输入图像的某个局部区块"与"卷积核"的内积。一张图像有多少个输出位置,就有多少次这样的内积。

im2col 做的事情是:

  1. 把每个输出位置对应的输入区块展平成一行
  2. 所有输出位置的行拼在一起,构成一个大矩阵;
  3. 把卷积核也展平成一个向量;
  4. 整个卷积就变成了一次矩阵乘法

矩阵乘法是 NumPy(以及所有深度学习框架底层)最擅长的操作,速度远超多层循环。


一个具体的例子

假设输入数据是一张1×4×4的图像(通道:1;高:4;宽:4),使用 3 个卷积核是3×2×2(输出通道:3;高:2;宽:2),步长为 1。

输入图像(1×4×4):

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

3 个卷积核(3×2×2):

1 0 | 1 1 | 0 1
0 1 | 0 0 | 0 1

输出尺寸是3×3×3(通道:3;高:3;宽:3)。

卷积核是 3 个输出通道,每次输出 3 个位置,一共需要 9 次加权求和。

im2col 把这 9 次计算对应的输入区块各展平成一行:

位置(0,0) → [1, 2, 5, 6 ]
位置(0,1) → [2, 3, 6, 7 ]
位置(0,2) → [3, 4, 7, 8 ]
位置(1,0) → [5, 6, 9, 10]
位置(1,1) → [6, 7, 10, 11]
位置(1,2) → [7, 8, 11, 12]
位置(2,0) → [9, 10, 13, 14]
位置(2,1) → [10, 11, 14, 15]
位置(2,2) → [11, 12, 15, 16]

拼在一起就是一个 9×4 的矩阵(输出尺寸:9;核尺寸:4)。

卷积核展平成3×4的向量:

[1, 0, 0, 1]
[1, 1, 0, 0]
[0, 1, 0, 1]

做一次矩阵乘法,直接得到 9×3 个输出值,再 reshape 成 3×3×3 就完成了。


关键实现:stride_tricks

im2col 的朴素写法是把那些区块逐个 copy 出来,但这会产生大量内存复制。

NumPy 有一个更巧妙的做法:np.lib.stride_tricks.as_strided

它不复制数据,而是通过调整内存步长(stride)来创建一个新的"视图",让我们可以用不同的下标规则访问同一块内存。


前向传播有了,但训练还需要把梯度传回去。

im2col 的反向就是"col2im":把输出位置的梯度分散回输入的对应区块。

每个输入像素可能被多个输出位置用到(相邻的卷积窗口会重叠),所以要用np.add.at累加而不是赋值。


im2col 本质上是一种以空间换时间的策略:

  • 代价:im2col 矩阵比原始输入大。对于 k×k 的卷积核,矩阵大约膨胀 k² 倍;
  • 收益:所有计算都变成矩阵乘法,NumPy 的向量化能力被充分利用,比嵌套循环快几十倍甚至更多。

📖 完整章节(可运行 Jupyter Notebook):https://n2gpt.github.io/from-neuron-to-gpt/convolution

整个系列从单个神经元出发,纯 NumPy,一路实现到 GPT。欢迎 Star、欢迎提问。

http://www.jsqmd.com/news/493650/

相关文章:

  • 英伟达 NIM API 配置 Claude Code 完整教程(使用 CCR UI)
  • 暗黑破坏神2存档编辑器终极指南:免费开源工具让你的游戏体验更完美
  • 向量数据库存储与检索
  • 01-N8N进阶指南-利用Docker容器化部署与云服务集成实战
  • 微信小程序文件索引化管理与高效检索实践
  • Z-Image-GGUF助力CAD设计:自动生成产品概念草图与渲染图
  • SpringBoot项目在IDEA中无法启动?手把手教你修复启动类识别与依赖问题
  • 揭秘lora-scripts:如何用低资源快速微调,打造你的专属AI助手
  • 语音剪辑神器:Qwen3-ForcedAligner精准定位音频中的每个词语
  • SQL Server容器化实战:用Docker同时运行2017和2008双版本的技巧
  • 腾讯龙虾矩阵落地:企业级AI Agent快速集成最佳实践
  • Windows上安装nvm
  • 爱思唯尔返修提交LaTeX手稿生成PDF乱码问题结果
  • Realistic Vision V5.1显存优化技术解析:CPU offload机制在SD1.5模型中的应用
  • 第7章:Docker network网络管理(docker网络使用与管理)
  • Qwen3-TTS-12Hz-1.7B-VoiceDesign效果展示:中文古诗吟诵+日文俳句朗读风格对比
  • 机器人灵巧手轻量化方案:从PEEK精密注塑到核心部件的降本量产
  • 重磅首发!OpenClaw养虾宝典,189页+9大模块+100多场景:从小白到高手(附pdf完整版)
  • Arduino实战指南 -- AS608光学指纹模块的智能门禁系统搭建
  • 《B4034 [GESP202409 一级] 小杨购物》
  • Phi-3-Mini-128K入门必看:streaming=True对长文本生成体验的提升
  • FastGPT本地AI智能客服:从零搭建到生产环境部署的避坑指南
  • Live Avatar数字人生成保姆级教程:手把手教你制作企业宣传视频
  • 多种灰狼优化算法-无人机集群规划 用法: matlab运行main.m 自带三种UAV_SetUp
  • AudioSeal部署教程:多模型共存场景下AudioSeal模型缓存路径隔离与版本管理
  • EditLite:一款轻量级跨平台文本编辑器,支持算法可视化
  • YOLOv9快速上手:官方镜像实测,从环境配置到模型训练一步到位
  • Draw.io Desktop完整指南:三步打造你的专业图表绘制工作台
  • SiameseUIE效果实测:10轮重复运行结果一致性100%验证
  • Nanbeige 4.1-3B赋能微信小程序开发:后端AI服务快速集成指南