想象你拥有一栋精心建造的豪宅——结构坚固、装修考究、造价上亿。现在,每个新住户都想按自己的喜好改造它:有人想加个书房,有人想要健身房,有人想要影音室。如果每来一位住户就把整栋豪宅拆了重盖,既耗钱又耗时,还可能把原先的承重墙搞坏。聪明的做法是什么?不动主体结构,在房间里加装一个个独立的小隔间——住户需要什么功能,就加装对应的小屋。 大模型就是那栋豪宅。它在大规模数据上“建成”后,已经掌握了海量知识。当我们要让它适应新任务(比如做情感分类、写代码、当客服)时,传统做法是“拆房子”——全量微调会更新模型的所有参数,昂贵且容易破坏原有能力。而 Adapter(适配器) 则采用了“加装小隔间”的思路:把大模型参数全部冻结,只训练插入其中的小型模块。这些模块采用瓶颈结构(先压缩再展开),参数量仅有原模型的不到 5%,却能让模型学会新任务。 这套思路被称为“参数高效微调”(PEFT)。它会成为大模型工程师的必备技能,因为在实际工作中,你不可能每次新任务都全量训练百亿参数的模型。学会 Adapter,就掌握了用最小成本撬动大模型能力的钥匙。

岗位关联

职业价值:产品需要频繁上线新场景、多任务共用一个底座时,Adapter 能帮你把单次微调成本降低一个数量级,是降本增效的核心手段。

目录
  1. 可视化演示
  2. 适配器概念
  3. 工作原理
  4. 插入位置
  5. 对比全量微调
  6. 训练策略
  7. 多任务适配
  8. 实践与展望
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示瓶颈结构设计、冻结训练策略、插入位置与全量微调LoRA对比。

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

瓶颈结构设计 在Transformer每层插入小型瓶颈模块 降维投影升维恢复实现参数高效微调 h = Up(ReLU(Down(x))) 瓶颈维度b远小于模型维度d实现参数压缩

适配器概念

假设你是一位豪宅管家,住户只是想在客厅加个读书角。你不会把整栋楼拆了重盖,而是让木匠在角落搭一个可拆卸的小隔间——隔间的门很窄,容量不大,但足够放下一张书桌和书架。大模型微调也是一样:全量微调是“拆楼重建”,而 Adapter 是在冻结的模型里加装“小隔间”。

小隔间的结构在论文中被称为 瓶颈结构(Bottleneck) 。它由两个小矩阵构成:一个把高维特征压缩到低维(降维),一个把低维特征还原到高维(升维)。因为中间层很窄,所以叫“瓶颈”。这个设计使得 Adapter 的参数极少,同时“旁路”式地挂在主干网络旁边,不影响原有结构。

Adapter 最核心的理念是 冻结原模型——豪宅的承重墙、水电管路一概不动,只对小隔间里的家具负责。这样训练时不需要给整栋楼重新布线,原本学到的知识也完好保留。典型的 Adapter 参数量只占全模型的 0.5% 到 5%,却能把模型定向改造成适合新任务的样子。

回到豪宅的比喻:小隔间的建材成本不足整栋楼的 5%,却能满足住户的新需求。Adapter 正是用这种“低成本装修”策略,让大模型高效适应新任务。

可训练参数占比=2×d×mNtotal\displaystyle \text{可训练参数占比} = \frac{2 \times d \times m}{N_{\text{total}}}
分子是两个投影矩阵的参数总量(降维矩阵加升维矩阵),分母是整个大模型的参数总量。数值通常在 0.005 到 0.05 之间,意味着你只需要拟合原来万分之几的参数。
  1. $d$ 隐藏维度大模型内部每一层特征向量的长度,相当于豪宅走廊的宽度。
    d=隐藏层维度d = \text{隐藏层维度}
  2. $m$ 瓶颈维度Adapter 内部窄门的宽度,$m$ 远小于 $d$,信息通过时被大力压缩。
    m≪dm \ll d
  3. $2 \times d \times m$ 投影矩阵总量一个降维矩阵 $W_{\text{down}} \in \mathbb{R}^{d \times m}$ 加一个升维矩阵 $W_{\text{up}} \in \mathbb{R}^{m \times d}$,二者参数个数相同。
    2×d×m2 \times d \times m
  4. $N_{\text{total}}$ 模型总参数量大模型(豪宅)的全部参数,包括所有层的权重,通常以十亿(Billion)计。
    Ntotal=全部可训练参数N_{\text{total}} = \text{全部可训练参数}

