想象你是一位十项全能教练,要带出一位真正的赛场多面手。百米短跑要爆发力,五千米长跑要耐力,铅球要绝对力量——你不可能只练跑步,也不能把十个项目的时间平均分掉,更不能今天猛攻投掷、明天就把短跑技巧忘得一干二净。练什么、每项练多久、按什么顺序、项目之间互相干扰怎么化解,才是一位教练真正的功力所在。大模型的多任务混合微调,和带十项全能运动员如出一辙。要让一个预训练好的大模型同时掌握摘要、问答、翻译、分类等多项能力,工程师同样要设计一套“训练计划”:任务采样决定每一轮练哪个项目,数据配比决定每项的练习量,任务权重体现教练的偏心,梯度冲突是项目间的身体干扰,灾难性遗忘则是顾此失彼的魔咒。本文就沿着这套“带运动员”的思路,把多任务混合微调的核心原理与实操方法一次讲透。

岗位关联

对 AI 从业者来说,多任务混合微调是让模型“一鱼多吃”的关键工程能力。掌握任务采样、数据配比与混合训练策略,你就能用一份算力训出多任务同时达标的可用模型,显著提升项目交付效率。

目录
  1. 可视化演示
  2. 任务采样:先决定今天练什么项目
  3. 数据配比:每项训练放多少料
  4. 任务权重:教练的偏心要动态调
  5. 梯度冲突:当两个项目练法打架
  6. 灾难性遗忘:练了新项目别忘旧技巧
  7. 混合训练:编排节奏与时序
  8. 评估监控:模拟赛成绩单
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

动态演示多任务混合微调怎么操作的核心机制,通过架构/数据/状态/性能四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

多任务混合微调怎么操作:多任务混 多任务混合微调的基本概念与目标 任务采样策略与数据配比设计 核心机制可视化 多任务混合微调怎么操作整体架构与关键组件

任务采样:先决定今天练什么项目

教练手中有一份训练计划表,上面写着每天练什么项目。如果只专注短跑,其他项目必然荒废;如果十个项目轮流来一遍,每个项目又“摸不到热”。计划表的核心问题是:每个项目在训练时间轴上应该出现多少次、以什么顺序出现——这就是任务采样要回答的问题。

在代码层面,任务采样表现为“每个训练步从哪个任务的数据池里抽取一个小批次”。最常见的三种做法:均匀采样,所有任务轮流出场;按数据量比例采样,数据多的任务出场次数天然更多;带温度的采样,用一个指数旋钮在“均匀”和“按量”之间连续调节。主流框架如 PyTorch 的 ConcatDataset 或 WeightedRandomSampler 都可以实现这类逻辑。

实际工程中,直接按数据量比例采样会让小任务“被淹没”,完全均匀又浪费大任务的数据量。折中方案是引入温度参数 $\alpha$,让采样概率落在两者之间:

这就像教练发现铅球训练器材多、跨栏器材少,于是给铅球排了更多时段,又不至于让跨栏被彻底挤没——$\alpha$ 就是这个“偏心但不失公平”的旋钮。

P(任务t)=Ntα∑j=1TNjα\displaystyle P(\text{任务}_t) = \frac{N_t^{\alpha}}{\sum_{j=1}^{T} N_j^{\alpha}}
  1. 任务数据量 $N_t$第 $t$ 个任务拥有的样本总数,相当于这个项目的训练器材和场地资源。数据量越大,说明可练的内容越丰富,基础出镜率越高。
    NtN_t
  2. 温度指数 $\alpha$教练手心的“偏心旋钮”,取值越大越向大数据量任务倾斜,取 $0$ 则完全均衡,取 $1$ 就是纯按数据量比例采样。
    α\alpha
  3. 分母归一化 $\sum_{j=1}^{T} N_j^{\alpha}$把所有任务的加权数据量加总作为概率分母,保证所有任务采样概率之和恰好等于 $1$。
    ∑j=1TNjα\sum_{j=1}^{T} N_j^{\alpha}
  4. 采样概率 $P(\text{任务}_t)$每个训练步抽中任务 $t$ 的概率。概率大的任务,它的批次会更频繁地进入混合训练流。
    P(任务t)=Ntα∑j=1TNjαP(\text{任务}_t)=\frac{N_t^{\alpha}}{\sum_{j=1}^{T} N_j^{\alpha}}

数据配比:每项训练放多少料

决定了训练计划表之后,还要决定每一堂训练课的内容结构。短跑课里不能全是练起跑反应,还要穿插途中跑、冲刺和放松跑。多任务混合微调也一样,任务之间的比例只是第一层,任务内部的样本结构才是第二层。

数据配比的四个维度:数量均衡指任务之间的样本量比例要合理,避免一个大任务把整个混合数据集“带偏”;难度平衡指每个任务内部要包含简单到困难的梯度样本,全是简单的样本模型只会“抄作业”;领域覆盖指样本要覆盖真实业务中的各种场景,比如摘要任务不能只有新闻、没有论文;质量筛选指剔除噪声、重复、错误标注的脏数据,它们会像训练中的错误动作示范一样带坏模型。

配比最终会体现在混合数据集的期望损失上。如果任务 $t$ 在混合数据集中占 $N_t$ 条样本,那么均匀抽取一个批次时,总期望损失就是各任务损失的加权平均:

想把模型练成“全能型选手”,配比就要像营养师配餐一样讲究:不能因为某个任务数据好采集就无限加量,也不能因为难就干脆不放——各项的“营养比例”要服务于最终比赛(业务目标)的需要。

E[L]=∑t=1TNt∑j=1TNj⋅E(x,y)∼Dt[ℓ(fθ(x),y)]\displaystyle \mathbb{E}[\mathcal{L}] = \sum_{t=1}^{T} \frac{N_t}{\sum_{j=1}^{T} N_j} \cdot \mathbb{E}_{(x,y)\sim \mathcal{D}_t}\left[\ell\left(f_\theta(x), y\right)\right]
  1. 数据占比 $\frac{N_t}{\sum_{j=1}^{T} N_j}$任务 $t$ 的样本量占混合数据集总量的份额。份额越大,这个任务对总损失的“话语权”越大。
    Nt∑j=1TNj\frac{N_t}{\sum_{j=1}^{T} N_j}
  2. 任务期望损失 $\mathbb{E}_{(x,y)\sim \mathcal{D}_t}\left[\ell\left(f_\theta(x), y\right)\right]$在任务 $t$ 的数据分布上,单样本损失的期望值,代表模型在这个项目上的平均失误水平。
    E(x,y)∼Dt[ℓ(fθ(x),y)]\mathbb{E}_{(x,y)\sim \mathcal{D}_t}\left[\ell\left(f_\theta(x), y\right)\right]
  3. 加权求和 $\sum_{t=1}^{T}$把所有任务的期望损失按份额加权后加总,得到混合数据集的整体期望损失。
    ∑t=1T\sum_{t=1}^{T}
  4. 整体期望损失 $\mathbb{E}[\mathcal{L}]$优化器实际最小化的目标。配比一旦改变,这个目标里的各项权重就随之改变。
    E[L]\mathbb{E}[\mathcal{L}]

任务权重:教练的偏心要动态调

教练对运动员总有偏心:跳远是弱项、最容易丢分,就多安排几组专项训练;短跑已经是强项,就维持性训练即可。多任务微调里,这个“偏心”就是任务权重。它的设置决定了模型优化的重点。

任务权重有两种设定方式。固定权重是训练前根据业务重要度人工设定,比如“翻译准确率是核心指标,权重给 2.0,情感分类是辅助,给 0.5”。动态加权则根据训练过程中的损失自动调整,朴素思路是“哪里损失高就练哪里”——损失高说明模型还没搞定这个任务,应该加大力度。用 $\beta$ 控制偏心程度,任务 $t$ 在训练步 $k$ 的权重为:

不过动态加权有个陷阱:如果某个任务的数据本身噪声大,损失永远降不下去,它就会一直“霸占”权重。所以工程上通常会做损失归一化,把不同任务损失的尺度拉到同一量级再比较,避免“嗓门大的任务抢走所有注意力”。

