策略建模
搜索引擎需要将用户最需要得到的内容排在前列,呈现给用户;而 AIGC 系统则可以结合自己的庞大知识储备,整合用户可能会感兴趣的信息,并进行创造性的结合,返回给用户。
搜索引擎作为服务提供者,一定程度上是适用于“避风港原则”的,也就是只需要配合审查,履行删除的义务即可。但是 AIGC 系统就不一样了,它返回给用户的内容,会被看成服务提供者“生成”的内容。
而 AIGC 系统则是会先根据用户请求生成推理计划,之后,再通过检索海量内容为生成模型提供实时的外部记忆,随后再由模型生成内容,最后,再通过搜索引擎验证内容的可信性,呈现给用户。其中和搜索引擎相同的部分可以继续沿用 AIRC 系统的评价标准。
提示语工程
让 LLM 思考的方法类似于“产婆术”,所以我们唯一能和 LLM 进行互动的方式就是通过“提示语 Prompt”。所以在实践中,提示语引擎在一定程度上就是智能体的本体。
提示语引擎的任务是针对用户的问题,对智能体的外部记忆进行排序,再将排名靠前的内容返回给大语言模型。这就是你在各类大语言模型对话系统上,都能发现类似“搜索增强”能力的原因。

生成模块


控制模块
为了赶上 ChatGPT 的数据收集和标注的速度,目前主流的在线大模型系统都采用了基于强化学习的增量模型训练方法。
控制模块不仅可以用于高效地标注用户反馈,还可以用于自动设置大模型在线服务的升降级策略,控制商业成本(即总体算力开销)。这样,就可以在有限的算力下,最大化客户体验。
风控模块
考虑内容安全的问题,使用专有服务对模型生成内容的合规性和品牌安全性进行综合评估,在第一时间过滤掉有风险的内容,并交由人工审核。此外,我们还需要考虑反馈数据的“反作弊”,确保服务所回收数据的安全性。
不同之处是,随着大语言模型的走红,针对大模型系统的攻ji手段层出不穷。攻ji者可以对训练数据进行“投毒”影响你的模型训练效果,或者是使用对抗样本对你的模型进行越狱,突破安全屏障,甚至还会诱导你的模型生成注入指令,进而入侵你的系统。
提示语工程
提示语引擎(prompt engine)是一个功能完备、拎包入住的成品房,而提示语工程(prompt engineering)就是搭建这个房子的设计图纸和施工手册。
提示语引擎是专门服务于生成式 AI 模型的系统模块,它需要具备各类知识表达和知识检索的能力,识别用户意图、选择检索结果,并且进行有效的信息压缩。而这些任务都需要精心设计的提示语工程来完成。
上下文学习 ICL
OpenAI 提出的一个更新的理念,即“上下文学习(In-Context Learning)”。这个能力允许我们在提示语中提供示例样本,以帮助模型理解要解决的任务的特点。
这种不需要模型微调,直接将知识通过提示词注入模型的方法也引出了全新的“少样本学习(Few-Shot Learning)”的概念。
零样本学习就是直接将模型需要处理的任务,通过提示语提供给模型,并要求模型输出结果。而少样本学习则会为模型提供例子,具体就是一组在目标任务上的高质量示例,通常每个示例都包括一组目标的输入和输出。
大模型系统会根据不同用户的使用记录,自行判断哪类提示词对自家模型更友好。在提示与长度限制相对宽松的情况下,系统还会自动使用 Few-Shot 的形式帮助用户使用更加友好的提示词来优化提示语质量。
提示语工程的核心目标是,基于 ICL 的特点为 LLM 选择适当的 Few-Shot 示例样本。
提示语工程设计
为模型提供外部知识,也就是通过示例样本来进行少样本学习(Few-Shot Learning)。
让模型理解指令任务,通过提示语策略,帮助模型解决复杂问题。
优选Selecting
为模型提供有效的示例样本

