开始前,会这些就够了
- 知道像素可以用数字表示
- 会做乘法与加法,认识 Python 列表
照片里的竖线出现在左边还是右边,都可能值得留意。我们可以拿同一个小窗口到处检查,而不是为每个位置单独发明一套规则。卷积层常用的做法,就包含这种“局部看一块,规则反复用”的想法。
先别想整套识图网络。下面只有一张三行四列的数字网格、一组手写权重和几次乘加。算完它,你就能说清一个输出格子从哪里来。
选一个输出格,展开四项乘加
窗口两行两列,每次移动一格,不补边。下标 (x, y) 从左上角的 (0, 0) 开始。
四个权重按行排列:[[-1, 1], [-1, 1]],始终由六个位置共同使用。
| y / x | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| 0 | 0 | 0选中 | 3选中 | 3 |
| 1 | 0 | 0选中 | 3选中 | 3 |
| 2 | 1 | 1 | 4 | 4 |
两行三列输出:点选一格
输出 (1, 0) 取左上角为 (1, 0) 的四个输入:
- 输入 (1, 0):
0 × (-1) = 0 - 输入 (2, 0):
3 × (1) = 3 - 输入 (1, 1):
0 × (-1) = 0 - 输入 (2, 1):
3 × (1) = 3
四项相加:(0) + (3) + (0) + (3) = 6。
当前权重总和为 0。全部输入加 5,会让每个输出增加 0;原来的四个权重总和是 0,所以那时输出不变。
这是无偏置、单通道的互相关:权重没有翻转,输出也没有经过激活函数。输入都加 5 后可能超过原来的取值范围,这是为了观察算式;没有把它们当作新照片或模型训练结果。
窗口圈出输入,权重决定怎么算
把两行两列的窗口盖在网格上,里面正好有四个数。再给四个位置各放一个权重,也就是参与相乘的系数:左列都是负一,右列都是一。对应位置相乘后加起来,相当于把右列两格的总和减去左列两格的总和。
如果这一小块左暗右亮,结果会是正数;四格一样亮,结果就是零。因此这组特意手写的权重能对某一种左右变化作出反应。它没有理解线条属于窗户还是猫,也不能保证遇到任何边缘都会给出大数。
移动的是窗口,不是换一套规则
窗口向右移一格,取到新的四个输入,但乘数仍是原来那四个。这叫权重共享:同一个输出通道在不同位置重复用同一组权重。我们的窗口能放六处,却只用四个权重;如果六处各自拥有一套四个权重,就要二十四个。这个比较暂时不计偏置。
共享的是计算规则,不是计算结果。各处输入不同,输出自然可能不同。输出网格里的数是计算后的响应,不必仍在原像素的取值范围里,也可能为负数。神经网络训练时通常从数据中学习这些权重;本例直接指定,目的是看清算式,没有展示它们怎样被学出来。
grid = [
[0, 0, 3, 3],
[0, 0, 3, 3],
[1, 1, 4, 4],
]
weights = [[-1, 1], [-1, 1]]
result = []
for y in range(len(grid) - 1):
row = []
for x in range(len(grid[0]) - 1):
value = sum(grid[y + dy][x + dx] * weights[dy][dx]
for dy in range(2) for dx in range(2))
row.append(value)
result.append(row)
for row in result:
print(row)
print('shared weights:', sum(len(row) for row in weights))怎样运行:保存为 window.py,用 Python 3 运行:python window.py。只用内置功能,无需安装深度学习框架。
运行后对照结果
[0, 6, 0]
[0, 6, 0]
shared weights: 4步长和边界决定能放几次
本例每次移动一格,这个移动距离叫步长。窗口必须完整落在输入里面,所以三行输入只能放出两行结果,四列输入只能放出三列结果。输出是两行三列,不是把原图照抄一次。
打开 CNN Explainer,找到 Understanding Hyperparameters。把 Input Size 设为五、Padding 设为零、Kernel Size 设为三,再比较 Stride 为一和二。先在纸上数窗口能停的位置,再看输出网格。Padding 指外围补上的格子;先固定为零,就不会把补边和移动距离两个因素混在一起。
这一小步,离完整识图还有多远?
这里没有偏置、激活函数、多通道或训练。真实网络可能对红绿蓝等多个通道分别乘加,再合成一个输出,并用多组权重得到多张输出网格。先理解一组小窗口,再补这些部分,会比一次背完所有名字更容易。
还要留一个术语边界:我们没有翻转权重,严格的数学名字是互相关;不少深度学习框架把这种层称作卷积层。共用权重也不保证物体随便平移、旋转或缩放后,整套模型都输出相同结果。边界、步长和后续层都会影响结果。
记住这三点
- 一个输出格子来自附近输入与权重的逐项乘加。
- 权重共享让同一规则在多个位置复用,输入不同仍会得到不同结果。
- 窗口大小、步长和填充是不同设置,先一次只改一个。
别把例子用过头
- 本例是单通道、无偏置、步长一且不补边的互相关计算,不能直接当成完整卷积网络。
- 手工选的左右差分窗口只观察特定变化,不是万能边缘检测器或物体识别器。
自己试一小步
把一张图的每个格子都加上同一个数,这组窗口的输出会怎样?
- 复制原网格,给每个输入都加五,先手算其中一个窗口。
- 保持权重不变,运行新网格并逐格比较结果。
- 再改一个权重,重新预测与验证,找出第一次现象成立的条件。
做到这些,就算完成
- 记录输入、权重和输出,而不是只说结果没变或变了。
- 能从四项乘加解释现象,没有把结论推广到任意权重。
- 移动窗口时一直复用同一组权重,没有偷偷为各位置改规则。
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- 动手学深度学习:从全连接层到卷积 (新标签页)↗
支持局部性与不同位置共享计算系数的设计动机。
- 动手学深度学习:图像卷积 (新标签页)↗
支持逐项乘加、无填充输出尺寸,以及互相关与严格卷积的区别。
- PyTorch:Conv2d (新标签页)↗
核对实际框架的互相关定义、步长、填充、通道、权重及偏置。
- CNN Explainer:交互卷积图解 (新标签页)↗
提供窗口尺寸、步长和填充的交互观察入口。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
CNN Explainer:移动小窗口,观察输出格子怎样变化 (新标签页)↗
只做卷积层下的尺寸实验,保持输入 5、窗口 3、填充 0,改变步长并预测输出格子会剩多少。
查看学习准备与访问说明 →