wt(k)=(Lt(k))β∑j=1T(Lj(k))β\displaystyle w_t^{(k)} = \frac{\left(L_t^{(k)}\right)^{\beta}}{\sum_{j=1}^{T} \left(L_j^{(k)}\right)^{\beta}}
  1. 任务当前损失 $L_t^{(k)}$第 $k$ 个训练步时,任务 $t$ 在最近一小批验证样本上的平均损失。损失越大代表这个项目越弱。
    Lt(k)L_t^{(k)}
  2. 偏心指数 $\beta$把损失差放大或缩小的旋钮。$\beta=0$ 时所有任务权重相等,退化为固定权重;$\beta$ 越大,对弱任务的偏心越明显。
    β\beta
  3. 权重分子 $\left(L_t^{(k)}\right)^{\beta}$任务的相对损失水平。弱任务(损失大)分子大,自然获得更高权重。
    (Lt(k))β\left(L_t^{(k)}\right)^{\beta}
  4. 归一化分母 $\sum_{j=1}^{T} \left(L_j^{(k)}\right)^{\beta}$把所有权重压缩到和为 $1$ 的区间,保证权重是“相对偏心”而非绝对值。
    ∑j=1T(Lj(k))β\sum_{j=1}^{T} \left(L_j^{(k)}\right)^{\beta}

梯度冲突:当两个项目练法打架

练短跑练出的爆发力型肌肉记忆,和练长跑需要的耐力型供能系统,在身体层面是互相拉扯的。模型训练里也有类似的“身体拉扯”——不同任务的损失对参数提出的更新要求不一致,就叫梯度冲突。

当一个任务的梯度要把参数往东推、另一个任务的梯度要把参数往西拉时,参数就在原地反复震荡,训练效率极低。量化冲突的工具是余弦相似度:两个任务梯度的夹角余弦接近 $1$ 说明方向一致、互相帮助;接近 $-1$ 说明方向相反、激烈冲突。缓解手段包括:梯度裁剪(限制更新步长)、PCGrad 投影(把冲突分量从另一个梯度上削掉)、分层更新(底层共享、高层按任务分开)。

这就是 PCGrad 的核心思想:既然两个训练动作互相干扰,就改变其中一个动作的发力方向,让它不在另一个动作的反方向上出力。训练中如果发现损失曲线剧烈震荡,不妨算一算各任务梯度间的余弦相似度——如果出现大量负值,就该上投影或分层的“协调方案”了。

cos⁡θ=gi⋅gj∥gi∥∥gj∥,gjproj=gj−gi⋅gj∥gi∥2gi\displaystyle \cos\theta = \frac{g_i \cdot g_j}{\|g_i\|\|g_j\|}, \qquad g_j^{\text{proj}} = g_j - \frac{g_i \cdot g_j}{\|g_i\|^2} g_i
  1. 梯度点积 $g_i \cdot g_j$两个任务梯度的逐分量乘积之和。正值表示方向大体一致,负值表示方向相反。
    gi⋅gjg_i \cdot g_j
  2. 模长归一 $\|g_i\|\|g_j\|$把点积除以两个梯度的模长,得到只反映方向、与大小无关的余弦值,范围在 $-1$ 到 $1$ 之间。
    gi⋅gj∥gi∥∥gj∥\frac{g_i \cdot g_j}{\|g_i\|\|g_j\|}
  3. 投影分量 $\frac{g_i \cdot g_j}{\|g_i\|^2} g_i$梯度 $g_j$ 在 $g_i$ 方向上的投影,即 $g_j$ 中与 $g_i$ 同方向或反方向的部分。
    gi⋅gj∥gi∥2gi\frac{g_i \cdot g_j}{\|g_i\|^2} g_i
  4. 投影去冲突 $g_j^{\text{proj}}$从 $g_j$ 中减去投影分量,保留与 $g_i$ 垂直的部分。这样两个梯度就不再互相打架。
    gjproj=gj−gi⋅gj∥gi∥2gig_j^{\text{proj}} = g_j - \frac{g_i \cdot g_j}{\|g_i\|^2} g_i

灾难性遗忘:练了新项目别忘旧技巧

运动员最怕的一件事:苦练三个月跳远新动作,结果发现原来的跨栏节奏完全找不回来了。模型也一样。在拟合新任务时,参数的更新会覆盖掉旧任务辛苦学到的知识,这叫灾难性遗忘。它是多任务混合微调里最隐蔽的敌人。

缓解灾难性遗忘有三大流派。数据回放最简单直接:训练时持续混入少量旧任务样本,相当于时不时让运动员复习旧动作。正则约束的代表是 EWC(弹性权重巩固):先算出旧任务中每个参数的重要程度,训练时给重要参数绑上“橡皮筋”,不许它们跑太远。参数隔离则用 LoRA、Adapter 这类技术,让不同任务各自使用独立的低秩参数子空间,物理上避免互相覆盖。

EWC 的核心公式是在原损失上追加一个二次惩罚项:

实际工程中,三者常组合使用:用 5% 的旧任务样本做回放(数据回放),对关键层加 EWC 约束(正则),对差异大的任务用 LoRA 隔离(参数隔离)。就像优秀教练既会安排复习课,也会在技术动作上做区分,防止新旧技巧互相污染。

L′(θ)=L(θ)+λ2∑kFk(θk−θk∗)2\displaystyle \mathcal{L}'(\theta) = \mathcal{L}(\theta) + \frac{\lambda}{2} \sum_{k} F_k \left(\theta_k - \theta_k^{*}\right)^2
  1. 旧模型参数 $\theta_k^{*}$多任务训练开始前,模型在第 $k$ 个参数上的取值,代表“旧任务已经学会的知识形态”。
    θk∗\theta_k^{*}
  2. Fisher 信息量 $F_k$衡量旧任务对参数 $\theta_k$ 的敏感程度。$F_k$ 越大,说明这个参数对旧任务越关键,越不能乱动。
    FkF_k
  3. 偏离惩罚 $\left(\theta_k - \theta_k^{*}\right)^2$当前参数与旧参数之差的平方。参数跑得越远,惩罚越大,就像橡皮筋拉得越紧。
    (θk−θk∗)2\left(\theta_k - \theta_k^{*}\right)^2
  4. 约束强度 $\lambda$整体遗忘惩罚的系数。$\lambda$ 太小,约束形同虚设;$\lambda$ 太大,新任务又学不动。
    λ2∑kFk(θk−θk∗)2\frac{\lambda}{2} \sum_{k} F_k \left(\theta_k - \theta_k^{*}\right)^2

混合训练:编排节奏与时序

有了项目、配比和权重,还要解决“节奏”问题:是每天都穿插练三个项目,还是先集中一周练跑步再集中练投掷?训练批次的组织方式、学习率的安排、阶段划分,对最终效果影响极大。

混合训练策略包含四个关键设计。数据混洗:多任务样本在拼接后要彻底打乱,让每个 batch 里的任务构成具有随机性,避免模型“学完一类立刻忘掉上一类”。批大小设置:混合训练的 batch 通常比单任务更大,因为更大的 batch 能平均掉任务间的梯度噪声,训练更稳。学习率策略:常用的组合是“预热 + 余弦衰减”,初期用小学习率稳住预训练知识,中期用较大学习率学新任务,后期再降下来收敛。两阶段训练:先在通用语料上继续预训练保持通用能力,再进入多任务混合微调阶段。

整个混合训练的更新方向可以用下面的式子概括:每个训练步的总梯度,是各任务批次梯度的加权和。

节奏感是教练和训练工程师共同的必修课。训练初期像热身,学习率小、步子稳;中期像强度课,各项轮番轰炸;后期像赛前调整,逐步收紧、防止过拟合。把节奏排好了,多任务训练才能“多而不乱”。

∇θL=∑t=1Twt⋅1∣Bt∣∑(x,y)∈Bt∇θℓ(fθ(x),y)\displaystyle \nabla_\theta \mathcal{L} = \sum_{t=1}^{T} w_t \cdot \frac{1}{|\mathcal{B}_t|} \sum_{(x,y) \in \mathcal{B}_t} \nabla_\theta \ell\left(f_\theta(x), y\right)
  1. 单样本梯度 $\nabla_\theta \ell\left(f_\theta(x), y\right)$一个样本对模型参数的更新方向,代表“这个样本想让模型怎么改”。
    ∇θℓ(fθ(x),y)\nabla_\theta \ell\left(f_\theta(x), y\right)
  2. 批内平均 $\frac{1}{|\mathcal{B}_t|} \sum_{(x,y) \in \mathcal{B}_t}$把任务 $t$ 当前批次内所有样本的梯度取平均,得到该任务这一轮的“集体意见”。
    1∣Bt∣∑(x,y)∈Bt\frac{1}{|\mathcal{B}_t|} \sum_{(x,y) \in \mathcal{B}_t}
  3. 任务权重 $w_t$任务 $t$ 在本轮的权重,可以是固定的也可以是动态的,决定它的意见占多大分量。
    wtw_t
  4. 加权求和 $\sum_{t=1}^{T}$把各任务的“集体意见”按权重合并,得到最终更新方向,交给优化器执行。
    ∇θL=∑t=1Twt⋅1∣Bt∣∑(x,y)∈Bt∇θℓ(fθ(x),y)\nabla_\theta \mathcal{L} = \sum_{t=1}^{T} w_t \cdot \frac{1}{|\mathcal{B}_t|} \sum_{(x,y) \in \mathcal{B}_t} \nabla_\theta \ell\left(f_\theta(x), y\right)