排序Ordering
因为业界的实践表明,在某些测试中,不同排序的示例可能导致生成内容的质量大幅波动。因此,除了示例样本的选取之外,示例的排序也会影响提示工程的有效性。目前,主要有一种常见的工业界使用的排序策略,也是最低成本的策略,那就是基于示例质量的排序,这个方法非常直接,只需要将质量较高的示例样本排在后面,以靠近问题输入的位置。
可信AI
现在工业界在 AIGC 系统的链路就是:检索 -> 生成 -> 检索的形态。你可能会问了,检索到生成的这个过程我倒是理解,可是为什么生成后面还会再加一个检索呢?
因为两次检索的目标是不同的。第一次检索的输入是用户问题,目的是为大语言模型提供外部记忆;而第二次检索的输入则是生成内容,目的是为生成的内容提供引用信息,增加生成内容的可信度,此外,我们还可以通过这次检索,来优选生成模型输出的多个结果。
推理步骤
思维链CoT 是一种用于引导大语言模型进行推理的方法,CoT 通过提示词指示模型,生成一系列简短的句子来给出推理步骤。CoT 的优势在复杂的推理任务更明显,在使用大参数模型(比如 50B 以上的模型)时,这个优势则更明显。
CoT 提示可以约束大语言模型理解和遵循推理逻辑,从而提高推理任务的准确性。虽然 CoT 可以用一些动态的步骤生成答案,但是还是存在一些局限性。这就催生出了很多方法来优化 CoT 的过程,比如自一致采样Self-Consistency Sampling
大模型热身
模型热身技术可以通过修改模型参数或添加额外的参数,来针对目标场景进行调整。模型热身和微调都是针对模型进行调整的方法,但它们的侧重点不同。模型热身是针对大语言模型上下文学习的整体能力进行调整,而微调是针对特定任务进行调整。
优选 Selecting
在获得了优质的外部记忆后,提示语引擎还会进行一个“优中选优”的过程。

KATE 就近法则
简单来说,进行了一组实验来验证一件事,那就是跟提问内容语义(高维空间距离)更相近的示例,是否能够帮助大语言模型得到更好的结果。实验结果显示,这个方法在文本理解和文本生成任务上,都明显优于随机选择示例的做法。
EPR 因材施教
这类方法只需要给出优秀示例和劣质示例的对比关系,就可以训练出一个打分模型,用来评价示例样本的质量。所以这里唯一的问题,就是如何定义提示语中示例样本的“质量”高低。文章中提出了一种方法,这种方法可以利用大语言模型本身来为打分模型提供输入。
首先,我们需要从示例样本集中选择一个示例样本。然后,基于这个示例样本和训练集中的题目进行组合作为提示语,通过大语言模型为这个提示语生成的输出结果打分。接着,将平均得分高的示例样本选为正样本,平均得分低的示例样本选为负样本。最后,基于正负样本进行对比学习训练,得到一个对示例样本打分的模型。
排序Ordering
基于熵的排序:对一个示例样本的序列,计算它概率的熵值。熵值越大,就表示该序列的质量越高。
全局熵(Global Entropy: GlobalE)和局部熵(Local Entropy: LocalE),跟随机排序的方法相比,可以分别平均提高 13% 和 9.6% 的性能。这种方法相较于“就近示例”方法更加复杂,但在理论上具有更强的可解释性,更适用于对稳定性要求较高的工业级系统。
提示词模版

