☰
CNN卷积神经网络:从LBP手工特征到卷积池化与实战选型
2026/10/29 3:27:39 网站建设 项目流程

简介:这份PPT面向准备系统入门计算机视觉与深度学习的学习者,聚焦CNN卷积神经网络的核心原理、典型结构与工程实践,帮助读者从传统人工特征提取平滑过渡到端到端特征学习。压缩包内共1个pptx文件,大小约7.58MB,以章节化课件形式组织,适合课堂讲授、课程复习与自学查阅。课件从传统LBP算子特征分类切入,系统讲解卷积层、池化层、全连接层、激活函数及卷积尺寸计算,并延伸到转置卷积、空洞卷积、深度可分离卷积、组卷积、可变形卷积和3D卷积等常见变体,同时补充优化算法与正则化等卷积外必备要素。内容还包含代码实战与知识脉络梳理,目前已吸引6671人学习,可用于搭建CNN知识框架、对照课程查漏补缺、准备技术面试或复盘计算机视觉项目思路。

1. 从一个"认猫"的困惑说起:CNN到底在干什么

你给一个三岁小孩看几张猫的照片,他下次见到猫基本就能认出来;可要是让你把"怎么认出猫"写成一条条规则——尖耳朵加几分、胡须几根、瞳孔是竖的还是圆的——你会发现根本写不出来。CNN卷积神经网络干的就是这件事:不靠人写规则,而是自己从一堆像素里把"像猫的局部图案"一层一层攒出来,最后凑成一个判断。

它是计算机视觉绕不开的地基,图像分类、目标检测、人脸比对背后都有它的影子。搞懂它,这些词就不再是玄学黑匣子,而是一条能讲清楚的流水线:谁在找图案、谁在缩小图、谁在最后拍板。这份资料把这条流水线拆成了四块——来源、细节、变种、实战,顺着看下来,脑子里会有一张完整的图。

2. 为什么要有CNN:手工特征的天花板在哪里

2.1 传统特征提取:像手工给图像做一把筛子

在CNN流行之前,让计算机认图的主流办法是"手工设计特征"。名字听着抽象,做法其实很朴素:人先想好"什么样的局部图案值得关注",再写一段固定规则把它量出来,最后把量出来的数字交给分类器。LBP算子就是这类思路里最经典的一个。

LBP全称局部二值模式,它的规则简单到可以用一句话说清:拿一个3×3的小窗口在图上滑动,把中心像素的亮度当基准,周围8个邻居谁比它亮就记1、比它暗就记0,顺时针排成一串8位二进制数。这个数落在0到255之间,正好对应256种"局部纹理暗号"——全暗是一种、全亮是一种、左边亮右边暗又是一种。统计一整张图里这256种暗号各出现多少次,就得到一根256维的特征向量,拿去做分类。

import numpy as np def lbp_simple(img): """最朴素的LBP:3x3窗口,中心像素与8邻域比较,拼成8位二进制码""" h, w = img.shape out = np.zeros((h - 2, w - 2), dtype=np.uint8) # 8个邻居相对中心的偏移,按顺时针排列 offsets = [(-1, -1), (-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1)] for i in range(1, h - 1): for j in range(1, w - 1): center = img[i, j] code = 0 for k, (di, dj) in enumerate(offsets): # 邻居不比中心暗就记1,再按位拼进整数 if img[i + di, j + dj] >= center: code |= (1 << k) out[i - 1, j - 1] = code return out

这段代码有三个地方值得留意。输出尺寸比输入小2,因为3×3窗口贴不到最外圈,边缘那一圈像素被丢掉了;code是一个0到255的整数,代表某一种局部明暗关系,不是亮度值本身;循环里做的是比较而不是乘法,所以LBP对整体光照变化不敏感——整张图一起变亮变暗,大小关系基本不变,这也是它在人脸、纹理任务里活了很多年的原因。参数上,窗口从3×3扩到5×5,邻域变成24个,模式数会涨到一千六百多万种,统计起来就不再轻便,所以实际用的时候很少有人直接扩大窗口。

这套办法的优势很实在:规则清楚、计算便宜、不需要训练、样本很少也能跑。资料里上一节留的作业,就是用LBP特征给手写数字图像分类,几十行代码就能出一个能看的结果。但劣势同样明显——特征是人拍脑袋定的。定规则的人假设"纹理靠邻域明暗关系就能描述",这个假设在受控环境下成立,一换光照、一换角度、一换尺度就开始漏。更关键的是,这套规则没有学习能力,它不会因为看了十万张图就变得更聪明,泛化能力全押在设计者身上。

2.2 端到端到底省掉了什么

传统图像识别的流水线很长:先去噪、二值化做预处理,再手工提特征,然后做特征选择(256维里挑出真正有用的几十维),最后喂给SVM之类的分类器。每一段都有几个参数要人调,每一段出错都会往下游传,调一圈下来像在拼一条不知道哪节漏水的水管。

端到端换了思路:只规定输入是像素、输出是类别,中间怎么从像素变到类别,交给网络自己去学。打个比方,以前做菜要自己种菜、自己配调料、自己掌握火候,现在给一个厨房和一批食材,让它反复试,试出能过关的做法。省掉的是"人来设计中间表示"这一整段,而不是省掉工作量——工作量从设计特征转移到了准备数据、搭结构、调训练。

代价也得说清楚。端到端要大量带标注的数据,要能接受的算力,还要忍受可解释性变差:模型判断错了,你很难像看LBP直方图那样一眼指出是哪一维特征出了问题。这份资料把"CNN从哪里来"放在最前面讲,用意大概也在这——不知道手工特征卡在哪,就看不懂CNN为什么值得这么折腾。

2.3 CNN提取特征的优势和代价

CNN能取代手工特征,靠的不是"更深"这种含糊说法,而是三个结构上的设计。

第一个是局部连接。图像有个天然性质:相邻像素关系密切,隔很远的像素关系弱。全连接层让每个输出和所有输入相连,等于无视这个性质;卷积层只让输出看输入的一小块邻域,符合图像的局部相关性。

第二个是权值共享。同一个卷积核滑遍整张图,用的都是同一组权重。这意味着"找竖边"这件事在左上角和右下角用同一套标准执行,参数不用翻倍。平移等变性也由此而来:物体挪个位置,响应跟着挪,形状不变。

第三个是层次化。浅层卷积学到的往往是最简单的边和角,中间层把这些边组合成纹理和部件,深层再组合成物体级别的模式。这套"简单拼复杂"的路子,恰好和人对视觉的理解对得上。

参数量的差距可以用一张表看明白。以一张224×224的彩色图为例:

对比项全连接处理3×3卷积(64个核)
输入维度224×224×3 = 150528同左
第一层参数量150528×1000 ≈ 1.5亿3×3×3×64+64 = 1792
是否利用局部性否是
平移适应性靠大量样本硬学结构自带

差距是五个数量级。代价同样存在:数据不够时CNN会狠狠过拟合,算力不够时训练一轮要等很久,超参数(学习率、卷积核数量、层数)怎么设还有不少经验成分。这一行的血泪经验是,结构上的巧思能省参数,但省不掉数据和算力这两笔账。

3. CNN的实现细节:卷积、池化、全连接各管一摊

3.1 卷积是什么:一把会自己学图案的放大镜

卷积这个词容易吓人,动作本身很简单:拿一个小方窗在图上从左到右、从上到下滑动,每停一处,把窗口覆盖的像素和窗口自带的权重逐个相乘再求和,得到一个数;滑完整张图,得到一张新的"响应图",叫特征图。

把这个动作想成一把放大镜,窗口里的权重就是"它想找的图案"。如果这组权重长得像一条竖边(左边一列负数、右边一列正数),那它扫到竖边时求和结果就大,扫到平坦区域就接近零。所以一张特征图可以理解成"整张图里,某个图案在哪些位置出现得强烈"。

