查看全课大纲(第 78 / 82 节)
- 1.函数与数据泛化
- 2.向量:从几何直觉到高维空间
- 3.矩阵与张量
- 4.特征值与特征向量:矩阵变换的不动方向
- 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
- 6.概率与统计:机器学习的数学基础
- 7.独立性与随机变量:贝叶斯公式与二项分布
- 8.期望、方差与协方差:随机变量的数字画像
- 9.联合概率:当一条轴不够用的时候
- 10.概率分布(一):从伯努利到伽马函数
- 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
- 12.机器学习的模型:找输入与输出的关系
- 13.机器学习的应用
- 14.过拟合
- 15.机器学习的分类
- 16.有监督学习:从房价预测说起
- 17.损失函数
- 18.梯度下降(上):更新规则与学习速率
- 19.梯度下降(2)
- 20.牛顿方法
- 21.KNN简介
- 22.距离测算
- 23.朴素贝叶斯
- 24.朴素贝叶斯的实际应用
- 25.信息熵:决策树构建的度量基础
- 26.信息增益与决策树的构建
- 27.ID3算法
- 28.决策树节点的划分
- 29.CART树
- 30.Bagging
- 31.Boosting:自适应提升算法
- 32.Gradient Boosting
- 33.线性模型:回归与分类的同一副骨架
- 34.最小二乘:把最优参数直接解出来
- 35.最小二乘法的几何与概率视角
- 36.极大似然:给最小二乘一个概率出身
- 37.逻辑回归(1):把线性模型接到分类上
- 38.逻辑回归(2):损失函数与多分类
- 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
- 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
- 41.优化方法(1):坐标下降与前向选择
- 42.优化方法(2):前向策略与最小角回归
- 43.拉格朗日乘子法(1):从无约束到等式约束
- 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
- 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
- 46.最大间隔:把分类问题变成优化问题
- 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
- 48.支持向量:边界上的少数派
- 49.SVM 计算过程:一笔到底算完,再走进核方法
- 50.贝叶斯公式:概率即信念
- 51.隐含变量和概率模型:给看不见的原因建模
- 52.EM 算法:期望与最大化的迭代
- 53.琴生不等式:EM 的数学钥匙
- 54.三个硬币问题:EM 的复盘与衔接
- 55.高斯混合模型:用隐变量解释双峰数据
- 56.隐马尔可夫模型
- 57.隐马尔可夫模型(2):前向算法与动态混合模型
- 58.隐马尔可夫模型在 NLP 中的运用
- 59.EM 算法:把「为什么收敛」推到底
- 60.HMM 在 NLP 中的运用:从分词到机器翻译
- 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
- 62.高维下的高斯模型
- 63.主题模型:从词袋到隐含主题
- 64.Dirichlet 分布(1)
- 65.Dirichlet 分布(2)
- 66.统计学习内容回顾
- 67.K-means 算法
- 68.PCA 降维
- 69.SVD 降维
- 70.从聚类到流形
- 71.局部线性嵌入
- 72.随机邻域嵌入:SNE 与 t-SNE
- 73.无参模型:让数据自己说话
- 74.高斯过程:函数空间里的正态分布
- 75.高斯过程(2):噪声、正则化与核的线性组合
- 76.神经网络简介:从感知器到自编码器
- 77.反向传播:把误差一层层传回去
- 78.卷积神经网络(CNN):让特征自己学出来
- 79.循环神经网络(RNN):给网络装上记忆
- 80.深度学习回顾:静态一类,动态一类
- 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
- 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传
卷积神经网络(CNN):让特征自己学出来
约 25 分钟
小象实战讲义 · 机器学习算法精讲
我们所说的深度学习,其实都是基于已有的神经网络;而深度学习的标志之一,就是 ConvNet(卷积神经网络)——里面是分层的特征架构。本节回答四个问题:为什么网络要「深」而不要「宽」(一个诚实的开放问题 + 视网膜的启发);卷积到底是什么(扔两次小球就懂了);卷积核、局部连接、共享权重、max pooling 这几个零件怎么拼出完整的 CNN(含 32×32×3 的完整数字链);以及 CNN 的本质收尾——特征学习:不知道哪个特征重要,就让网络按目标自己调。
💡 核心导读
- 为什么要深:加宽(每层多放节点)参数也一样多——讲师先定调「很多解释都是因为它效果好了,我才类似的解释」,再给最主流的一条:视网膜就是层状结构、细胞只对小块区域敏感。
- 卷积的直观:扔两次小球,问停在离原点 3 的概率——把所有 的组合概率相加,这就是两个函数的卷积 。
- 两个省钱设计:partial connection(局部连接——5×5 全连接要 25 个权重,3×3 核只要 9 个)+ share weight(一个核扫全场、权重共用)。
- max pooling 与美图相反定律:均值把痘痘磨平(美图干的事),max 把痘痘留到最后——图缩多小,它还是一个黑点。
- 特征学习:不同卷积核关注图像不同侧面——「再造特征」;调整各层权重,实际是在调整各个特征里边的重要性。
为什么要「深」:一个诚实的问题
讲师开场先问了一个特别简单、也特别扎心的问题。神经网络参数特别多、模型能力强,那——为什么参数非要往「加深」的方向加?我不要很多层,把每一层多加一些节点,参数不仍然很多嘛?为什么反过来了:每层节点不要多、但要很多层,模型才具有更好的能力、更好的准确率?
讲师先诚实地定调:这实际上有很多不同的解释,「但这种解释都是因为它效果好了,我才类似的解释」——效果先好、解释后到,学术史的常态。然后给最主流的一条。
视网膜的启发:1981 年的那批工作
这种层状的形式,更类似于我们人的视网膜。1981 年那批诺贝尔奖工作(讲师点名的斯佩里、哈勃等人;课件上写的是 Hubel 与 Wiesel,「因视觉系统信息处理方面的发现」获奖)发现:视网膜神经本身就是一个层状的结构,而且视网膜细胞有的时候只对某些小的区域才产生敏感——这就是 receptive field(感受野)。敏感的程度还不一样:有些细胞对 edge(边)特别敏感,有的对不同东西敏感。不同的敏感性激活不同的细胞,做成层状的结构,我们才去识别这个物体——这是一个重大的发现。
也许我们再造的神经网络需要多层,也是希望这台计算的机器通过多层的建构,找到不同 level 的特征:给定一个图像,最底层都是一些简单的、非常底层的特征;通过一种计算方式把它提升,变成高层的特征,再更高层——一直到最后,通过某些组合或其他计算方式,对这个图像进行分类或处理。这就是我们认为的 ConvNet 思想的本质。
爱因斯坦的马
课件上引了爱因斯坦的一句话,讲师逐句念了、也逐句解读了:
I admire the elegance of your method of computation; it must be nice to ride through these fields upon the horse of true mathematics while the like of us have to make our way laboriously on foot. (我钦佩你计算方法的优雅;骑着「真数学」的骏马驰骋在这些原野上想必很美妙,而我们这样的人只能步履蹒跚地徒步前行。)
会计算的人、有好的计算方法,就像骑着一匹特别漂亮的马——在整个数学的、抽象的空间里游荡,很快找到最好的结果;而我们没有这好工具的,只好用腿慢慢走、一步一步找,追寻真理。
卷积:扔两次小球
卷积(convolution)有各种不同的说法,尤其在信号处理里面有完整的定义——讲师不推那一套,给一个稍稍简单、比较直观的。
想象一个小球,落到某一个点的时候,它会以中心为均值、以某种概率分布停在周围的地方。回过头来,如果它停在了某处,在原地拿起小球再扔一遍,它会以另一个分布(比如这个分布叫 、那个叫 )停在原点附近。我们想问:执行这样两次扔小球,它停在离原点为 3 的距离处的概率有多大?
拆开算:第一种情况,它以「到 2」的概率乘上「到 1」的概率;但 只是凑 3 的一种,、、……所有可能的组合都有份。每个组合都是概率,全部加在一起,就得到两次的结果相加为 的概率。把第二段距离写成 (,把 替换掉),再对 从负无穷到正无穷全部叠加——这就是两个函数 跟 的卷积在 这个点上的值:
从一维延伸到二维、三维完全同理: 变成一个向量, 和 也是向量,、,跟刚才没有任何区别。
卷积核扫图:5×5 变 3×3
回到图像。看一个例子:一个 3×3 的卷积核(讲师口中叫「卷积盒」,kernel),它每一个值都是 0、1 构成的数;给定的原始图片,一个绿色的 5×5 的黑白图像,也是由 0、1 构成。图像卷积执行的就是:把 3×3 的卷积核沿着图、从左上角到右下角扫一遍;每次扫过,核的每个值乘上对应位置绿色图像的值,加出一个和,写到输出上——这就变成了我们最后卷出来的特征。算一下尺寸:5×5 通过 3×3 的卷积核,就变成 3×3 的一个新特征表示——原来的图片变小了之后的一种高层特征。
把三个不同的核扫过同一张图(后两个核——「−1 与 9」「1 与 −7」——正是讲师板书点名的两种卷积核;图是讲义设定的 5×5 黑白小方块):
def convolve2d(img, kernel):
"""二维卷积(无 stride/padding): 核在图上滑动, 每个窗口对应相乘再相加(内积)"""
h, w = len(img), len(img[0])
kh, kw = len(kernel), len(kernel[0])
out = [[0] * (w - kw + 1) for _ in range(h - kh + 1)]
for i in range(h - kh + 1):
for j in range(w - kw + 1):
s = 0
for ki in range(kh):
for kj in range(kw):
s += img[i + ki][j + kj] * kernel[ki][kj]
out[i][j] = s
return out
# 5x5 黑白图(0/1): 中间一个亮方块(讲义设定)
img = [[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0]]
kernels = [
("竖边缘核 [1,0,-1]x3", [[1, 0, -1], [1, 0, -1], [1, 0, -1]]),
("锐化核 -1 与 9", [[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]]),
("纹理核 1 与 -7", [[1, 1, 1], [1, -7, 1], [1, 1, 1]]),
]
for name, k in kernels:
out = convolve2d(img, k)
print(f"{name}:")
for row in out:
print(" ", row)
print()竖边缘核 [1,0,-1]x3:
[-2, 0, 2]
[-3, 0, 3]
[-2, 0, 2]
锐化核 -1 与 9:
[6, 4, 6]
[4, 1, 4]
[6, 4, 6]
纹理核 1 与 -7:
[-4, -2, -4]
[-2, 1, -2]
[-4, -2, -4]同一张图、三个核、三种看法。竖边缘核只看左右差:方块左边界处输出 −3(左列全白、右列全黑)、右边界 +3、中间平地 0——它是一个「左右比较器」。锐化核(−1 与 9)反过来把亮方块的边缘抬高(6、4),均匀的内部压到 1——轮廓清晰。纹理核(1 与 −7)又反着来:均匀的内部是唯一正数(1)、四周全负——它把「这一块长得均不均匀」点了出来。这正是讲师说的:不同的卷积核作为乘积和内积之后,关注了图像的不同层面——通过卷积核,我们「再造」了很多不同的特征。
partial connection:为什么 9 个权重就够
关键来了:卷积核,在神经网络里面就可以理解成是权重——只不过,它只有部分的权重,这就是 partial connection(局部连接)。
算一笔账。5×5 的图片,如果是一个正常的全连接神经网络、隐层有一个节点,权重应该是 25 个;而 3×3 的卷积核只带 9 个权重(讲师口播「三个」,按 3×3 的核应为 9 个——数目以核的大小为准)——只用了一部分。
再用一维的平面例子看清楚。从 到 ,原则上 8 个输入、隐层一个节点的话,应该是 这么多个权重;但实际上我们只用了两个权重: 乘 加 乘 ,输出 ;往后挪一格,、 还是乘同一个 、,输出 ;再往后 、 配 、 得 ……把整个权重矩阵写出来:完整矩阵有 8 列那么大,但只有两列有数,其他全是 0——这就叫 partial connection,只是部分的连接。
好处很直接:减少了计算量,我们不需要算那么多 了。
那信息会不会丢?——为什么必须多层
这是局部连接最要紧的一段推演,讲师替大家问了出来。看这个一维例子: 到 里,其实包含了所有 到 的信息——但是它缺了一个东西:跟普通神经网络比,它缺少了比如 和 之间的组合——现在所有的组合都是「相邻域之间」的, 和 隔了四位,这一层组合不出来。那怎么办?
看连接: 跟 、 有关; 跟 有关。如果再多一层——、 汇集到一个节点上, 汇到另一个节点,这两个节点再相连——那这个新节点就同时包含 ,也就是包含了 和 之间的关联。
所以结论是:用 partial connection 的形式,层数必须增多,才能找到更底层数据之间的关联。以前不做 partial connection、一个节点跟 ……全都相连,信息一次就完全了;现在每一层只拿到部分信息,为了得到全面的信息,只能架构多层来保证信息的完整。这也是「神经网络为什么需要多层」的另一半答案。
share weight:一个核扫全场
刚才卷积核里边的参数,就是 share weight(共享权重):大家共同用这样一个 ——每一次扫描出来的卷积核,都是同样的权重。同一个核,扫到哪里都用同一套数。把这个一维的例子引申到二维、三维的图像上,就是大家看到的卷积层。
max pooling:美图做的恰恰是相反的事
有了卷积,就能搭基础架构了:从最底层的 开始,用卷积提最明显的信息,然后用 max(最大值)保留,之后再进行权重、再提取、再取最大值……这个取最大值的过程,叫 pooling(池化),又叫降采样(subsampling)——它主要的问题就是:找到底层特征里边最重要的信息。
那为什么要取 max,取别的行不行?——不是不行,比如取均值可不可以?可以,但取均值的话,实际上会模糊里边重要的东西:把这个邻域所有的值叠加起来做平均。讲师给了一个谁都用过的例子:美图。你脸上有一个坑、一个包、一个麻子、一个痦子——周围皮肤都是白的,只有这个地方有一个青春痘;做一个均值,大家都变成白的了。所以美图磨皮、让皮肤光滑,主要的操作就是类均值的操作。
但反过来,我们取最大值:给了这一块皮肤,里面最重要的特征就是这个痘痘——所以痘痘得以保留。比如你有一个 600×800 的大图,把它缩成 1/4 的大小(边长减半),脸上的痘痘还在;再缩得更小,人已经变得很小了,但那个痘痘仍然是一个黑点。为什么?因为它取的是这块区域里的最大值——这个痘痘是周围所有脸里边最重要的信息,一定要保留。图像从大变小的过程里,最重要的是把特殊的、不同于别人的信息保留下来——或者是一个痣,或者是一个痘痘,它会一直在。这跟美图恰恰做的是相反的事情。所以一般我们用 max pooling:保留不同层级的最大值信息。
板书上有个现成数例(右上角): 这一块取最大值得 6; 取最大值得 8——最明显的特征得以保留;一张 224×224 的图取一半,出来的是 112×112。把这几件事跑成代码:
# --- GT 板书数例: 右上角 {1,1,5,6} 取最大值得 6; {2,4,7,8} 取 8 ---
feature = [[1, 1, 2, 4],
[5, 6, 7, 8]]
def pool2x2(block, mode):
"""2x2 池化: mode='max' 取最大值, mode='avg' 取均值"""
h, w = len(block), len(block[0])
out = []
for i in range(0, h, 2):
row = []
for j in range(0, w, 2):
win = [block[i][j], block[i][j + 1], block[i + 1][j], block[i + 1][j + 1]]
row.append(max(win) if mode == "max" else sum(win) / 4)
out.append(row)
return out
print("输入 2x4:", feature)
print("max 池化 ->", pool2x2(feature, "max"))
print("avg 池化 ->", pool2x2(feature, "avg"))
print("224x224 取一半(2x2 池化边长减半): 224 ->", 224 // 2, "-> 112x112")
print()
# --- 美图相反定律: 皮肤全白(0)、一个痘痘(9), 两轮 max 池化后痘痘还在 ---
face = [[0, 0, 0, 0],
[0, 9, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
p1 = pool2x2(face, "max")
p2 = pool2x2(p1, "max")
print("4x4 皮肤(一个痘痘=9):")
print(" 第 1 轮 max 池化 ->", p1)
print(" 第 2 轮 max 池化 ->", p2, " 痘痘还在")
a1 = pool2x2(face, "avg")
a2 = pool2x2(a1, "avg")
print(" 对照: 第 2 轮 avg 池化 ->", a2, " 痘痘被抹平(美图干的活)")输入 2x4: [[1, 1, 2, 4], [5, 6, 7, 8]]
max 池化 -> [[6, 8]]
avg 池化 -> [[3.25, 5.25]]
224x224 取一半(2x2 池化边长减半): 224 -> 112 -> 112x112
4x4 皮肤(一个痘痘=9):
第 1 轮 max 池化 -> [[9, 0], [0, 0]]
第 2 轮 max 池化 -> [[9]] 痘痘还在
对照: 第 2 轮 avg 池化 -> [[0.5625]] 痘痘被抹平(美图干的活)三行对照就是本节的「美图定律」。同一块 :max 留下 6(那个大数是这块的特征),均值只剩 3.25(被三个小数摊平了)。再看痘痘实验:max 两轮之后图缩到只剩一个数 9——痘痘从头活到尾,「这块皮肤里最重要的信息一定保留」逐轮兑现;而均值两轮之后只剩 0.5625——,痘痘的能量被摊到了每一块皮肤上,这就是磨皮。
完整的 CNN:从 32×32×3 到全连接
搭齐零件,就有了完整的 full CNN(Convolutional Neural Networks)——最早由 1998 年 LeCun(ASR 里的「乐坤」)他们提出来。这里还有个故事:98 年的时候,大家觉得神经网络已经不好用了,所以这个东西提出来以后没有得到很多关注,甚至还被人拒稿——这几个人也比较生气。讲师的感慨:学术界有的时候也是很盲从的,不知道什么该做、什么好坏。这是最早出现的卷积神经网络。
从零到一:图像怎么变成「一点」
现在从零到一,看卷积到底怎么做。一个大向量,比如 ( 的图像拉直),乘上一个 ,比如 的——一乘之后,就把 3072 维压缩成 10 维,每一个最后得到的结果就是一个数。这就是我们怎么把图像变成「一点点」的:叫卷积也好、叫内积也好,都可以。
一层卷积:5×5×3 的核扫过 32×32×3
给定一个 的图像(三个通道的彩色图),配一个 5×5×3 的卷积核。怎么卷?把核「压」进图像的每个位置:每个点互相对应相乘、最后再相加——因为区域是 5×5 的、厚度是 3,它们乘完做内积,得到的是一个数( 的那个数)。这一个数,是核在这个位置上的「响应」。
把 5×5 的核在 32×32 的整个区域里扫过一遍:,所以变成 28×28×1——这是一组数、一个 feature map。换一个卷积核(另外一组不同的值),又卷出新的一个 feature map;用 6 个不同的核,原来 32×32×3 的图像就变成了 28×28×6——被卷积了一层特征之后的图像。
再卷一层:厚度要对上
在 28×28×6 上继续:再取一个卷积核——讲师先说「5×5×3」马上纠正——5×5×6(厚度必须跟输入的 6 对上),用 10 个这样的核:,就变成了 24×24×10。这样一层一层不断深化地卷下去。
但你会发现一件有意思的事:每卷一层,特征变化得很明显(不同的高级特征),可图像的 size 还是比较大。为了方便计算,把主要特征保留就可以了——这就回到刚才的 pooling。
stride 与 padding:两个具体技巧
先看卷积本身还有两个可调的地方。7×7 的图被 3×3 卷,默认 stride(步长) 是 1——每一步只挪一个位置,出来 5×5;如果每一步挪两个位置(stride=2),7×7 就变成 3×3 的输出。还有一种方式是为了保持大小统一:加 padding(填充)——在周围加一圈 0,本来 7×7 变成 9×9,再被 3×3 卷出来还是 7×7。这些都是满足不同需求的实际技巧。把整条尺寸链跑一遍:
# --- 尺寸链: GT 的 7x7/3x3 与 32x32x3 主链 ---
def out_size(n, k, stride=1, padding=0):
return (n + 2 * padding - k) // stride + 1
print("7x7 图, 3x3 核:")
print(f" stride=1, padding=0 -> {out_size(7, 3)}x{out_size(7, 3)}")
print(f" stride=2, padding=0 -> {out_size(7, 3, stride=2)}x{out_size(7, 3, stride=2)}")
print(f" stride=1, padding=1(周围加一圈 0) -> {out_size(7, 3, padding=1)}x{out_size(7, 3, padding=1)}")
print()
print("32x32x3 主链:")
print(f" 32*32*3 = {32 * 32 * 3} 维(全连接对照: 1x3072 的向量 x W(10x3072) -> 10 维, 内积)")
print(f" 5x5x3 核扫过 32x32: 32-5+1 = {32 - 5 + 1} -> 28x28x1")
print(f" 换 6 个核 -> 28x28x6")
print(f" 再用 5x5x6 核(10 个): 28-5+1 = {28 - 5 + 1} -> 24x24x10")
print()
print("这一层的权重数: 6 个 5x5x3 的核 =", 6 * 5 * 5 * 3, "个(共享)")7x7 图, 3x3 核:
stride=1, padding=0 -> 5x5
stride=2, padding=0 -> 3x3
stride=1, padding=1(周围加一圈 0) -> 7x7
32x32x3 主链:
32*32*3 = 3072 维(全连接对照: 1x3072 的向量 x W(10x3072) -> 10 维, 内积)
5x5x3 核扫过 32x32: 32-5+1 = 28 -> 28x28x1
换 6 个核 -> 28x28x6
再用 5x5x6 核(10 个): 28-5+1 = 24 -> 24x24x10
这一层的权重数: 6 个 5x5x3 的核 = 450 个(共享)值得盯住最后一行:6 个 5×5×3 的核,一共只有 450 个权重——而且是 28×28×1 整张 feature map 共享的。要是按全连接来算,一个接住整个 3072 维输入的节点就要 3072 个权重——这就是「部分连接 + 共享权重」省出来的钱。
最后一步:越来越厚的小 patch,接一个全连接
之后的事情就是不断地「卷积、pooling、卷积、pooling」——用了不同的激活函数(上一节的 sigmoid 等),到厚层的时候会变来越来越厚;但实际上它每一小块都是特别小的 patch。把这些 patch 再通过一个全连接层组合到一起,就变成了对物体、对东西的最后分析。
课件上还留了一个可视化 demo(讲师原话:「这个 demo 我不知道现在还能不能用,大家试一下这个网站」——scs.ryerson.ca/~aharley/vis/conv/):在上面写一个手写的数字,比如写个 3——它告诉你 3 的最底层长什么样;第一层卷积出来的 feature map,通过不同的卷积核卷成各种各样的 3;这个 3 经过 pooling,变成大小是原先 1/4 的那些 3;再用不同的 feature map 重新卷积,变成新的图像;再 pooling、再卷积、再 pooling;最后 pooling 合在一起,直接全连接到背后的一个节点——所谓输出。它甚至可以点开看:第几层、第几个 filter 里边的什么权重。这是把「每层实际上是什么样子」摊开给你看的好例子。
特征学习:CNN 的本质收尾
把本节串起来。不同的卷积核作为乘积和内积,关注了图像的不同的层面——通过这种卷积核,我们把图像不同的侧面「再造」出了很多不同的特征;而这些特征,在某种程度上就作为后来分类、预测、识别的重要特点。
所以深度学习、尤其是 CNN,很多时候在讲它是可以做特征学习(feature learning)的——这是里边最重要的一部分。不只是参数大就好:需要去构造不同的特征,而这种层状的结构,一层一层不断地卷积和迭代,构建了很多不同可能型的特征。之后自动地根据我们的目标调整不同层数的权重——实际上是在调整各个特征里边的「重要性」。给了一个图片,我们提了从底层、中层、中高层各个不同 level 的特征,甚至不知道哪个特征重要——怎么办?主动地让它去学习,根据我们的目标(分类或其他)学到我们的特征。
所以对神经网络来讲,很大的一个层面——尤其对深度神经网络——它是一个特征学习。这个特征学习,是它跟其他机器学习模型比起来非常明显的优势,也使它具有了更好的能力、更好的准确率。
发展史上再一笔(讲师口径):UIUC 和 NEC 合作做了 2010 年那样的网络;2012 年那个大家经常叫 SuperVision 的网络(即后来熟知的 AlexNet 那一队);之后到 googlenet;再后来到 VGG——层数很多(讲师口播 24 层;常见的 VGG-16/VGG-19 是 16/19 层);到微软亚洲研究院(MSRA)做了更复杂的——ResNet;还有 DenseNet、各种不同的卷积网络。形式很多,但都还是以卷积操作和这种层状架构为主的模型。这些都是卷积神经网络的重要部分。下次课讲深度学习里另一个常用的东西——除了 CNN 之外,还有 RNN。
📝 动手练一练
- 手算一个卷积窗口:用正文代码块 1 的图和「竖边缘核」,窗口放在左上角(覆盖前三行、前三列)。(a) 手算这个窗口的输出;(b) 输出矩阵里 −3 那个位置,对应原图的什么地方?为什么中间那行是 −3、上下两行只有 −2?
👉 点击查看参考答案
(a) 窗口覆盖的九个数是 ,核每行是 :每行贡献「左列减右列」,三行分别是 、、,合计 −2(与代码输出左上角一致)。(b) −3 在输出矩阵的中间行第一列——它的窗口左列全 0、右列全 1,完整地压在亮方块的左边界上;上下两行的窗口因为碰到了图的白边(第一行/最后一行是 0),右列有一个 0,所以只有 −2。「窗口完全落在边界上时响应最强」——这就是边缘检测核的原理。
- 池化与美图:对正文代码块 2 的 4×4「皮肤」(一个痘痘=9,其余全 0),把 max 换成平均池化,做两轮。(a) 两轮平均池化后剩多少?(b) 用一句话说清「磨皮」和「max pooling」分别干了什么。
👉 点击查看参考答案
(a) 第一轮每块取平均:含痘痘的块是 ,其余三块都是 0;第二轮再平均,(代码对照行:[[0.5625]])。(b) 磨皮(均值)把特殊的能量摊到每一块皮肤上,痘痘从 9 被稀释成 0.5625,图「变干净」;max pooling 反过来只留每块里最大的那个,痘痘从头活到尾——图像从大变小,特殊信息不丢。
本章小结
本节五个零件拼一台完整的 CNN。第一,为什么要深:一个诚实的开放问题(加宽参数一样多),最主流的解释来自视网膜——层状结构、感受野、对 edge 敏感的细胞层层激活(1981 年那批诺奖工作);多层就是机器找不同 level 特征的方式。第二,卷积:扔两次小球、所有 的组合概率相加——。第三,卷积核=权重,但只连局部:5×5 全连接 25 个权重、3×3 核 9 个(partial connection),且一个核扫全场(share weight);代价是丢掉了远距离组合——所以必须多层才能把 与 的关联重新接上。第四,max pooling:均值磨皮(美图的活)、max 保痘(缩到多小都是黑点),、、224→112。第五,完整链路:32×32×3 被 5×5×3 核扫成 28×28×6,再被 5×5×6×10 卷成 24×24×10,卷积、pooling 交替,最后小 patch 接全连接——6 个核只有 450 个权重、全场共享。收尾的本质一句话:CNN 是特征学习——不同核「再造」不同侧面的特征,调各层权重就是调各特征的重要性;特征哪个重要不知道,就让网络按目标自己学。
📋 行动清单
- 口述两遍「为什么深」:视网膜层状结构版、partial connection 必须多层版——不看笔记各讲一遍。
- 手推扔小球:在纸上列出 的全部四种组合(0+3、1+2、2+1、3+0),算出 。
- 跑改代码:把代码块 1 的图换成一个「左暗右亮」的图,看竖边缘核的输出符号怎么变;再给纹理核换一组值。
- 玩 demo:打开课件里的可视化 demo,写一个手写数字,点开第几层、第几个 filter,看看「每层长什么样」。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第19章课件:神经网络(BP/CNN/RNN)