ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深度学习计算机视觉入门:从卷积神经网络到项目实战完整指南

深度学习计算机视觉入门:从卷积神经网络到项目实战完整指南 深度学习在CV方向上的学习曲线确实有点陡。网上资料铺天盖地有讲数学的、有讲框架的、有直接甩代码的但对一个刚入门的初学者来说最容易卡住的往往不是某个模型结构看不懂而是脑子里没有一个完整的地图——不知道这些知识是怎么串起来的也不知道学到的东西到底用在哪。这篇笔记就是基于我自己从传统图像处理转到深度学习CV方向的经历把最基础也最核心的知识点按一条实用的逻辑线重新梳理了一遍。如果你正准备入门深度学习CV或者学了一段时间总觉得知识点是散的这篇内容应该能帮你把框架搭起来。我尽量用大白话讲原理把每一步为什么这么做说清楚也会穿插一些实际操作中常见的坑。1. 先搞清楚CV到底在解决什么问题1.1 从图像分类到像素级理解CV任务的层级CV这个领域听起来很大但拆开来看所有任务本质上都是在回答一个问题图像里有什么、在哪里、是什么状态。这三个问题对应着不同的任务层级难度也是逐级递增的。最基础的是图像分类就是给一整张图打一个标签比如这是一只猫。这个任务只关心图里最主要的物体是什么不关心物体在哪、有多大。第二个层级是目标检测不仅要判断图里有没有猫还要用一个矩形框把猫框出来告诉你在图像的哪个位置。第三个层级是图像分割要求像素级地判断每个像素属于哪个物体。分割又分为语义分割和实例分割语义分割不区分同类的多个物体把所有猫都标成一类实例分割则要求把每一只不同的猫区分开。再往上还有姿态估计关键点检测、图像生成、图像检索、超分辨率等等。理解这个任务层级非常重要因为不同的任务对应不同的模型输出设计和损失函数初学者最容易犯的错误就是拿着分类的模型和思维去做检测或分割结果发现怎么调都效果不对。1.2 为什么以前的方法做不到深度学习凭什么可以在我刚接触图像处理的时候行业内主流做法是手工特征加传统机器学习分类器。比如做人脸检测用Haar特征配合Adaboost分类器做行人检测用HOG特征配合SVM。这些方法的核心思想是人类专家先观察目标物体的视觉特点然后设计出能描述这些特点的特征再用分类器去区分。这种思路有个致命问题特征设计完全依赖人的经验。如果目标物体外观变化很大比如同一个物体在不同光照、角度、遮挡下差异巨大手工设计一套鲁棒的特征就变得极其困难。而且一旦换一个应用场景之前精心设计的特征可能就完全失效了。深度学习的革命性在于它把特征设计这件事也交给了模型自己。卷积神经网络通过多层非线性变换能够从原始像素中自动学习从低级边缘、颜色、纹理到高级部件、整体结构的特征表示。所以深度学习做CV的本质不是换了一个分类器而是换了一套完整的范式从人为定义特征简单分类器变成数据驱动特征学习端到端训练。这也是为什么深度学习在图像识别上的准确率能一夜之间超过传统方法好几个数量级。2. 深度学习做CV的基础范式从特征工程到特征学习2.1 卷积为什么是图像处理的第一选择要说清楚深度学习怎么处理图像必须先理解卷积操作。卷积神经网络的第一个单词卷积不是随便起的它是这套方法能奏效的核心原因之一。图像在计算机里本质上是一个二维矩阵灰度图或三个二维矩阵RGB三通道每个像素都是0到255之间的数值。我们要从这个矩阵里提取信息第一个直觉是能不能像传统图像处理那样用一个小窗口在图像上滑动对窗口内的像素做加权求和这个滑动的小窗口就是卷积核加权求和的参数就是卷积核的权重。卷积操作有两个极其重要的特性。第一个是局部连接每个输出像素只跟输入图像上一个小邻域有关这符合图像中物体局部相关的天然属性——一个像素的语义主要由它周围的像素决定。如果不用卷积而用全连接每个输出都要跟整张图的所有像素连接参数数量会爆炸到无法训练。第二个是权值共享同一个卷积核在图像上所有位置滑动时参数是一样的这大大减少了参数量同时让模型具备了空间平移等变性——猫在图片左上角还是右下角检测它的卷积核都是一样的。2.2 池化、激活、全连接每个组件存在的理由网络里每个组件都不是花架子它们各自解决一个具体的问题。卷积层的输出通常会接一个激活函数最常用的是ReLU。激活函数的角色是给网络引入非线性。如果没有非线性不管堆多少层卷积数学上都可以等价成一个线性变换那深度就没有意义了。ReLU之所以普及一是计算极其简单max(0, x)二是它能在正区间保持梯度不衰减缓解梯度消失问题。早期常用的Sigmoid函数在输入绝对值较大的时候梯度趋近于零多层叠加后梯度传不过去导致网络很难训练。池化层Pooling解决的是另一个问题特征图尺寸太大且对位置过于敏感。最常用的最大池化是取一个小区域内最大的值这样做一方面把特征图尺寸缩小降低后续计算量另一方面增强了特征的平移不变性——物体稍微挪动几个像素最大值可能仍然在那个池化窗口里。现在很多新网络倾向于用步长为2的卷积替代池化做下采样效果类似但可学习的参数更多表达能力更强。全连接层一般出现在网络尾部作用是把卷积和池化提取到的空间特征压扁成一个固定长度的向量再映射到类别得分。可以理解为前面所有层在做特征提取最后几层全连接在做分类决策。现在的主流做法是越来越多地用全局平均池化代替全连接因为全连接层参数占比巨大且容易过拟合。2.3 特征图尺寸怎么算一份能直接抄的计算表这个东西虽然书上都有但几乎每个初学者都会在这里卡一阵。给定输入尺寸、卷积核大小、步长和填充输出特征图的尺寸计算公式是输出尺寸 (输入尺寸 - 卷积核尺寸 2 × 填充) / 步长 1举几个实际例子比光看公式直观得多输入尺寸卷积核步长填充输出尺寸备注224×2243×311224×224尺寸不变的经典配置224×2243×321112×112下采样一半224×2247×723112×112ResNet首层常用32×325×51028×28无填充时尺寸收缩实际操作中有一个经验法则如果希望特征图尺寸不变就用3×3卷积核配padding1如果希望尺寸减半就用stride2。这个规则我在调试网络结构时反复用到因为手算每一层输出尺寸很容易出错但有了这个法则整个网络各层的尺寸变化一目了然也能快速发现结构上的bug——比如某层卷积后尺寸意外变成0或者负数多半是参数填错了。3. 主流网络结构演进每个模型解决一个具体痛点3.1 AlexNet到VGG更深更大带来的问题2012年AlexNet在ImageNet上一战成名深度学习正式统治CV领域。AlexNet的核心贡献是验证了深度卷积神经网络在大规模数据上的有效性它用了8层网络、ReLU激活、Dropout和数据增强把分类错误率从26%降到了15.3%。之后VGG网络的思路更简单粗暴——把所有卷积核都换成3×3把网络堆到16到19层。VGG有一个深刻洞察两个3×3卷积堆叠等价于一个5×5卷积的感受野三个堆叠等价于7×7但参数量更少、非线性更强。这个思想后来成为几乎所有主流网络的基础设计原则。但VGG推到了19层就推不动了。原因在于梯度消失反向传播时梯度要逐层向前传播每经过一层就要乘以该层的权重矩阵。如果权重矩阵的特征值小于1连乘之后梯度指数级衰减到接近零如果大于1梯度又可能指数级爆炸。层数越深这个问题越严重。20层以内的网络还能勉强靠良好的初始化和BatchNorm撑住再深就训练不动了。3.2 ResNet的残差思想梯度消失的破解ResNet的解决方案看似简单、实则极其精妙在每两层之间加一条捷径连接shortcut connection让输入直接跳到输出上网络学习的目标从直接拟合期望的映射H(x)变成了拟合残差F(x) H(x) - x。为什么要学残差因为直接学恒等映射很难——让一堆非线性层学会什么都不做其实是很大的负担。但如果让网络学残差最坏情况把残差学成0那输出就等于输入至少不会比什么都不做的浅层网络更差。更重要的是residual连接让梯度在反向传播时多了一条高速公路梯度可以直接通过短路连接传到前面的层不会因为连乘而衰减——甚至可以把ResNet理解为一系列浅层网络的集成。ResNet最常用的版本是ResNet-50和ResNet-101数字表示层数它第一次把网络做到了上百层还能稳定训练成为了之后几乎所有CV模型的标准骨架从检测到分割到人体姿态估计都在用它做backbone。我做目标检测实验时的第一选择就是ResNet-50性能和速度的平衡最好。3.3 轻量化网络的工程价值MobileNet的深度可分离卷积学术界的模型追求精度但工程落地时还要考虑速度、参数量、功耗。VGG和ResNet直接搬到手机上显然不现实于是轻量化网络应运而生。MobileNet的核心是深度可分离卷积。普通卷积一个卷积核同时处理所有输入通道而深度可分离卷积把这个过程拆成两步第一步每个通道单独用一个卷积核做空间卷积深度卷积第二步用1×1卷积把所有通道的信息融合起来逐点卷积。这样拆分后计算量和参数量大约是普通卷积的九分之一到八分之一精度损失却很小。这个思想现在已经被广泛吸收进各种大网络里比如MobileNetV2引入的倒残差结构和ReLU6激活EfficientNet通过神经架构搜索自动确定网络宽度、深度和分辨率的最优组合。对于要在嵌入式设备、移动端、边缘计算盒子上跑CV任务的场景轻量化网络是必须掌握的基础知识不要一上来就只会调大模型。4. 训练CV模型最关键的几个环节4.1 数据准备标注质量、数据增强、归一化模型效果的上限由数据决定。这句话在CV领域尤其真实。首先是标注质量。分类数据要保证标签没有噪声错误检测数据的标注框要贴合物体边缘框大了小了都会干扰训练分割数据的掩膜标注更是要仔细到像素级。我见过不少项目模型效果上不去调来调去最后发现问题出在标注上——几千张图里有一百多张标签标反了模型再强也扛不住这种系统性噪声。数据增强是CV训练里性价比最高的技巧。它通过对训练样本做随机变换产生多样化的新样本相当于免费扩充了数据集同时降低了模型对位置、光照、颜色等无关因素的过拟合。最常用的增强包括随机水平翻转、随机裁剪、随机旋转、颜色抖动、尺度变换。更进阶的还有MixUp把两张图按比例混合和CutMix把一张图的区域粘贴到另一张图上对提升鲁棒性很有帮助。归一化也是不能忽略的细节。标准做法是用ImageNet数据集的均值[0.485, 0.456, 0.406]和标准差[0.229, 0.224, 0.225]对图像每个通道做减均值除标准差的操作让输入分布落在激活函数的敏感区间。迁移学习时如果用了在ImageNet上预训练的模型输入归一化的参数一定要和预训练时保持一致否则效果会明显变差。4.2 损失函数怎么选分类、检测、分割各不同深度学习训练的本质就是通过优化算法最小化损失函数。不同任务需要选择不同的损失函数这是很多初学者最容易忽视的环节。分类任务最常用的是交叉熵损失。它衡量的是预测概率分布和真实标签分布之间的差异。PyTorch里的CrossEntropyLoss实际上包含了softmax和log运算直接输入网络raw logits即可不需要手动做softmax。注意要设置ignore_index来跳过某些不参与损失的像素或类别比如分割任务里的边缘区域。目标检测的损失函数就复杂多了因为同时要优化分类和回归两个目标。拿两阶段检测器Faster R-CNN举例RPN阶段有前景/背景的二分类损失和候选框位置的Smooth L1回归损失第二阶段还有具体类别的分类损失和精细回归损失。这些损失按权重相加组成总损失比如分类损失权重为1回归损失权重可能在部分实现里设为1或2训练过程中可以打印出每一项的数值变化判断网络到底有没有学起来——如果分类损失在下降但回归损失纹丝不动多半是正负样本匹配或者回归目标归一化出了问题。图像分割最常用的损失是逐像素交叉熵但有一个痛点是类别不均衡。比如一张街景图里天空和道路占了大面积行人和自行车只有寥寥几个像素模型会牺牲小众类别去优化大类别的准确率。这时候可以用Dice Loss或Focal Loss。Dice Loss直接优化分割区域的重叠度对小目标更友好Focal Loss通过降低易分类样本的权重、提高难分类样本的权重在正负样本极度不均衡的场景下效果显著。4.3 epoch、batch size、学习率之间的微妙关系模型训练中几个最常见的超参数——epoch、batch size、学习率——不是互相独立的它们之间的关系如果理解不到位训练过程会非常痛苦。Epoch表示整个训练集被完整遍历的次数。太少了模型没学够太多了模型开始死记硬背训练集导致过拟合。一般做法是把训练集随机划出一部分做验证集比如8:2每训练完一个epoch就在验证集上测一次准确率选择验证集表现最好的那个checkpoint作为最终模型。Batch size指的是每次迭代喂给网络的样本数。它影响训练速度和梯度稳定性。Batch size太小梯度方向噪声大训练震荡剧烈太大梯度平滑但每次迭代时间变长显存也不一定装得下。一个常见误区是以为batch size越大越好实际上有研究表明过大的batch size会让模型收敛到尖锐极小值泛化能力反而下降。实际使用中8到64之间是常用范围。学习率控制着每次更新权重的步长。学习率太大损失函数会在最小值附近震荡甚至发散太小收敛速度慢得让人怀疑人生。最实用的策略是预热加余弦退火先用几个epoch从很小的学习率线性升到目标值warmup再用余弦函数把学习率逐渐降到接近零。另外学习率要随batch size调整经验法则是batch size扩大n倍学习率大致也乘以n的平方根或线性扩大具体倍数根据设备情况实测调整。5. 环境配置与工具链跑不起来一切白搭5.1 框架选型PyTorch为什么是当前的主流现在入门深度学习CV闭眼选PyTorch基本不会错。虽然TensorFlow在工业界部署上仍然有地位但从学术研究和快速迭代的角度看PyTorch的动态计算图优势太明显了——网络结构可以在每次前向传播时根据输入动态改变调试时还能像普通Python代码一样用print和断点逐行检查这对学习和实验来说是极其友好的体验。一个典型的PyTorch训练循环看起来是这样的import torch import torch.nn as nn import torch.optim as optim model MyModel() # 自定义网络 criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-4) for epoch in range(num_epochs): for images, labels in train_loader: optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() train_loss loss.item()这个流程里最容易踩的坑就是忘记调用optimizer.zero_grad()。如果不清空梯度PyTorch默认会把梯度累加到上一次的值上导致梯度错误地变大训练直接发散。还有一个坑是model.train()和model.eval()的切换——模型里有Dropout和BatchNorm时这两种模式的行为差别很大eval模式下偷偷用train模式跑推理结果会很离谱。5.2 硬件与CUDA环境的常见坑没有GPU的深度学习就像没有轮子的车所以环境配置部分是逃不掉的。最常见的诉求是安装PyTorch的GPU版本。这里面最大的坑是CUDA、cuDNN和PyTorch版本的对应关系。PyTorch的预编译包里面已经捆绑了它所需要的CUDA运行库所以对大多数用户来说只需要在官网选择对应CUDA版本的安装命令即可不需要自己单独去装一套系统级CUDA来适配PyTorch。比如在PyTorch官网生成安装命令时选择CUDA 11.8或12.1安装后用torch.cuda.is_available()验证是否可用。如果执行torch.cuda.is_available()返回False大概率是下面几种情况之一显卡驱动太老不支持当前CUDA版本PyTorch装成了CPU版本安装命令里没有cu118之类的标识或者系统环境变量没有正确识别显卡。排查时先用nvidia-smi查看显卡驱动支持的CUDA版本号再确认PyTorch版本里对应的CUDA编译版本。这里提供一个快速验证GPU是否可用的命令组合python -c import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))如果输出里第二行是True、第三行是显卡型号说明GPU环境没问题。如果第一行版本号里带cpu字样说明装成CPU版本了重新用带cu后缀的命令安装。5.3 OpenCV版本问题与常见异常处理做CV项目的过程中OpenCV几乎是绕不开的依赖库。它负责图像读取、预处理、可视化这些深度学习框架不擅长的活儿。但是OpenCV的安装和使用中也有几个让人抓狂的问题。最典型的是cv2和深度学习框架之间的图像格式不匹配。OpenCV默认用BGR顺序读图而PyTorch和TensorFlow的预训练模型都期望RGB顺序。如果直接拿OpenCV读的图喂给模型颜色通道是反的模型的分类结果会莫名其妙地出错特别是在那些对颜色敏感的任务上。解决方式是一行代码img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)。这是我见过初学者踩得最隐蔽的坑因为模型还能跑出结果只是结果不准。还有一个常见的异常是terminate called after throwing an instance of cv::exception。这类C层面的异常通常意味着传给OpenCV的输入数据有问题比如cv2.imread读取失败返回了空指针、图像路径包含中文导致读取失败、或者cv2.resize的目标尺寸参数传成了负值。排查思路是先确认图像是否真的读进来了img cv2.imread(test.jpg) if img is None: print(image read failed, check path or filename encoding) else: print(img.shape)路径含中文在Windows系统上经常导致读取失败解决办法是把文件路径中的中文目录名去掉或者用cv2.imdecode配合np.fromfile来读import numpy as np import cv2 def imread_unicode(path): data np.fromfile(path, dtypenp.uint8) return cv2.imdecode(data, cv2.IMREAD_COLOR)这个函数在我的本地项目中直接解决了中文路径读图失败的问题属于那种看起来不起眼但能省半天排查时间的小工具函数。6. 给新手的实际学习路径建议6.1 课程与资料怎么选别贪多跟紧一条主线就行很多初学者在入门阶段最大的问题不是没有资料而是资料太多了。今天收藏一份课程明天下载一本电子书后天刷到一个知识博主的精讲存货越来越多真正学完的没有几个。我的建议是选一套视频课加一本电子书按部就班跟到底中途不要换线。视频课方面吴恩达在Coursera上的深度学习专项课程Deep Learning Specialization适合建立整体认知尤其是对完全没有机器学习基础的初学者来说它的数学门槛很低很多东西讲得极其直观。李沐的《动手学深度学习》在实操层面更贴合这个时代的玩法它不只是讲理论每个章节都带可运行的代码而且踩坑的部分讲得很实在。鱼书风格的日本教材《深度学习入门基于Python的理论与实现》也值得刷一遍虽然讲的是老一点的底层实现但对理解反向传播和梯度下降的直觉极有帮助。我的建议路径是先看吴恩达的课程建立概念再用《动手学深度学习》里的代码做实验验证。需要特别提醒的是光看不练一点用都没有。一定要亲手把代码敲一遍改改超参数看看损失曲线怎么变化哪怕只是把学习率从0.01改成0.0001对比一下训练效果也比纯看课收获大得多。6.2 第一个入门项目怎么设计从分类到检测的完成闭环在掌握基础概念之后一定要做一个完整的项目来把知识串成线。这里说的完整至少包含数据准备、模型设计、训练调参、评估、推理部署五个环节。我建议的第一个项目是做一个小规模的自定义图像分类任务比如区分猫和狗、识别不同品种的花、甚至分辨自己拍的桌面物品。数据集不用大每个类别几百张图就够了可以从网上下公开数据集也可以自己拍照收集。项目不需要做得很复杂但要走完整个流程自己写数据加载和预处理、定义或调用现有的分类网络ResNet-18就很合适、写训练循环和验证逻辑、保存最佳模型、加载模型对新的图片做推理。做完这一步你对数据流、模型结构、训练过程的整体把握会脱胎换骨。第二个项目再尝试目标检测用现成的检测框架比如Ultralytics的YOLO系列在自建的小数据集上跑通检测的完整流程。第一次跑通YOLO训练后用模型检测视频里每一帧并实时可视化那份成就感会坚定你继续深入下去的信心。之后再看模型结构的原理、看Faster R-CNN的源码、理解anchor机制和NMS都会顺畅得多。6.3 坚持做笔记并建立自己的知识索引最后分享一个影响深远的习惯用笔记软件沉淀自己的学习过程。深度学习CV领域知识太庞杂靠脑子里存是存不住的。我的笔记法是把每一篇学过的内容拆成三个维度来记录。第一个维度是This is——这个东西是什么用一两句话讲明白最好是能讲给完全不懂的人听的那种大白话。第二个维度是Why——为什么这样设计解决了什么痛点当时的设计者在面对什么问题。第三个维度是Pitfall——实际使用中踩过的坑和解决办法。这个习惯的价值会在三个月后体现出来当你回头复习时不再需要把网课从头刷一遍只需要看笔记里Pitfall部分就能快速唤醒记忆。另一个实际的好处是写作练多了表达能力会明显提升。做技术的同学大多不太擅长把复杂的东西讲清楚而能讲明白恰恰是工程师从执行者向技术负责人转变的关键能力。动手写笔记、写博客、在社区回答问题都是锻炼这个能力的有效途径。最后再分享一个做项目时经常被忽略的细节训练完模型后一定要跑一个完整的错误分析——把验证集上预测错的样本批量打印出来逐个看为什么会错。是标注错了还是图片本身太模糊还是模型把背景当成了物体这种看坏case的习惯比盲目调参提升模型效果快得多。我在实际项目中发现很多长期调不上去的问题最后都是靠这一步找到真正的原因的。学习深度学习CV没有捷径但是有一条效率最高的路先把基础概念吃透再动手做一两个完整项目过程中坚持记录和复盘——按这个节奏走三个月就能建立对深度学习CV的整体认识剩下的就是不断在实践中加深理解。
返回列表