第二章:深度学习基础回顾:卷积神经网络核心概念
各位同学,欢迎来到第二章。说实话,每次讲到这一章我都挺感慨的。当年我刚接触深度学习时,被各种概念搞得晕头转向。现在回过头来看,其实核心就那么几块砖——卷积、激活、池化、全连接。今天我就带大家把这些砖一块块拆开看明白。
2.1 卷积层:特征提取的“扫描仪”
卷积层是CNN的灵魂。说白了,它就是一个滑动窗口,在输入图像上“扫”一遍,提取局部特征。
卷积核是什么?
卷积核就是一个小的权重矩阵,比如3×3或5×5。它像一个小探照灯,在图像上移动,每次照亮一小块区域,计算这块区域和卷积核的“相似度”。
我举个例子。假设你有一张28×28的手写数字图片,用一个3×3的卷积核去扫。每次卷积核覆盖3×3=9个像素,计算这9个像素和卷积核权重的点积,得到一个数值。然后滑动一个步长,继续算。最终你会得到一张新的“特征图”。
关键参数:
- 卷积核大小(Kernel Size):常用3×3、5×5。我个人习惯用3×3堆叠,效果往往比一个大核更好。
- 步长(Stride):每次滑动的像素数。步长=2时,输出尺寸减半。
- 填充(Padding):在输入边缘补0,保持输出尺寸不变。我常用“same”填充。
- 通道数(Channels):每个卷积核输出一个通道。多个卷积核可以提取多种特征。
# 一个简单的卷积层示例(PyTorch风格)
import torch.nn as nn
conv_layer = nn.Conv2d(
in_channels=3, # 输入通道数(RGB图像)
out_channels=64, # 输出通道数(64个卷积核)
kernel_size=3, # 卷积核大小
stride=1, # 步长
padding=1 # 填充
)
我的经验:在嵌入式部署时,3×3卷积核是最友好的。为什么?因为它计算量适中,而且很多硬件(比如NPU)对3×3做了专门优化。我曾经在项目里用5×5核,结果推理速度慢了30%,换成两层3×3后,效果没降,速度反而上来了。
2.2 激活函数:给网络注入“非线性”
如果没有激活函数,多层卷积就相当于一层卷积——线性变换的叠加还是线性变换。激活函数就是打破这种线性关系的“扳手”。
2.2.1 ReLU:最常用的激活函数
ReLU的公式简单到令人发指:f(x) = max(0, x)。正数保留,负数归零。
为什么它这么流行?两个原因:
- 计算快:就一个比较操作,没有指数运算。在嵌入式设备上,这太重要了。
- 缓解梯度消失:正半轴梯度恒为1,不会像Sigmoid那样梯度越传越小。
注意:ReLU有个问题叫“神经元死亡”。如果某个神经元一直输出负数,它的梯度就是0,再也学不动了。我曾经在一个深层网络里遇到这个问题,后来换成了Leaky ReLU(给负数一个很小的斜率,比如0.01),问题就解决了。
2.2.2 Sigmoid:二分类的“老将”
Sigmoid把任意实数映射到(0,1)区间,公式是 f(x) = 1 / (1 + e^(-x))。
它常用于二分类的输出层,因为输出可以解释为概率。但在隐藏层,我基本不用它。为什么?
- 梯度饱和:当x很大或很小时,梯度接近0,网络几乎学不动。
- 计算量大:涉及指数运算,在嵌入式设备上比较耗时。
# ReLU vs Sigmoid 对比
import torch.nn as nn
relu = nn.ReLU() # 推荐用于隐藏层
sigmoid = nn.Sigmoid() # 仅用于二分类输出层
我的建议:在嵌入式部署中,隐藏层无脑用ReLU。输出层根据任务选:二分类用Sigmoid,多分类用Softmax。别整花里胡哨的,稳定第一。
2.3 池化层:降维与特征压缩
池化层的作用,说白了就是“浓缩精华”。它把一小块区域的信息压缩成一个值,减少数据量,同时保留最重要的特征。
两种主流池化方式:
| 类型 | 操作 | 特点 | 使用场景 |
|---|---|---|---|
| 最大池化(Max Pooling) | 取区域内的最大值 | 保留最显著特征,对纹理敏感 | 图像分类、目标检测 |
| 平均池化(Average Pooling) | 取区域内的平均值 | 保留整体信息,更平滑 | 全局特征聚合 |
举个例子,一个2×2的最大池化,步长为2,会把4个像素中最大的那个保留下来,输出尺寸直接减半。你想想看,一张224×224的图,经过几次池化,就变成几十×几十了,计算量大幅下降。
池化的好处:
- 降低计算量:特征图变小,后续层计算更少。
- 防止过拟合:减少了参数数量。
- 平移不变性:微小的位置变化不影响池化结果。
避坑指南:我曾经在一个项目中,为了追求更小的模型,把池化层加得太多。结果模型精度掉了5个点。后来发现,池化虽然能降维,但也会丢失信息。现在我的习惯是:每2-3个卷积层后加一次池化,不要贪多。
2.4 全连接层:特征整合与决策
全连接层(FC层)是CNN的“大脑”。卷积层和池化层负责提取特征,全连接层负责把这些特征整合起来,做出最终决策。
它的原理很简单:每个输入节点都连接到每个输出节点,每个连接都有一个权重。说白了,就是一个矩阵乘法加偏置。
# 全连接层示例
fc_layer = nn.Linear(
in_features=512, # 输入特征数
out_features=10 # 输出类别数(比如10个数字)
)
全连接层在CNN中的位置:
- 卷积层提取局部特征 → 得到特征图
- 池化层降维 → 特征图变小
- 展平(Flatten) → 把二维特征图拉成一维向量
- 全连接层 → 整合特征,输出分类结果
我的经验:在嵌入式部署中,全连接层往往是参数量的大头。举个例子,一个512输入、256输出的FC层,就有512×256=13万个参数。而一个3×3卷积层,64通道输入、64通道输出,才3×3×64×64≈3.7万个参数。所以,我建议尽量用全局平均池化(Global Average Pooling)替代全连接层,能大幅减少参数量。
2.5 总结:CNN的“流水线”
好了,我们把CNN的核心组件都过了一遍。现在把它们串起来,看看一个典型的CNN是怎么工作的:
- 输入层:原始图像,比如224×224×3
- 卷积层:提取边缘、纹理等低级特征
- 激活函数(ReLU):引入非线性
- 池化层:降维,保留重要特征
- 重复2-4步:提取更高级的语义特征
- 展平/全局池化:把特征图变成向量
- 全连接层:整合特征,输出分类结果
嵌入式部署的黄金法则:
- 卷积核用3×3,别用大的
- 激活函数用ReLU,别用Sigmoid
- 池化用最大池化,步长2
- 全连接层能省就省,用全局平均池化替代
这些原则,我在多个嵌入式项目里验证过,效果稳定。你照着做,基本不会出大问题。
下一章,我们会把这些知识用到实战中——在嵌入式设备上部署一个真正的CNN模型。到时候,我会带着大家一步步走完从训练到部署的全流程。嗯,敬请期待。