qinghuan
← 回到知识探索

AI 直觉 / 动手实验

卷积的小窗口,为什么到处用同一组数?

让一个两格高的小窗口扫过图片,亲手算出局部乘加和权重共享。

展开本文目录
  1. 先动手看看
  2. 窗口圈出输入,权重决定怎么算
  3. 移动的是窗口,不是换一套规则
  4. 步长和边界决定能放几次
  5. 这一小步,离完整识图还有多远?
  6. 记住这三点
  7. 别把例子用过头
  8. 自己试一小步
  9. 查原始资料
  10. 找一份资料继续练
开始前,会这些就够了
  • 知道像素可以用数字表示
  • 会做乘法与加法,认识 Python 列表

照片里的竖线出现在左边还是右边,都可能值得留意。我们可以拿同一个小窗口到处检查,而不是为每个位置单独发明一套规则。卷积层常用的做法,就包含这种“局部看一块,规则反复用”的想法。

先别想整套识图网络。下面只有一张三行四列的数字网格、一组手写权重和几次乘加。算完它,你就能说清一个输出格子从哪里来。

选一个输出格,展开四项乘加

窗口两行两列,每次移动一格,不补边。下标 (x, y) 从左上角的 (0, 0) 开始。

先一次只改一个条件

四个权重按行排列:[[-1, 1], [-1, 1]],始终由六个位置共同使用。

三行四列输入;“选中”标出当前窗口
y / x0123
000选中3选中3
100选中3选中3
21144

两行三列输出:点选一格

输出 (1, 0) 取左上角为 (1, 0) 的四个输入:

  1. 输入 (1, 0):0 × (-1) = 0
  2. 输入 (2, 0):3 × (1) = 3
  3. 输入 (1, 1):0 × (-1) = 0
  4. 输入 (2, 1):3 × (1) = 3

四项相加:(0) + (3) + (0) + (3) = 6。

当前权重总和为 0。全部输入加 5,会让每个输出增加 0;原来的四个权重总和是 0,所以那时输出不变。

这是无偏置、单通道的互相关:权重没有翻转,输出也没有经过激活函数。输入都加 5 后可能超过原来的取值范围,这是为了观察算式;没有把它们当作新照片或模型训练结果。

窗口圈出输入,权重决定怎么算

把两行两列的窗口盖在网格上,里面正好有四个数。再给四个位置各放一个权重,也就是参与相乘的系数:左列都是负一,右列都是一。对应位置相乘后加起来,相当于把右列两格的总和减去左列两格的总和。

如果这一小块左暗右亮,结果会是正数;四格一样亮,结果就是零。因此这组特意手写的权重能对某一种左右变化作出反应。它没有理解线条属于窗户还是猫,也不能保证遇到任何边缘都会给出大数。

移动的是窗口,不是换一套规则

窗口向右移一格,取到新的四个输入,但乘数仍是原来那四个。这叫权重共享:同一个输出通道在不同位置重复用同一组权重。我们的窗口能放六处,却只用四个权重;如果六处各自拥有一套四个权重,就要二十四个。这个比较暂时不计偏置。

共享的是计算规则,不是计算结果。各处输入不同,输出自然可能不同。输出网格里的数是计算后的响应,不必仍在原像素的取值范围里,也可能为负数。神经网络训练时通常从数据中学习这些权重;本例直接指定,目的是看清算式,没有展示它们怎样被学出来。

小实验 · python
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 指外围补上的格子;先固定为零,就不会把补边和移动距离两个因素混在一起。

这一小步,离完整识图还有多远?

这里没有偏置、激活函数、多通道或训练。真实网络可能对红绿蓝等多个通道分别乘加,再合成一个输出,并用多组权重得到多张输出网格。先理解一组小窗口,再补这些部分,会比一次背完所有名字更容易。

还要留一个术语边界:我们没有翻转权重,严格的数学名字是互相关;不少深度学习框架把这种层称作卷积层。共用权重也不保证物体随便平移、旋转或缩放后,整套模型都输出相同结果。边界、步长和后续层都会影响结果。

记住这三点

  • 一个输出格子来自附近输入与权重的逐项乘加。
  • 权重共享让同一规则在多个位置复用,输入不同仍会得到不同结果。
  • 窗口大小、步长和填充是不同设置,先一次只改一个。

别把例子用过头

  • 本例是单通道、无偏置、步长一且不补边的互相关计算,不能直接当成完整卷积网络。
  • 手工选的左右差分窗口只观察特定变化,不是万能边缘检测器或物体识别器。

自己试一小步

把一张图的每个格子都加上同一个数,这组窗口的输出会怎样?

  1. 复制原网格,给每个输入都加五,先手算其中一个窗口。
  2. 保持权重不变,运行新网格并逐格比较结果。
  3. 再改一个权重,重新预测与验证,找出第一次现象成立的条件。

做到这些,就算完成

  • 记录输入、权重和输出,而不是只说结果没变或变了。
  • 能从四项乘加解释现象,没有把结论推广到任意权重。
  • 移动窗口时一直复用同一组权重,没有偷偷为各位置改规则。

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。

找一份资料继续练

先选一项,按下面的范围继续学;不必从头读完整门课程。