APE 方法可以提高模型的回答质量,将提示语工程的工作自动化掉,提高用户产品体验。这样用户在和大语言模型对话的时候,只要给出自己要完成的任务,APE 就会自动帮他选择更合适的沟通方式来与大语言模型对话了。
自一致采样
第一步,使用思维链(CoT)提示语言模型,分步骤地解决给定问题。
第二步,从语言模型的解码器中随机采样,生成一组不同的推理路径。
第三步, 在众多最终答案中,选择最一致的答案,作为最终的推理结果。
这个思想,和我们在数学考试中,通过“一题多解”来验证答案的正确性,其实是一样的。它通过让 LLM 从多个不同的推理路径中生成答案,并根据答案的自洽性来选择最优的结果。
自一致采样是目前最广泛使用的提示语工程方法。这个方法既可以提高模型的回答质量,又可以提高模型的鲁棒性。
反刍知识
通过增强大语言模型输入的质量,来提升生成内容质量。
为了不滥用模型的“涌现”能力,在一些情况下,我们可以先帮助模型,去唤醒一些它的“深度记忆”,让它知道该用这些学过的知识来,回答问题。
这里的“记忆”是指模型在训练过程中获得的记忆,而不是提示词提供的外部记忆。这个方法的具体步骤是先让大语言模型生成一些,跟问题内容相关的“知识”,然后再把这个知识做为输入示例样本反哺给大语言模型,辅助回答目标问题。
类似于人类把自己想到的和问题相关的所有知识写在纸上,再盯着这张纸去思考问题答案的过程。
大模型觅食
Self-Ask 本质上是通过给大语言模型几个的示例样本(Few-Shot),帮助模型理解在什么情况下,它需要向外界索要更多的外部知识。
在条件不足的情况下,通过索要更多信息,补全条件后问题得以解决的例子。这个例子可以帮助大语言模型理解在什么场景下,它应该提出需求,来索要更多的信息辅助它回答蓝框中给定的问题。
绿色部分则是模型的输出内容,这些输出内容会告诉我们它是否需要更多的额外信息(Follow up)。如果需要,提示引擎则会调用外部记忆或工具去获取更多的信息。
智能体记忆管理
每个智能体都有自己独特的剧本,也就是种子记忆,其中记录了他们的出身和成长经历。

除了单个智能体的行动。智能体还会知道周围存在着哪些其他智能体,它可以自己决定无视对方还是进行对话。对话的方式跟人一样,也是通过自然语言沟通。

记忆能力
为了解决这个问题,论文中提出了一种叫做记忆流的方案。记忆流保存了智能体的完整经历,是一个记忆对象的列表,每个对象包含自然语言的描述、创建的时间戳以及最近访问的时间戳。记忆流中最基本的元素是记忆事件,这是每个智能体直接感知到的事件,你可以理解成这是智能体的生活日记。

根据给定的当前人物(persona)和焦点事件(focal points),从记忆流中检索相关记忆事件,并将结果返回放入一个字典。

计算最终的综合分数,将新旧程度、相关性和重要性得分按照一组权重进行组合,得到每个节点的最终得分。
接下来,我们会从所有节点中选取分数最高的节点,数量不超过 n_count。
更新选中节点的 last_accessed 属性,这个属性是每个记忆事件最后一次被访问的时间。

智能体反思能力
在智能体使用记忆流原始观测记录时,很难进行跨时空的概括或推理。比方说,让 Klaus Mueller 判断他和谁的关系最好时,他会直接选择和他互动最频繁的那个人,而忽略了他们之间的社交关系和对话质量。
反思区别于观察事件记忆,它是一种更高层次的认知能力,包括对经验的分析、评价和总结。反思能力是人类认知的重要组成部分,它可以帮助人类从错误中吸取教训,从而提高学习和适应能力。为了让你的智能体拥有反思能力,反思的结论也会成为记忆流中的记忆,帮助智能体更好地理解和处理未来的事件。这样智能体在“回忆”的时候,同样会把反思的结果检索出来。
论文中的反思机制也受到了人类反思行为的启发。人类会在各种时间和情况下进行反思,比如在深夜,或是读到了一本特别的书。周期性的反思,可以帮智能体定期去总结经验,而随机性的反思呢,可以帮智能体更好地去应对突发事件。
存储反思
不断把反思存储在记忆流中,并建立反思与相关记忆的连接,便可以逐渐生成一颗智能体的反思树。树的叶子节点代表观察事件记忆,而非叶子节点代表反思记忆,随着节点高度的增加,它所代表的思维,也变得更加抽象和高级。
智能体规划能力
如果仅仅依据当前情境信息生成行为,很可能会导致智能体出现不合理或不连贯的行为,比如智能体可能经常一天吃两次午饭。所以,除了日常的生活和偶尔的反思之外,一般而言,智能体需要在更长的时间轴上进行规划,以确保其行为序列的一致性和可信度。
引入规划能力,它描述了智能体未来的行为序列,这个行为序列可以帮助它在时间维度上保持行为的一致性。一个规划包含地点、开始时间和持续时间。
全天规划
第一步就是粗略地制定一个当天行程的计划。通常这个计划会描述智能体未来的一系列动作,包括位置、开始时间和持续时间。具体来说,创建初始计划时,我们需要告诉大语言模型,该智能体历史描述,例如姓名、特征及其最近经历的摘要还有他们前一天的摘要来提示大语言模型,帮助 LLM 更好地生成新的规划。
与反思一样,计划也存储在智能体的记忆流中,这允许智能体在回忆时同时参考观察、反思和计划的记忆。智能体可以根据需要随时更改其计划。
细节规划

根据智能体的摘要性描述(如姓名、特征和最近经历的概括)以及前一天的摘要生成一个初始的计划,然后递归地将该计划拆解为更详细的行动。
临时规划
智能体可以根据环境的变化或自身的目标和需求来调整规划。

知识表征技术

首先是倒排索引,这是知识表征技术中最经典的技术之一,直接通过文档中的字面关键词,作为文档的特征表示。
接下来是嵌入表征,它是利用知识在高维空间的映射,来表示知识的语义信息。
最后是知识图谱,知识图谱则可以将知识的内容进一步结构化,并且通过图神经网络的方式,把知识转化为富含结构语义的高维特征信息。
每种单一的表征方法都会有自己的局限性。所以在真实的应用场景中,为了发挥各类表征能力的优点,补足其他方法的不足,通常会使用多路召回的方式进行表征。
每种不同的表征方法也对应了不同的存储和检索策略,上述方法中倒排索引的存储逻辑我们已经很熟悉了,其中 ElasticSearch 是当仁不让的开源顶流,这里重点介绍一下向量检索引擎。
最大内积搜索 MIPS
向量检索的根本目标是找到与查询向量最相似的 K 个向量(KNN),或者找到距离查询量小于某个距离阈值的所有向量(RNN)。但工业场景中数据量往往很大,如果采用精确最近邻算法,会导致检索效率太低。所以我们通常选择牺牲一定准确性来提高速度,采用近似最近邻算法(ANN)。

在 Faiss 中就使用了 PQ 算法,PQ 算法是一种近似最近邻算法,它将高维数据映射到低维空间进行检索。PQ 算法的基本思想是将原始向量空间分解为多个低维向量空间的笛卡尔积,然后对每个低维向量空间进行量化。这样,每个原始向量就可以由多个低维量化编码表示。
向量检索数据库
其中一个挑战是超大规模索引的精度和效率问题。随着数据量的不断增加,在亿级、甚至十亿级时,向量索引的构建和检索成本也会随之增加,这可能会影响向量检索的性能和准确性。
另一个挑战是高维数据的处理问题。随着维度的增加,向量检索的计算复杂度也会呈指数级增长,这可能会导致查询效率下降、存储成本升高。
HA3 是为了让你深入学习其中外部记忆的存储和检索能力,它是阿里巴巴 AI 在线架构 AI·OS 的最重要的组成部分之一。
模型工程
如何获取训练数据
OpenAI 为了对齐 LLM 与人类的交互方式,投入了大量的人工标注成本,对 GPT-3 这类大型语言预训练模型进行了指令微调。
Self-Instruct
是一种数据增强方法,其目标是减少对人工标注人员的依赖。Self-Instruct 从一组初始示例样本开始,以 LLM 的自我引导方式生成新的指令和示例。
第一步,准备种子数据。我们需要创建一个种子数据集,这一步,是唯一需要标注人员的参与环节。我们需要为每个任务准备一条指令和一个示例,同时在这里我们需要明确这些指令是用于分类,还是其他的用途。
第二步,生成提示指令,也就是利用大型预训练语言模型(LLM)来生成新的指令。就像之前提到的,每个任务都包含一条指令和一个示例。在每一轮的数据生成中,我们都从这个任务池里面,随机选择 8 个任务指令作为上下文示例。这 8 个指令中包含了 6 个人工编写的样本和 2 个来自前一轮模型生成的样本,这可以有效地提高生成指令的多样性。
第三步,判断任务类型。我们要判断生成的指令是否属于分类任务。为什么需要这一步呢?因为这两类任务后续的处理方法和生成步骤是不同的,要区分处理。
对于分类任务,更适合使用输出优先方法。这种方法会首先生成分类标签,然后根据分类标签生成输入。
第四步,我们会过滤生成内容,完成数据过滤和后处理这些任务。为了确保生成数据的质量,只有当新生成的指令与现有指令的 ROUGE-L 重叠小于 0.7 时,才会将其添加到任务池中,以确保指令的这质量和多样性。同时,这一步还会排除包含“图片”和“图表”这些关键词的指令,并过滤掉完全相同的,或具有相同输入、但输出不同的示例。
最后,在获得足够的指令和示例后,算法就会停止生成任务。

