本节摘要:卷积网络的两条立身之本是局部连接与权值共享——前者承认"图像的语义先出现在小块区域里",后者承认"检测边缘的规则在全图通用"。本节从视觉神经的实验史讲到参数量账目:同样接一层 4096 个神经元,全连接要六亿多参数,卷积只要一千多,差距三十多万倍。这两条设计解释了 CNN 为什么天然适合图像。
卷积网络的直觉来源不是数学,是手术刀。1950 年代末,休布尔与维泽尔把微电极插进猫的视皮层,发现一个惊人的分工:某些神经元只对视野中一小块区域里的特定朝向线条放电——这块区域后来被命名为感受野,这类细胞被称为简单细胞;另一些细胞的反应对位置不敏感,只要视野里出现那种朝向就放电,被称为复杂细胞。两级串联,恰好构成"先检测局部模式、再容忍位置变化"的层级。这项工作拿下 1981 年诺贝尔生理学奖。
1980 年,福岛邦康照着这套"简单细胞加复杂细胞"的蓝图造出新认知机,第一次把交替堆叠的特征层与池化层写进了网络结构;1989 年前后,杨立昆在贝尔实验室把它落到反向传播上,训练出能读手写邮编的卷积网络,也就是第三章档案柜里 LeNet-5 的前身。生物学给了草图,工程补上了可训练性——这条脉络记住三站就够:猫的视皮层、新认知机、贝尔实验室。
阅读完本节,你应当能够:
全连接层的态度是"每个输入都可能与每个输出有关"。处理一张 224×224 的彩色图时,输入是 224×224×3 = 150,528 个数;若接一层 4096 个神经元,参数量如下:
import torch.nn as nn fc = nn.Linear(224 * 224 * 3, 4096) # 全连接:人人握手 conv = nn.Conv2d(3, 64, kernel_size=3, padding=1) # 卷积:只看邻域,核全图复用 n_fc = sum(p.numel() for p in fc.parameters()) n_conv = sum(p.numel() for p in conv.parameters()) print("全连接层参数量:", n_fc) # 616566784,约 6.17 亿 print("卷积层参数量: ", n_conv) # 1792 print("倍数: ", round(n_fc / n_conv)) # 约 344000 倍
六亿参数,仅一层就吃掉约 2.4 GB 的 float32 存储,还没算训练时的梯度与动量副本;而它换来的表达能力,大量浪费在"左上角的边缘检测器"与"右下角的边缘检测器"其实是两套无关知识这种荒诞假设上。更要命的是统计上的破产:参数越多,需要的样本越多,标注数据永远追不上参数膨胀。
图像有一条与表格数据截然不同的先验:相邻像素强相关,相距远的像素关系弱。判断一小块是不是边缘、是不是猫耳朵的纹路,只需要它周围的邻域,不需要知道图像右下角是什么。局部连接据此把每个神经元的视野限制在比如 3×3 的小窗内,这条先验在机器学习里叫归纳偏置——它不是限制,是省力杠杆:网络不用从数据里重新发现"邻近才相关",可以直接把容量花在更有用的模式上。
邻域之外还有一条隐含福利:堆叠。第一层看 3×3,第二层在第一层的输出上再看 3×3,等效于看到了 5×5 的原始区域;层数越深,单个神经元的"视野"越大,低层拼边缘、高层拼部件的层级结构由此自然生长。感受野如何逐层扩大,第三章讲 VGG 时会给出精确的叠加算法。
局部连接解决了"看哪里",权值共享解决"用几套探测器"。边缘、角点、渐变这些基础模式在图像的任何位置都可能出现,那么检测"左倾 45 度边缘"的那组系数,理应在左上角和右下角用同一套——这正是卷积操作的定义:一组小小的卷积核在整张图上滑动,每个位置做同样的加权求和。
共享带来两份红利。第一份是参数账:上面的对照里,卷积层只需 3×3×3×64 个权重加 64 个偏置,共 1792 个,与六亿形成三十多万倍的差距;参数少了,过拟合风险和显存压力一起下来。第二份是行为性质:同一个核扫遍全图,意味着"平移输入,输出特征图跟着平移",这叫平移等变。注意它不等于不变——特征图上的响应位置确实挪动了,后续要靠池化(2.4 节)把"位置挪了"折叠成"有还是没有"。
import torch import torch.nn.functional as F x = torch.zeros(1, 1, 8, 8) x[0, 0, 2:6, 2] = 1.0 # 一条竖线 k = torch.tensor([[[[-1.0, 0.0, 1.0]]]]) # 水平方向取差的边缘核 y1 = F.conv2d(x, k) y2 = F.conv2d(torch.roll(x, shifts=2, dims=3), k) # 输入整体右移两格 expected = torch.roll(y1, shifts=2, dims=3) print("输出随输入平移而平移:", torch.allclose(y2, expected, atol=1e-6)) # True
第二份红利解释了为什么卷积网络换一张同类的猫图,特征响应依然对得上号:探测规则不锚定在具体坐标上。训练时,梯度会把这套规则在所有位置上共同打磨——一个模式只要在图的任何角落出现过,就能被学会。
💡 关键直觉:把卷积核想成"盖章"。章面(核系数)只有一套,整张图纸到处可盖;训练做的事,就是把这一套套章面刻得越来越有区分力。
至此,为什么是卷积已经有了答案。下一站回答"卷积吃进去的到底是什么":像素与通道如何组装成规范张量,进料手续怎样才算合格。