评估监控:模拟赛成绩单

教练不能等到正式比赛才知道训练成果。平时要频繁组织模拟赛、单项测试,把每个项目的成绩变化记录下来。模型训练也一样:只盯混合总损失远远不够,因为大任务会“淹没”小任务的退化信号。

评估监控要做三件事。分任务指标:每个任务单独计算自己的核心指标——摘要任务算 ROUGE、问答任务算准确率、翻译任务算 BLEU,就像田径教练给每个项目单独计时。综合得分:按业务重要度把各任务指标加权汇总成一个总分,用于快速判断整体走向。回归测试:定期在历史全量验证集上测试一遍所有任务,确保学新任务没有破坏旧能力。一旦某个任务指标连续多个评估周期下滑,就要触发早停或调整配比。

教练看成绩单绝不是只看总分——总分第一的运动员可能跳远垫底。多任务微调的评估监控也是同一个道理:综合得分用于汇报,分任务指标用于定位,回归测试用于兜底。三者配合,才能及时发现问题、保住多任务训练的成果。

Acct=1∣Dt∣∑i=1∣Dt∣1(y^i=yi),S=∑t=1Tβt⋅Acct\displaystyle \text{Acc}_t = \frac{1}{|\mathcal{D}_t|} \sum_{i=1}^{|\mathcal{D}_t|} \mathbb{1}\left(\hat{y}_i = y_i\right), \qquad S = \sum_{t=1}^{T} \beta_t \cdot \text{Acc}_t
  1. 指示函数 $\mathbb{1}\left(\hat{y}_i = y_i\right)$第 $i$ 个样本预测正确记 $1$,错误记 $0$,是“对错计数器”。
    1(y^i=yi)\mathbb{1}\left(\hat{y}_i = y_i\right)
  2. 分任务准确率 $\text{Acc}_t$任务 $t$ 验证集上预测正确的样本占比,相当于这个项目今天的“达标率”。
    Acct=1∣Dt∣∑i=1∣Dt∣1(y^i=yi)\text{Acc}_t = \frac{1}{|\mathcal{D}_t|} \sum_{i=1}^{|\mathcal{D}_t|} \mathbb{1}\left(\hat{y}_i = y_i\right)
  3. 业务重要度 $\beta_t$每个任务在真实业务中的分量。比如客服场景里意图识别权重 0.5,情感分类 0.2,回复生成 0.3。
    βt\beta_t
  4. 综合得分 $S$按重要度加权汇总的总分。它告诉你“整体状态如何”,分任务指标告诉你“哪里出了问题”。
    S=∑t=1Tβt⋅AcctS = \sum_{t=1}^{T} \beta_t \cdot \text{Acc}_t

AI 应用场景

电商智能客服多技能融合

阶段:模型微调与部署

解决的问题:传统做法为每个技能单独微调一个模型,导致维护多套模型和多套推理服务,资源浪费且知识不共享。多任务混合微调用一个模型同时学习意图识别、情感分类和回复生成三类能力。

具体案例:某电商平台将 3 年客服语料按任务组织:意图识别 200 万条、情感极性 80 万条、回复生成 50 万对。用温度 $\alpha=0.6$ 的采样配比 + 动态任务权重,训练一个 7B 模型替代原 3 个专用模型,推理成本下降 60%,意图识别准确率提升 2.1 个百分点。

金融研报多任务信息抽取

阶段:数据准备与训练策略设计

解决的问题:金融研报信息抽取包含实体识别、关系抽取、事件分类、摘要生成四个任务,标注数据量差异悬殊(实体 5 万条、关系仅 1 万条)。单任务训练时,小数据任务严重过拟合。多任务混合微调让数据量小的任务与其他任务共享底层语义表示,相当于“蹭”到了大任务的知识。

具体案例:某券商 NLP 团队将配比设为 6:3:1,并叠加 EWC 约束防止摘要任务“遗忘”抽取能力。训练后关系抽取 F1 从 78.4% 提升至 84.2%,四个任务指标全部达标。

端侧语音助手多技能同步

阶段:模型压缩与端侧部署

解决的问题:设备端小模型既要唤醒词分类、又要命令词识别、还要简短对话生成,三种任务混训容易发生梯度冲突,互相干扰。

具体案例:某智能音箱团队采用两阶段训练:先在通用对话数据上继续预训练,再混合三个任务微调,并用 PCGrad 消除唤醒分类与对话生成之间的梯度冲突。离线评测三项指标全部达到产品线要求,线上误唤醒率下降 32%。

Python 演示

python101 行
import mathimport random random.seed(42) FEAT = 8      # 特征维度LR = 0.05     # 学习率STEPS = 200   # 训练步数BATCH = 16    # 批大小  class LinearModel:    """极简线性模型, 模拟多任务共享参数训练"""     def __init__(self):        self.w = [random.gauss(0, 0.1) for _ in range(FEAT)]        self.b = 0.0     def forward(self, x):        return sum(wi * xi for wi, xi in zip(self.w, x)) + self.b     def apply_grad(self, g, gb):        self.w = [wi - LR * gi for wi, gi in zip(self.w, g)]        self.b -= LR * gb  def make_data(task, n):    """为每个任务生成 n 条模拟样本, 回归目标各不相同"""    rows = []    for _ in range(n):        x = [random.uniform(-1, 1) for _ in range(FEAT)]        if task == "reg":            y = 2.0 * x[0] - 1.0 * x[1]      # 回归任务盯住特征0和1        elif task == "cls":            y = 1.0 if x[2] > 0.1 else 0.0   # 分类任务看特征2        else:            y = 1.5 * x[0] + 0.8 * x[3]      # 排序任务用特征0和3        rows.append((x, y))    return rows  def grad_of(model, x, y):    """MSE 损失对参数求梯度"""    pred = model.forward(x)    err = 2.0 * (pred - y)    return [err * xi for xi in x], err  def batch_grad(model, data):    """计算一个任务批次内的平均梯度"""    gs = [0.0] * FEAT    gb = 0.0    for x, y in data:        g, ge = grad_of(model, x, y)        gs = [a + b for a, b in zip(gs, g)]        gb += ge    return [a / len(data) for a in gs], gb / len(data)  def cosine_sim(g1, g2):    """两个任务梯度方向的余弦相似度, 越低冲突越大"""    dot = sum(a * b for a, b in zip(g1, g2))    n1 = math.sqrt(sum(a * a for a in g1))    n2 = math.sqrt(sum(b * b for b in g2))    return dot / (n1 * n2) if n1 and n2 else 0.0  def main():    tasks = {        "reg": make_data("reg", 1000),    # 任务A: 1000条        "cls": make_data("cls", 600),     # 任务B: 600条        "rank": make_data("rank", 200),   # 任务C: 200条    }    names = list(tasks.keys())    sizes = [len(tasks[n]) for n in names]    total = float(sum(sizes))    probs = [s / total for s in sizes]    # 按数据量比例采样    model = LinearModel()     for step_i in range(STEPS):        task = random.choices(names, weights=probs)[0]  # 1. 任务采样        batch = random.sample(tasks[task], BATCH)        g, gb = batch_grad(model, batch)                # 2. 算批次梯度        model.apply_grad(g, gb)                         # 3. 更新共享参数         if step_i % 50 == 0:            losses = {}            gs = {}            for name in names:                sample = random.sample(tasks[name], 50)                losses[name] = sum(                    (model.forward(x) - y) ** 2 for x, y in sample                ) / 50                gs[name], _ = batch_grad(model, sample)            cos = cosine_sim(gs["reg"], gs["cls"])      # 4. 冲突监控            print("step %3d | losses: %s | reg-cls余弦: %.2f"                  % (step_i, losses, cos))  if __name__ == "__main__":    main()
目的:用 200 行以内的线性模型演示多任务混合微调全流程——按数据量比例采样、混合批次更新、分任务评估、梯度冲突监控。
  1. probs = [s / total for s in sizes]数据量越大的任务采样概率越高,对应“容量加权采样”
  2. task = random.choices(names, weights=probs)[0]每个训练步按配比随机抽一个任务,这就是任务采样
  3. gs = [a + b for a, b in zip(gs, g)]把一个批内所有样本的梯度逐位累加,得到该任务的“集体意见”
  4. g, gb = batch_grad(model, batch)计算任务内批次的平均梯度,作为更新方向
  5. model.apply_grad(g, gb)用混合批次的梯度更新全部共享参数
  6. cos = cosine_sim(gs["reg"], gs["cls"])计算两个任务梯度的余弦相似度,监控梯度冲突
  7. losses[name] = sum(...) / 50分任务评估损失,防止总损失掩盖单任务退化

常见误区

误区把几十个任务的数据一股脑全倒进训练集,模型自然什么都会了。任务越多,微调效果越全面,反正模型参数够大。

正解任务越多,梯度冲突越复杂,任务间还可能互相带偏。就像教练一次给运动员报二十个项目,每个都练不透。真正做法是像排课一样先选 3-8 个高价值任务,用温控采样控制出场比例,再逐步增加任务并持续观察旧任务指标。贪多往往导致十个任务九个平庸。

为何常见:直觉上“多即全”,忽略了共享参数容量有限和任务间竞争成本。

误区既然都是任务,就该轮流练,每个任务一样的出场机会,这样对每个任务最公平,模型能力也最均衡。

正解公平不等于均匀。任务的数据量、难度、对业务的价值都不同。小任务如果和 100 倍数据量的大任务均匀出场,小任务的样本会被反复重复使用,大任务的知识又学不完。数据量比例采样或温控采样,才是工程意义上的“公平”。

为何常见:“平均分配”是日常生活中的公平直觉,但统计采样讲究的是按分布走。

误区训练震荡、损失不降,肯定是学习率调太大了,把它调小一点梯度就不打架了。

正解降低学习率只是让冲突造成的震荡幅度变小,并没有消除方向上的矛盾。就像两个教练一个让你往东跑、一个让你往西跑,你跑慢点只是吵架吵得温柔些,方向依然拧着。要真正解决问题,需要用 PCGrad 投影、梯度裁剪、分层更新等手段从方向上化解冲突。

为何常见:学习率是日常调参最常用的旋钮,容易把一切问题都归因到它头上。

误区我在训练集里混了 5% 的旧任务样本,数据回放做了,灾难性遗忘肯定就解决了。

正解5% 的旧样本只是回放的一种形式,比重太小容易被新任务“淹没”,需要在训练中动态调整。回放之外通常还要配 EWC 正则和参数隔离。就像复习课只安排一节课,期末照样忘光——复习的频率、方式、强度都要根据遗忘曲线来定。

为何常见:数据回放是直觉上最直接的方法,但它只是多种缓解手段中的一环。

误区训练时盯着混合总损失下降就行,总 loss 降了说明模型在变好,其他指标不用看。

正解总损失会被大数据量任务主导,小任务的退化完全可能被掩盖。混合损失 90% 来自问答任务,摘要任务已经崩了总 loss 依然好看。必须分任务监控指标 + 综合得分 + 回归测试,就像教练要逐项看成绩单,不能只看平均分。

为何常见:单一数字最省事,但混合训练的本质矛盾恰恰是“平均数掩盖个体差异”。

面试题

Q1. 多任务混合微调中,任务采样的主要作用是?

单选题 · easy

  • A.决定训练数据的增强方式
  • B.决定每个训练步抽哪个任务← 正确答案
  • C.决定神经网络的总参数量
  • D.决定损失函数的具体公式

答案:B。任务采样解决“训练计划表”问题——每个训练步从哪个任务的数据池抽样。数据增强、参数量、损失函数都与采样无关。

Q2. 在公式 $P(\text{任务}_t) = \frac{N_t^{\alpha}}{\sum_j N_j^{\alpha}}$ 中,当 $\alpha=0$ 时等价于?

单选题 · easy

  • A.均匀采样,所有任务概率相等← 正确答案
  • B.按数据量比例采样大任务占优
  • C.退化为完全随机乱序采样
  • D.只采集数据量最大的任务

答案:A。当 $\alpha=0$ 时,$N_t^0=1$,所有任务的分子都变成 1,概率完全相等,退化为均匀采样。

Q3. 动态任务权重“哪里损失高练哪里”的朴素实现是?

单选题 · mid

  • A.权重与任务损失大小成正比← 正确答案
  • B.权重与任务损失大小成反比
  • C.权重永远等于固定常数一
  • D.权重只跟任务数据量有关

答案:A。损失反映模型在该任务上的短板,损失越高越要加练,所以权重与损失成正比。若成反比,模型越弱的任务练得越少,越练越偏科。

Q4. 两个任务梯度的余弦相似度为 $-0.9$,说明?

单选题 · mid

  • A.两任务梯度方向互相促进
  • B.两任务梯度方向几乎相反← 正确答案
  • C.两任务之间毫无关联影响
  • D.模型已经完全收敛无需再训

答案:B。余弦接近 $-1$ 表示梯度方向几乎相反,一个往东推一个往西拉,冲突严重,需要 PCGrad 或分层更新等机制干预。

Q5. PCGrad 解决梯度冲突的核心做法是?

单选题 · mid

  • A.将学习率直接调成零停止更新
  • B.投影去除冲突方向的梯度分量← 正确答案
  • C.只保留梯度中最大的一个分量
  • D.把各任务梯度简单求平均处理

答案:B。PCGrad 将一个任务梯度在另一任务方向上的投影扣除,等价于“削掉互相抵消的分量”,使更新方向不再对抗。

Q6. EWC 正则中的 Fisher 信息量 $F_k$ 表示什么?

单选题 · hard

  • A.参数 $k$ 对旧任务的重要程度← 正确答案
  • B.参数 $k$ 的梯度更新速度
  • C.任务 $k$ 对应的数据总量
  • D.当前损失函数的具体大小

答案:A。$F_k$ 衡量旧任务对参数 $\theta_k$ 的敏感度。重要参数的 $F_k$ 大,EWC 会施加更强的“橡皮筋”约束,防止新任务把它拉走。

Q7. 两阶段混合训练策略的正确顺序是?

单选题 · hard

  • A.先任务微调再通用预训练
  • B.先通用预训练再任务微调← 正确答案
  • C.只进行任务微调不预训练
  • D.两阶段完全随机交替进行

答案:B。先在大规模通用语料上继续预训练以维持通用能力,再进入多任务混合微调学习具体技能,避免一上来就“偏科”。

Q8. 下列哪些是缓解灾难性遗忘的常用手段?

多选题 · mid

  • A.训练时混入旧任务样本回放← 正确答案
  • B.使用 EWC 正则约束重要参数← 正确答案
  • C.大幅提高学习率快速学新任务
  • D.使用 LoRA 做参数空间隔离← 正确答案

答案:ABD。数据回放、EWC 正则、参数隔离(LoRA/Adapter)都是有效手段。加大学习率只会让模型在新任务方向越跑越远,加剧遗忘。

Q9. 多任务混合训练的评估监控应包含哪些内容?

多选题 · mid

  • A.每个任务单独计算评测指标← 正确答案
  • B.按业务重要度加权综合得分← 正确答案
  • C.全量验证集上的回归测试← 正确答案
  • D.只观察混合总训练损失即可

答案:ABC。混合总损失会被大数据量任务主导,掩盖小任务退化。必须逐项看成绩单(分任务指标)、加权总分、回归测试三者配合。

Q10. 关于数据配比,以下哪些说法是正确的?

多选题 · hard

  • A.任务内部的数据也要有难度梯度← 正确答案
  • B.领域覆盖越全面泛化能力越强← 正确答案
  • C.配比一旦设定训练中不可调整
  • D.低质量噪声样本应当过滤剔除← 正确答案

答案:ABD。难度梯度、领域覆盖、质量筛选都是配比设计要点。配比是动态的,训练中应根据评估指标持续调整,因此 C 错误。