前言
前面我们了解了,给我们一张图片,想要计算机识别它,需要先处理图片,比如灰度化,二值化,裁剪,去噪等等,那么我们处理完成之后,就要开始思考计算机是如何识别这张图片的。简言之,一张手写数字图片,计算机到底怎么知道它是数字几?相信你也好奇,我们一起来看一下吧。
大脑识别数字的方式
这是一张手写图片5,我们看一眼可以不加思考的说出来是5,但是仔细想一下,大脑做了三件事:你的眼睛捕捉到光线->大脑分析形状->调出记忆'这是5'。尽管这个过程快到感觉不出来,但是它确实发生了。
实际上,我们大脑是这样工作的:
第一层:看到小零件
- 这里有一条横线
- 这里有一条竖线
- 这里有一个拐角
第二层:把小零件拼成部件
- 横线 + 竖线 = 一个直角
- 弧线 + 弧线 = 一个圆圈
第三层: 把部件组合成数字
- 顶部一个弧 + 中间一个弧 + 底部一个弧 = 这是8
- 顶部横线 + 中间横线 + 底部横线 + 连接它们的右边竖线和左边竖线 = 这是5
最后得出结论,这是数字5。
计算机识别数字的方式
计算机和人不同,他没有眼睛和脑子,前面我们讲过,计算机看到的数字是一个像素矩阵
在这里,我们1代表有笔迹,0代表空白,电脑看到的就是这样的像素矩阵,他也不知道什么是横线,什么是竖线,什么是拐角。那么计算机怎么从这样的一个像素矩阵得出来这个数字是5呢?
这里就需要用到卷积神经网络,也就是CNN,用它来模拟人脑的分层机制。
CNN在这里可以简单的理解为3层(实际上可以更多,这里以三层为例)
第一层:识别小零件
第二层:把小零件组合成部件
第三层:把部件组合成完整的数字
生动的例子
想象在一个小房间,墙上贴着一张数字5的图片(28x28),房间里站着32个人,每个人手里拿着一个小零件探测器(3x3),1号探测横线,2号探测竖线,3号探测左斜线等等,每个人都不同,这些人开始工作了。
1号(横线探测器) 拿着自己的探测器,在图片上从上到下、从左到右扫。每照到一个位置,探测器就会显示一个数字:
- 照到“5”的顶部横线 → 显示 0.9(很亮,因为很匹配)
- 照到“5”的中间横线 → 显示 0.8(也很亮)
- 照到“5”的底部横线 → 显示 0.9(很亮)
- 照到空白区域 → 显示 0.0(不亮)
- 照到竖线区域 → 显示 0.1(几乎不亮,因为不匹配)
1号的任务结束后,他交出了一张“横线热度地图”:整张图片上,哪里横线强、哪里横线弱,标得清清楚楚。2号(竖线探测器) 也干同样的事。他照到“5”的左边竖线 → 显示0.7;照到右边竖线 → 显示0.6;照到横线区域 → 几乎不亮。最后,2号交出一张“竖线热度地图”。
3号、4号、5号……一直到32号,每个人交出一张自己的“热度地图”。
一个小时后,墙上不再是一张图片,而是32张热度地图,每张都标注了"某一小零件在图片各个位置的强度"。第一批32个人干完活后,共有32张热度地图,每个都是26x26的,这时候来了一个偷懒的助手,“这些地图太大了,格子数太多了,我懒得看,需要精简一下”
如何精简呢?
以2*2小块为例子,我原本是,但是现在助手看一眼说,最亮的是9,那现在我就要这个9,其他的都舍弃,那么原先26x26的图片,就会变成13x13,格子数直接变成原来的1/4。32张地图都这么处理。
第一批人下班了,第二批人来了,共64个,他们看的不是原始图片,而是第一批人画的32张热度地图,而他们的探测器更复杂一下,不再是简单的横线探测器,而是一个组合,比如1号是左边有竖线 + 底部有横线的└, 2号就是右边有竖线 + 顶部有横线的 ┐......
这批人干完活,有交出64张热度地图,每个都是11x11的,每张对应一种零件组合。
这个偷懒助手还是会再次偷懒,再次去除,最后得到5x5的。
第二批人下班了,再来第三批人,这时候有128个人,他们看的是第二批的64张热度地图。他们的探测器更高级,可以探索复杂的局部特征,这些特征已经非常接近完整的数字部件,比如1号是“0”的大圆圈特征, 2号是“5”的左上角特征,3号是“4”的旗子形特征,4号是“5”的左上角特征 ......
每人拿着自己的复杂特征探测器,在5×5的64张热度地图上滑动。每照到一个位置,探测器计算“这个位置有没有我要找的复杂模式”,输出一个数字(越亮越符合)。
注意:5×5的地图已经很小了,滑动几次就覆盖完了。最终每个探测器会输出一张更小的热度地图,再经过最后一次池化,变成1个数字。也就是说,第三批的128个人,每人最终只产出1个数字,代表“整张图片里,我要找的那种复杂特征的强度”。
1号是0.1 ,2号是0.9,3号是0.1,4号是0.6......
最后再进来一批人,10个投票者,每人负责一个数字(0-9),他们不看图片,不看热度地图,而是看第三批的128的数字。5号投票者拿到这128个数字,发现2号权重是0.9,4号是0.6 ...... 4号投票者也拿到这128个数字,发现1号是0.1.....然后计算出得分,发现5的最高,那么得出结论这是5。
卷积层
在第一层,CNN准备了32个"探测器",每个探测器专门负责找一种小零件:1号 -> 找横线, 2号 -> 找竖线, 3号 -> 找左斜线, ......
怎么找?
简单来说,可以将探测器想象成3*3的小方块,里面填充了一组数字,这个数字决定了这个探测器是什么样子的。
比如横线探测器长这样:把它盖在图片上,如果上面一排是1会加分,如果下面一排是1会减分,而如果是中间一排则不管,就是我期待第一排是横线,第三排是空白的,第二排不管。
卷积计算
从左上角开始,盖住行1-3,列1-3,这部分全是0,则计算后的结果是0。我们直接滑到有数字的位置,进行计算 1 x 1 + 1 x 1 + 0 + 1 x (-1) = 1,滑动到
进行计算: 1 x 1 + 1 x 1 + 1 x 1 + 0 + 1 x (-1) + 0 = 2, 如果滑动到这里
,此时计算结果是-3, 但是我们不取-3,ReLU会把负数变成0,所以最后输出=0。
ReLU = Rectified Linear Unit(线性修正单元),把负数清零,正数保留。让模型只关注"有特征"的地方,忽略"反特征"的地方。ReLU将负数变为0,正数保持不变。
我们可以看出,经过滑动之后,原本10x10的像素矩阵变成了8x8的特征图:
这张图说明了:数字大的地方 = 清晰的边缘,数字小的地方 = 微弱的边缘, 0的地方 = 没有这种边缘。
一共32个不同的卷积核进行滑动,得到不同的特征图。
池化
32张热度图,每张都挺大的。为了减少计算量,CNN做了一个操作:池化。
池化很简单。以2×2的池化为例:把热度图分成2×2的小块,每个小块只保留最大的那个数字,其他三个扔掉。
原来8×8的图(64个数字)会变成4×4的图(32个数字),数据量减少75%。
为什么要池化?两个原因:1.计算量变小,跑得快; 2.让模型对“小偏移”不敏感 —— 数字写歪了一点点,不影响判断。
池化之后,32张热度图每张都缩小了一半(比如从8×8变成4×4)。
第二层卷积层
第一层找到的是小零件(横线、竖线)。第二层的任务是把这些小零件组合成更大的部件。
这一层有64个探测器。注意:这层的探测器不再看原始图片,而是看第一层输出的32张热度图。也就是说,第二层的探测器能看到“哪里横线强、哪里竖线弱”这种组合信息。
比如有一个探测器专门找“直角”:它要求:左边有竖线(强度高) + 底部有横线(强度高),两个条件同时满足 → 这个探测器亮起来。
第二层输出64张新的热度图,每张代表一种部件的分布。
再池化一次,图片继续缩小。
此时热度图变成了1x1的,不需要复杂特征探测器了。
第三层卷积层
但是如果是28x28的图片,再经过第二层处理与池化后图片变为变为5x5的图片,可以再次进行第三层处理。
第二层找到的是部件组合(直角、门框形)。第三层的任务是把这些部件组合成更复杂的特征,这些特征已经非常接近完整的数字了。
这一层有128个探测器。注意:这层的探测器不再看原始图片,也不再看第一层的热度图,而是看第二层输出的64张热度图。也就是说,第三层的探测器能看到“哪里直角强、哪里门框形强”这种更高级的组合信息。
比如:
1号探测器:专门找“0”的大圆圈特征 → 它要求:上下左右都有弧线,围成一个封闭的圈
2号探测器:专门找“5”的左上角特征 → 它要求:顶部横线的左端 + 左边竖线的上端,同时出现
......
还有一类探测器专门负责不同写法:
50号探测器:找偏左写的5
51号探测器:找偏右写的5
......
第三层的每个探测器最终只产出1个数字,代表“整张图片里,我要找的那种复杂特征的强度”。
结果示例(对于一张数字5的图片):
- 1号探测器(0的大圆圈)→ 0.1(5没有大圆圈)
- 2号探测器(5的左上角)→ 0.8
- 3号探测器(4的旗子形)→ 0.1
- 4号探测器(8的两个圆圈)→ 0.0
- 5号探测器(7的横线+斜线)→ 0.2
- ......
- 50号探测器(偏左写的5)→ 0.2
- 51号探测器(偏右写的5)→ 0.1
- 52号探测器(笔画粗的5)→ 0.3
- 53号探测器(笔画细的5)→ 0.2
- ......
第三层输出:128个数字。每个数字在0-1之间,代表某种复杂特征的存在强度。
全连接层(Dense)与输出层
第三层结束后,我们有128个特征值。这128个特征值就是这张图片的“特征向量”。
最后一步,我们需要根据这128个特征值判断是数字几。
这一层有10个神经元,每个神经元对应一个数字(0-9)。它们不看原始图片,不看特征图,只看第三层的128个特征值。每个神经元都有自己的权重,对128个特征值进行加权求和,然后通过Softmax函数转换为概率分布,概率最高的即为识别结果。
CNN如何学会这些?
你可能会问:探测器里的数字(比如横线探测器里那组[1,1,1,0,0,0,-1,-1,-1])是谁设计的?第二层、第三层的探测器又是谁设计的?为什么偏偏是这些数字?
答案是:没有人设计。这些数字是训练出来的。
CNN一开始什么都不会。它需要经过一个“学习”过程,才能变成我们前面描述的那个样子。
随机初始化
在训练开始之前,所有探测器(卷积核)里的数字都是随机的。也就是说,第一层的32个探测器并不知道自己要找横线还是竖线,第二层的64个探测器也不知道自己要找什么部件组合。它们只是32个装满随机数的3×3小方块。
可想而知,这个时候CNN的识别结果完全是乱猜的。给它一张5,它可能说这是3;给它一张8,它可能说这是0。
从错误中学习
接下来进入训练环节。我们给CNN看大量图片,每张图片都附带一个“正确答案”标签。
比如给CNN看一张5的图片,同时告诉它:“这张图正确的答案是5。”
CNN先用自己的随机探测器做一次完整的计算(卷积→ReLU→池化→全连接→Softmax),得到一个预测结果,比如“5的概率10%,3的概率80%,其他数字各占一点”。它会猜错。
然后我们计算一个“损失值”——这个值代表预测结果和正确答案之间的差距。猜得越离谱,损失值越大。
有了损失值之后,CNN从最后一层开始,一层一层往前倒推,计算每个探测器里的每个数字对这次错误“贡献”了多少。贡献大的数字需要大调,贡献小的数字需要小调。这个过程叫反向传播。
最后,根据反向传播的结果,微调每个探测器里的数字。调完之后,再用下一张图片重复这个过程。
训练与测试
通常我们会把数据分成两份:
训练集(比如60000张):用来训练模型。CNN通过这些图片学习,每次学完都调整自己的参数。
测试集(比如10000张):模型从未见过的图片。训练完成后,用这些图片来验证模型的真实效果。如果模型在测试集上也能达到很高的准确率,说明它是真的学会了,而不是仅仅背下了训练集。
训练的结果
经过几万次、甚至几十万次这样的调整(术语叫“迭代”),探测器里的数字逐渐从随机变成了有意义的形状。
你会发现:
第一层的某个探测器变成了横线检测器(数字[1,1,1,0,0,0,-1,-1,-1])
第一层的另一个探测器变成了竖线检测器
第二层的某个探测器变成了直角检测器(看到左边有竖线、底部有横线就亮)
第三层的某个探测器变成了“5的整体骨架”检测器
有些探测器变成了人类能理解的有意义形状,有些则变成了人类看不懂的奇怪形状——但CNN觉得好用。这就是它的厉害之处:不需要人教它“长什么样”,它自己从数据里学。
问题
将可能存在的疑惑总结在下面。
问题1:为什么用3×3的卷积核,不用2×2或者5×5?
3×3是目前最常用的选择。2×2太小,看不到足够的上下文信息(比如看不到横线两端的延伸);5×5太大,参数量多(25个参数 vs 9个参数),计算更慢。而且两个3×3卷积层堆叠,感受野相当于一个5×5,但参数量更少(9+9=18 < 25)。所以3×3是效率最高的选择。
问题2:32、64、128这些数字是怎么来的?为什么不能是10个或者1000个?
这些数字是经验值,不是数学推导出来的。32、64、128是经典的倍增模式,因为越往后特征越复杂,需要更多探测器。用10个太少,学不到足够的特征;用1000个太多,计算慢而且容易过拟合。32/64/128是在效果和效率之间的平衡。你可以理解为:32个找基础特征(横竖斜),64个组合成部件,128个识别复杂模式。
问题3:池化一定要用2×2吗?能不能用别的?
可以用别的。3×3池化、4×4池化都可以,但2×2最常用。原因很简单:2×2不丢失太多信息,又能减少75%的数据量。如果池化窗口太大(比如4×4),信息丢失太严重,可能会把重要的特征也扔掉了。
问题4:ReLU把负数变成0,会不会丢掉重要信息?
不会。负数代表“和要找的特征相反”。在手写数字识别中,我们只关心“有没有这个特征”,不关心“有没有相反的特征”。比如横线探测器,我们只想知道“这里有没有横线”,不需要知道“这里有没有反过来的横线”(那种情况在正常图片里不存在)。所以把负数变成0是安全的,反而让下一层的计算更干净。
结语
一开始,计算机看到的只是一堆0和1组成的数字矩阵。它不知道什么是横线,什么是竖线,更不知道什么是数字。
然后,通过卷积层,它学会了用小方块去扫描图片,寻找横线、竖线、斜线这些小零件。通过ReLU,它只保留有用的信息,扔掉负数。通过池化,它把图片缩小,让计算更快,也让模型对数字的偏移不那么敏感。
接着,通过第二层卷积,它把小零件组合成部件——直角、圆圈、门框形。通过第三层卷积,它把部件组合成更复杂的特征——数字的整体骨架、不同写法的模式。
最后,通过全连接层和Softmax,它把所有这些特征汇总起来,投出每一票,选出得分最高的数字。
计算机从头到尾不知道什么叫“5”。它只知道:当一张图片经过这些计算后,5号投票者的分数最高。
这就是CNN的核心思想:层层抽象,逐级组合。从像素到边缘,从边缘到部件,从部件到完整数字。每一层都建立在上一层的基础上,每一层都比上一层看得更“大”、更“抽象”。
以上是我关于CNN最基础知识的理解,如果存在问题,感谢指出!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。