彩色图有三个通道,卷积核也做成三通道,各通道分别乘再加,最后合成一个数。想要同时找多种图案,就准备多个卷积核,每个核输出一张特征图,叠在一起就是多通道输出。

import torch import torch.nn as nn # 输入:1张 3通道 32x32 的图;输出:16张特征图 conv = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1) x = torch.randn(1, 3, 32, 32) y = conv(x) print(y.shape) # torch.Size([1, 16, 32, 32])

四个参数各管一件事。in_channels=3必须和输入的通道数对上,对不上直接报错;out_channels=16是卷积核个数,也等于输出特征图的张数;kernel_size=3是窗口边长;padding=1是在图像四周补一圈零,让输出尺寸和输入保持一致。这一层的参数量是3×16×3×3+16=448,最后那个16是每个核附带的偏置。448个参数对比全连接的1.5亿,差距就在权值共享上。

3.2 卷积层的尺寸怎么算:输出大小、步长、填充

搭网络时最常算的一个数,是每层输出多大。公式只有一行:

out = ⌊(in + 2×padding − kernel_size) / stride⌋ + 1

其中in是输入边长,padding是四周补零的圈数,kernel_size是窗口边长,stride是每次滑动的步长。向下取整意味着除不尽时多余的行列会被直接丢掉,这也是为什么有些网络在拼接特征时会尺寸对不上——算错了一层,后面全歪。

def conv_out_size(size, k, s=1, p=0): """输入边长 size,卷积核 k,步长 s,填充 p,返回输出边长""" return (size + 2 * p - k) // s + 1 print(conv_out_size(32, 3, 1, 1)) # 32,尺寸不变 print(conv_out_size(32, 5, 2, 0)) # 14,差不多砍半 print(conv_out_size(224, 7, 2, 3)) # 112,经典骨干网的第一层

三行输出对应三种典型配置。第一行是"保持尺寸"的写法,padding取(k-1)/2就能做到,3×3核配1圈零、5×5核配2圈零;第二行是"降采样"的写法,步长2让输出边长大约减半;第三行是很多骨干网络开头的7×7大核加步长2,一上来就把224压到112,先把计算量砍下去再堆卷积。

顺带说清核为什么常取奇数。奇数核有唯一的中心点,padding补零时左右对称,输出尺寸好保持;偶数核在"哪个像素算中心"这件事上含糊,实际很少有人用。另一个常被提起的经验是:两层3×3堆叠的感受野等于一层5×5,但参数是2×9=18对25,还多了一次非线性,所以同等感受野下宁可堆小核。这是被反复验证过的结论,不是玄学。

3.3 池化层与全连接层:先眯眼,再投票

池化层干的事情,说穿了就是眯着眼睛看图。在一个小窗口里取最大值(Max Pooling)或平均值(Average Pooling),把特征图缩小一半。细节糊掉了,但"这一片有没有出现那个图案"的信息还在。它的作用有四个:降维、减参、扩大后续层的感受野、带来一点平移不变性——图案在窗口内挪一两个像素,最大值还是那个最大值。

需要提醒的是,池化不是必需品。很多现代结构用步长为2的卷积代替池化,理由是卷积在降维的同时还能学东西,而池化只是一个固定的聚合规则,没有参数可学。这不是说池化被淘汰了,而是说它不再是"必须有的那一层"。

全连接层在网络的尾巴上。做法是把最后一张多通道特征图拉平成一维向量,接一两层神经元,输出每个类别的分数,再经过softmax变成概率。打个比方,前面的卷积层像一群各看一角的专家,每人给出自己那块区域的判断,全连接层是汇总投票的那间会议室。参数量的大头往往就压在这里,所以近年的网络会先用全局平均池化把每张特征图压成一个数,再送给全连接,以此砍掉大部分参数。

3.4 卷积以外的必备要素:非线性、优化、正则化