工作原理

当信息流经过豪宅走廊时,如果选择进小隔间,需要先穿过一道窄门,屋里做好加工,再从小门出去汇入主路。这个过程在数学上就是 Adapter 的前向传播:

其中 $h_{\text{in}}$ 是输入特征,$W_{\text{down}}$ 把 $d$ 维压到 $m$ 维,$f$ 是激活函数(如 ReLU),$W_{\text{up}}$ 再把 $m$ 维升回 $d$ 维。最后的 $+h_{\text{in}}$ 是残差连接——相当于小屋旁边那条“快速通道”:即使不进小屋,信息也能原样通过,保证初始阶段模型能力不退步。

残差连接是整个设计的点睛之笔。因为 Adapter 刚初始化时,输出接近零,$h_{\text{out}} \approx h_{\text{in}}$,等于“小屋不存在”。随着训练推进,小屋才逐渐学会对信息做有意义的加工。这就像一个刚搭好的隔间,先让它闲置着不碍事,再慢慢往里面添置家具,不会一上来就挡路。

窄门压缩、小屋加工、出门还原,再加一条残差快速通道——这就是 Adapter 的全部工作原理。理解它,你就理解了参数高效微调的最小单元。

hout=hin+f(hinWdown)Wup\displaystyle h_{\text{out}} = h_{\text{in}} + f(h_{\text{in}} W_{\text{down}}) W_{\text{up}}
  1. $h_{\text{in}}$ 输入特征上一层 Transformer 子层送来的信息向量,长度为 $d$。
    hin∈Rdh_{\text{in}} \in \mathbb{R}^{d}
  2. $W_{\text{down}}$ 降维矩阵把 $d$ 维输入压缩进 $m$ 维瓶颈,形状为 $d \times m$。
    Wdown∈Rd×mW_{\text{down}} \in \mathbb{R}^{d \times m}
  3. $f(\cdot)$ 非线性激活对压缩后的信息做非线性变换(ReLU 等),让小屋具备学习复杂模式的表达能力。
    f(x)=max⁡(0,x)f(x) = \max(0, x)
  4. $W_{\text{up}}$ 升维矩阵把 $m$ 维特征还原成 $d$ 维,形状为 $m \times d$。
    Wup∈Rm×dW_{\text{up}} \in \mathbb{R}^{m \times d}
  5. 残差相加 $+h_{\text{in}}$把加工结果与原始输入相加,保证旁路在初始时近似恒等映射,不破坏预训练知识。
    hout=hin+Δhh_{\text{out}} = h_{\text{in}} + \Delta h

插入位置

小隔间放在豪宅的哪个房间最合适?总不能放在车库门口吧。Adapter 的“装修位置”同样讲究。大模型的骨架是很多层 Transformer 堆叠而成,每一层又包含两个子层:多头注意力(Attention) 和前馈网络(FFN)。信息依次穿过这两个子层,就像人先经过会客厅再经过餐厅。

原始 Adapter 论文的做法是:在每个 Transformer 子层之后都挂一个小隔间——Attention 之后挂一个,FFN 之后也挂一个。后来的变体(如 AdapterBias)为了省参数,只在 FFN 之后插一个,同样效果不错。位置实验表明,FFN 层承载了大部分知识记忆,所以在它后面加装小屋收益最高。

整栋豪宅有 $L$ 层,每层都插小屋,那么总参数量就是单层小屋乘以层数:

小屋均匀分布在每一层楼,既不过度集中也不疏漏,模型各阶段的信息流都能被“定向改造”。这就是 Adapter 插入位置的核心策略。

Nadapter=L×2×d×m\displaystyle N_{\text{adapter}} = L \times 2 \times d \times m
一栋 12 层“豪宅”,每层装一个瓶颈维度为 64 的小隔间,总参数量大约只有全量模型的 1%——这就是“多层插入”的代价公式。
  1. $L$ 层数Transformer 的层数,相当于豪宅的楼层数,常见如 12 层、24 层。
    L=Transformer 层数L = \text{Transformer 层数}
  2. $2 \times d \times m$ 单层小屋参数每一层插入的 Adapter 包含两个投影矩阵。
    N单层=2dmN_{\text{单层}} = 2dm
  3. $L \times N_{\text{单层}}$ 总参数所有层 Adapter 参数之和,随层数线性增长。
    Nadapter=L×2dmN_{\text{adapter}} = L \times 2dm

对比全量微调

现在我们来算一笔装修账。全量微调是“拆楼重建”,要更新豪宅里的每一块砖;Adapter 则只新增小屋的建材。直观对比一下二者的规模:

以 BERT-base 为例,$N_{\text{total}}$ 约 1.1 亿参数,12 层、$d=768$、$m=64$ 时,Adapter 总参数约 117 万,比值接近 100 倍。也就是说,同样的训练成本,Adapter 只需要更新不到原来的 1% 参数。

训练成本方面的差距更为惊人。全量微调需要更新所有参数的梯度,显存中要同时存放全部中间激活和梯度;Adapter 只对新增参数求梯度,主干参数冻结后不需要梯度存储,显存占用大幅下降,普通单卡就能跑起来。性能上,大量实验证明 Adapter 在下游任务上能达到与全量微调接近的水平——对于某些任务甚至略高(因为正则化效应),而训练时间缩短一个数量级。

| 对比维度 | 全量微调 | Adapter |

|---------|---------|---------|

| 更新参数 | 100% | 0.5%~5% |

| 显存需求 | 高 | 低 |

| 训练速度 | 慢 | 快 |

| 任务表现 | 基准 | 接近基准 |

| 多任务存储 | 每任务完整模型 | 每任务小模块 |

“拆楼重建”和“加装小屋”的账一目了然:后者省下了 99% 的训练成本,换来了接近全量微调的性能。对于追求性价比的工程团队,这笔账几乎不用犹豫。

NfullNadapter=NtotalL×2×d×m\displaystyle \frac{N_{\text{full}}}{N_{\text{adapter}}} = \frac{N_{\text{total}}}{L \times 2 \times d \times m}
  1. $N_{\text{full}}$ 全量参数微调时需要更新的全部模型参数。
    Nfull=NtotalN_{\text{full}} = N_{\text{total}}
  2. $N_{\text{adapter}}$ 小屋参数所有层 Adapter 参数总和。
    Nadapter=L×2dmN_{\text{adapter}} = L \times 2dm
  3. 比值 $\frac{N_{\text{full}}}{N_{\text{adapter}}}$约 50~200 倍,视瓶颈维度而定。
    NfullNadapter≈50∼200\frac{N_{\text{full}}}{N_{\text{adapter}}} \approx 50 \sim 200

训练策略

隔间搭好了,接下来是“布置家具”阶段——训练。这里的核心策略是参数冻结:把大模型的全部参数设为 requires_grad = False,只让 Adapter 参数的梯度可更新。反向传播时,梯度流经冻结层但不更新它们的权重,只在小屋处落脚:

其中 $\theta_A$ 是 Adapter 的参数,$\eta$ 是学习率,$\mathcal{L}$ 是任务损失。因为被冻结的主干不会更新,预训练学到的通用知识(比如语法、常识)被完整保留;同时梯度也无法“破坏”冻结层的权重,训练更稳定。

工程上有几个实用技巧。一是学习率设置:Adapter 参数少,通常用比全量微调稍大的学习率(如 $1\times10^{-4}$ 到 $5\times10^{-4}$),收敛更快;二是显存优化:冻结参数后,优化器状态只需保存 Adapter 参数的动量,显存占用急剧降低;三是混合精度训练配合梯度裁剪,单卡即可完成大部分 Adapter 微调任务。

布置家具的原则是“只动小屋不动墙”:冻结主干、单独调学习率、优化显存。这套策略让 Adapter 微调变得轻量、稳定、可复现。

θA←θA−η∇θAL\displaystyle \theta_A \leftarrow \theta_A - \eta \nabla_{\theta_A} \mathcal{L}
  1. $\theta_A$ Adapter 参数所有小屋的降维、升维矩阵,是唯一被更新的对象。
    θA={Wdown,Wup∣all layers}\theta_A = \{ W_{\text{down}}, W_{\text{up}} \mid \text{all layers} \}
  2. $\eta$ 学习率控制每一步参数更新的步长,Adapter 通常取 $10^{-4}$ 量级。
    η≈1×10−4\eta \approx 1 \times 10^{-4}
  3. $\nabla_{\theta_A} \mathcal{L}$ 梯度损失函数对 Adapter 参数的偏导,只在小屋处非零。
    ∇θAL=∂L∂θA\nabla_{\theta_A} \mathcal{L} = \frac{\partial \mathcal{L}}{\partial \theta_A}
  4. 参数更新用梯度下降法更新小屋参数,冻结层参数 $\theta_{\text{frozen}}$ 保持不变。
    θfrozen←θfrozen\theta_{\text{frozen}} \leftarrow \theta_{\text{frozen}}

多任务适配

豪宅只住一位住户太浪费了。想象一个共享大厅里挂着许多专属小隔间:住户 A 的健身角、住户 B 的影院室、住户 C 的书房,互不干扰。这就是 Adapter 在多任务场景下的杀手锏——一个底座模型,多个任务专属小屋。

每个任务训练时,只新建并训练自己的 Adapter,底座模型完全共享。当任务数增长到 $T$ 个时,总存储成本是:

切换任务时,只需在推理代码中动态切换激活的 Adapter,比如 model.set_active_adapters("sentiment") 就切换到情感分类小屋,set_active_adapters("ner") 就切换到命名实体识别小屋。多个任务的小屋可以同时激活并组合,形成更强大的能力(如“情感分析 + 立场检测”联合推理),这被称为 AdapterFusion 等进阶方案。

“一房多用”让大模型从“一人一栋豪宅”变成了“一栋豪宅服务多人”。任务切换即装即用,存储成本极低,这正是多任务产品架构最喜欢 Adapter 的原因。

S=T×L×2×d×m\displaystyle S = T \times L \times 2 \times d \times m
存储开销随任务数线性增长,但每增加一个任务只需多存约 1% 模型大小的参数,而不是重新部署一个完整模型。
  1. $T$ 任务数量需要适配的下游任务个数,例如 10 个不同行业的客服场景。
    T=任务个数T = \text{任务个数}
  2. $L \times 2dm$ 单个任务存储每个任务新增一套 Adapter 权重。
    N阿达=L×2dmN_{\text{阿达}} = L \times 2dm
  3. $S$ 总存储所有任务 Adapter 的存储总和,与 T 成正比。
    S=T×NadapterS = T \times N_{\text{adapter}}

实践与展望

小屋思路的实践版图已经铺开。HuggingFace 的 PEFT 库原生支持 Adapter,一行代码就能给模型加装小屋并冻结主干。生态中还有 AdapterHub 等社区,共享大量训练好的 Adapter 权重,让“下载一个小屋”像安装 App 一样简单。同时 LoRA 作为另一种主流 PEFT 方法,与 Adapter 形成了互补:LoRA 通过低秩矩阵并联改写权重,Adapter 则串联插入小模块,二者在参数效率上各有千秋。

但小屋也有自己的软肋。第一,推理延迟:Adapter 毕竟是串联在主干上的额外计算,每层多两次矩阵乘法,batch 小的场景延迟增加明显:

第二,容量瓶颈:$m$ 太小会欠拟合,$m$ 太大参数收益递减。未来发展更多集中在如何让 Adapter 与 LoRA、Prefix Tuning 等方法互相融合,找到“容量-速度-成本”的最优三角。

小屋不是万能的,但它提供了一个极佳的成本-性能平衡点。理解它的优势与边界,你就能在实际项目中做出最合适的 PEFT 选型。

延迟比=1+tadaptertbase\displaystyle \text{延迟比} = 1 + \frac{t_{\text{adapter}}}{t_{\text{base}}}
  1. $t_{\text{base}}$ 基础推理时间原模型处理一个样本的时间,比如 10ms。
    tbase=主干推理耗时t_{\text{base}} = \text{主干推理耗时}
  2. $t_{\text{adapter}}$ 附加推理时间穿行所有小屋带来的额外计算耗时。
    tadapter=Adapter 额外耗时t_{\text{adapter}} = \text{Adapter 额外耗时}
  3. 延迟比总耗时与基础耗时之比,恒大于 1,体现 Adapter 的推理代价。
    延迟比=1+tadaptertbase\text{延迟比} = 1 + \frac{t_{\text{adapter}}}{t_{\text{base}}}

AI 应用场景

多语言客服系统

阶段:模型部署与多任务适配

解决的问题:传统方案为每个语种训练一个完整的客服大模型,10 种语言就要存 10 份几十 GB 的模型,GPU 资源与人力成本巨大。采用 Adapter 后,底座模型共享一份,每个语种只训练一个约 200MB 的 Adapter 小屋,切换语种时动态加载对应小屋即可。

具体案例:某出海电商公司有一套基于 7B 模型的中文客服系统,需要扩展英、日、西三语。工程师用 PEFT 库给底座模型分别训练了三个 Adapter,总耗时从“训练三个完整模型”的 6 周缩短到 3 天,部署时只维护一个模型服务。

开源社区共享微调成果

阶段:模型微调与社区协作

解决的问题:传统的“模型共享”需要把整个微调后的模型上传到 HuggingFace,动辄数 GB,别人想复用还得重新适配。AdapterHub 解决了这个痛点——只上传几 MB 的 Adapter 权重 + 一个 config 文件,其他人加载底座模型后挂上你的小屋即可复现实验结果。

具体案例:研究者在 AdapterHub 发布了用于法律文书领域的 Adapter,另一个团队直接复用它做合同审查,把原本需要一整天的微调实验压缩到半小时,模型效果不打折。

边缘设备上的按需更新

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

解决的问题:端侧设备(手机、车载盒子)存储和算力有限,无法容纳大模型,更无法做全量微调。Adapter 让端侧模型可以共享一个轻量底座,按需从云端拉取特定任务的小屋权重,做到“用完即抛”,隐私数据不出设备。

具体案例:某智能座舱公司用 2B 模型做语音指令识别,为“导航”“空调”“音乐”三个场景各训练一个 50MB 的 Adapter,在车机芯片上实时切换,用户说出“导航到机场”时只激活导航小屋,响应延迟低于 200ms。

Python 演示

python45 行
"""使用 HuggingFace PEFT 为 BERT 加装 Adapter,微调情感分类任务"""import torchfrom transformers import AutoModelForSequenceClassification, AutoTokenizerfrom peft import get_peft_model, AdapterConfig, TaskType def main():    # 1. 加载预训练模型(豪宅底座)与分词器    model_name = "bert-base-uncased"    tokenizer = AutoTokenizer.from_pretrained(model_name)    model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)     # 2. 加装 Adapter 瓶颈小屋,自动冻结主干参数    config = AdapterConfig(        task_type=TaskType.SEQ_CLS,        bottleneck_size=64,        reduction_factor=16,    )    model = get_peft_model(model, config)    model.print_trainable_parameters()     # 3. 构造小批量训练数据(演示用)    texts = ["I love this movie", "This film is boring", "What a great day"]    labels = torch.tensor([1, 0, 1])    inputs = tokenizer(texts, truncation=True, padding=True, return_tensors="pt")     # 4. 训练循环:只更新 Adapter 参数    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)    model.train()    for epoch in range(5):        optimizer.zero_grad()        out = model(**inputs, labels=labels)        out.loss.backward()        optimizer.step()        print(f"epoch {epoch}: loss = {out.loss.item():.4f}")     # 5. 推理并保存 Adapter 权重    model.eval()    test = tokenizer(["I love this film"], return_tensors="pt")    with torch.no_grad():        pred = torch.argmax(model(**test).logits, dim=-1).item()    print("预测结果:", pred)    model.save_pretrained("adapter_demo") if __name__ == "__main__":    main()
目的:演示如何用 PEFT 库给 BERT 加装 Adapter,在冻结主干的前提下完成情感分类微调,并保存小屋权重。
  1. model = get_peft_model(model, config)一行代码把 BERT 包装成“冻结主干 + 挂载小屋”的参数高效模型。
  2. bottleneck_size=64设置瓶颈维度 m=64,信息经过小屋时先压缩到 64 维再展开。
  3. model.print_trainable_parameters()打印可训练参数量,你会看到它只占原模型的 1% 左右。
  4. optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)优化器虽然拿到所有参数,但冻结的层 requires_grad=False,实际只更新 Adapter。
  5. out.loss.backward()梯度只回传到 Adapter 参数,主干的梯度为 None 且不会被更新。
  6. model.save_pretrained("adapter_demo")只保存小屋权重,文件体积通常只有几 MB,上线时挂回底座即可。

常见误区

误区“Adapter 就是把模型变小了,训练时还是所有参数一起更新,只是参数量少了点。”

正解不对。Adapter 的“小”体现在两处:一是新增的瓶颈矩阵参数少,二是主干模型的全部参数被冻结。训练时只有小屋里的两个矩阵(降维 + 升维)在更新,主干模型的值从头到尾不变。用豪宅比喻说,小屋的家具每天都在换,但承重墙从来没动过。

为何常见:多数人习惯了全量微调“所有参数都在变”的心智模型,看到“参数量小”就自动理解为“更新的参数量小”,而忽略了“冻结”这个关键机制。

误区“反正都是加模块,插在 Embedding 层、Attention 前或输出层都一样。”

正解位置至关重要。实证研究表明,插在每个 Transformer 子层之后(尤其 FFN 层之后)效果最佳,插在 Embedding 层或输出层会导致信息没有被充分处理。FFN 层承担着知识存储的功能,在其后挂小屋能最大程度裁剪/改写知识。

为何常见:初学者把 Transformer 看作黑盒,以为“哪里能塞进一个模块就在哪里塞”,没有意识到不同子层的功能分化。

误区“瓶颈维度 m 越大,小屋容量越大,性能一定越好。”

正解实验曲线呈“倒 U 型”。m 太小(如 8)会欠拟合,m 太大(如 512)性能提升微乎其微,参数却成倍增加。典型设置是 m 取 d 的 1/16 到 1/4 之间。这和给小屋买家具一样:书架太小装不下书,书架太大占了客厅空间反而碍事。

为何常见:“更多参数 = 更强能力”的直觉在容量受限场景中失效,忽视了边际收益递减规律。

误区“训练时只更新少量参数,推理时也应该和原模型一样快。”

正解Adapter 层在推理时是真实参与计算的,每层多两次矩阵乘法和一次激活函数,batch 较小时延迟可能增加 20%-30%。这就像小隔间虽然只在需要时使用,但每次路过走廊都要多开一次门。

为何常见:人们容易混淆“训练开销”和“推理开销”——Adapter 确实大幅降低了训练成本,但推理路径上它的计算并没有消失。

面试题

Q1. Adapter 的核心微调思想是什么?

单选题 · easy

  • A.重新训练整个大模型的所有参数
  • B.冻结原模型,只训练新增的小模块← 正确答案
  • C.在大模型外部并联一个完整模型
  • D.丢弃预训练权重,从头开始训练

答案:B。Adapter 的关键是“冻结主干 + 训练小屋”。A 是全量微调的做法,C 相当于两个模型并行,D 是重新预训练,都与 Adapter 的旁路设计不符。

Q2. Adapter 的瓶颈结构中,$m$ 与 $d$ 的关系通常是?

单选题 · easy

  • A.$m$ 大于 $d$
  • B.$m$ 等于 $d$
  • C.$m$ 远小于 $d$← 正确答案
  • D.$m$ 与 $d$ 无关

答案:C。瓶颈设计就是要让中间层远窄于输入输出层,$m \ll d$。如果 $m \ge d$,就不是瓶颈结构,参数也不会有数量级的缩减,与高效微调的初衷相悖。

Q3. Adapter 中的残差连接在初始化时起什么作用?

单选题 · mid

  • A.加速梯度传播,防止梯度消失
  • B.保证输出接近输入,避免破坏预训练知识← 正确答案
  • C.增加模型的非线性表达能力
  • D.减少训练时的显存占用