随后,斯坦福的研究团队基于 LoRA 训练框架和 Llama 开源预训练模型,对这些生成增强数据进行了微调训练,得到了 Alpaca 模型,事实表明通过这种方法得到的模型,可以在指定任务上达到 SOTA 的效果。
领域大型语言模型如雨后春笋般涌现,比如 Guanaco 模型在 Alpaca 的基础上增加了多语言语料和指令任务。“小羊驼” 模型 Vicuna-13B 则是基于 LLaMA, 并使用从 ShareGPT 采集的对话数据对进行了指令微调,在特定的实验设置下,仅使用了 300 美元训练成本,就达到了与 ChatGPT 相媲美的性能水平。
如何使用少量算力训练
LoRA:低秩适应
LoRA 是一种通用的模型训练方法。它最早本就是用来加速大语言模型训练的,这点你从它的全称 “Low-Rank Adaptation of Large Language Models” 就能看出来。
回顾一下什么是权重变化 ΔW ?假设 W 代表神经网络层中的权重矩阵。使用标准的反向传播,我们可以计算梯度来得到权重的更新 ΔW 。至于梯度下降法的原理(如下图)。

LoRA 技术的核心观点是,预训练的大语言模型在适应特定任务时,可能仅仅依赖于较低的“内在维度”。即使将其权重投射到较小的子空间,模型仍然可以有效地学习,这一观点构成了 LoRA 技术的理论基础。
LoRA 的作者认为模型通常是过度参数化的,它们具有更低的“内在维度”,模型主要依赖于这个更低的维度来完成任务。LoRA 技术允许我们通过在微调过程中,使用全连接层(dense layer)的秩分解矩阵,间接训练神经网络中的一些全连接层,同时保持预先训练的权重不变。
LoRA 的实现思想很直观:我们首先冻结一个预训练模型的矩阵参数,然后选择使用 A 和 B 矩阵来代替这些参数。在下游任务的训练中,我们只对 A 和 B 进行更新即可。

该方法会在原始的预训练模型右侧添加一个侧通道,进行降维和升维的操作,以模拟内在维度的概念。在训练的过程中,需要保持预训练模型的参数不变,只对降维矩阵 A 和升维矩阵 B 进行训练。模型的输入输出维度保持不变,在输出时,将 BA 矩阵与预训练的参数相叠加即可。
使用随机高斯分布来初始化矩阵 A,同时使用零矩阵初始化矩阵 B,这样可以确保在训练开始时,这个侧通道矩阵是一个零矩阵。
LoRA数学原理
当我们需要对一个预训练语言模型(如 GPT-3)进行下游任务的微调时,我们需要更新预训练模型的参数。这个过程可以用如下数学公式表示:W0+ΔW。
在这个公式中,W0 代表了预训练模型的初始参数,而 ΔW 代表了需要更新的参数。如果我们要进行全参数微调,那么 ΔW 的参数量将等于 W0 的参数量,比如对于 GPT-3 来说, W0 包含了约 175B 个参数,所以全参数微调对于大语言模型而言,所需的计算资源是巨大的。
然而,得益于低维“内在维度(intrinsic dimension)”的存在,让我们可以在任务适配过程中做到“四两拨千斤”。也就是说,即使将权重参数随机投影到较小的子空间,模型仍然能够继续有效地学习。
从本质上看,LoRA 的目标就是引入一个较小的参数模块,用于学习参数变化 ΔW。
LoRA 使用低秩分解的方法来表示预训练的权重矩阵 W0 的更新,预训练的权重矩阵表示为 W0∈Rd×k,其中 d 是输入维度, k 是输出维度。更新可以表示为 W0+ΔW=W0+BA,其中 B 是一个 d×r 的矩阵, A 是一个 r×k 的矩阵,而秩 r≪min(d,k)。
在训练过程中,我们保持 W0 不变,不接受梯度更新,而 A 和 B 包含了可训练的参数。当输入向量 x 通过 W0 进行线性变换,得到输出向量 h=W0x 时,修正后的前向传播可以表示为 h=(W0+ΔW)x。
在推理的过程中,只需要将 ΔW 放回原始模型。我们可以把 W 表示为后面的形式,也就是 W=W0+BA。
如果需要切换到另一个任务,只需在切换过程中减去 BA,然后使用另一个任务的训练好的参数 B’ 和 A’ 来替代就可以了。


开源项目Alpaca
看一下数据生成算法的实现,Alpaca 模型是通过对 7B 的 LLaMA 模型使用 self-instruct 论文中的技术生成的 5.2 万条指令遵循数据。self-instruct 论文提出了一种新的生成数据增强方法,可以有效地提高大语言模型的性能。
目前 Alpaca 模型仍在发展中,存在一些局限性,例如安全性问题。不过,安全性问题是开源大语言模型的通用问题,因此在使用 Alpaca 模型时,我们需要采取相应的安全措施,例如使用安全的输入数据和训练方法,以及在部署模型时应采取相应的风控和内容安全策略。
数据生成过程
Alpaca 对 self-instruct(自我教导)数据生成的方式进行了升级,提高了效率,降低了花费。这些升级包括用 text-davinci-003(代替 davinci)来生成指令数据,并且创建了一个新的提示词模版 prompt.txt。
还采用了更大胆的批量解码方式,每一次会生成 20 条指令,大幅度降低了生成数据的费用。同时,数据生成流程也变得更简单了,取消了分类指令和非分类指令之间的差异,每个指令只生成一个实例,而不再生成 2 到 3 个实例。
优化成功生成了包含 52000 个实例的指令数据集,而成本只有不到 500 美元。初步研究还显示,与之前的 self-instruct 发布的数据相比,这样生成的这 52000 条数据更加多样化。









全量参数训练
数据集包含了 52000 条指令,每个指令由以下字段组成。
instruction:一个字符串,描述模型应该执行的任务,这 52000 条指令都是独一无二的。
input:是一个可选的字段,用来表示指令的可选上下文或输入。大约 40% 的示例包含这个字段,例如,当指令是 “总结以下文章” 时,该字段的输入则是文章原文内容。
output:一个字符串,由 text-davinci-003 生成的、作为指令数据的输出。
接下来,需要在拥有 4 个 A100 80G GPU(FSDP full_shard 模式)的机器上,使用我们的数据集对 LLaMA-7B 进行微调。
LoRA 低成本训练
Alpaca 提供了一个 Instruct 模型,而且训练的质量与 text-davinci-003 类似,可以在树莓派上运行的(用于研究)、而且代码也可以轻松扩展到 13b、30b 和 65b 的模型。
该项目还发布了已微调好的模型和 LoRA 权重以及推理的脚本。为了让微调的过程节省资源并且高效,该项目使用了 HuggingFace 的 PEFT 和 Tim Dettmers 的 bitsandbytes 来优化性能。






推理
如果没有领域定制训练的需求,只是想看看 Alpaca 默认实现的模型效果是什么样的,可以直接使用 generate.py 从 Hugging Face 模型库读取基础模型和 LoRA 权重,然后运行一个 Gradio 接口,用于对指定输入进行推理。

