只有卷积是搭不出网络的。原因在数学上很干脆:卷积是线性运算,多层线性运算叠起来,等价于一层线性运算。堆一百层和堆一层,表达能力上没有区别。所以每层卷积之后必须接一个非线性激活函数,把这条路打开。

激活函数里最常用的是ReLU,规则就是max(0, x),负数归零、正数原样通过。它的好处是计算便宜、正区间不饱和、梯度不容易消失。Sigmoid和Tanh属于上一代选择,输出被压在固定区间里,输入一大就饱和,梯度几乎归零,深层网络很难训起来。

优化算法负责告诉每个权重"往哪个方向调、调多少"。从最基础的梯度下降,到带动量的版本,再到自适应学习率的Adam,本质都是在解决同一个问题:损失函数构成的那张高维曲面上,怎么走才能又快又不抖。学习率设大了会震荡甚至发散,设小了训练慢得让人怀疑人生,这个参数至今仍带着点调参玄学的味道。

正则化负责防过拟合,让模型在没见过的图上别翻车。常用的手段有Dropout(训练时随机丢一部分神经元,逼网络别依赖某几条固定通路)、权重衰减(限制权重别长太大)、数据增强(把原图翻转、裁剪、调色,人为造出更多样本)。批归一化严格说属于归一化手段,但常和正则化放在一起讲,它把每层输入拉回相近的分布,让训练更顺、对初始化没那么敏感。

把这几块拼起来,一个最小的网络骨架长这样:

import torch.nn as nn model = nn.Sequential( nn.Conv2d(3, 32, 3, padding=1), # 卷积提特征,输出32张图 nn.ReLU(inplace=True), # 非线性,这层不能省 nn.MaxPool2d(2), # 32x32 -> 16x16 nn.Conv2d(32, 64, 3, padding=1), # 输入通道要接上一层的32 nn.ReLU(inplace=True), nn.MaxPool2d(2), # 16x16 -> 8x8 nn.Flatten(), # 拉平成一维 nn.Linear(64 * 8 * 8, 10), # 10类输出 )

读这段代码有两个对应关系要看准。第二层卷积的in_channels=32必须等于第一层的out_channels=32,通道对不上的报错八成出在这;Linear的输入维度是64×8×8,来自"64张特征图、每张8×8",这个数一旦算错,训练开始的第一步就会崩。整个结构里,卷积负责找图案,池化负责缩小,ReLU负责打开非线性,全连接负责拍板,四类层各管一段,缺一段都跑不通。

4. 变种卷积:为什么要给卷积"变形"

4.1 转置卷积、空洞卷积:一个放大,一个扩视野

标准卷积要么让特征图变小,要么保持不变。可有些任务需要它变大:语义分割要把小特征图还原回原图尺寸,生成模型要从一个向量长出一张图。转置卷积就是干这个的——它把输入元素按比例"摊开",中间补零,再正常卷积一遍,输出尺寸随之变大。

这里有个坑得提前说:转置卷积在中文里常被叫成"反卷积",但它不是卷积的数学逆运算。卷积是多对一映射,信息本来就丢了,转置卷积只能把尺寸放大,还原不出原来的像素值。把它理解成"解压"更准确:压缩包解开还是那些内容,但被卷积压掉的东西回不来。

空洞卷积解决的是另一个矛盾。想让网络看到更大范围(感受野更大),常规做法是加深或者降采样,但降采样会丢分辨率,对分割这类逐像素任务不友好。空洞卷积的办法是在卷积核的元素之间插空洞:3×3的核,膨胀率设为2,实际覆盖范围变成5×5,但参与计算的还是9个权重。等于不增加参数、不降分辨率,白得一块更大的视野。膨胀率再往上调,覆盖范围继续扩大,代价是采样点之间跳过太多,细小的连续结构容易被漏掉。

4.2 深度可分离卷积、组卷积:省算力的两条路

把标准卷积的计算量写出来,是Dk×Dk×M×N×Df×Df,其中Dk是核边长,M是输入通道,N是输出通道,Df是特征图边长。四个因子连乘,涨起来很快。省算力的思路有两条,都被验证过有效。

第一条是深度可分离卷积,把一次卷积拆成两步。第一步depthwise,每个输入通道配一个自己的卷积核,各卷各的,通道之间不交流;第二步pointwise,用1×1卷积把各通道的结果加权融合,输出想要的道数。计算量变成Dk×Dk×M×Df×Df + M×N×Df×Df,和标准卷积一比,压缩比约等于1/N + 1/Dk²。取Dk=3、N=64,大约是原来的八分之一。

第二条是组卷积,把输入通道分成g组,每组独立卷积,最后把结果拼起来。分组的好处是并行度高、参数少,组数取到和通道数相等时,就退化成depthwise卷积。分组带来的副作用是通道之间的信息交流被限制在组内,所以后面通常还要接一个1×1卷积做融合。

几种变体放在一起,各自的定位就清楚了:

卷积类型核心动作主要收益典型场景
标准卷积全通道加权求和表达能力强通用骨干网络
深度可分离卷积逐通道卷 + 1×1融合参数量和算力大降移动端、嵌入式
组卷积通道分组、组内卷并行度高、省算力宽通道骨干
空洞卷积核内插空扩视野不降分辨率语义分割
转置卷积上采样特征图变大分割、生成
可变形卷积采样点可偏移适应不规则形状检测、细粒度识别
3D卷积多一个时空维时空联合建模视频、体数据

4.3 可变形卷积、3D卷积:对付形状和时间

标准卷积的采样点是钉死的规整网格,这在物体形状规整时没问题,遇到弯曲的文字、变形的零件、姿态各异的行人就别扭了——网格采样点可能一半落在物体上、一半落在背景上。可变形卷积的思路是给每个采样点再学一个偏移量,让采样点自己"跑"到物体上。偏移量不是人定的,也是训练出来的,所以它天然适应形变。代价是实现比标准卷积复杂,偏移量的学习也需要更多数据支撑。

3D卷积则是往另一个方向扩:卷积核多一个维度,比如3×3×3,在视频的(时间、高、宽)三个方向上滑动,或者在医学CT的(深度、高、宽)上滑动。2D卷积把每一帧当独立图片处理,帧与帧之间的运动信息丢了;3D卷积能把"这个动作在几帧之间怎么变"一起学进去。代价是参数量和显存占用都按核的第三维线性增长,训起来更吃资源。

5. 常见误区与易混概念:这几种说法最容易翻车

5.1 卷积核大小不等于感受野

易混点:看到3×3卷积,就以为输出上一个点只看输入3×3的范围。

为什么混:名字里都带"3×3",读者容易把核尺寸当成视野尺寸。实际感受野是逐层累加的:第一层3×3,第二层再3×3,输出点能追溯到的输入范围就是5×5,第三层是7×7。要是中间夹了步长为2的层或者空洞卷积,还得另算。

怎么区分:记住一句话——核大小决定"这一层看多宽",感受野决定"这个输出点总共能追溯到多宽"。判断一个结构的视野够不够,看的是感受野,不是核尺寸。堆小核能用更少参数换到同样感受野,这也是小核流行的原因。

5.2 池化降维与卷积降维不是一回事

易混点:两者都能让特征图变小,就被当成可互换的操作。

为什么混:结果上都是尺寸减半,初学者容易觉得"用哪个都行"。

怎么区分:池化没有参数,规则固定(取最大或取平均),只做聚合,不学新特征;步长为2的卷积有参数,一边降维一边学新特征。池化更省、更确定,卷积更灵活、表达更强。近年的网络更偏后者,但在需要严格控制计算量的场合,池化仍然是划算的选择。

5.3 "反卷积"不是卷积的逆运算

易混点:以为转置卷积能把卷积的输出原样还原成输入图。

为什么混:中文俗称"反卷积"三个字太有误导性,听起来像求逆。

怎么区分:卷积把一个邻域压成一个数,是多个输入对应一个输出,数学上不可逆。转置卷积只是把尺寸放大回去,数值上是另一套运算,不是逆。要验证这点很简单:拿一张图卷一遍再转置卷积回来,得到的东西和原图差得远。它在分割里起的作用是"把特征图放大到原图尺寸再逐像素判断",不是"还原原图"。

5.4 参数少不等于算得快

易混点:看到深度可分离卷积参数量只有标准卷积的八分之一,就认为速度也能快八倍。

为什么混:参数量和计算量(FLOPs)都被用来衡量"模型轻不轻",指标混着用。

怎么区分:参数量看的是权重的个数,计算量看的是乘加次数,实际速度还要看内存访问效率、并行度、硬件适配。depthwise那一步虽然乘加少,但每个通道单独算,访存效率偏低,在某些硬件上实际加速远达不到理论值。评估轻量化效果,得在目标设备上实测,纸面数字只能当参考。

5.5 CNN与传统特征提取不是替代关系

易混点:学了CNN,就觉得LBP这类手工特征已经没用了。

为什么混:教材叙事习惯写"新方法取代旧方法",容易让人以为旧方法该进博物馆。

怎么区分:CNN强在数据充足时的端到端学习,样本只有几十上百张时它很容易过拟合;手工特征在少样本、强可解释要求、算力受限的场景里依然好用,工业质检里LBP加简单分类器的组合仍有部署。更常见的做法是两者配合:先用传统特征跑一个基线,知道任务大概能做到什么水平,再上CNN看能提升多少。

6. 什么时候该用CNN,什么时候别硬上

判断一个任务该不该上CNN,我一般先问四个问题,答案清楚了,选择也就定了。

第一问:输入是不是图像或类图像数据?是,CNN有天然优势;不是,比如结构化表格、纯文本,梯度提升树或序列模型往往更省事。

第二问:标注数据够不够?每个类别几百张起步,才谈得上从头训练;只有几十张,要么用预训练模型微调,要么先回到传统特征加简单分类器。

第三问:能不能接受黑盒?医疗辅助、司法取证这类场景,模型给出结论还得给出依据,CNN的中间特征很难翻译成人能读的理由,这时候传统特征或者可解释性更强的模型更合适。

第四问:算力和延迟卡不卡?手机端、嵌入式设备上跑,得先做剪枝、量化、知识蒸馏,或者直接选轻量结构;如果任务规则明确、几何关系清楚,传统视觉算法反而更快更稳。

场景建议理由
自然图像分类、检测、分割上CNN数据充足,端到端优势明显
每类只有几十张样本预训练微调或传统特征从头训练必过拟合
结构化表格预测梯度提升树优先CNN不擅长这类输入
需要逐条解释判断依据传统特征或可解释模型CNN内部表示难翻译
极低算力设备轻量结构 + 压缩标准CNN跑不动
规则清晰的几何测量传统视觉算法更快、更确定、好调试

举个具体例子。资料里那个用手写数字做分类的作业,如果只是区分印刷体数字,光照均匀、笔画规整,LBP加一个简单分类器几十行代码就能到不错的准确率,上CNN属于杀鸡用牛刀。可要是换成自然场景下的手写数字,笔画粗细不一、背景杂乱、光照不均,手工特征的规则就开始漏,这时候CNN的层次化特征提取才真正显出价值。同一类任务,数据形态一变,最优解就变了。

我自己踩过的坑是这样的:早些年接到一个只有几百张图的小任务,上来就搭了个不浅的CNN,训练集准确率冲到很高,验证集死活上不去,调了两天参数才发现问题不在网络,在数据量。后来改成先跑一个LBP加简单分类器的基线,半天出结果,心里有了底,再用预训练模型微调,提升多少一目了然。从那以后我每次开新任务都强制先跑一遍最笨的基线,哪怕它注定会被替换掉——没有基线,你连"提升"两个字都没法定义。希望这份资料能帮你少走一遍这条弯路,先把概念理顺,再动手不迟。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询