答案:B。Adapter 初始时输出接近零,加上残差后 $h_{\text{out}} \approx h_{\text{in}}$。这让小屋刚搭好时“不影响原有通道”,预训练能力不退化。A 是残差在深层网络中的通用作用,但这里最关键的是保护预训练知识。

Q4. Adapter 通常插入在 Transformer 层的什么位置?

单选题 · mid

  • A.只在 Embedding 层之后
  • B.只在输出层之前
  • C.每个 Transformer 子层之后← 正确答案
  • D.任意位置效果相同

答案:C。原始 Adapter 论文在每个 Transformer 子层(Attention 和 FFN)之后各插入一个 Adapter,后来的变体常在 FFN 之后插入。Embedding 或输出层插入效果差,C 是正确的工程实践。

Q5. 隐藏维度 $d=768$、瓶颈维度 $m=64$ 时,单个 Adapter 的参数量约为多少?

单选题 · mid

  • A.约 9 万← 正确答案
  • B.约 4.9 万
  • C.约 98 万
  • D.约 49 万

答案:A。单个 Adapter 含降维矩阵 $W_{\text{down}}$($768 \times 64$)和升维矩阵 $W_{\text{up}}$($64 \times 768$),总参数 $2 \times 768 \times 64 = 98{,}304$,约 9.8 万,最接近 9 万。

Q6. 关于 Adapter 与全量微调的对比,下列说法正确的是?

单选题 · mid

  • A.Adapter 在任务性能上总是超过全量微调
  • B.Adapter 参数少,但性能接近全量微调← 正确答案
  • C.全量微调所需显存更少
  • D.Adapter 无法在 NLP 任务中使用

答案:B。大量实验表明 Adapter 性能与全量微调基本持平,偶有超出但并非“总是”。全量微调需存储全部梯度和优化器状态,显存远高于 Adapter。Adapter 最早就是在 NLP 的 Transformer 上提出的。

Q7. 以下哪些属于 Adapter 方法的优点?

多选题 · mid

  • A.训练参数量大幅减少← 正确答案
  • B.多任务场景下共享一个底座← 正确答案
  • C.推理速度一定快于原模型
  • D.每个任务只需新增一个小模块← 正确答案

答案:ABD。A、B、D 都是 Adapter 的核心优势。C 错误,Adapter 在推理时增加额外计算层,常带来一定延迟开销,不可能“一定更快”。

Q8. 训练 Adapter 时,梯度在反向传播中的行为是?

单选题 · mid

  • A.梯度更新所有 Transformer 层参数
  • B.梯度流经冻结层但不更新其权重← 正确答案
  • C.梯度只更新 Embedding 层参数
  • D.梯度直接跳过 Adapter 层

答案:B。PEFT 框架冻结主干后,梯度仍然要穿过冻结层回传到 Adapter,但更新步骤只在 Adapter 参数上执行(冻结层 requires_grad=False)。A 是普通微调,C、D 与 Adapter 机制不符。

Q9. Adapter 的已知局限性包括哪些?

多选题 · hard

  • A.串联结构带来额外推理延迟← 正确答案
  • B.瓶颈过小导致容量不足、性能下降← 正确答案
  • C.无法将训练好的 Adapter 保存为文件
  • D.只能用于 Transformer 架构

答案:AB。A 是推理开销问题,B 是容量-性能权衡,二者都是论文和工程中公认的局限。C 错误,save_pretrained 可以保存 Adapter 权重。D 错误,Adapter 最早在 Transformer 上提出,但同样可迁移到其他网络。

Q10. 在多任务 Adapter 场景中,切换任务时正确的做法包括?

多选题 · hard

  • A.重新加载整个底座模型
  • B.动态激活对应任务的 Adapter 权重← 正确答案
  • C.把多个任务的 Adapter 拼接成一个超大模型
  • D.保持底座不变,切换小屋← 正确答案

答案:BD。多任务 Adapter 的核心就是“底座共享、小屋独立”,切换任务只需调用 set_active_adapters 动态激活相应权重。A 重新加载底座完全没必要,C 把多个 Adapter 拼接也误解了多任务适配的意图。