1. 总章在回答什么问题:先别急着调参,看清全景
经常有朋友问我:“我想入门深度学习,该先学哪个模型?”“那么多算法,学到什么时候才算会?”
我一般会反问:你知不知道你要解决的是分类、生成、还是序列预测?你知不知道训练一个模型要经过哪些环节?如果这些问题答不上来,那缺的不是某一篇教程,而是一张地图。这篇文章就是这张地图的总章,不聚焦某一个具体算法,而是把深度学习算法/模型这条线的整体骨架讲清楚,让你知道自己现在站在哪、接下来该往哪走。
1.1 算法与模型:两个经常被混着用的词
先解决一个最容易被绕晕的概念问题:算法(algorithm)和模型(model)到底有什么区别?
我的理解很简单:算法是一套规则和流程,模型是这套规则在数据上运行后产生的结果。用做饭来类比,算法是菜谱,模型是做出来的那道菜。菜谱告诉你放多少盐、炒几分钟,但实际炒出来的味道还取决于食材、火候、锅具——这些对应的是数据、算力和调参。同一个菜谱,不同人炒出来味道不同;同一个算法,不同数据训出来的模型效果也不同。
所以在深度学习里,"算法/模型"经常被写在一起,是因为它们本身就是一个连续过程:你选择算法,喂入数据,得到一个模型。学习的时候不要把两者割裂开,但思考的时候要清楚——你调的是算法参数,你部署的是训练好的模型文件。
1.2 深度学习的本质:就三件事
如果你把深度学习看成黑盒,会觉得它神奇得不得了;但拆开来看,所有深度学习算法/模型做的都是同一件事:建立一个从输入到输出的映射函数,然后用数据把这个函数的参数调好。
这个过程中有三件事是绕不开的,也是总章要提前铺垫的:
第一,表征学习。传统机器学习需要人手工设计特征——比如做图像识别,要人工提取边缘、角点、颜色直方图。深度学习不这么干,它让模型自己从原始数据里学出合适的特征表达。这就是为什么它能处理图像、语音、文本等非结构化数据。
第二,层次化抽象。深度网络是一层一层堆起来的,每一层都在做某种变换。底层学到的是细节特征,比如图片的纹理、文字的字母组合;高层学到的是语义特征,比如图片里有猫、句子表达的是正面情绪。这种层次结构是"深度"二字的来源。
第三,梯度驱动的参数更新。模型有数以百万计甚至千亿级的参数,靠人手工调是不可能的。深度学习用损失函数衡量模型的错误程度,然后通过梯度反向传播,把"如何调整每个参数能让损失变小"这个信息一层层传回去,用优化算法自动更新参数。
理解了这三件事,你就理解了为什么深度学习能火:它把"人工设计特征"这个最费力的环节变成了"自动学习",代价是需要大量数据和算力。数据和算力刚够的今天,它自然就成了主流。
2. 主流模型版图:记住这几条主线,别被名字吓住
深度学习领域模型非常多,新名字层出不穷,但如果你摊开来看,主流家族其实就那么几支。每一支解决一类核心问题,彼此之间有清晰的演进关系。
2.1 全连接网络:万能逼近器,但只适合小规模数据
多层感知机(MLP)是最古典的深度学习模型,也叫全连接网络(FCN)。它的结构就是一个输入层、若干隐藏层、一个输出层,每层神经元和下一层全部相连。数学上有个"万能逼近定理":只要隐藏层够宽,MLP可以近似任意连续函数。
但全连接有个致命问题——参数爆炸。一张 224×224 的彩色图片,输入维度就是 150528,如果第一层接 1024 个神经元,那一层就有 1.5 亿个参数。所以纯 MLP 在图像这种高维数据上基本没法用,它适合的是表格数据、低维特征这类场景。我到现在处理结构化数据时,第一选择仍然是 MLP 或它的变体,简单、稳定、不容易出幺蛾子。
2.2 卷积网络:给图像而生的结构先验
CNN 解决了全连接网络的参数爆炸问题,它的核心是四个字:局部连接。图像里一个像素和周围像素的关系最紧密,离得远的关系弱,那就不需要全连接,每个神经元只看一个局部区域就够了。再加上权重共享(同一套卷积核扫过整张图)和池化降采样,CNN 把参数数量降了几个数量级。
这套设计暗含了一个先验假设:图像特征具有平移不变性——一只猫在图片左上角还是右下角,它都是猫。这个假设在图像领域成立,所以 CNN 在计算机视觉里统治了整整十年,从 AlexNet 到 ResNet,后来甚至被用到文本、语音领域。如果你做的是图像相关的任务,CNN 仍然是最值得优先掌握的模型家族。
2.3 循环网络:序列建模的旧时代王者
RNN 和它的改良版 LSTM、GRU,是为序列数据设计的。它的核心思想是"记忆":网络在每一个时间步接收当前输入,同时接收上一个时间步的隐藏状态,就像一个人一边读书一边记住前面的内容。LSTM 引入门控机制,解决了 RNN 在长序列上的梯度消失问题。
我在 2016 年做文本分类的时候,LSTM 还是标配,那时候 Transformer 还没大规模流行。现在回头看,RNN 系模型在建模超长序列时有两个致命弱点:一是必须按时间步顺序计算,无法并行,训练慢;二是记忆容量有限,超过一定长度前面的信息就丢了。它现在仍然有用,但已经是"旧时代王者"的定位。
2.4 Transformer:注意力机制统治了几乎所有任务
2017 年出现的 Transformer,用一句话概括就是:把序列中任意两个位置之间的关系直接建模,不再依赖顺序传递。它用自注意力机制计算"每个词和其他词的相关程度",每个位置都能直接看到序列中的其他所有位置,解决了并行化和长距离依赖两大问题。
之后的 BERT、GPT 等预训练模型,都是 Transformer 的变体。BERT 用 Transformer 的编码器部分做双向理解,GPT 用解码器部分做自回归生成。到今天,不仅自然语言处理被 Transformer 统治,视觉领域也被 ViT(Vision Transformer)攻占,语音、推荐、多模态,处处都是它。如果你只学一个模型家族,我建议就是 Transformer——它已经是深度学习的事实标准。
2.5 生成模型:VAE、GAN 与扩散模型
前面说的都是判别式模型——输入一个样本,输出一个标签或预测值。生成模型反过来:从噪声中创造一个全新样本。这个领域是近年最热闹的赛道,也是图像生成、语音合成等应用的基础。
VAE 用编码器把样本压缩成潜在向量,再用解码器从潜在向量重建样本,它的强项是潜在空间的连续性。GAN 用生成器和判别器对抗训练,生成器想把假样本伪装成真的,判别器要分辨真假,两者互相促进,但训练稳定性是个老大难。扩散模型是现在的当红炸子鸡——先给图像逐步加噪声直到变成纯噪声,再训练一个网络学会逐步去噪还原,Stable Diffusion 一类的图像生成模型就是它的代表。
我的建议是:生成模型不用一开始就啃,它需要的数学基础和理解深度更高。先把判别式模型搞扎实,再来碰生成式。
3. 训练闭环:从数据到模型的四个关键环节
很多初学者最大的误区是:拿到框架,复制一段官方代码,跑通就觉得自己会了。跑通 demo 离训练好一个可用模型,中间还隔着一条鸿沟。一个完整的训练闭环,有四个环节缺一不可。
3.1 数据:模型的上限由数据决定
这句话值得刻在工位上:"Garbage in, garbage out"。模型的容量再大,数据喂错了,结果一定错。数据工作是整个深度学习项目里最耗时、最不性感、但回报最高的部分。
按步骤来说:第一是采集和清洗,去重、过滤噪声、检查标注一致性;第二是划分数据集,训练集、验证集、测试集必须严格隔离,划分时要考虑数据的分布,比如分类任务做分层采样,确保每个类别在三个集合中的比例一致;第三是数据增强,图片做随机裁剪、翻转、颜色抖动,文本做同义词替换、回译,这些都能让模型看到更多变体,提升泛化能力。
一个非常容易被忽视的细节:在线学习中常见的数据增强是在训练时随机做,而不是预先做死。这样每个 epoch 模型看到的数据都略有不同,相当于免费扩大了数据量。
3.2 损失函数:告诉模型往哪个方向走
损失函数衡量模型预测和真实值之间的差距,所有优化都是围绕最小化这个差距进行的。选错损失函数,模型训得再久也训不出正确结果。
分类任务几乎无脑用交叉熵损失,它配合 Softmax 输出,梯度性质好,收敛稳定。回归任务用均方误差(MSE)或平均绝对误差(MAE),前者对大误差敏感、收敛快但容易被离群点带偏,后者更鲁棒但在接近最优时收敛变慢,实践里也常见 Huber Loss 这种折中方案。度量学习任务用 Triplet Loss 或 InfoNCE。生成任务里,GAN 有专门的对抗损失,扩散模型用噪声预测的均方误差,Transformer 语言模型用的还是交叉熵。
我踩过一个经典的坑:做目标检测时用错了损失函数,把分类的交叉熵用在了边界框回归上,结果是模型学了个寂寞。后来改成 Smooth L1 Loss,训练曲线立刻正常。损失函数不是随便选的,它直接决定了模型在学什么东西。
3.3 优化器与学习率:控制训练的步幅
有了方向,还得有步子。优化器决定参数怎么更新、更新多快。你最常用的两个是:SGD 和 Adam。
SGD 最朴素,它用全部样本的梯度去更新,稳定但慢;改进版用 mini-batch 随机梯度下降,每次用一小批样本估计梯度,快了很多但引入了噪声。Adam 在 SGD 基础上加入了自适应学习率,每个参数有自己的更新步长,对初始学习率不那么敏感,收敛快,是我在大多数任务上的首选。
学习率是整个训练过程中最需要手调的超参数,没有之一。学习率太大,损失会震荡甚至爆炸;太小,训练慢到怀疑人生。我习惯用余弦退火或学习率预热策略——先小学习率跑几个 epoch 让模型稳定下来,再调大学习率快速收敛,最后再逐步降低做精细打磨。还有一个实用技巧:画一下 loss 曲线,如果你的 loss 在前期就出现锯齿状震荡,先别急着调网络结构,大概率是学习率偏大。
3.4 评估与迭代:别只盯着训练集损失
训练集的 loss 降得再低,模型在真实场景里也可能完全不能用,这就是过拟合。评估必须用独立于训练过程的验证集、测试集。
我最常犯的错,也是很多新手最容易犯的错:反复用测试集调参。测试集是"考试卷",你拿它当"练习册"用了,分数自然失真。正确的做法是:训练过程中只看验证集表现来决定是否调整超参数、是否早停;全部定稿后再用测试集做一次最终评估。如果测试集还是不够说明问题,就用交叉验证或留出多个验证集做多重估计。
评估指标也不能只看准确率。类别不均衡时,准确率是骗人的——比如 99% 的样本属于 A 类,模型把所有样本都判成 A,准确率 99%,但它完全没学会分类。要看精确率、召回率、F1,二分类还要看 AUC,排序任务看 NDCG。指标怎么选,取决于业务上哪种错误代价更高。
4. 选型不是玄学:按任务和资源对号入座
总章最后要解决的一个现实问题:面对一个实际任务,我该选什么模型?我的经验是三个字:看约束。
4.1 按任务类型匹配模型家族
先判断你要做的事属于哪一类:图像分类、目标检测、文本分类、序列生成、多模态理解,需求决定了选型方向。
| 任务类型 | 推荐模型家族 | 备注 |
|---|---|---|
| 表格数据分类/回归 | MLP、XGBoost | 数据量小时传统方法反而更强 |
| 图像分类/检测/分割 | CNN(ResNet 系)、ViT | 数据量充足时 ViT 优势明显 |
| 文本分类/语义理解 | BERT 系预训练模型 | 先用现成模型微调 |
| 文本生成/对话 | GPT 系自回归模型 | 从开源基座模型做起 |
| 语音识别/合成 | Wav2Vec 2.0、Whisper 系 | 直接微调现成模型 |
| 图像/音频生成 | 扩散模型 | 算力要求很高 |
这个表是经验之谈,不是铁律。实际工作中经常会混合使用,比如图像任务先用 CNN 抽特征再接入 Transformer,这都很正常。
4.2 按资源约束做务实选择
模型越大的效果越好,但算力和成本不是无限的。如果你是个人开发者,手里只有一张消费级显卡,硬上几百亿参数的大模型就是自找麻烦。我见过太多项目死在"想用的模型跑不起来"上。
资源有限时,我推荐的做法是:先找一个同任务的最轻量方案跑通全流程——确认数据没问题、训练代码没问题、评估管线没问题,再考虑换更大的模型提升精度。这个策略尤其适合从零开始的项目。你在小模型上踩过的坑,换大模型大概率也会遇到,但先在小模型上踩,成本低得多。
还有一个务实选择:直接使用别人训练好的预训练模型。这也是现在绝大多数团队的做法——从预训练权重开始做下游任务微调,而不是从头训练。从头训练一个大模型在绝大多数情况下既没有性价比也没有必要性。
4.3 先跑通,再谈优化
我见过很多人一上来就研究各种 SOTA 技巧:数据增强花活、模型结构魔改、分布式训练调优……结果连一个简单 baseline 都没跑通过。
我给自己定的规矩是:任何新任务,第一版必须是一个最朴素的 baseline——小模型、简单结构、默认超参数。目标是打通数据加载、模型定义、训练循环、评估指标这整条链路,并拿到一个有参考意义的初始分数。然后以这个 baseline 为锚点,逐步加技巧,每次只改一个变量,看这个变量对结果的影响是否正向。这个方法俗,但极其有效。它保证你的每一次改动都能落在可比较的基准上,而不是在黑暗中乱撞。
5. 实战中真正踩过的坑:三条最容易翻车的经验
我不是理论派,说自己踩过的坑比说一百遍理论更有说服力。下面是三个我反复遇到、并且几乎每个团队都会遇到的典型问题。
5.1 数据泄漏:模型"作弊"了你还不知道
有一次做用户行为预测,前期离线测试准确率接近 90%,上线之后惨不忍睹。排查了很久,发现原因非常低级:做特征工程的时候,我用到了"用户当次行为之后"的信息——比如用户是否在第二天回访,这个标签本身就是预测目标,我用它做了特征。模型在训练时可以直接"看到答案",离线评估自然好看到爆。
数据泄漏很难完全避免,尤其是特征工程环节。我的经验是:每做一版特征,都要问一遍"这个特征在预测时刻,我是否真的已经能知道?"如果是用未来信息预测过去,那就是泄漏。另一个实操办法是:把泄漏特征单独剔除后再跑一遍 baseline,对比两个模型在验证集上的差距,如果差距大得离谱,基本可以断定有泄漏。
5.2 显存溢出:不是你代码写得不对,是没算好账
做视觉或大模型训练时,GPU 显存溢出(OOM)是家常便饭。很多人一看到 OOM 就以为是代码 bug,其实大多是显存预算没算清楚。
显存消耗主要来自三块:模型参数本身(包括优化器状态的额外副本)、每个 batch 的中间激活值、框架的临时缓存。模型参数好算,但中间激活值是大头,它和 batch size、序列长度、网络深度都成正比。我处理 OOM 的顺序一般是:先减小 batch size 看能否跑通(这是成本最低的调整),再做梯度累积(用多个小 batch 的梯度累加模拟大 batch),还不行就换混合精度训练(FP16/FP32 混合,显存占用几乎减半),最后才考虑模型并行或梯度检查点这类偏复杂的方案。
另一个容易忽略的点:PyTorch 的torch.cuda.empty_cache()并不能真正节省显存,它只是把缓存标记为可复用。真正要减少的,是每个 tensor 的实际占用。排查显存分配问题,可以用torch.cuda.memory_summary()或者nsys profile工具逐段分析。
5.3 随机种子与复现性:你上周的结果这周跑不出来了
深度学习里有非常多的随机源:参数初始化、数据加载顺序、数据增强、dropout。如果不固定随机种子,同一个脚本跑两次结果不一样是常态。这在科研和工业上线场景里都是大问题——你没法判断某个改动是真的有效,还是只是随机波动。
我现在的做法是:凡是重要的实验,统一在脚本入口固定三个种子——Python 随机种子、NumPy 随机种子、框架随机种子(PyTorch 里还有torch.manual_seed和 CUDA 相关种子)。同时把数据加载器的shuffle行为固定住,把能关掉的确定性相关开关开起来。即使这样,我也接受一个事实:完全复现是不现实的,尤其是 GPU 上的并行操作本身就有不确定性。所以对超参数比较,我通常做多次重复实验取平均,而不是单次跑完就下结论。
6. 从总章出发的学习路径:下一步往哪走
总章的作用是搭框架,框架有了,接下来就是往里面填东西。我在带人的时候经常被问:该学哪些数学?该读哪些代码?我给的建议很朴素,不追求高大上,只追求实用。
6.1 数学底线:会用就行,别被公式劝退
很多人一看深度学习要学数学就头大。我的观点是:你需要的是数学直觉,不是严格的数学证明。线性代数你至少要懂矩阵乘法、矩阵求导、特征分解是怎么回事;概率统计至少要懂期望、方差、常见分布、极大似然估计;微积分至少要懂导数、偏导数、链式法则——尤其是链式法则,反向传播全靠它。
不用买一堆数学教材啃,等你实际遇到一个具体的数学困惑——比如某个 loss 的梯度为什么是这个形式——再去针对性查资料,效率高得多。我现在写网络结构、调 loss 时用到的最多的数学知识,就是这三块的基本功。
6.2 代码主线:手写一个最小训练循环
我不建议一上来就学庞大框架的高级 API。我建议你至少手写一次完整的最小训练循环:自己定义一个小网络(比如两层 MLP)、自己写 forward、自己算 loss、自己写 backward(用自动微分就行)、自己写参数更新。这个过程会让你对"优化器到底在做什么"有很深的体会。
之后把数据加载、模型定义、训练循环、评估函数分别用框架实现一遍。这时候你再看各种开源项目或官方示例,就会觉得它们是"熟悉的陌生人",而不是天书。从第一行代码到能独立训练一个图像分类模型,我见过最快的同学用了两周,慢一点的也就一个月。关键不是速度,是每一步都搞清楚自己在写什么。
6.3 长期建议:以项目带学习
最有效的学习方式永远是带着真实问题学。论文看到一个模型,不如亲手在一个实际任务里跑通它。我特别推荐的方式是复现一个中等难度的开源项目——比如一个文本分类模型或者图像分割模型——从数据准备到模型评估完整跑一遍,然后尝试在里面加一个自己的改动:换一种损失函数、改一下模型结构、加一个数据增强策略,对比效果变化。
这个"改一个变量、对比一次效果"的过程,比你看十篇论文都更有收获。它会逼你把前面说的数据、损失、优化、评估这些环节串起来,形成真正的项目经验。
总章就是这些。没有深入任何一个具体算法的细节,但如果你能把这里的每一节都真正理解透——算法和模型的关系、五大模型家族在解决什么样的问题、训练闭环的四个环节、选型的三条约束、实战的三大坑——你就已经有了一个清晰的深度学习世界观。下一篇开始,我们会逐个进入具体算法/模型的内部,把它们一层层拆开来看。