专业书籍精读 · Generative AI System Design Interview · 第 10 章
Generative AI System Design Interview · Ch 10 · Ali Aminian, Hao Sheng & Alex Xu · 2024
你传十几张手机自拍上去,选一个「职业照」风格,等几分钟,拿回来几十张西装革履、影棚灯光的头像——脸还是你的脸。这一章讲的就是这条链路是怎么搭起来的。
上一章那个模型明明已经很能画了。你说「一张三十岁女性的职业照」,它画得又快又好看——但那是个陌生人。你再补充:单眼皮、鼻梁偏高、左脸有颗痣……加到第二十个形容词,出来的仍是另一个人。
其实想想就懂:你能只靠一段话,让一个没见过你妈妈的人在人群里把她认出来吗?语言里根本没有那些词。脸的辨识力藏在毫米级的几何关系里,形容词是很粗的工具。
既然说不清,就换一种教法:把你的几张照片摆到模型面前让它补一次课,并给你起个只属于你的新名字。此后你说「用那个名字画一张雪山上的照片」,它就知道该画谁了。
补课有两种上法。一种是让它整个重学一遍:记得最牢,但每人都要单独存一整套重学后的「大脑」,几个 G 起步;而且容易走火入魔,学你学得太投入,从此画谁都像你。
另一种是几乎所有产品的选择:原来的本事一点不动,只在旁边别一枚很小的「徽章」。几十兆,出图时挂上、出完摘下——同一套大脑因此能挨个伺候成千上万人。
补课补过头,它学到的就不是「你的脸」,而是把那十几张照片整个背了下来。你让它画你站在雪山上,它给的还是书房那面白墙,只是脸更准了。
所以「像不像本人」这把尺子不能单独看——它一路上涨的时候,「听不听话」正在往下掉。要找的是两者交叉的那个甜点。这也是为什么十几张不同场景、不同角度的照片,比四十张同一面墙前拍的更有用。
前面几章生成的都是虚构的人,这一章生成的是一个真人。于是一整套前面不需要的东西必须做进流程:上传时确认这张脸确实是你的,出图时拦掉不该出的内容,图上留可追溯的标记,注销时那枚徽章必须跟照片一起销毁——它等于一份「随时能复现这个人」的能力。
代价也很实在:每个用户都要单独训一次,这是一笔前面几章都没有的、按人头计的算力账。
身份是唯一没法用嘴说清的条件,所以只能拿样本去教;教法的胜出者不是「整个重学」而是「别一枚几十兆的小徽章」,因为它让一套大脑能共享给所有人、也让那份记忆删得干净;而「像不像」这把尺子必须跟「听不听话」一起看,否则你会得到一个只会复读训练照的模型。
想进到具体机制、记号和示意图? → 切到精读版
上一章的条件是一句话,这一章换成几张照片——身份是唯一无法编码进提示词的条件。解法是用少样本把它「写进」模型:只学一个词向量(Textual Inversion)、整模微调并自带解药(DreamBooth 的先验保持)、或挂低秩旁路(LoRA)。但真正的系统难题在模型之外:每用户一份产物的存储与热插拔、「像不像」与「听不听话」互相拉扯的评估,以及肖像权——全书唯一一章里,防滥用不是加分项而是需求本身。
Part II 十道真题的第 9 题。Ch9 把文本接进扩散模型,本章换成图像,下一章再加上时间——条件一章加一维,是本书后半程的暗线。对应各类 AI 头像 App 与绘图工具里的「参考人像 / 角色一致性」。
先把场景钉死:用户上传 10–20 张自拍,选一个风格包,系统在几分钟内返回几十张职业头像——典型的异步产品形态,不是 Ch9 那种秒级同步出图。
Ch9 那套为什么不够?因为77 个 token 装不下一张脸。文本条件被语言离散化过,只能表达「语言里有词的那些东西」;而人脸的辨识力落在眼距、鼻唇沟、颧骨投影这类毫米级几何关系上——人脸识别模型要一个 512 维连续向量才装得下,语言里没有对应这些维度的词。
不解决会怎样?这里没有上一章那种「错得流畅」的退路:头像画得再精致,只要不是本人,产品价值直接归零——用户一眼就否。
另外两重约束几乎单枪匹马决定了后面的技术选型:
既然身份说不清,就只能拿样本去教。训练目标没变——还是 Ch8 那个去噪损失:给照片加噪,让模型还原。变的只有一件事:这一轮允许改动哪些参数。三条路线的差别全落在这里:只改词嵌入表新开的一格、改整张去噪网络、还是在注意力层旁并联一条很窄的旁路。
最轻的那条。扩散模型和文本编码器全部冻结,只在词表里新开一个伪词 S*,训练时只对它那一个嵌入向量求梯度:让模型看到「a photo of S*」时能把你的照片从噪声里还原出来。
白话就是——模型的本事一点没动,你只是给它的字典加了个词条。此后 S* 能像普通名词一样进提示词:「S* 穿宇航服,油画风」。
好处极诱人:产物只有一个向量、几 KB,词条之间可自由组合,且天生不可能污染基座——你什么都没改。
代价同样直白:表达力被一个向量的容量卡死。相似度是三条路里最低的,痣、发际线这类「恰恰用来认人」的细节常常丢掉;而且可训参数太少、梯度信号弱,训起来反而比 LoRA 更慢,常需上千步。它更适合装一种风格或一个物件——而这一章要的偏偏是脸。
另一个极端。用罕见标识符 + 类别词组成的提示(形如「a sks person」)微调整张去噪网络,常连文本编码器一起训。
为什么要「罕见」?因为你需要一个模型几乎没有先验的符号来挂这张脸。用「john」会跟它已有的 john 打架,结果半个你、半个它想象中的 john。sks 这类无意义短串,是在词表里找一块空地。
但它有两个绕不开的副作用:
解药叫先验保持损失(prior preservation loss):训练时额外喂一批模型自己生成的同类别图(「a photo of a person」,实现里常见两百张量级),要求模型在这批上的输出基本不变。白话就是一边教它「sks 是你」,一边不停提醒它「普通人长什么样」——用模型自己的旧知识给自己做正则。
它揭示了一条通则:用少量数据去改大模型,就必须同时准备好保住它原有能力的机制,否则你得到的是一个窄领域变强、别处变笨的模型。
相似度和可控性都是三条路里最好的,但产物是整份权重副本(Stable Diffusion 一系完整快照 2–4 GB 量级),每用户一份——这条存储死线把它挡在消费级产品门外。
全章工程上的落点。LoRA 的观察是:微调想做的事——把权重矩阵 W 挪一点——不需要满秩的挪动。于是冻结 W,旁边并联一条窄通道:
输出 = W·x + B·A·x,其中 A 是 r×d、B 是 d×r,秩 r 通常取 4–32
白话讲这个式子在说什么:把「允许挪多少」限制成一个很窄的形状。参数量从 d×d 掉到 2×d×r——d=1024、r=8 时约 1/64。只训 A 和 B,原权重一字节不改。它挂在注意力层的投影矩阵上,也就是 Ch9 里条件进来的地方——要改「模型怎么被条件牵着走」,动这几层性价比最高。
产物落到几 MB 到一百多 MB,比整模小两到三个数量级。但存储只是账面收益,真正的红利是服务形态:基座常驻显存,推理时按 user_id 把适配器叠上去、出完摘掉,一块 GPU 因此能轮流服务成千上万个用户;整模微调做不到——每人独占一份显存,GPU 利用率从设计上就废了。适配器作为独立文件还能进用户的删除流水线,这点下面会用到。
质量上它逼近 DreamBooth,所以实践中最常见的是两者组合:DreamBooth 的配方(罕见标识符 + 先验保持),产物落成一个 LoRA。
还有一条不训练的路:另训一个身份编码器,用现成人脸识别模型抽出身份向量,再开一路与文本解耦的交叉注意力注入去噪网络——用户来了直接推理,零训练、秒级出图,代价是个性化上限被编码器一次性定死。原书成于 2024 年,主线仍是逐用户微调。
三把尺子,互相拉扯:
CLIP score——你要的西装、雪山、光线给没给。FID 加人评。本章最反直觉的一条:身份相似度单调上升并不是好消息。步数一路加上去,相似度确实继续涨——但后半段的涨幅来自模型开始复制训练照:你让它画「站在雪山上」,它给的还是那面书房白墙,只是脸更准。贴合度崩掉、多样性塌缩,主指标却一片飘红。
所以要画两条曲线:横轴训练强度(步数、学习率、秩 r),纵轴分别是身份相似度与提示词贴合度,在交叉区早停。这和 Ch9 里 FID 与 CLIP score 沿 w 反向移动是同一种病,只是旋钮从采样期搬到了训练期。再加一把本章独有的尺子:「是不是在复制」——生成图和训练照做近重复检测,命中就是过拟合的铁证。
训练是异步作业,不是同步请求。 训一个用户的 LoRA 在单张主流训练卡上是分钟级(常见五到二十分钟),远超任何 HTTP 超时。形状因此固定:上传 → 校验 → 入队 → GPU worker → 产物落对象存储 → 回调通知。这也解释了产品为什么都做成「稍后通知你」。
推理端共享基座 + 热插适配器。 第一次为某用户出图要把几十 MB 的适配器从对象存储拉下来,同区域百毫秒量级;按活跃度做一层 LRU 缓存,冷启动基本看不见。
删除是这一章的硬需求。 注销时要删的不止原始照片,还有适配器本身——它是生物特征的派生数据,留着等于留着一份「随时能复现这个人」的能力。这给出一条硬架构约束:别把适配器 merge 进共享权重,一旦烘进基座就分不出来、删不干净;运行时叠加、用完摘掉才是可删除的形态。
同一套能力用在别人的照片上就是深度伪造。所以防护必须设计进流程,而不是事后补一个过滤器:
表 1 · 四条个性化路线怎么选
| 路线 | 改什么 | 产物 / 训练 | 代价 | 什么时候选 |
|---|---|---|---|---|
| Textual Inversion | 只训一个新词的嵌入向量 | 几 KB;训练反而慢 | 相似度最低,细节丢失 | 装风格或物件,脸勉强 |
| DreamBooth 全量 | 整张去噪网络(常含文本编码器) | 2–4 GB;分钟级 | 每人一份整模,无法共享显存 | 质量上限对标;消费级不可行 |
| LoRA(+ DreamBooth 配方) | 注意力层旁的低秩旁路 B·A | 几 MB–一百多 MB;分钟级 | 略逊全量;秩 r 要调 | 产品默认解:共享基座、热插拔、可删除 |
| 身份编码器(零样本) | 不为用户训练,推理时注入人脸向量 | 无逐用户产物;秒级出图 | 相似度与个性化上限被编码器一次性定死 | 不接受几分钟等待的场景 |
表 2 · 训练强度(步数 / 秩 r / 学习率)怎么定
| 档位 | 你得到 | 你付出 | 怎么认出来 |
|---|---|---|---|
| 偏弱 | 提示词照单全收,背景姿势任你换 | 不像本人——像个远房亲戚 | 余弦相似度明显低于同人不同照的基线 |
| 甜点区 | 认得出是本人,指定的场景 / 服装 / 光线也基本都给 | 要扫一遍强度才找得到 | 两条曲线的交叉区;多存几个快照人工挑 |
| 偏强 | 身份相似度最高,主指标最好看 | 复制训练照:背景姿势一起被背下来,提示词失效 | 近重复检测命中;CLIP score 掉 |
还有一条进不了表、却最便宜的杠杆:照片该怎么收。十几张不同场景、不同角度、不同光线的自拍,显著好过四十张同一面墙前拍的——过拟合吃的正是「背景与姿势的共性」。这条落在产品引导文案上,不花一分算力。
面试里被问「怎么做个性化」,答「微调一下」等于没答。这一章给的是三层能分别下手的抓手:改什么(词向量 / 整模 / 低秩旁路)、在哪训(每用户一次 / 一次性训个编码器)、怎么服务(独占显存 / 共享基座热插拔)。能把三层分开谈,就胜过大多数答案。
LoRA 的意义也早已溢出这一章:它今天是所有大模型定制的默认形态——语言模型的领域适配、绘图模型的风格包,同一套「冻结主干、挂低秩旁路」。
1.25 亿 降到 80 万。这个比例、而非画质,才是本章技术选型的决定性变量。Microsoft · LoRA 官方仓库(2021)· github.com/microsoft/LoRAsks 只是在词表里找一块先验最弱的空地;换成常见人名就会和已有语义打架。① 核心命题:身份是唯一写不进提示词的条件——77 个 token 装不下一张脸,人脸的辨识力要 512 维连续向量才装得下,只能拿样本去教。
② 三条路线的差别只有一个:这一轮允许改动哪些参数——词嵌入表新开一格(Textual Inversion)、整张去噪网络(DreamBooth)、注意力层旁的低秩旁路(LoRA)。第四条不训练:训个身份编码器推理时注入人脸向量,秒级但上限被定死。
③ DreamBooth 必须自带先验保持损失:一边教「sks 是你」,一边用模型自己生成的同类别图提醒它「普通人长什么样」,否则语义漂移、画谁都像你。
④ LoRA 是产品默认解:W·x + B·A·x,秩 r 取 4–32,产物几十 MB。真正的红利不是存储,是共享基座热插拔(GPU 利用率)与可删除(别 merge 进基座)。
⑤ 评估最反直觉:身份相似度单调上升不是好事,后半段涨幅靠复制训练照换来。要画两条曲线、在交叉区早停,再加近重复检测。
⑥ 服务形态由训练时长决定:分钟级 → 必然是异步队列 + 回调;推理端共享基座 + 按 user_id 热插适配器。
⑦ 最便宜的杠杆在产品侧:让用户传不同场景、角度、光线的照片,十几张胜过同一面墙前的四十张。
⑧ 防滥用是需求不是附录:上传侧活体验证、训练侧拦名人、输出侧严一档的过滤、水印只作证据链、人脸属需明示同意的特殊类别数据。