动手点关注
干货不迷路
本文旨在让无大模型开发背景的工程师或者技术爱好者无痛理解大语言模型应用开发的理论和主流工具,因此会先从与LLM应用开发相关的基础概念谈起,并不刻意追求极致的严谨和完备,而是从直觉和本质入手,结合笔者调研整理及消化理解,帮助大家能够更容易的理解LLM技术全貌,大家可以基于本文衍生展开,结合自己感兴趣的领域深入研究。若有不准确或者错误的地方也希望大家能够留言指正。
本文体系完整,内容丰富,由于内容比较多,分多次连载 。
第一部分 基础概念
1.机器学习场景类别
2.机器学习类型(LLM相关)
3.深度学习的兴起
4.基础模型
第二部分 应用挑战
1.问题定义与基本思路
2.基本流程与相关技术
1)Tokenization与Embbeding
2)向量数据库
3)finetune(微调)
4)模型部署与推理
5)prompt
6)编排与集成
7)其它(预训练等)
第三部分 场景案例
常用参考
2.基本流程与相关技术
点此查看前面内容:
5)其它技术
在前面的文章里,我们介绍了当前业内LLM应用开发经常遇到的、也需要掌握的技术,但仍然有一些这些相对低频,启用成本高的技术需要了解,比训练阶段的预训练、基于人类反馈的强化学习的指令微调(RLHF)。
在本章你将学习到:
1)预训练(Pre-Train)的概念与相关技术
2)基于人类反馈的强化学习的指令微调(RLHF)的概念与相关技术
在之前的内容基础上再结合这些内容以便对整个以GPT模型为代表大模型技术全貌有更深理解。
预训练(Pre-Training)
概念与理论
预训练对于大模型来讲是获取知识和智能的关键阶段,通过在大规模语料中进行预训练,LLM就可以获得基本的语言理解和生成能力,其进一步的推理能力也是在此环境习得。一般情况下,对于大模型应用开发者来讲,在既有的预训练模型上进行微调,就可以达到注入领域知识以及对齐指令的目的。但是,这样做的前提是,预训练模型内涵的知识与下游任务需要的内容差距不大。但对于以下一些情况就变得不那么有效,需要进行预训练。
首先是语言问题,众所周知,llama系列的模型在中文语料稀少,在llama2中仅占0.13%[1]。因此,虽然llama2在英文场景下表现很不错,但直接用在中文场景下表现并不好。
其次是领域知识问题,对于一些专业垂直领域,比如法律、医学等,通常有大量的概念和名词在一般的通用语料中并不存在。因此,为了使得模型能够在这些领域表现更好,就有必要加入一些领域内的语料,从而提升其在垂直领域的表现。
对于一个预训练过程,大体分为如下几个阶段:
1)数据收集。要开发一个优秀的大语言模型,第一步就是能够找到大量的语料库,这些数据包括两类:通用文本数据和专用文本数据。其中通用文本主要是一些网页,对话文本,书籍等。而专用文本指的是一些多语言文本,如不同的语言,科学文本,比如论文、教材、数学公式等,还有代码,比如github上的大量代码,这样多样的数据对于后期多样的下游任务有帮助,比如让大模型写代码、做翻译,并且有研究表明代码语料可能是大模型复杂推理能力(如COT)的来源。下图是一些大模型的各类型内容占比。
2)数据预处理。有了大量的语料后,并不能直接用于训练,需要进行一些数据预处理,比如消除噪声、冗余、无关以及潜在有害的数据,它对于模型质量有非常重要的作用,直接影响到模型的性能。
如图,是预处理的基本流程,包含了质量过滤、去重、隐私信息过滤和分词。其中分词是预处理的关键步骤,它核心目的是将原始的文本分割成词序列,作为LLM的输入。在传统的 NLP 研究中,基于词的切分是主要方法,它更符合人类的语言认知。然而,基于词的切分会对某些语言的相同输入产生不同的分词结果(例如,中文分词),生成包含许多低频词的巨大词词汇表,还会受到“词典外”问题的困扰。因此,一些深度学习模型使用字符作为最小单位来推导出词的表征。虽然可以使用现有通用分词器,但使用专门针对预训练语料库设计的分词器更好,尤其是对于由不同领域、语言和格式组成的语料库,如专门用于预训练分词的SentencePiece库。预训练时代表性分词方法有以下三种:
- 字节对编码分词(Byte-Pair Encoding (BPE) tokenization)BPE 最初于 1994 年被提出,作为一种通用数据压缩算法 ,后来被改造用于 NLP 分词。它从一组基本符号(例如字母和边界字符)开始,并迭代地将语料库中连续的两个标记的频繁对组合为新token(称为合并)。对于每次合并,选择标准基于两个连续标记的共现频率:将选择最频繁的对。合并过程一直持续到达到预定义的大小。此外,BPE 已被用于通过将字节视为合并的基本符号来提高多语言语料库(例如包含非 ASCII 字符的文本)的标记化质量。采用这种标记化方法的代表性语言模型包括 GPT-2、BART 和 LLaMA。
- WordPiece 分词。WordPiece 是谷歌内部的一个词内分词算法。它最初由谷歌在开发语音搜索系统时提出 。然后,它被用于 2016 年的神经机器翻译系统中,并于 2018 年被采纳为 BERT 的词分词器。WordPiece 与 BPE 的思想非常相似,都是通过迭代合并连续的token来实现的,但合并的选取标准略有不同。为了进行合并,它首先训练一个语言模型,并用它对所有可能的对进行评分。然后,在每次合并中,它选择导致训练数据似然性增加最多的对。
- 一元分词(Unigram tokenization.)。与 BPE 和 WordPiece 不同,一元分词 从语料库中足够大的可能子字符串或子token集合开始,并反复删除当前词表中的token,直到达到预期的词表大小。作为选择标准,它计算出假设从当前词表中删除某个token后,训练语料库似然度增加的幅度。此步骤基于训练好的一元分词模型进行。为了估计一元分词模型,它采用了期望最大化 (EM) 算法:在每次迭代中,我们首先根据旧的语言模型找到单词的当前最优标记化,然后重新估计单字的概率以更新语言模型。在此过程中,动态规划算法(即Viterbi算法)用于根据语言模型有效地找到单词的最优分解方式。采用此分词方法的代表性模型包括 T5 和 mBART。
实际上,对于开发者来讲,解决中文问题或者领域专有词就是在这个环节通过构建自己的分词器(tokenizer)完成。比如:
#词表格式:
案件受理费
按揭
按揭贷款
案卷
澳门特别行政区基本法
颁布法律
办公室
搬迁合同
版权
版权侵权行为
版权转让
版权转让合同
版式权
# Load custom vocabulary
new_tokens = open(VOC_PATH, "r").read().split("\n")
for token in new_tokens:
if token not in llama_spm_tokens_set:
new_token = model.ModelProto().SentencePiece()
new_token.piece = token
new_token.score = 0
llama_spm.pieces.append(new_token)
print(f"Size of merged llama's vocabulary: {len(llama_spm.pieces)}")
比如Chinese-LLaMA-Alpaca就对llama增加了中文词表从32000扩展到49953,并在此基础上在中文语料(20GB+)上做了二次训练。。
https://arxiv.org/pdf/2304.08177.pdf
3)数据策略(Data Scheduling),准备好数据后,下一步就是确定每个数据源的比例(数据混合(data mixture))以及每个数据源被安排用于训练的顺序(数据课程(data curriculum))。研究表明设置一个合适的数据混合比例对于LLM的能力有很大作用,在预训练期间,将根据混合比例选择来自不同来源的数据样本:来自权重较大的数据源的数据将被更多地采样。比如:LLaMA的预训练数据主要由网页(超过 80%)组成,此外还有来自 GitHub 和 StackExchange 的 6.5% 的大量代码数据,来自书籍的 4.5%,以及来自 arXiv 的 2.5% 的科学数据,作为代表模型,其他模型也会参考它配比。 此外,可以使用特殊数据混合来促进不同的目的。例如,Falcon在纯网页上训练,CodeGen 大大增加了代码数据量。在实践中,数据混合通常是凭经验确定的,常见的数据混合策略有增加数据源的多样性、优化优化数据混合的比例和专注特定的目标能力,如提高特定数据源的比例来增强某些模型能力。
另一方面,除了数据混合的比例外,数据用于训练的顺序也对模型性能有影响。结果表明,在某些情况下,为了学习某种技能,按照技能集顺序(例如,基本技能 → 目标技能)进行学习优于直接从仅关注目标技能的语料库中学习 。这就和大学学生学习安排课程的逻辑一样,先学基础课再学专业课,也是被称作数据课程的原因之一。
为了确定数据课程,一种实用方法是根据专门构建的评估基准来监控 LLM 的关键能力的发展,然后在预训练期间自适应地调整数据混合。
为了确定这两个设置,一种实用方法是首先使用多个候选方案训练几个小型语言模型,然后从中选择一个好的方案。在实践中,人们可以监控特定评估基准上的中间模型检查点的性能,并在预训练期间动态调整数据混合和分布。在此过程中,探索数据源与模型能力之间的潜在关系( grokking)对于指导数据课程的设计也很有帮助。因此也催生了一个新的热点领域——大语言模型数据工程(LLM Data Engineering)。
4)训练架构。有了数据接下来就是选择合适的模型架构及超参数配置。大模型的模型架构是基于Transformer的。
在此基础上主流的大模型的架构可分为四类:
- Encoder-decoder(编码解码器)架构,采用该架构的大模型有T5和BART。但到了当下,这类架构由于其泛化性、推理性能等因素已经少有使用。
- Causal Decoder(因果解码器)架构,即decoder-only架构,所谓因果采用单向的注意力掩码,以确保每个token只关注过去的token和它本身。输入和输出token通过编码器以相同的方式进行处理。因此基于此KVcache的设计可以大大加速推理的速度。并且研究表明,因果解码器架构可以实现更优越的zero-shot和few-shot的泛化能力,即使在没有进行多任务微调的情况下,它一样能够比别的架构(Encoder-decoder、Prefix Decoder)表现出更好的zero-shot性能。代表模型有GPT系列模型。
- Prefix Decoder(前缀解码器)架构,也称非因果解码器架构,修正了因果解码器的掩码机制,使其能够对前缀token执行双向注意力,并仅对生成的token执行单项注意力。代表模型有GLM系列模型。
- Mixture-of-Experts(混合专家 )架构,在此基础上,也可以通过专家混合MoE扩展它们,其中每个输入的神经网络权重的一个子集被稀疏激活,如Switch Transformer 和 GLaM。MoE 的主要优点是它是一种灵活的方法,可以在保持恒定计算成本的同时扩展模型参数。结果表明,通过增加专家数量或总参数大小,可以观察到实质性的性能改进 。尽管有优点,但由于路由操作的复杂性和硬切换特性,训练大型 MoE 模型可能会遇到不稳定问题。为了增强基于 MoE 的语言模型的训练稳定性,已经引入了诸如在路由模块中选择性地使用高精度张量或使用较小范围初始化模型等技术。代表模型有当红开源模型mistral以及GPT-4 (未证实)。
除了上面四类架构外,也有一些新兴架构尝试,解决因为传统的 Transformer 架构的二次计算复杂度带来的效率问题。
为提升Transformer模型训练的稳定性,业内提出了各种改进措施来增强其训练稳定性、性能和计算效率。这里涉及到确定相应组件的超参数等配置,包括归一化方法(LayerNorm,RMSNorm,DeepNorm)、归一化位置(Post Norm、Pre Norm、Sandwich Norm)、激活函数(ReLu,GeLU,Swish,SwiGLU,GeGLU)、Attention(Full attion、Sparse attention、Multi-query/grouped-query attention、FlashAttention、PagedAttention)以及位置embedding(Absolute,Relative,RoPE,ALiBi)。
除了确定模型架构和超参数外,还有其它一些任务需要设置,比如语言建模和去噪自动编码。随着LLM的实际应用,对其上下文长度的要求越来越高,也会确定长上下文建模(Long Context Modeling)相关配置,其包含扩展位置嵌入(Scaling Position Embeddings)和调整上下文窗口。另外,预训练完成的LLM,采用何种解码策略也是非常重要的,这里涉及到Greedy Search的策略(如Beam search设置和Length penalty系数)及Random Sampling的设置,(如常见温度采样或Top-k采样)。
5)模型训练。有了数据,并且确定了模型架构、超参数配置以及其它相关配置后,就可以进入正式的训练。在此阶段,仍然有以下训练参数需要配置以优化训练速度和模型性能,包括:
- 批量训练大小(Batchsize),对于语言模型预训练,现有工作通常将批大小设置为一个大数字(例如,2,048 个示例或 4M 个token)以提高训练稳定性和吞吐量。对于诸如 GPT-3 和 PaLM 之类的 LLM,它们引入了一种新的策略,即在训练期间动态增加批大小,最终达到百万级。具体来说,GPT-3 的批大小从 32K 逐渐增加到 3.2M 个token。经验结果表明,批大小的动态调度可以有效地稳定 LLM 的训练过程 。
- 学习率(Learning Rate),现有的 LLM 通常在预训练期间采用具有热身和衰减策略的类似学习率计划。具体来说,在训练步骤的初始 0.1% 到 0.5% 中,采用线性热身计划来逐渐将学习率增加到最大值,该值范围约为 5 × 10−5 到 1 × 10−4(例如,GPT-3 为 6 × 10−5)。然后,在后续步骤中采用余弦衰减策略,逐渐将学习率降低到其最大值的约 10%,直到训练损失收敛。
- 优化器(Optimizer),Adam 优化器和 AdamW 优化器 被广泛用于训练 LLM(例如,GPT-3),这些优化器基于一阶梯度优化的一阶矩的自适应估计。通常,其超参数设置为:β1 = 0.9、β2 = 0.95 和 ϵ = 10−8。同时,Adafactor 优化器也已被用于训练 LLM(例如,PaLM 和 T5),它是 Adam 优化器的变体,专为在训练期间节省 GPU 内存而设计。Adafactor 优化器的超参数设置为:β1 = 0.9 和 β2 = 1.0 − k−0.8,其中 k 表示训练步骤数。
- 稳定训练(Stabilizing the Training)。在 LLM 的预训练过程中,它经常会遇到训练不稳定问题,这可能会导致模型崩溃。为了解决这个问题,权重衰减和梯度裁剪已被广泛使用,其中现有研究通常将梯度裁剪的阈值设置为 1.0,权重衰减率设置为 0.1。然而,随着 LLM 的扩展,训练损失峰值也更容易发生,从而导致训练不稳定。为了减轻这个问题,PaLM 和 OPT 使用了一个简单的策略,即在峰值发生之前从较早的检查点重新启动训练过程,并跳过可能导致问题的数据。此外,GLM发现嵌入层的异常梯度通常会导致峰值,并提出收缩嵌入层梯度以缓解它。
下图是常见的模型的训练配置总结:
随着数据量和模型的参数规模越来越大,单机训练已经变得不可能,因此,大模型的并行训练变成了关键问题。当前主流的方法有以下几种:
- 3D 并行训练、即三种维度的并行技术,包括数据并行、流水线并行和张量并行。
1)数据并行(data parallelism, DP),即每张显卡上都完整保存一个模型,将数据分割成多个batch,然后独立计算梯度,最终再将梯度值平均化,作为最终梯度。在推理过程中,本质就是独立并行推理,可增加设备数来增加系统整体吞吐。
2)张量并行,也叫模型并行(model parallelism/tensor parallelism, MP/TP):模型张量很大,一张卡放不下,将tensor分割成多块,一张卡存一块,让每个卡分别计算,最后拼接(all gather)在一起。在推理过程中,可以将计算任务拆分到多个卡中并行计算,可横向增加设备数,从而提升并行度,以加少延迟。
- 流水线并行,将网络按层切分,划分成多组,一张卡存一组。下一张显卡拿到上一张显卡计算的输出进行计算,通过纵向增加设备数量可以提高流水线的并行性,减少显卡的等待时间,从而提升设备的利用率。
通过将模型并行和流水线并行联合使用,可以支持更大的模型,并获得最佳的效果。
但需要注意的是,使用多个 GPU 并行推理并不会加快推理速度,而是可以将单个显卡无法运行的模型分片到多个显卡上运行。
- Zero训练
Zero Redundancy Optimizer(零冗余优化器)由 DeepSpeed 库提出的 ZeRO 技术侧重于数据并行中的内存冗余问题。如前所述,数据并行性要求每个 GPU 存储相同的大语言模型副本,包括模型参数、模型梯度和优化器参数。然而,并非上述所有数据都需要保留在每个 GPU 上,这会导致内存冗余问题。为了解决这个问题,ZeRO 技术旨在在每个 GPU 上只保留一小部分数据,而其余数据可以在需要时从其他 GPU 检索。具体来说,ZeRO 提供了三种解决方案,根据这三部分数据的存储方式,分别是优化器状态分区、梯度分区和参数分区。实证结果表明,前两种解决方案没有增加通信开销,第三种解决方案增加了大约 50% 的通信开销,但节省的内存与 GPU 的数量成正比。PyTorch 实现了与 ZeRO 类似的技术,称为 FSDP。
- 混合精度训练(Mixed Precision Training)
为了减少显存占用,在训练时不必采用全精度训练,而是部分采用半精度(FP16)训练。此外,由于流行的 NVIDIA GPU(例如 A100)的 FP16 计算单元数量是 FP32 的两倍,因此 FP16 的计算效率可以进一步提高。然而,现有工作发现 FP16 可能会导致计算精度的损失,从而影响最终的模型性能,为了缓解它,称为大脑浮点 (Brain Floating Point ,BF16) 的替代方法已用于训练,与 FP16 相比,它分配了更多的指数位和更少的有效位。对于预训练,BF16 在表示精度上通常比 FP16 表现更好。
框架与工具
单GPU训练使用PyTorch及Huggingface Transformers等就可以完成,对于分布式大模型训练场景,代表性工具与平台有Megatron-LM、 DeepSpeed和Accelerate等方案,其中Megatron-LM是英伟达开发的一款专门针对大语言模型开发的分布式训练工具,包含了数据并行,模型并行混合精度训练和FlashAttention等优化技术。DeepSpeed则是由微软开发的深度学习优化库,它提供了各种分布式优化技术支持,包括前面提到的ZeRO及pipeline并行等,微软结合Megatron-LM与DeepSpeed的优势开发了Megatron-DeepSpeed,提供了大量的优化技术和策略,实现大规模模型的高效训练,被众多开发者选用。Accelerate是由huggingface出品的深度学习模型训练的优化库,支持常见的优化策略,轻松支持多GPU/TPU/fp16下的训练,Accelerate同样整合了Megatron-LM 的特性,还能进一步跑在DeepSpeed或者MPI集群上,由于huggingface transformers的流行,这个框架也被越来越多人使用。
https://github.com/hiyouga/LLaMA-Factory
在此基础上,给读者介绍一个国内相对集成度高,便于使用的开源分布式低代码大模型训练框架LLaMA-Factory,它的前身是ChatGLM-Efficient-Turning,旨在为开发者提供可视化训练、推理平台及一键配置模型训练,可支持单机或利用Huggingface Accelerate或者DeepSpeed集群训练。
基于LLaMA-Factory, 用户可轻松选择业界最全面的微调方法和优化技术,通过使用私域数据,或是LLaMA-Factory内置的中文数据集(GPT-4优化后的alpaca中文数据集、ShareGPT数据集和llama-factory提供的模型认知数据集),对常见的基座大模型进行进一步的训练和微调,在复杂的算力基础设施下完成领域大模型的定制开发。
https://huggingface.co/spaces/hiyouga/LLaMA-Board
基于人类反馈的强化学习的指令微调(RLHF)
RLHF(Reinforcement Learning from Human Feedback)是一种通过人类反馈来训练强化学习模型的方法。它允许人类专家通过提供奖励或惩罚来指导模型的学习过程。这使得强化学习模型能够学习人类期望的行为,而无需明确地指定这些行为。其基本原理是将强化学习模型与人类专家结合起来,共同完成一个任务。人类专家通过提供奖励或惩罚来指导模型的学习过程。模型根据这些奖励或惩罚来调整自己的行为,从而逐渐学习到人类期望的行为,重点表现在有用性,诚实性,无害性等,比如希望回答更礼貌,幽默风趣以及对齐一些价值观。
在前面的内容里,我们了解到大模型在预训练和指令微调阶段已经学到了知识和以及如何遵从用户指令完成具体下游任务的能力。2022 年 3 月OpenAI发布一篇论文,为了更好的对齐用户意图,提出了人类反馈的强化学习的指令微调(RLHF)技术。其中提到的InstructGPT就是在GPT-3基础上采用了该技术训练得到,也是ChatGPT 3.5背后的模型。
RLHF 系统主要包括三个关 键组件:要对齐的预训练模型(PLM)、由高质量的标记样本学习的奖励模型(RM),以 及训练 LM 的 RL 算法(例如,近端策略优化(Proximal Policy Optimization, PPO)。
指令数据的示例
标注员打分排序
结果对比
对比之前的 GPT-3模型,从效果上看RLHF为模型带来了以下能力:
- 详实的回应:InstructGPT 的生成通常比 GPT-3长。ChatGPT 的回应则更加冗长,以至于用户必须明确要求“用一句话回答我”,才能得到更加简洁的回答。
- 公正的回应:ChatGPT 通常对涉及多个实体利益的事件(例如种族等)给出非常平衡的回答。
- 拒绝不当问题:这是内容过滤器和由 RLHF 触发的模型自身能力的结合,过滤器过滤掉一部分,然后模型再拒绝一部分。
- 拒绝其知识范围之外的问题:例如,拒绝在2021 年 6 月之后发生的新事件(因为它没在这之后的数据上训练过)。这是 RLHF 最神奇的部分,因为它使模型能够隐式地区分哪些问题在其知识范围内,哪些问题不在其知识范围内。
RLHF在实际应用时,高质量经过标注的的数据集是关键,在实现上可通过huggingface transformers实现自己的奖励模型及利用PPO算法对齐模型[3]。另外,前面提到的LLaMA-Factory也支持RLHF微调。
总结
本节介绍了在大模型应用开发过程中虽然不是很高频应用,但十分重要的两个阶段的技术,预训练和RLHF。至此,我们对整个LLM应用开发涉及到的主要领域(不含评估)都有了完整的认识。在下一部分中,将结合实际的案例进行实战的介绍,完整经历整个大模型应用开发的全过程。
参考
[3]https://pub.towardsai.net/rlhf-training-pipeline-for-llms-using-huggingface-205ebdbee6d4
点此查看合集: