跳转至

深度学习基础

深度学习基础

深度学习是 2012 年之后 AI 全面爆发的那次「技术跃迁」:图像识别超过人类、语音助手进入手机、AlphaGo 击败李世石,直到今天的大模型,底层的核心引擎都是深度学习。讲清深度学习是什么、神经网络怎么构成、怎么训练、CNN/RNN/GNN 等经典架构各解决什么问题,并用 Encoder-Decoder 解释变长序列如何训练与推理,为下一站 Transformer 架构 与 大模型基础 铺路。机器学习的基础概念(损失函数、过拟合、评估)见 机器学习基础,通用的训练、调试、部署与 MLOps 见深度学习工程实践。

什么是深度学习

深度学习(Deep Learning)是机器学习的一个分支,用「多层的神经网络」自动从数据中学习特征表示。「深」指的是网络层数多:从几层到上百层,数据在层与层之间逐级加工,越深的层学到越抽象的特征。

表示学习:从手工特征到自动特征

传统机器学习(见 机器学习基础)有一个绕不开的瓶颈:特征要靠人手工设计。识别一张猫的图片,人要告诉模型「耳朵尖、有胡须、脸圆」:这需要大量领域专家时间,而且很多特征人类自己也说不清(怎么用手工特征描述「这首歌伤感」?)。

深度学习把特征设计也变成了学习的一部分:输入层吃原始数据(像素、音频波形、文本 token),第一层学到边缘和色块,第二层组合出纹理与形状,再往上组合出眼睛、耳朵,最终层组合出「猫」这个完整概念:每一层自动从前一层的输出里提炼更抽象的特征,这就是表示学习。

维度传统机器学习深度学习
特征人工设计(耗时、依赖专家)网络自动学习(端到端)
数据需求万级以下就能干活越大越好,小数据容易过拟合
算力需求CPU 即可依赖 GPU/TPU 并行计算
可解释性好(规则、权重可读)差(黑盒,解释要靠事后归因)
适用数据表格、结构化数据图像、语音、文本等非结构化数据
性能天花板受特征质量限制随数据量与模型规模提升

为什么 2012 年后爆发:数据 + 算力

神经网络的思想 1940 年代就有了,多层结构 1980 年代也有了,为什么 2012 年才起飞?因为深度学习的两个「燃料」直到那时才配齐:

  1. 数据:互联网沉淀出海量可用的数据(ImageNet 千万级标注图片、全网文本语料),深度学习是数据越多越强的模型:喂不饱,它就发挥不出优势。
  2. 算力:GPU 的并行计算让矩阵运算快了几个数量级。2012 年 AlexNet 在 ImageNet 竞赛上把错误率从 26% 打到 15%,靠的就是两块 GPU 训练出的深度卷积网络。这个事件被普遍视为深度学习时代的奇点时刻。

之后的路径清晰可辨:2014 年 VGG/GoogLeNet 加深网络,2015 年 ResNet 解决深网络难训练问题,2017 年 Transformer 登场,2018 年 BERT/GPT 开启预训练大模型时代,2022 年 ChatGPT 把这一切变成大众产品。这条路的每一步,都是更大的数据 + 更大的模型 + 更强的算力的循环。

端到端学习:一条流水线 vs 一段段拼装

传统方法做语音识别,是「声学模型 → 语言模型 → 词典」多段流水线,每一段单独训练、误差在段间累积。端到端学习让一个神经网络直接吃「音频波形 → 输出文字」,中间的特征、对齐、语言规律全部由网络自己学。省去大量领域工程的代价是:需要海量配对数据和大量算力,且中间过程不可观测、难调试。

产品经理的取舍:端到端上限高但难控(错了不知道错在哪段),分段模型每段可解释、可替换但误差累积。工程上常用折中:先分段做,再在数据足够后逐步端到端化:这也是语音助手、机器翻译产业演进的真实路径。

深度学习的核心假设:分层特征有效

深度学习赌的是一个关于世界的假设:复杂概念可以由简单概念分层组合而成:图像由边缘组合成纹理、纹理组合成部件、部件组合成物体;语言由字母组合成词、词组合成短语、短语组合成句子。如果这个假设成立(事实证明它在视觉与语言上都成立),分层堆叠就是参数效率最高的表达方式:每一层都复用前一层学到的零件。

局限与挑战:大力并非万能

深度学习的短板,产品经理要心里有数:

  • 数据饥渴:性能与数据量强相关,小数据场景表现平平,数据质量差时「垃圾进、垃圾出」会被放大。
  • 黑盒难解释:决策过程不可直接读,监管、审计、用户信任都需要额外成本(见文末「可解释性现状」)。
  • 分布漂移敏感:训练分布与线上分布不一致时性能跳水;对抗样本(肉眼无差别的微小扰动)可以轻易骗过模型。
  • 算力与环境成本:训练与推理都耗能,端侧部署要专门做压缩(量化、蒸馏)。
  • 缺乏常识与因果:模型学的是相关性,不是因果:换一个它没见过的组合方式就可能犯低级错误。

这些局限是深度学习 vs 传统 ML vs 大模型选型的底层依据,完整对比见 机器学习基础 与 模型能力与边界。

产品经理视角:为什么「大力出奇迹」只在深度学习成立

传统 ML 的性能随数据增长先升后平(特征到头了),深度学习的性能在数据量足够后继续随数据与参数同步增长:这就是「Scaling Law」的雏形,也是行业敢持续烧钱堆数据堆算力的底气。

前提是:数据要够多。小数据场景深度学习反而打不过传统 ML,取舍见文末「产品视角」。

神经网络的基本构成

神经元:线性变换 + 激活

神经网络的最小单元是人工神经元,它做两件事:

  1. 线性变换:把输入 x 与权重 w 加权求和,再加一个偏置 b:z = w₁x₁ + w₂x₂ + … + b。
  2. 非线性激活:把 z 过一个非线性函数 f,输出 a = f(z)。

没有第 2 步,多层线性变换叠起来还是线性(一堆直线拼不出曲线),网络再深也表达不了复杂关系:激活函数是神经网络变弯的来源。多个神经元并排组成一层,层与层之间全连接(每个输入连到每个神经元),就是全连接网络;多层堆叠就是多层感知机(MLP)。

flowchart LR
    input["原始输入<br/>像素 / 音频 / token"] --> layer1["隐藏层 1<br/>线性变换 + 激活"]
    layer1 --> layer2["隐藏层 2<br/>组合更抽象特征"]
    layer2 --> head["输出层<br/>类别概率 / 回归值"]
    head --> loss["损失函数"]
    loss -.梯度回传.-> layer2
    layer2 -.梯度回传.-> layer1

核心关系:隐藏层把原始输入逐层变成抽象表示,输出层产生任务结果,损失再沿计算图反向提供更新方向。

层、权重与偏置

  • 层(Layer):一组并行的神经元。输入层吃数据,隐藏层逐级加工,输出层给结果(分类任务输出层是各类别概率)。
  • 权重(Weight):每条连接的重要性,训练要学的核心参数。
  • 偏置(Bias):每个神经元的自带底数,让激活可以在输入全 0 时也产生输出。

一个直觉:权重决定重视哪些输入,偏置决定什么条件下才激活。参数量 = 上一层神经元数 × 本层神经元数 + 本层偏置数:全连接层参数随层宽平方级增长,这是后来 CNN 用局部连接省参数的动机。

通用近似定理与参数初始化

  • 通用近似定理:只要神经元够多、激活函数非线性,单隐藏层网络就能以任意精度逼近任何连续函数。听起来「一层就够了」?实际不行:单层需要指数级多的神经元,而深层的网络能用少得多的参数表达同样函数。深度的价值是参数效率,不是表达能力的有无。
  • 参数初始化:训练开始时权重不能全为 0(所有神经元对称,梯度一样,永远学不出差异),也不能太大(一开始就梯度爆炸)。主流做法:按层大小设计的随机初始化(Xavier 针对 Sigmoid/Tanh、He 针对 ReLU),让每层输出的方差大致不变,训练才稳。

激活函数四兄弟

函数形状与公式(文字表述)特点与用途
SigmoidS 形曲线,输出 0 到 1二分类输出概率的经典选择;两侧饱和,深层网络里梯度容易消失,隐藏层基本退役
TanhS 形曲线,输出 -1 到 1,零中心比 Sigmoid 收敛快一点(输出均值接近 0);同样两侧饱和,深度网络仍易梯度消失
ReLU负数压成 0,正数原样保留计算极快、正区间梯度恒为 1,深度网络主力激活;缺点是负数区梯度为 0,可能导致神经元死亡(永远不再激活)
GELU对输入按高斯概率做软开关平滑、非单调,Transformer 与大模型 FFN 的主流选择(ReLU 的平滑改良版)

一句话选型:隐藏层默认 ReLU(或其变体 Leaky ReLU),Transformer 用 GELU/SwiGLU,输出层看任务:二分类用 Sigmoid,多分类用 Softmax,回归不用激活或线性输出。

损失函数:MSE 与交叉熵

损失函数告诉模型错得有多离谱(定义见 机器学习基础「损失函数」一节),深度学习的两个默认选择:

  • 均方误差(MSE):回归任务。预测与真实的差平方求平均。
  • 交叉熵:分类任务。衡量预测概率分布与真实标签分布的距离,配合 Softmax 输出层使用:它的梯度直接正比于误差大小,误差大就学得快,不会像 Sigmoid + MSE 那样误差大时梯度反而被压没。

前向传播与反向传播

训练神经网络只有两个阶段,来回循环:

前向传播:数据从输入流到输出

把一批数据从输入层喂进去,逐层做线性变换 + 激活,最后在输出层得到预测结果,拿损失函数算出预测与真相的差距。前向传播就是一次完整的走网络:它不改变任何参数,只是算出当前模型错多少。

反向传播:误差从输出流回输入

反向传播(1986 年 Rumelhart 等人系统化)解决的是误差怎么分摊给每个参数。核心是微积分的链式法则:

  • 损失对输出层的梯度最容易算(直接拿预测减真相);
  • 输出层的梯度往前传一层时,乘上这一层的激活导数与权重,得到更前一层参数的梯度;
  • 如此逐层往回,每个参数都拿到自己该为误差负多少责的数值,即损失对各权重的导数。

直觉就是把误差一层层分发回去:最后一层先说「我错了这么多」,前一层听到后按「我对这个错贡献了多少」分账,再往前传。有了每个参数的梯度,就可以用梯度下降更新参数,让损失变小。

flowchart LR
    batch["输入 batch"] --> forward["前向传播"]
    forward --> prediction["预测输出"]
    prediction --> loss["损失函数"]
    loss --> backward["反向传播:计算梯度"]
    backward --> update["优化器更新权重"]
    update -.-> forward

核心关系:前向传播计算当前模型错多少,反向传播把误差分配给参数,优化器更新权重后进入下一轮。

计算图与自动微分

现代框架(PyTorch、TensorFlow)把网络记录成一张计算图,前向传播时顺手把每个运算的导数规则记下来,反向传播时自动沿图反向累乘:你永远不需要手算梯度。

这正是深度学习能快速迭代的工程基石。

训练循环的三个时间单位

工程师嘴里的「训练了几轮」是产品经理常听到的黑话,三个词说清:

术语含义直觉
Step(步)用一个 batch 的数据做一次前向 + 反向 + 参数更新走一步
Epoch(轮)把整个训练集完整过一遍把全部教材学一遍
Batch size(批大小)每步用多少样本每口吃多少

一轮的步数 = 样本总数 ÷ 批大小。比如 10 万样本、批大小 256,一个 epoch 约 391 步,训练 30 个 epoch 就是 1 万多次参数更新。epoch 越大越好是错觉:训练损失还在降、验证损失开始升的那一刻,就是该停的时点(早停)。

优化训练

梯度下降三兄弟

训练就是沿着损失下降最快的方向(负梯度)更新参数。按一次用多少数据算梯度,分三种:

方式每次更新用多少数据特点
批量梯度下降全部数据方向准但极慢,大模型根本用不起
随机梯度下降(SGD)1 个样本每步噪音大、震荡,但快;几乎只在教学里出现
小批量 SGD(mini-batch)一批(如 32-512 个样本)兼顾效率与稳定,深度学习的事实标准

学习率与优化器

  • 学习率:每次更新的步长。太大→震荡甚至发散;太小→训练慢得像蜗牛。现代做法是学习率调度:先 warmup(从小步长爬升,稳定早期训练),再按余弦/阶梯衰减。
  • 动量(Momentum):让更新带上惯性,沿历史方向的累积前进,冲过小坑、加速收敛。
  • Adam / AdamW:每个参数按历史梯度的均值与方差自适应调整步长,几乎免调学习率,是当前事实默认;AdamW 修正了 Adam 与权重衰减的耦合,大模型训练的标准选择。

梯度消失与爆炸:深网络的生死线

反向传播是连乘:每往回传一层,梯度就乘一次激活导数和权重。如果每层都乘小于 1 的数,传到浅层时梯度趋近 0:梯度消失,浅层永远学不动,网络实际退化成浅层;反过来每层都乘大于 1 的数,梯度爆炸,参数一步跨飞。

为什么 ReLU 和残差重要:ReLU 正区间的导数恒为 1,乘它不衰减;残差连接(ResNet)让梯度有一条直通快车道(见 CNN 一节)。另外还有:权重初始化(用 Xavier/He 初始化把初始方差控制好,避免开局就消失)、批归一化(把每层输入拉回标准分布)、梯度裁剪(梯度过大就砍一刀)。

归一化:批归一化与层归一化

归一化把每层的输入拉回均值 0、方差 1 的稳定区间,让深层网络不再被激活值尺度漂移折磨:

  • 批归一化(BN):按一个 batch 内、每个特征维度统计均值方差归一化。CNN 标配,大幅加速收敛;缺点:依赖 batch 大小(小 batch 不稳)、训练与推理行为不一致(推理用累计统计量)。
  • 层归一化(LN):按每个样本、所有特征维度统计归一化,与 batch 无关,训练推理行为一致:这正是 Transformer 选它不选 BN 的原因(详见 Transformer 架构)。

损失曲面:局部最优与鞍点

高维损失函数的形状不是我们熟悉的一个大坑,而是崎岖的高维山地:

  • 局部最优:周围都比它高的小坑,梯度下降可能困在里面。高维下其实没那么可怕,大量研究表明局部最优的损失往往与全局最优接近。
  • 鞍点:某个方向是下坡、另一个方向是上坡的马鞍,梯度接近 0 但既不是峰也不是谷:高维空间里鞍点远比局部最优常见,这才是训练卡住的头号嫌疑。

对策就是上一节的优化器家族:动量冲过鞍点、Adam 自适应步长,加上随机小批量带来的梯度噪声,大部分卡住都能被救回来。对产品经理而言,理解这一点就够了:训练不收敛常见,真到了死路罕见,先怀疑学习率与数据,再怀疑损失函数设计。

过拟合正则化全家桶

深度模型参数多,是过拟合重灾区。防过拟合的手段(除传统 ML 那套外):

  • Dropout:训练时随机关掉一部分神经元(输出置 0),强迫网络不依赖特定神经元组合:效果等价于同时训练一堆子网络再平均;只在训练时生效,推理时全部保留。
  • 权重衰减(L2 正则):损失里加权重平方惩罚,把权重压小,模型变「钝」更抗噪。
  • 数据增强:对图像做翻转/裁剪/变色、对文本做同义改写,免费扩充数据(见 机器学习基础)。
  • 早停(Early Stopping):盯着验证集损失,验证损失开始回升就停:训练损失还在降但模型已经在背答案了。

超参数速查:训练效果不好先查谁

现象优先怀疑对策
损失不降学习率(太大震荡/太小爬行)、数据没归一化调学习率,看损失曲线形状
训练好、验证差过拟合加正则、减模型、加数据增强、早停
训练验证都差欠拟合 / 数据问题换更大模型、查数据质量与标签
梯度爆炸(NaN)学习率过大、初始化不当调小学习率、梯度裁剪
收敛极慢学习率太小、优化器不合适换 Adam、加大学习率加 warmup

这些排查顺序有个共同原则:先看损失曲线,再动超参数:损失曲线的形状(震荡/平滑/先降后升)会直接告诉你问题出在哪一类。

卷积神经网络(CNN)

卷积神经网络专为图像设计,核心洞察:相邻像素比相隔万里的像素关系大得多,且同一特征(边缘、纹理)在全图各处都适用:因此不需要每个位置一套参数。

典型 CNN 把空间结构逐步压缩成语义表示,再接不同任务头:

flowchart LR
    image["输入图像"] --> conv1["卷积块 1<br/>边缘 / 色块"]
    conv1 --> pool1["池化 / 下采样"]
    pool1 --> conv2["卷积块 2<br/>纹理 / 形状"]
    conv2 --> pool2["池化 / 下采样"]
    pool2 --> feature["高层特征图"]
    feature --> classify["分类头"]
    feature --> detect["检测头"]
    feature --> segment["分割头"]

核心关系:卷积与下采样逐层扩大感受野、提炼特征,最后保留空间信息的任务接检测或分割头,压缩成整图语义的任务接分类头。

卷积运算:局部连接 + 权重共享

卷积层用一个小的卷积核(如 3×3)在输入图像上滑动,每个位置计算核与局部像素的加权和,得到一个输出值:

  • 局部连接:每个输出只看核大小的局部区域,参数从全连接的全图规模降到核规模。
  • 权重共享:同一个核滑遍全图,同一套权重识别全图各处的同一种特征:参数再降一个量级,同时带来平移等变性:猫往左挪一格,识别「猫」的特征响应也跟着挪一格,模型天然对位置不敏感。
  • 步长(Stride):核每次滑动几格,步长 2 等于输出缩小一半(下采样)。
  • 填充(Padding):输入边缘补 0,让边缘像素也能被核覆盖、控制输出尺寸。

池化与感受野

  • 池化(Pooling):对局部区域取最大值(最大池化)或均值(平均池化),进一步下采样,同时提供少量平移不变性;没有参数。
  • 感受野:输出上一个点往回看到的输入区域大小。层越深、卷积叠加越多,感受野越大:浅层看细节、深层看整体,这就是 CNN 层次化特征的来源。

经典结构演进

网络年份关键贡献
LeNet1998第一个实用 CNN,手写数字识别
AlexNet2012ImageNet 冠军,深度 + ReLU + Dropout + GPU,深度学习奇点
VGG2014规律堆叠 3×3 卷积,证明深的价值;代价是参数多、算力贵
ResNet2015残差连接:让层学残差(输出 = 输入 + 学到的变化),梯度有了直通路径,百层网络第一次可训练:深度学习深度的分水岭

常用卷积技巧

  • 1×1 卷积:只跨通道、不跨空间的卷积,作用是调整通道数(升维/降维)和跨通道融合:ResNet 的 Bottleneck、轻量化网络的标配。
  • 小核堆叠:两个 3×3 卷积的感受野等于一个 5×5,参数却更少、非线性更强:VGG 之后的默认习惯。
  • 深度可分离卷积:把标准卷积拆成逐通道卷积 + 1×1 逐点卷积两步,参数与计算量降到约九分之一,代价是精度略降:MobileNet 系列用它在手机上跑出实时图像识别。

CNN 的三大任务形态

CNN 不只是图像分类这一个形态,同一套卷积-池化骨架撑起了计算机视觉的半壁江山:

任务输出形态代表结构
图像分类整图一个类别卷积骨干 + 全连接分类头
目标检测框出每个物体的位置与类别YOLO(一步到位)、Faster R-CNN(先提候选再分类)
语义分割每个像素一个类别U-Net(编码-解码 + 跳跃连接)、DeepLab

检测与分割这类输出也是空间结构的任务,是理解卷积特征图一路保留空间位置信息的最佳例子:分类把空间信息压缩没了,检测与分割必须留着它。

CNN 为什么适合图像:参数少(不易过拟合)、自带平移等变(位置变化不用重新学)、层次化感受野天然匹配局部细节 → 整体语义的视觉结构。这套优势让它统治图像十年;直到 ViT 证明图像切成 patch 当 token 扔进 Transformer 也能赢(见 Transformer 架构 与 多模态理解)。

循环神经网络(RNN)

序列建模:共享参数地处理每个时刻

文本、语音、时间序列的共同点是顺序:第 t 个词的意思依赖前 t−1 个词。RNN 的思路是:同一套循环权重在每个时刻重复使用,每步输入当前 token 与上一步的隐状态,输出本步隐状态:

  • 循环权重:一个记忆模块,把过去的信息压缩进隐状态一路带下去:RNN 的参数与序列长度无关(共享),这是它与全连接的本质区别。
  • 训练用随时间反向传播(BPTT):把时间轴展开成深网络再反向传播。

沿时间展开后,每个时刻都复用同一个 RNN 单元:

flowchart LR
    x1["x₁"] --> cell1["RNN 单元"]
    h0["h₀"] --> cell1
    cell1 --> h1["h₁"]
    h1 --> cell2["RNN 单元"]
    x2["x₂"] --> cell2
    cell2 --> h2["h₂"]
    h2 --> cell3["RNN 单元"]
    x3["x₃"] --> cell3
    cell3 --> h3["h₃"]
    cell1 -.共享参数.-> cell2
    cell2 -.共享参数.-> cell3

核心关系:每个时间步接收当前输入与上一步隐状态,输出新的隐状态;单元参数共享,所以序列变长不会新增一套权重。

长依赖问题与 LSTM/GRU

RNN 的隐伤和深网络同源:误差沿时间步连乘,几步之后梯度消失:距离远了,前面的话就记不住了(梯度爆炸同理,靠梯度裁剪治)。长依赖是序列建模的核心难题。

LSTM(长短期记忆,1997) 的解法是给记忆装门:用三个门控制信息的流进、流出与遗忘:

  • 遗忘门:决定上一刻的记忆保留多少;
  • 输入门:决定当前新信息写进记忆多少;
  • 输出门:决定此刻的记忆对外输出多少。

GRU 是 LSTM 的简化版,把三个门合并成两个(更新门 + 重置门),参数更少、训练更快,效果通常与 LSTM 相当。两个模型的本质都是给长期记忆修了一条可以稳定传梯度的通道:门控让信息要么几乎原样保留、要么明确丢弃,而不是被连乘慢慢磨没。

双向 RNN:让每个词看见前后文

普通 RNN 只按时间正序读序列,第 t 步只能看见过去的词。双向 RNN 同时跑一个正向和一个反向的 RNN,每个位置把两个方向的隐状态拼接起来:于是每个词都同时获得前后文信息。命名实体识别、情感分析这类必须看后文才能定的任务,双向是标配。一个简单的例子:「苹果发布会」里的「苹果」到底是水果还是公司,要看了后文才知道:单向模型在「发布会」这个词上才恍然大悟,双向模型在「苹果」处就已经有正确答案了。

RNN 家族一览

模型核心机制优势短板
朴素 RNN循环权重 + Tanh结构最简单长依赖梯度消失,记不住远距离信息
LSTM遗忘/输入/输出三门长记忆能力强参数多、训练慢
GRU更新门 + 重置门参数少、效果接近 LSTM略弱于 LSTM 的极致长记忆
双向 RNN正反两个方向拼接每步获得完整上下文不是因果结构,不能做自回归生成

这张表的演化主线就是怎么让信息传得更远:从循环连乘,到门控保护,再到双向补全。理解这条主线,就理解了 Transformer 为什么要彻底换一种传信息的方式(见 Transformer 架构)。

序列到序列与注意力的起源

机器翻译、摘要、对话这类序列 → 序列任务,经典结构是 seq2seq:编码器把整句压成最后一个隐状态,解码器再展开生成。瓶颈明显:一个定长向量装不下整句信息,长句必丢。

Bahdanau 等人把注意力加进这条链路:解码每一步不再只看编码器的最终向量,而是按当前解码状态给源序列各位置打分,加权读取相关隐状态(Neural Machine Translation by Jointly Learning to Align and Translate)。注意力因此先是机器翻译里的对齐模块,后来才被 Transformer 升级成唯一骨干。机制展开见 Transformer 架构。

Encoder-Decoder 的信息流与张量形状

张量形状、Teacher Forcing、collate_fn 与 loss mask 的工程契约见 深度学习工程实践。下面只保留信息流,便于衔接到 Transformer。

机器翻译把一个源序列转换成另一个目标序列。设源序列为 x₁...xₛ,目标内容 token 为 y₁...yₘ,训练样本会构造成 tgt=[<BOS>, y₁...yₘ, <EOS>]。源长度 S 与目标内容长度 M 没有固定相等关系,也不存在可靠的长度换算公式。中文分词、英文分词和语序都会改变 token 数量。

Encoder 读取完整的源序列,Decoder 根据源序列表示和已经生成的目标前缀逐步预测下一个 token:

1
2
z = [y₁,...,yₘ,<EOS>]
p(z | x) = ∏ᵢ p(zᵢ | z₁...zᵢ₋₁, x₁...xₛ)

公式中的最后一步就是预测 <EOS>。

Encoder 与 Decoder 通常是两个独立的 RNN 模块:Encoder 的循环权重在所有源时间步共享,Decoder 的循环权重在所有目标时间步共享,但两套权重默认不相同。Encoder 的最终状态可以作为 Decoder 的初始状态,也可以把每个源位置的隐状态交给 Attention 使用。

以 batch_first=True 为例,下面的 S 是补齐后的源序列长度,T 是补齐后的目标 tensor 长度,目标 tensor 已包含 <BOS> 和 <EOS>,常见接口形状如下:

对象典型形状含义
Encoder 输入[B, S]源 token ID
Encoder 输出[B, S, D×H]每个源位置的隐状态,双向时拼接两个方向
Decoder 输入[B, T-1]<BOS> 加上目标前缀
Decoder logits[B, T-1, V]每个位置对词表的预测
目标标签[B, T-1]右移后的真实 token
LSTM hidden[L×D, B, H]层数、方向、batch、隐藏维度

其中 B 是 batch size,H 是 hidden size,V 是目标词表大小,L 是 RNN 层数,D 是方向数。双向 Encoder 通常有 D=2,自回归 Decoder 通常有 D=1。工程实现必须统一 batch_first、hidden state 形状和源/目标词表的约定。

训练时:目标右移与 Teacher Forcing

训练数据要显式加入特殊 token:<BOS> 表示开始,<EOS> 表示目标序列结束,<PAD> 只用于把一个 batch 补齐。目标句为:

1
2
3
目标序列:    [y₁, y₂, y₃, <EOS>]
Decoder 输入:[<BOS>, y₁, y₂, y₃]
监督标签:    [y₁, y₂, y₃, <EOS>]

这种右移让 Decoder 在第 i 步根据前缀预测 yᵢ。Teacher Forcing 指训练时把真实的前一个 token 喂给 Decoder,而不是使用模型刚刚预测的 token。它让训练稳定、可以批量计算,但也造成训练和推理的输入分布不同:推理时一个错误会成为下一步的输入,这种不一致叫 exposure bias。工程上先用真实自回归评估确认错误是否由此引起;仍需使用 RNN 且误差累积明显时,再评估 scheduled sampling,长文本转换任务则通常比较 Transformer 基线。

训练时应把 Teacher Forcing 下的 loss 与真实自回归推理的质量分开记录。前者用于优化,后者才接近线上表现。<PAD> 位置不能参与 loss,否则模型会学会预测补位符,而不是学习翻译。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 示意代码:省略模型定义、设备处理与 batch 整理
# tgt: [B, T],包含 <BOS>、真实 token、<EOS>、<PAD>
decoder_in = tgt[:, :-1]   # [B, T-1]
gold = tgt[:, 1:]           # [B, T-1]

logits = model(src, decoder_in)  # [B, T-1, V]
loss = F.cross_entropy(
    logits.reshape(-1, vocab_size),
    gold.reshape(-1),
    ignore_index=PAD_ID,
)

变长 batch:padding、mask 与 packing

单条样本可以是不同长度,但 GPU 通常需要把多个样本组成规则张量。因此 DataLoader 会把短序列补成 batch 内的最大长度:

  • <PAD>:为了对齐形状加入的占位符,没有语义。
  • <EOS>:表示一条序列在语义上结束,参与目标标签。
  • Padding mask:告诉 Encoder 或 Attention 哪些位置是补位,避免模型读取 <PAD>。
  • Causal mask:限制 Decoder 不能看到未来 token,主要用于 Transformer Decoder。
  • Loss mask:计算损失时忽略 <PAD> 位置,通常通过 ignore_index=PAD_ID 实现。

RNN 中可以用 pad_sequence 组成 batch;当 padding 比例很高时,Encoder 可以使用 pack_padded_sequence 跳过补位计算,也可以先按长度分桶,让同一 batch 的样本长度更接近。使用 packing 时要同步维护原始 lengths、样本顺序和反排序逻辑,不能拿 padding 后的长度代替真实长度。

1
2
3
4
5
6
7
8
9
# 示意代码:batch 中每项是 (src_tensor, tgt_tensor)
def collate_fn(batch):
    src_items, tgt_items = zip(*batch)
    src_lengths = torch.tensor([len(x) for x in src_items])
    tgt_lengths = torch.tensor([len(x) for x in tgt_items])

    src = pad_sequence(src_items, batch_first=True, padding_value=PAD_ID)
    tgt = pad_sequence(tgt_items, batch_first=True, padding_value=PAD_ID)
    return src, src_lengths, tgt, tgt_lengths

最常见的变长 bug 是:长度字段和 padding 后的 batch 顺序不一致、mask 在 CPU 而 logits 在 GPU、把 <EOS> 也屏蔽掉、或让 <PAD> 参与交叉熵。排查时先打印一条样本的 token、长度、mask 和右移后的输入/标签,再看整体 loss。

推理时:<BOS>、<EOS> 与最大长度

推理时没有真实目标句,Decoder 从 <BOS> 开始,每一步把自己生成的 token 作为下一步输入。生成 <EOS> 后,该样本结束;如果模型始终不生成 <EOS>,达到 max_length 后强制结束。max_length 是防止死循环和异常输出的安全上限,不是模型真正理解出的目标长度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 示意代码:批量贪心解码,省略 beam search 与状态缓存
state, memory = model.encode(src)
prev = torch.full((src.size(0),), BOS_ID, device=src.device)
finished = torch.zeros(src.size(0), dtype=torch.bool, device=src.device)
outputs = []

for _ in range(max_length):
    logits, candidate_state = model.decode_step(prev, state, memory)
    next_token = logits.argmax(dim=-1)
    active = ~finished
    next_token = torch.where(
        active,
        next_token,
        torch.full_like(next_token, PAD_ID),
    )

    # 生产实现要冻结已结束样本的 state,只更新 active 样本
    state = keep_state(state, candidate_state, active)
    outputs.append(next_token)
    finished |= active & next_token.eq(EOS_ID)
    prev = next_token

    if finished.all():
        break

长度上限应从数据和业务约束共同确定:

  1. 统计验证集目标长度分布,用较高分位数加安全余量作为默认上限。
  2. 按用户可接受的等待时间、输出大小和成本设置绝对上限。
  3. 源序列长度的线性启发式只能作为兜底,不能把源长度直接当作目标长度。
  4. 线上记录 <EOS> 命中率、max_length 截断率、空输出率、输出长度分布和目标/源长度比。

如果训练标签中从未出现 <EOS>,模型就没有学到停止信号;如果第一个预测就是 <EOS>,需要按业务定义为空输出还是合法的空目标。批量推理时不同样本可以在不同时间结束,必须用 finished mask 隔离已结束样本,不能把后续的 <PAD> 或垃圾 token 当作有效输出。

解码策略:Greedy、Beam Search 与长度惩罚

  • Greedy 解码:每一步选择当前概率最高的 token,速度快、结果稳定,适合作为基线。
  • Beam Search:保留多个候选前缀,继续展开后比较完整序列得分;beam_size=1 可以看作 Greedy。翻译、摘要等任务常用它寻找整体概率更高的序列。
  • 长度归一化或长度惩罚:直接累加 log probability 会偏好短序列,因为每增加一个 token 通常都会增加负分。长度惩罚用来缓解这种短序列偏置,但会带来新的长度偏好,需要在验证集上校准。

Beam Search 的代价是更多候选、更高延迟和显存占用,不代表一定更准确。对比解码策略时至少同时看序列质量、输出长度、<EOS> 命中率、截断率和延迟;开放式聊天通常优先使用采样或 Greedy,机器翻译等转换任务再评估 Beam Search。LLM 运行时的停止字符串和结构化约束见模型推理与部署。

状态生命周期:重置、截断与流式会话

RNN 的 hidden state 是数据的一部分,不是可以随意留在服务进程里的全局变量:

  • 每个独立样本或独立请求默认重置 hidden state,避免不同用户、不同 batch 之间串状态。
  • 只有同一条连续时间序列或同一会话,才跨 chunk 传递 state;服务接口应显式定义传入、返回和过期方式。
  • 使用截断 BPTT 训练长序列时,在 chunk 边界对 hidden state 做 detach,否则计算图会随时间无限增长。
  • 流式服务要定义 session ID、超时、显式 reset、服务重启后的恢复策略;无法可靠持久化时,宁可重新建立状态,也不要静默复用旧状态。
  • 双向 Encoder 可以利用完整源序列的前后文,但不适合直接承担因果生成的 Decoder 角色。
RNN 的现状

大模型时代,RNN/LSTM 在长文本与大规模任务上已被 Transformer 全面超越;但语音、时序预测、小模型场景仍是它的舒适区,而且它对序列长度的计算成本是线性的(Transformer 是平方级):2023 年后 Mamba 等新一代序列模型也在延续线性成本 + 长记忆的路线。

历史作用上,RNN 是理解注意力机制为什么被发明的最佳教材。

图神经网络(GNN)

图数据:关系与结构本身就是信息

朋友关系、分子结构、知识图谱、交通网络:这些数据由节点(实体)和边(关系)构成,没有一行一个样本的规整结构,也没有固定的邻居数量:每个节点的邻居数都不一样。CNN 的卷积核、RNN 的时间步都套不上,于是有了图神经网络。

消息传递:向邻居「要信息」

GNN 的核心机制叫消息传递,每层做三件事:

  1. 聚合:把当前节点的邻居特征收集起来(求和、取平均、取最大,按任务选);
  2. 更新:把自己的特征与聚合到的邻居信息合并,经过线性变换与激活,得到本层新表示;
  3. 重复:堆多层,信息逐层扩散:第 1 层看到直接邻居,第 2 层看到邻居的邻居,层数越多视野越远。

GCN(图卷积网络) 是代表实现:把邻居加权求和写成矩阵运算(邻接矩阵 × 特征矩阵 × 权重),再用度矩阵归一化,避免大度节点(社交达人)的信息淹没小度节点。更进阶的 GAT 在聚合时用注意力给不同邻居分配不同权重:谁重要就多听谁的。

一层消息传递可以画成:

flowchart LR
    neighbor1["邻居节点 A"] --> aggregate["聚合邻居消息<br/>求和 / 平均 / 注意力加权"]
    neighbor2["邻居节点 B"] --> aggregate
    center["中心节点自身特征"] --> update["更新节点表示"]
    aggregate --> update
    update --> next["下一层表示 hᵥ′"]

核心关系:中心节点把自身特征与邻居消息合并,经过更新得到新表示;堆叠多层后,信息视野从一跳邻居扩展到多跳邻居。

三类任务:节点、边、整张图

GNN 的任务按预测对象分三类:

任务预测什么例子
节点分类每个节点的类别论文领域分类、用户画像、欺诈账户识别
链接预测两个节点间有没有边/边强度好友推荐、知识图谱补全、商品关联
图分类整张图的类别/属性分子毒性预测、程序代码语义分析

一个直觉例子:识别转账网络中的洗钱账户:单个账户的特征(金额、频率)不足以判断,但它在网络里的位置(把钱转给谁、谁又转给谁)是强信号。这就是 GNN 的核心价值:把关系结构本身变成特征。

应用:分子、社交、知识图谱

场景节点 / 边任务
分子性质预测原子 / 化学键预测毒性、药效(图分类)
社交网络用户 / 关注关系好友推荐(链接预测)、社区发现
知识图谱实体 / 语义关系知识补全、问答推理
推荐系统用户 / 商品 / 行为兴趣扩散与冷启动
交通/电网传感器 / 连接拥堵、负荷预测(时空 GNN)

词嵌入(Embedding)

分布假说:词的语义藏在它的邻居里

「国王」和「皇帝」为什么相似?分布假说的回答:因为它们在语料里经常出现在相似的上下文里。这是现代 NLP 的起点:把词的语义从人查字典变成统计邻居。

Word2Vec:静态嵌入的里程碑

Word2Vec(2013) 用神经网络把每个词映射成一个低维稠密向量(如 300 维):训练时让模型从上下文预测目标词(CBOW)或从目标词预测上下文(Skip-gram),学完后每个词对应一个向量:语义相近的词向量距离近,甚至出现经典的「国王 − 男人 + 女人 ≈ 女王」式向量运算。

但 Word2Vec 是静态嵌入:一个词只有一个向量,一词多义处理不了(「苹果」是水果还是公司?取决于上下文,向量却只有一个)。同期还有 GloVe(2014),用全局共现统计而非滑动窗口训练,各有所长;工程上如今都已被上下文嵌入取代。

上下文嵌入:ELMo 与 BERT

ELMo(2018) 第一次做到按上下文给词不同的表示:双向语言模型为每个词产出随语境变化的向量。BERT(2018) 更进一步,用 Transformer 双向编码,每个词的表示由整句话动态决定:「苹果」在水果句和科技句里得到完全不同的向量。从此 NLP 进入预训练 + 微调时代:先在海量文本上预训练出通用表示,再在少量标注数据上微调下游任务。

向大模型的过渡:BERT 之后,GPT 系列沿解码器 + 下一个词预测路线把同一个思想推向极致:预训练的规模从亿级参数涨到千亿级,嵌入从词级升级为 token 级、从静态变成逐层动态,最终成为今天的大语言模型(完整链路见 大模型基础)。

嵌入对 LLM 的意义

  • 从词嵌入到 token 嵌入:现代大模型的输入层就是一张可学习的嵌入表,把 token 变成向量,之后每一层都在加工这些向量:Transformer 的整个计算都是向量之间的运算。
  • 语义空间:嵌入空间里距离近 = 语义近,这是向量检索(RAG)的物理基础(见 RAG 与知识检索)。
  • 多模态对齐:CLIP 把图像与文本嵌入到同一空间,是图文搜索与文生图的关键桥梁(见 多模态理解)。
  • 嵌入的现代实现就是 Transformer 的输入层,详见 Transformer 架构。

嵌入在工程里的四个用法

嵌入不只是模型的输入层,它本身是产品可以反复利用的资产:

  1. 语义检索:把文档与查询都转成向量,按余弦相似度找最相关的片段:RAG 与语义搜索的核心,比关键词匹配更能处理同义不同词。
  2. 聚类与分群:把文本/商品/用户嵌入后做 K-means,得到语义分群:工单主题聚类、竞品评论分析,零标注就能出洞察(聚类见 机器学习基础)。
  3. 相似推荐:和你收藏的这篇最像的文章 = 向量最近邻;推荐系统里物品嵌入、用户嵌入的匹配是经典打法。
  4. 可解释诊断:把嵌入降维(如 t-SNE)画出来,肉眼检查模型是否把语义相近的样本聚在一起:模型行为的粗检。
产品经理能用嵌入做什么

嵌入是把非结构化内容变成结构化向量的万能转换器:文本、图片、音频都能变成数字向量,然后接上任何传统 ML 的玩法(检索、聚类、分类)。

很多 NLP 需求的最简实现,就是嵌入 + 向量检索 + 少量规则,不用微调大模型也能上线。

产品视角:深度学习的边界与成本

能力与数据量的关系曲线

深度学习的能力曲线不是线性的,大致三段:

  • 数据很少(千级以下):深度模型大概率过拟合,传统 ML 或迁移学习更稳。
  • 数据中等(万级到百万级):深度学习开始发力,数据越多优势越大;此时预训练模型 + 微调性价比最高:借一个大模型/预训练模型的先验知识,用少量自己的数据适配,是绝大多数 AI 产品团队的真实打法。
  • 数据海量(千万级以上):深度学习/大模型一骑绝尘,传统 ML 触顶:这也是基础模型的领地。

训练成本:算力与数据标注

  • 算力:深度学习训练是算力密集型。一张图分类模型在单卡上小时级;大模型预训练是 GPU 集群×数周的千万级账单(详见 模型训练与对齐 与 模型推理与部署)。
  • 数据标注:监督学习需要标注数据,标注成本常常超过算力成本。缓解手段:自监督预训练(标签免费)、半监督、数据增强、主动学习(只标注模型最拿不准的样本)。
  • PM 的成本观:小任务用预训练模型微调,几乎总是比从零训练便宜一个量级;只有在任务极特殊、数据极多、或要自研基础模型时,才谈得上从零训练。

什么时候值得上深度学习

场景建议原因
图像/语音/复杂文本深度学习(或大模型)特征无法手工设计,表示学习是唯一解
表格数据、数据量小传统 ML树模型又准又可解释,深度学习无优势(见 机器学习基础)
低延迟/端侧轻量 CNN/蒸馏小模型深度模型可以压缩到手机端跑,但要专门做轻量化
强可解释合规场景传统 ML 或事后归因深度学习黑盒,监管解释成本高
有预训练模型可借微调而非从零训练数据、算力、时间都省一个量级

可解释性现状

深度学习的黑盒问题是产品化的真实约束。当前主流思路:

  • 事后归因:Grad-CAM(图像热力图)、注意力权重可视化、LIME/SHAP(给局部决策找解释):解释的是模型看了哪里,不是模型为什么这么想,但已经够大部分产品与合规场景使用。
  • 结构先行:用可解释的组件(规则、树)包裹黑盒,或让黑盒输出可解释的中间产物(引用来源、给出依据)。
  • 评估兜底:解释不清,就用充分的测试与监控兜底:把为什么这样换成在各种情况下都测过了(方法论见 评估与评测)。

深度学习的评估指标速览

分类/回归的基础指标(准确率、F1、AUC)见 机器学习基础,深度学习的特色指标补几个:

任务指标含义
图像分类Top-1 / Top-5 准确率第一名命中 / 前五名包含正确答案;ImageNet 时代的行业标准
目标检测mAP(平均精度均值)对不同置信度阈值下的精确率-召回率曲线求面积再平均,兼顾定位与分类
语义分割IoU / mIoU预测区域与真实区域交集 ÷ 并集,衡量画得准不准
生成质量PSNR / SSIM / FIDPSNR 看像素误差、SSIM 看结构相似、FID 看生成分布与真实分布的距离(生成类详见 图像生成)

产品经理只需要记住:不同任务的好长得完全不一样,拿图像分类的准确率去验收检测模型、拿像素误差去验收生成模型,都会得出荒谬结论。验收指标在立项时就和团队对齐,写进验收标准,而不是模型上线前临时拍一个。

给产品经理的一句话

深度学习强在自动学特征,贵在数据 + 算力,短在解释与稳定。选型时先问:特征能不能人工设计?能→传统 ML;不能→深度学习;再问数据够不够、成本扛不扛得住、解释过不过得了关。

它是工具,不是信仰。

落地前清单:八个问题

深度学习项目立项时,把下面八个问题问完再拍板,能避开大部分坑:

  1. 任务能映射成什么输入输出?输入形态(图/文/音/表)直接决定架构选型。
  2. 数据有多少、质量如何?够不够支撑深度学习;不够就考虑迁移学习或传统 ML。
  3. 有没有可借的预训练模型?有就微调,别从零训练:这是性价比第一原则。
  4. 标注成本谁来扛?预算里有没有标注费;能不能用自监督/半监督省标注。
  5. 评估指标在立项时定好了吗?按任务定指标并写进验收标准(见上节)。
  6. 延迟与成本预算?端侧还是云端、每秒多少请求、单次推理预算,决定模型要不要轻量化。
  7. 失败怎么办?错误输出有没有兜底(降级链、人工复核、可回退开关)。
  8. 合规与可解释?数据授权、敏感信息、监管解释需求,越早对齐越省钱。

这八问和 AI 系统架构 的四层地图是同一套思路:先想清楚每一层要什么,再动手。

练习:检验你的理解
  1. 为什么激活函数必须是非线性的?如果全部用线性激活,堆 100 层会发生什么?
  2. 深度网络训练时梯度消失,为什么 ReLU 和残差连接能救?如果换成 Sigmoid,问题为什么更严重?
  3. LSTM 的门解决的是 RNN 的什么问题?这个问题的本质和深网络的什么问题同源?
  4. 公司要做工单文本分类,只有 2000 条标注:你会从零训练一个深度网络,还是用预训练模型微调?为什么?
  5. Word2Vec 与 BERT 的词表示有什么本质区别?「苹果公司」里的「苹果」,哪个模型能正确区分?
  6. 机器翻译中源句长度为 S、译文长度为 T,为什么两者不必相等?<EOS> 与 max_length 各自负责什么?
  7. 一个 batch 中两个样本分别在第 4 步和第 8 步生成 <EOS>,如何用 finished mask、<PAD> 和 loss mask 处理?
  8. Teacher Forcing 为什么会造成 exposure bias?为什么评估时必须运行真实的自回归推理?

参考答案提示:1) 线性函数的复合还是线性函数,堆多少层都等价于一层:非线性是网络表达复杂关系的唯一来源。2) ReLU 正区导数恒 1、残差提供梯度直通路径,连乘不再衰减;Sigmoid 最大导数只有 0.25,连乘必消失。3) 长依赖的梯度消失/爆炸,与深网络的梯度问题本质同源,都是连乘效应。4) 微调:数据太少,从零训练必然过拟合;预训练模型的知识是最大的杠杆。5) BERT 的表示随上下文变化,能区分一词多义;Word2Vec 每个词只有一个静态向量。6) S 和 T 分别由源语言与目标语言的 token 化和表达方式决定,没有固定换算;<EOS> 是模型学到的语义终止信号,max_length 是未终止时的安全上限。7) 各样本独立更新 finished mask,结束后输出位置填 <PAD> 并冻结状态;训练 loss 忽略 <PAD>,不能把补位符当作真实标签。8) 训练使用真实前缀,推理使用模型自己的前缀,错误会逐步累积;只有真实自回归结果才能暴露提前终止、重复和截断问题。

来源说明

本文为原创整理,概念与结论参考以下资料(引用日期:2026-09-04):

AIGC-Interview-Book(仅供维护者交叉验证,读者以公开论文、教材与官方文档为准) - 深度学习核心概念、网络架构、训练优化、注意力与图神经网络等考点提纲(微信读书《AIGC 面试指南》,WeThinkIn 团队;本地预取,非公开链接)

课程笔记(结构参考,未照抄) - 地球科学大数据课程笔记:2026-04-03(ANN 入门)、2026-04-10(ANN + CNN)、2026-04-17(RNN/LSTM/GRU)、2026-04-24(GNN)、2026-05-22(Transformer 第 1-2 节)

经典论文与教材 - LeCun Y, et al. Gradient-Based Learning Applied to Document Recognition (LeNet),1998 - Krizhevsky A, et al. ImageNet Classification with Deep Convolutional Neural Networks (AlexNet),2012(https://papers.nips.cc/paper/2012/hash/c399862d3b9d6b76c8436e924a68c45b-Abstract.html) - He K, et al. Deep Residual Learning for Image Recognition (ResNet),2015(https://arxiv.org/abs/1512.03385) - Hochreiter S, Schmidhuber J. Long Short-Term Memory (LSTM),1997(https://www.bioinf.jku.at/publications/older/2604.pdf) - Mikolov T, et al. Efficient Estimation of Word Representations in Vector Space (Word2Vec),2013(https://arxiv.org/abs/1301.3781) - Devlin J, et al. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding,2018(https://arxiv.org/abs/1810.04805) - 李航《统计学习方法》;Ian Goodfellow《深度学习》(Deep Learning,花书)——神经网络训练与优化原理的权威参考