LLM 系统设计模式与架构最佳实践
深度总结构建 LLM 产品系统的核心模式、优化策略和陷阱,对 AI 工程师的系统设计和决策有直接指导价值。
深度总结构建 LLM 产品系统的核心模式、优化策略和陷阱,对 AI 工程师的系统设计和决策有直接指导价值。
[ LLM 工程 生产环境 🔥 ] · 阅读时长 66 分钟
HackerNews、Twitter 和 LinkedIn 上的讨论
“有一大类问题,构想起来很容易,做出演示也很容易,但要将其打造成产品却极其困难。例如自动驾驶:演示一辆汽车绕着街区自动行驶很容易,但将其变成产品却需要十年时间。”——Karpathy
本文介绍将大语言模型(LLM)集成到系统和产品中的实用模式。我们将基于学术研究、行业资源和从业者经验,将其提炼为关键思想与实践。
共有七种关键模式。它们也按照提升性能与降低成本/风险这一维度,以及更接近数据与更接近用户这一维度进行组织。
评估:衡量性能
RAG:添加最新的外部知识
微调:提升特定任务的表现
缓存:降低延迟与成本
护栏:确保输出质量
防御性 UX:预见并妥善处理错误
收集用户反馈:构建数据飞轮
(另请参阅这篇补充文章,了解如何将这些 LLM 模式与潜在问题相匹配。)
LLM 模式:从数据到用户,从防御到进攻(参见各模式之间的联系)
评估:衡量性能
评估是一组用于衡量模型在某项任务上表现的测量方法,包括基准数据和指标。引自一条 HackerNews 评论:
团队对评估的重视程度,是区分那些急着推出热门垃圾产品的人与真正认真在这个领域打造产品的人的关键因素。
评估让我们能够衡量系统或产品的运行效果,并检测任何性能回退。(一个系统或产品可能由多个组件组成,例如 LLM、提示词模板、检索到的上下文,以及 temperature 等参数。)一组具有代表性的评估,可以让我们进一步大规模衡量系统变更的影响。如果没有评估,我们就只能盲目前行,或者在每次变更后逐一目视检查 LLM 的输出。
语言建模领域有许多基准测试,其中一些值得注意的包括:
MMLU:一组包含 57 项任务的基准,涵盖基础数学、美国历史、计算机科学、法律等领域。要取得良好表现,模型必须具备广泛的世界知识和解决问题的能力。
EleutherAI Eval:一个统一框架,通过零样本/少样本设置,在 200 项任务上测试模型。它整合了大量评估,包括 BigBench、MMLU 等。
HELM:HELM 并不局限于特定任务和指标,而是通过跨领域评估对 LLM 进行全面衡量。指标包括准确性、校准度、鲁棒性、公平性、偏见、毒性等;任务包括问答、信息检索、摘要、文本分类等。
AlpacaEval:一种自动化评估框架,用于衡量强大的 LLM(例如 GPT-4)偏好某个模型输出而非参考模型输出的频率。指标包括胜率、偏差、延迟、价格、方差等。经验证,它与 2 万条人工标注具有高度一致性。
我们可以将指标分为两类:依赖上下文和不依赖上下文。
依赖上下文:这类指标会考虑上下文,通常针对特定任务提出;将其改用于其他任务时,需要进行一定调整。
不依赖上下文:这类指标在评估生成结果时不依赖上下文,只会将输出与给定的标准参考答案进行比较。由于与任务无关,它们更容易应用于各种任务。
为了更好地理解这些指标及其潜在缺陷,我们将探讨 BLEU、ROUGE、BERTScore 和 MoverScore 等几种常用指标。
BLEU(Bilingual Evaluation Understudy,双语评估替补)是一种基于精确率的指标:它统计生成输出中同时出现在参考文本里的 n-gram 数量,然后除以输出中的总词数。它主要用于机器翻译,并因成本效益较高而一直广受欢迎。
首先,计算不同 (n) 值下的精确率:
(Count_{clip}(\text{n-gram})) 会根据某个 n-gram 在任意对应参考句中出现的最大次数进行截断。
计算出不同 (n) 值下的精确率后,将所有 (precision_n) 分数的几何平均值作为最终的 BLEU-N 分数。
然而,由于精确率仅依赖 n-gram,而不考虑生成输出的长度,因此,如果输出只包含一个常用词的一元语法单元(例如停用词),也会获得完美的精确率。这可能产生误导,并鼓励通过减少输出词数来提高 BLEU 分数。为解决这一问题,需要加入简短惩罚,对过短的句子进行惩罚。
因此,最终公式为:
ROUGE(Recall-Oriented Understudy for Gisting Evaluation,面向召回率的摘要评估替补):与 BLEU 不同,ROUGE 以召回率为导向。它统计参考文本中同时出现在输出里的词数,通常用于评估自动摘要任务。
ROUGE 有多个变体。ROUGE-N 与 BLEU 最为相似,同样会统计输出和参考文本之间匹配的 n-gram 数量。
其他变体包括:
ROUGE-L:衡量输出与参考文本之间的最长公共子序列(LCS)。它会考虑句子级结构的相似性,并重点关注按顺序共同出现的最长 n-gram 序列。
ROUGE-S:衡量输出与参考文本之间的跳跃二元语法单元。跳跃二元语法单元是保持句中顺序的一对单词,无论两者之间夹着哪些单词。
BERTScore 是一种基于嵌入的指标,它使用余弦相似度,将生成输出中的每个 token 或 n-gram 与参考句进行比较。BERTScore 包含三个组成部分:
召回率:参考文本中每个 token 与生成输出中最接近匹配项之间余弦相似度的平均值。
精确率:生成输出中每个 token 与参考文本中最接近匹配项之间余弦相似度的平均值。
F1:召回率与精确率的调和平均值。
BERTScore 的优势在于它能够处理同义词和释义。BLEU 和 ROUGE 等较简单的指标依赖精确匹配,因此无法做到这一点。研究表明,在图像描述和机器翻译等任务上,BERTScore 与人工评价具有更好的相关性。
MoverScore 同样使用上下文化嵌入来计算生成输出与参考文本中 token 之间的距离。但 BERTScore 基于 token 的一对一匹配(即“硬对齐”),而 MoverScore 允许多对一匹配(即“软对齐”)。
BERTScore(左)与 MoverScore(右;来源)
MoverScore 能够将一个序列中语义相关的词映射到另一个序列中的对应词。它通过求解一个约束优化问题实现这一点,找出将一段文本转换成另一段文本所需的最小代价。其思想是衡量将一个序列转换为另一个序列时,单词需要移动的距离。
然而,使用这些传统基准和指标存在若干陷阱。
首先,这些指标与人类判断之间的相关性较差。BLEU、ROUGE 等指标与人类对流畅度的评价呈负相关;它们与人类对内容充分性的评分也只有中等或更低程度的相关性。尤其是对于需要创造力和多样性的任务,BLEU 和 ROUGE 的相关性很低。
其次,这些指标对更广泛任务的适应能力通常较差。将为一种任务提出的指标应用到另一种任务上,并不总是明智的。例如,BLEU 和 ROUGE 等精确匹配指标并不适合抽象式摘要或对话等任务。由于它们依赖输出与参考文本之间的 n-gram 重叠,因此不适用于可能存在多种合理回答的对话任务。某个输出与参考文本的 n-gram 重叠率可能为零,但它仍然可能是一个很好的回答。
第三,这些指标的可复现性较差。即使使用同一种指标,不同研究报告的结果也可能存在很大差异,这可能源于人工判断收集方式或指标参数设置的差异。另一项对 2,000 项研究中 ROUGE 分数的分析发现,这些分数难以复现、难以比较,而且经常有误,因为评估往往使用了未经测试且不正确的 ROUGE 实现。
使用 ROUGE 进行模型评估的维度(来源)
即使使用 MMLU 等较新的基准,同一模型也可能因评估实现不同而获得差异显著的分数。Hugging Face 将原始 MMLU 实现与 HELM 和 EleutherAI 的实现进行了比较,发现同一个样例在不同提供方的实现中可能采用不同的提示词。
不同 MMLU 实现中同一问题使用的不同提示词(来源)
此外,这三个基准所采用的评估方法也各不相同:
原始 MMLU:仅比较各答案(A、B、C、D)的预测概率。
HELM:使用模型给出的下一个 token 概率,并选择概率最高的 token,即使它并非选项之一。
EleutherAI:计算每个答案完整序列(即一个字母,后跟答案文本)的概率,然后选择概率最高的答案。
不同 MMLU 实现对同一问题采用的不同评估方式(来源)
因此,即使是同一项评估,绝对分数和模型排名也可能因评估实现方式不同而大幅波动。这意味着,除非评估实现完全一致,甚至连提示词和分词方式等细微之处都相同,否则模型指标并不真正具备可比性——哪怕用的是同一项评估。同样,QLoRA 的作者发现 MMLU 过于敏感,并得出结论:“不要使用、报告或相信 MMLU 分数”。
除了上述传统评估之外,一个正在兴起的趋势是使用能力强大的 LLM 作为无需参考答案的指标,评估其他 LLM 生成的内容。这意味着,评估可能不再需要人工判断或标准参考答案。
G-Eval 是一个使用思维链(Chain-of-Thought,CoT)和表单填写范式来评估 LLM 输出的框架。首先,他们向一个 LLM 提供任务介绍和评估标准,并让它生成由评估步骤组成的 CoT。然后,为了评估新闻摘要的连贯性,他们将提示词、CoT、新闻文章和摘要拼接起来,并让 LLM 输出一个 1 到 5 之间的分数。最后,他们利用 LLM 输出 token 的概率对分数进行归一化,并通过加权求和得到最终结果。
G-Eval 概览(来源)
他们发现,使用 GPT-4 作为评估器时,其结果与人工判断之间具有较高的 Spearman 相关系数(0.514),优于此前所有方法。在连贯性、一致性、流畅性和相关性等方面,它也优于传统指标。在主题聊天任务上,它在自然度、连贯性、吸引力和有据可依性等多项标准上,都优于 ROUGE-L、BLEU-4 和 BERTScore 等传统指标。
Vicuna 论文采用了类似的方法。他们首先定义了八个类别(写作、角色扮演、信息提取、推理、数学、编程、STEM,以及人文与社会科学),然后为每个类别设计 10 个问题。接下来,他们让五个聊天机器人生成答案:LLaMA、Alpaca、ChatGPT、Bard 和 Vicuna。最后,他们让 GPT-4 根据有用性、相关性、准确性和细节程度对答案质量进行评分。
总体而言,他们发现 GPT-4 不仅能给出一致的分数,还能详细解释评分理由。在单答案评分范式下,GPT-4 与人类之间的一致率(85%)高于人类评估者彼此之间的一致率(81%)。这表明 GPT-4 的判断与人类评估者高度一致。
QLoRA 也使用一个 LLM 来评估另一个 LLM 的输出。他们让 GPT-4 在 Vicuna 基准上,对多个模型相较于 gpt-3.5-turbo 的表现进行评分。在获得 gpt-3.5-turbo 和另一个模型的回答后,通过提示词要求 GPT-4 分别以 10 分制为二者评分,并解释评分理由。他们还通过模型之间的直接比较来衡量性能,将任务简化为包含平局在内的三分类评分方案。
为了验证自动化评估,他们在 Vicuna 基准上收集了人工判断。通过 Mechanical Turk,他们为与 gpt-3.5-turbo 的比较招募了两名标注者,为成对比较招募了三名标注者。他们发现,人类和 GPT-4 对模型的排名大体一致,在模型层面的 Spearman 秩相关系数为 0.55。这又提供了一项数据,表明基于 LLM 的自动化评估可以成为人工评估的一种经济且合理的替代方案。
构建可靠的评估体系,应当是任何基于 LLM 的系统或产品(以及传统机器学习系统)的起点。
遗憾的是,对于生成式摘要或对话等更复杂的任务,BLEU 和 ROUGE 等经典指标并不适用。此外,我们已经看到,MMLU 等基准(以及 ROUGE 等指标)对具体实现和测量方式非常敏感。坦率地说,除非你的 LLM 系统正在备考学校考试,否则使用 MMLU 作为评估并不太合理。
因此,我们可以不使用现成的基准,而是先收集一组针对具体任务的评估样本(即提示词、上下文,以及作为参考的预期输出)。这些评估将进一步指导提示词工程、模型选择、微调等工作。随着系统不断更新,我们可以运行这些评估,快速衡量改进或回退。可以把它看作评估驱动开发(Eval Driven Development,EDD)。
除了评估数据集,我们还需要有用的指标。它们能将性能变化浓缩为一个数字,以便在不同评估运行之间进行比较。如果能够简化问题,我们就可以选择更容易计算和解释的指标。
最简单的任务可能是分类:如果使用 LLM 执行类似分类的任务(例如有害内容检测、文档分类),或处理不包含对话的抽取式问答,我们可以采用召回率、精确率、PRAUC 等标准分类指标。如果任务没有唯一正确答案,但我们拥有参考答案(例如机器翻译、抽取式摘要),则可以采用基于匹配的参考指标(BLEU、ROUGE)或基于语义相似度的指标(BERTScore、MoverScore)。
然而,这些指标可能不适用于生成式摘要、对话等更加开放的任务,而收集人工判断又可能既缓慢又昂贵。因此,我们可以选择借助能力强大的 LLM 进行自动化评估。
与通常带有较多噪声的人工判断相比——噪声源于不同标注者之间的偏差差异——LLM 的判断往往噪声更少,因为其偏差更具系统性,但偏差程度更高。尽管如此,既然我们知道这些偏差的存在,就可以采取相应措施来缓解:
位置偏差:LLM 往往偏爱排在第一个位置的回答。为了缓解这一问题,我们可以交换同一对回答的顺序,分别评估两次。如果同一个回答在两种顺序下都更受青睐,就将其记为胜出;否则记为平局。
冗长偏差:LLM 往往更偏爱较长、更啰唆的回答,而非更简洁的回答,即使后者更加清晰且质量更高。一种可行的解决方案是确保用于比较的回答长度相近。
自我增强偏差:LLM 会略微偏爱自己生成的答案。GPT-4 对自身答案的偏爱会使其胜率提高 10%,而 Claude-v1 对自身答案的偏爱会使其胜率提高 25%。为了抵消这种偏差,不要使用同一个 LLM 执行评估任务。
另一个技巧是:与其让 LLM 直接评估并给出分数,不如向它提供一个参考答案,并让它进行比较。这有助于减少噪声。
最后,有时最好的评估方式就是人工评估,也就是凭感觉检查。(不要与命名不佳的代码评估基准 HumanEval 混淆。)正如 MosaicML 在 Latent Space 播客中提到的那样(第 34 分钟):
基于感觉的评估绝不能被低估。……我们的其中一项评估,就是准备一批提示词,在模型训练过程中持续观察它们的回答,看看是否发生变化。坦率地说,我并不真的相信这些评估指标中的任何一个能够捕捉到我们真正关心的东西。我们的一个提示词是“推荐一些适合 3 岁和 7 岁儿童一起玩的游戏”,观察这个问题的答案在训练过程中如何变化,价值要大得多。——Jonathan Frankle
另请参阅这篇关于生成式摘要评估的深入分析。它涵盖了基于参考答案、上下文和偏好的指标,并讨论了幻觉检测。
检索增强生成(Retrieval-Augmented Generation,RAG)从基础模型外部获取相关数据,并用这些数据增强输入,从而提供更丰富的上下文并改善输出。
RAG 通过让模型以检索到的上下文为依据来减少幻觉,从而提高事实准确性。此外,保持检索索引及时更新的成本,低于持续预训练 LLM 的成本。得益于这种成本效益,可以更容易地通过 RAG 让 LLM 访问最新数据。最后,如果需要更新或删除存在偏见或有害内容的文档等数据,更新检索索引也更加直接,相较之下,微调 LLM 或通过提示词要求其不要生成有害输出会更复杂。
简而言之,RAG 将信息检索领域中成熟且更简单的理念应用于辅助 LLM 生成。在 Sequoia 最近开展的一项调查中,88% 的受访者认为检索将成为其技术栈中的关键组成部分。
在深入了解 RAG 之前,最好先对文本嵌入有一个基本认识。(如果你已经熟悉这个主题,可以随意跳过本节。)
文本嵌入是文本数据的一种压缩、抽象表示形式,可以将任意长度的文本表示为固定大小的数值向量。它通常从 Wikipedia 等文本语料库中学习得到。可以将其视为一种通用的文本编码:相似的条目彼此接近,而不相似的条目则相距较远。
好的嵌入应当能在下游任务(例如检索相似条目)中取得良好效果。Huggingface 的大规模文本嵌入基准(Massive Text Embedding Benchmark,MTEB)会在分类、聚类、检索、摘要等多种任务上对不同模型进行评分。
简要说明:虽然这里主要讨论文本嵌入,但嵌入可以涵盖多种模态。例如,CLIP 是多模态模型,它将图像和文本嵌入到同一空间中,使我们能够找到与输入文本最相似的图像。我们还可以根据用户行为(例如点击、购买)或图关系对产品进行嵌入。
RAG 起源于开放域问答。Meta 的一篇早期论文表明,通过 TF-IDF 检索相关文档,并将其作为上下文提供给语言模型(BERT),可以提升开放域问答任务的性能。他们将每项任务转换为完形填空语句,然后查询语言模型以获得缺失的 token。
随后,Dense Passage Retrieval(DPR)表明,使用稠密嵌入(而不是 TF-IDF 之类的稀疏向量空间)进行文档检索,可以超越 Lucene BM25 等强基线(top-5 准确率分别为 65.2% 和 42.9%)。他们还证明,更高的检索精度会转化为更高的端到端问答准确率,凸显了上游检索的重要性。
为了学习 DPR 嵌入,他们使用现有的问答对,对两个相互独立、基于 BERT 的编码器进行了微调。段落编码器((E_p))将文本段落嵌入为向量,而查询编码器((E_q))则将问题嵌入为向量。随后,系统使用查询嵌入检索与问题最相似的 (k) 个段落。
他们训练编码器,使点积相似度成为良好的排序函数,并将损失函数优化为正样本段落的负对数似然。DPR 嵌入经过优化,以最大化问题向量与相关段落向量之间的内积。其目标是学习一个向量空间,使问题及其相关段落组成的配对彼此接近。
在推理阶段,他们通过 (E_p) 嵌入所有段落,并离线将其索引到 FAISS 中。然后,在查询时给定一个问题,他们通过 (E_q) 计算问题嵌入,使用近似最近邻检索排名前 (k) 的段落,并将其提供给语言模型(BERT),由模型输出问题的答案。
Retrieval Augmented Generation(RAG)正是这一模式名称的来源,它指出了预训练 LLM 的一些缺点,包括无法扩展或修订记忆、无法解释生成的输出,以及会产生幻觉。
为了解决这些缺点,他们引入了 RAG(也称为半参数模型)。其中,稠密向量检索充当非参数组件,而预训练 LLM 则充当参数组件。他们复用 DPR 编码器来初始化检索器并构建文档索引。LLM 方面,他们使用了 BART,一个拥有 4 亿参数的 seq2seq 模型。
Retrieval Augmented Generation 概览(来源)
在推理过程中,他们将输入与检索到的文档拼接起来。随后,LLM 根据原始输入、检索到的文档以及前 (i-1) 个 token 生成 (\text{token}_i)。在生成方面,他们提出了两种方法,区别在于如何利用检索到的段落生成输出。
第一种方法是 RAG-Sequence,模型使用同一篇文档生成完整序列。因此,对于检索到的 (k) 篇文档,生成器会针对每篇文档生成一个输出。接下来,对每个输出序列的概率进行边缘化(将 (k) 个输出序列各自的概率相加,并根据对应文档被检索到的概率进行加权)。最后,选择概率最高的输出序列。
另一方面,RAG-Token 可以基于不同文档生成每个 token。给定检索到的 (k) 篇文档,生成器会先为每篇文档生成下一个输出 token 的概率分布,然后再进行边缘化(聚合所有单独的 token 分布)。接着,对下一个 token 重复这一过程。这意味着,每次生成 token 时,它都可以根据原始输入和先前生成的 token,检索一组不同的 (k) 篇相关文档。因此,不同文档可以具有不同的检索概率,并以不同程度影响下一个生成的 token。
Fusion-in-Decoder(FiD)同样将检索与生成模型结合起来,用于开放域问答。它支持两种检索方法:BM25(使用默认参数的 Lucene)和 DPR。FiD 的命名源于它只在解码器中对检索到的文档进行融合。
Fusion-in-Decoder 概览(来源)
对于每个检索到的段落,系统会将其标题和正文与问题拼接起来。这些组合会在编码器中相互独立地进行处理。他们还会在相应部分之前添加 question:、title: 和 context: 等特殊 token。解码器则对这些检索段落拼接后的结果执行注意力计算。
由于编码器会独立处理各个段落,因此它可以扩展到大量段落,因为每次只需对一个上下文执行自注意力计算。这样一来,计算量会随检索段落数量线性增长,而不是呈平方增长,因此比 RAG-Token 等替代方案更具可扩展性。随后,在解码过程中,解码器会联合处理编码后的段落,从而能够更好地聚合多个检索段落中的上下文。
Retrieval-Enhanced Transformer(RETRO)采用了类似的模式,它结合冻结的 BERT 检索器、可微分编码器和分块交叉注意力来生成输出。不同之处在于,RETRO 会在整个预训练阶段持续执行检索,并且