LLM学习笔记

1. 评估榜单

1.1. C-Eval

C-Eval 是一个全面的中文基础模型评估套件。它包含了13948个多项选择题,涵盖了52个不同的学科和四个难度级别。

https://cevalbenchmark.com/static/leaderboard_zh.html?ref=nav.6aiq.com

全部都是各个学科的选择题,例如:

企业联合是指企业之间为增强市场竞争力、获取更大经济效益而实行的合营或合并。大企业之间的联合通常叫强强联合。下列甲乙两公司的行为属于企业强强联合的是____

①甲公司利用乙公司的生产技术力量,投资新项目

②甲公司通过资本市场收购乙公司20%股份

③乙公司把自有资产抵押给银行,用贷款来购买甲公司的设备

④乙公司产品通过甲公司的销售网络迅速进入国际市场

1.2. MMLU

Multi-task Language Understanding on MMLU

https://paperswithcode.com/sota/multi-task-language-understanding-on-mmlu

衡量文本模型的多任务准确性。测试涵盖了57个任务,包括基本数学、美国历史、计算机科学、法律等多个领域。

旨在通过仅在zero-shot和few-shot设置下评估模型来衡量预训练期间获得的知识。

同样,数据集也是选择题:

1.3. GSM8K

GSM8k 数据集包含大约 7500 个训练数据和 1319 个测试数据,主要是小学水平的数学问题,每个数据集都包含基本算术运算(加、减、乘、除),一般需要 2 到 8 步来解决。该数据集可用于多步数学推理。

这个数据集示例如下,可以看到答案里是包含数据逻辑解答步骤:

1.4. BBH

BIG-Bench Hard(BBH)是BIG-Bench的一个子集,它是一个用于语言模型的多样化评估套件。BBH专注于BIG-Bench的23个具有挑战性的任务,这些任务被认为超出了当前语言模型的能力范围。这些任务是在之前的语言模型评估中没有超越平均人工评分者的任务。

BBH任务要求multi-step reasoing,并且发现只使用BIG-Bench评估中的few shot,而不使用Chian-of-Thought(CoT)会大大低估语言模型的最佳性能和能力。当在BBH任务中应用CoT提示时,PaLM在23个任务中有10个超过了平均人工评分者的表现,而Codex在23个任务中有17个超过了平均人工评分者的表现。

本质上也是选择题,但是需要多步reasoning的能力:

2. Context Length

LLM在生成text时,长context length可以记住的大token数量。

在训练过程中,模型会将文本数据分割成块或固定长度的窗口进行处理。模型需要在长文本上进行训练,才能真正充分利用长上下文。训练序列必须包含包含数千个标记的文档、书籍、文章等长文本。

2.1. 为什么不用长context length

训练数据的长度限制了可用上下文长度。那为什么不在更长的sequence上进行训练?=》太慢

增加context length会增加模型需要准确预测的可能token组合的数量。这使得模型能够更可靠地进行long-range的建模,但也需要更多的内存和处理能力,从而导致更高的训练成本。

如果没有任何优化,计算量与上下文长度呈二次比例关系,这意味着一个4096 token的模型需要比一个512 token的模型多64倍的计算量。

可以使用sparse或approximate attention的方法来减少计算量,但是这样也会影响模型的准确率。

Transformer中使用的标准注意力机制会为所有可能的输入token pair计算注意力权重,其复杂度为O(n²)。这意味着计算和内存需求会随着输入序列长度的增加而呈二次增长,限制了Transformer的可扩展性和效率。

在生成文本时,模型首先需要计算注意力矩阵。使用100K个上下文和注意力,模型开始生成文本前可能需要几分钟的时间。

2.2. 如何提升Attention效率

主流有2个大方向:

  1. Approximating attention:
    1. 降低计算成本,但是也降低了复杂任务上的accuracy
    2. 例如Sparse attention
  2. Exact attention using hardware-aware optimization

最近的主流工作关注在:依赖GPU架构来优化attention计算。

Sparse attention

Sparse attention approximates attention by only computing the attention weights for a subset of the input tokens instead of all possible pairs, thus saving time and memory.

There are different ways to implement sparse attention, such as using fixed or static patterns (e.g., local, strided, or block attention) or dynamic or adaptive patterns that depend on the input sequence (e.g., entmax or dynamic sparse attention).

Sparse attention can improve the efficiency and scalability of Transformers, especially for long sequences, but it may also sacrifice some representation power and accuracy. Quadratic attention can achieve high performance and quality, but it may also be computationally expensive and impractical for large-scale applications. Therefore, there is a trade-off between sparsity and complexity in attention mechanisms.

Flash attention

基本原理是避免创建大型的N x N的attention矩阵,因为这个需要对序列长度为N的token上做二次方读/写。

FlashAttention应用了两种技术——切片和重计算。

  1. 切片将输入分成块,加载到fast GPU on-chip SRAM.。逐块计算注意力,以避免创建整个矩阵。
  2. 重计算仅存储足够的信息,在反向传播期间,在chip上重建注意力矩阵,避免存储大型中间结果。

作者分析了IO复杂度,证明FlashAttention需要O(N²/M)的内存访问,而标准注意力则需要O(N²),其中M是SRAM的大小。即使增加了重计算的FLOPs(每秒浮点运算次数)的情况下,这种IO-awareness仍使得FlashAttention能够更快地运行。

实验证实了加速效果——FlashAttention训练BERT比MLPerf记录快15%,GPT-2快3倍,Long Range Arena快2.4倍。

这个想法在FlashAttention-2中得到了进一步的发展。改进的重点是增强序列块之间的并行性,并优化在GPU上的线程块和warp之间的工作划分。关键技术包括减少非矩阵乘操作,将注意力计算在线程之间进行划分以增加占用率,并在warp之间分配工作以减少共享内存的通信。实证验证显示,FlashAttention-2在A100 GPU上实现了约2倍的加速,达到了理论峰值FLOPs的73%。当用于端到端地训练GPT模型时,训练吞吐量达到每个A100的225 TFLOPs/s,比FlashAttention训练速度快1.3倍。

The improvements promise to enable training models on much longer sequences than before at a similar cost.

Accelerating attention speeds up inference and training, but fitting text into the model while maintaining high output quality remains an issue.

An efficient training and inference is not enough to have an high-quality model.

3. Multi Query Attention

MQA是对Multi Head Attention的一种优化实现。两者区别在于:

  1. MHA里,每个head都有一组不同的Q、K、V参数矩阵
  2. MQA里,每个head都共享相同的K、V矩阵,但是Q独有

推理速度上,生成1个token时,MHA和MQA的encoder分别耗时1.7us和1.5us,而decoder分别是46us和3.8us。说明decoder上MQA比MHA快很多。

在效果上,MQA的PPL有所上升(越小越好),BLEU有所下降(越大越好),换句话说就是效果有所下降。

在实际测试中,模拟chatGLM2的设置,hidden_size = 4096、num_heads =32,num_layers=24输入一个维度为(5,128,4096)的向量进行文本解码,生成100个token,耗时对比为:

生成100个token时,MQA解码平均耗时2.7826秒,MHA解码平均耗时6.4796秒,简单来看MQA在decoder解码加速了一倍。从模型结构来看原始的MHA一层5034W参数,而MQA只有1783W参数,还是通过压缩参数量来实现显存占用的减少以及推理时间的减少。

显存占用和推理耗时减小是显而易见的,因为参数量减少了。至于效果变化得很小,只能说多头attention机制中的多头其实并不是一定,之前的bert模型有人探索了改变head头数目,也会保持效果变化不大。在大模型这,可能只需要有不同的head采用不同的query向量,k和v一样来保证每个头提取到不同的特征就够了。

什么时候使用MQA有效呢?

1、采用attention的模型,模型规模越大,那么收益就约明显。

2、decoder生成任务相比较encoder任务收益明显大很大,其实decoder生成任务的收益来源于每一次softmax(QK)V注意力计算微小耗时差异的累积,一次生成任务要生成许多个token,一个token需要经历模型结构层数次的softmax(QK)V注意力的计算。

https://blog.csdn.net/HUSTHY/article/details/131631510

4. 量化

本质上是把例如float point16/32 转换为int8或int4的精度,降低加载模型的显存,降低部署LLM的门槛。

模型量化即以较低的推理精度损失将连续取值的浮点型模型权重进行裁剪和取舍,它是以更少位数的数据类型用于近似表示32位有限范围浮点型数据的过程,而模型的输入输出依然是浮点型,从而达到减少模型尺寸大小、减少模型内存消耗及加快模型推理速度等目标。

从效果来看,ChatGLM2-6B不量化的时候加载模型参数显存占用12.8G左右,生成每个token耗时17ms;采用其int8量化占用显存7.3G,生成每个token耗时37ms。也就是说chatGLM的量化并没有加速推理的能力,只有降低显存的能力。

现在 nlp 模型,动不动就 7b、13b 的,有的甚至更大,如果直接使用 bfloat16 加载推理、训练,需要太大的显存了。

于是现在大家都在用 int4、int8 对这种 100b 以上的模型、对 60b 以上的模型做量化。量化之后的模型,虽然效果会变差,但是好像效果还不错。

有的人,甚至结合 lora,做了 qlora。比如这个项目https://github.com/artidoro/qlora,现在很火。

现在,在最新的 transformers 包里面,已经集成了相关的方法,你可以使用 int4、int8 对任何 transformers 家族的模型做量化。而且就传递几个参数即可。很简单。

现在的 int4、int8 方法实现,包括 transformers 集成的量化方法,基本上都是基于 bitsandbytes 包的。

4.1. 量化加载ChatGLM3

默认情况下,ChatGLM3 6B以FP16精度加载,运行起来大概需要13GB显存。

6000000000 * 2 / 1024 /1024 / 1024 ~= 11.175GB

GPU显示内存占用:

| 0 N/A N/A 7892 C .../envs/text2sql/bin/python 12108MiB |

8 INT量化:

model = AutoModel.from_pretrained("THUDM/chatglm2-6b", trust_remote_code=True).quantize(8).cuda()

量化后的GPU占用为7GB:

| 0 N/A N/A 12413 C .../envs/text2sql/bin/python 7014MiB | 

4.2. BitandByte, GPTQ与AWQ

GPTQ和AWQ是目前最优的LLM量化方法之一。GPTQ是Google AI提出的一种基于group量化和OBQ方法的量化方法。AWQ是Facebook AI提出的一种基于activation-aware方法的量化方法。

  • Bitandbyte是常规的quantize方法,bitandbyte量化完,清一色速度慢很多
  • gptq则要case by case。有时候会提速,有时候会变慢
  • autogptq用起来比gptq简单

5. Fine Tune

在大语言模型中,LoRA,P-Tuning的效果并不好。LoRA在文生图的效果上还不错。

LLM模型fine-tune上,也有客户报LoRA收敛比full tuing收敛要慢。例如full tuing在1个iteration就可以有明显梯度下降,但是LoRA需要几个iteration才能达到同样的效果。

一般fine tune大语言模型还是建议full tuning。ChatGLM 6B建议文本量在1-2w条左右对称样本。13B建议在3-4万左右。

5.1. LLAMA Fine Tune

准备的数据结果(举例):

train_dataset[0]

{'input_ids': tensor([ 0, 5196, 29906, ..., 32002, 32002, 32002]),

'attention_mask': tensor([1, 1, 1, ..., 0, 0, 0]),

'labels': tensor([ 0, 5196, 29906, ..., 32002, 32002, 32002])}

由于Llama这种自回归结构,所以input_ids 和 labels是一样的。都是通过对应模型的tokenizer,把单词转为对应token id。Attention mask是表示有效的token是哪些,后续补全的pad token的mask就全为0。

5.2. deepspeed训练后的模型大小

做baichuan2-13b的全参数微调,base model本身是24.8GB的,微调后的模型是74.1GB,训练是用的deepspeed,设置了参数bf16=True,训练好的文件config也可以看到torch_dtype为bfloat16,什么原因微调后的模型变得这么大?

用deepspeed训练的话,每个checkpoint folder下会保存一份ds版的ckpt和hf的ckpt,ds版的会在global step这个folder下,因为额外保存了当前的optimizer state,所以会大很多。load的时候直接load到checkpoint-xxx路径下应该就是正常模型的大小

5.3. QLoRA Fine Tune

https://baijiahao.baidu.com/s?id=1767094857485891526&wfr=spider&for=pc

对于650亿参数规模的LLaMA模型来说,利用LoRA模型微调依然是成本很高。为此,华盛顿大学的NLP小组提出了QLoRA方法,即LoRA的量化版本。可以支持在单张消费级显卡上微调330亿参数的大模型,在单张专业级显卡上微调650亿参数规模大模型。

具体来说,QLoRA方法的实现主要用来3个方面的优化:

  1. 4位NormalFloat:这是一种理论上最优的量化数据类型,用于处理正态分布的数据,其表现优于4位整数和4位浮点数
  2. Double Quantization:这种方法可以通过对量化常数进行量化来减少平均内存占用,平均每个参数可以节省约0.37个bits(对于一个650亿参数的模型来说大约节省3GB)。

Paged Optimizers:使用NVIDIA统一内存来避免在处理小批量的长序列时出现的梯度检查点内存峰值。

论文中也给出了QLoRA方法与全量微调和LoRA方法差异的示意图,简单明了:

简单来说,相比较原始的LoRA,QLoRA将16-bit的transformer模型变成了4-bit的transformer模型(不过这里说的是4bit的NormalFloat,不是传统的4-bit),此外利用的NVIDIA的显存页优化器进行了优化,避免微调过程中出现显存的spikes。

其实,对于LoRA方法本身来说,它的内存占用非常小。因此,可以考虑使用多个Adapter来提高性能。换句话说,大模型微调的过程中,对显存的消耗主要是激活梯度,而不是其它的部分,其中,LoRA的Adapter参数规模只有模型权重的千分之二左右。那么,即使我们增加Adapter其实也不会提高显存的消耗。因此,将模型权重进行高强度的量化降低显存,再使用多个Adapter来减少模型量化带来的性能损失,就可以达到降低显存要求的同时保留原始模型的性能这2个目标的均衡了。

目前生产上,QLoRA基本上不用。

6. 分布式训练基本概念

https://mp.weixin.qq.com/s?__biz=MzAxMzA2MDYxMw==&mid=2651608167&idx=2&sn=01cc4707a1d9c8030b140c8705af05c3&chksm=8050ab0db727221b0c88a6f234a4cb46359839821d88581fc3b1820fe1fe8ebc94058c43d41a&scene=27

6.1. 数据并行

把模型参数复制到多个GPU,在不同的GPU上运行1个batch的不同子集。单纯的数据并行仍需要模型符合单个GPU的内存要求。如果用多个GPU,那代价就是存储多份重复的参数副本。有些策略可以增加GPU可用的有效RAM,例如在两次使用之间将参数暂时卸载到CPU内存。

6.2. Pipeline并行

将模型的顺序块分割到不同的GPU上,每个GPU只持有部分参数。因此同一个模型在每个GPU上消耗的内存比例较小。

将一个大模型分割成连续层的大块是很直接的一种方式。但是,各层的输入和输出之间存在着顺序上的依赖性,所以一个朴素的实现可能会导致大量的空闲时间,而worker在等待前一个机器的输出被用作其输入。

这些等待的时间称为气泡(bubbles),浪费了空闲机器可以完成的计算。

目前有更优化的方式实现,核心思想是把1个batch分为多个microbatches。每个微批的处理速度应该是成比例的,每个worker在下一个微批可用时就开始工作,从而加速管道的执行。

有了足够的微批,worker在大部分时间内都处于工作状态,并且在每个step的开始和结束时气泡最小。梯度是所有微批的平均值,只有所有微批完成之后才会进行参数更新。

模型被分割的worker的数量通常被称为pipeline深度。

6.3. Tensor并行

Pipeline并行是把一个模型按“垂直”的层进行分割,而Tensor并行是在一个层内“横向”分割某些操作。

对于现代模型(例如Transformer)来说,计算瓶颈主要来自激活batch矩阵与大权重矩阵相乘。矩阵乘法可以认为是成对的行和列之间的点积,所以是有可能在不同的GPU上独立计算点积,或者在不同的GPU上计算每个点积的一部分,最后再将结果相加。无论采用哪种策略,我们都可以将权重矩阵切分成偶数大小的“碎片”,将每个“碎片”放在不同的GPU上,并使用该“碎片”来计算整个矩阵乘积的相关部分。然后再进行GPU间通信来合并结果。

大型语言模型中的张量并行机制旨在通过在模型的不同部分上并行处理张量计算来提高训练和推理的效率。这种方法主要应用于深度学习模型,尤其是包含数十亿或数百亿参数的大型模型,如GPT-3。以下是大语言模型中张量并行机制的一般步骤:

  • 模型分割: 将整个模型分割成多个子模型,每个子模型负责处理模型的一个子集。这通常通过沿模型的层或其它结构进行划分来完成。
  • 张量切割: 将每个子模型的输入张量和参数张量切分成多个子张量。这样,每个子模型都只负责处理一部分数据。
  • 分配到不同设备: 将每个子模型分配到不同的处理单元,如GPU或TPU。每个处理单元负责执行相应子模型的计算。
  • 前向传播: 在前向传播过程中,每个处理单元独立计算其负责的子模型的部分。这意味着输入数据通过分布在不同设备上的子模型进行流动,通过并行化的方式加速整个前向传播过程。
  • 反向传播: 在反向传播过程中,梯度信息沿着相反的路径传播回每个处理单元。每个处理单元更新其负责的子模型的参数。
  • 同步和通信: 由于每个处理单元都在独立地计算和更新,需要在训练过程中进行同步和通信,以确保模型的全局一致性。这通常涉及到梯度聚合和参数同步的操作。
  • 重复迭代: 重复进行多个训练迭代,每次迭代都通过张量并行机制加速计算。

通过张量并行机制,可以更有效地利用多个处理单元,减少训练和推理时间。这种方法的有效性取决于模型的结构、硬件的性能,以及如何划分模型和张量。

6.4. 混合专家系统(MoE)

对于任何输入,MoE策略都只有一部分网络用来计算输出。

比如说一个网路里有很多权重,网络可以在推理时通过门控机制选择具体使用哪套。这样就可以在不增加计算成本的情况下增加参数量。

每个权重被称为一个“专家”,训练目标是希望网络能够学会将专门的计算和技能分配给每个“专家”。不同的专家可以托管在不同的GPU上,为扩大模型使用的GPU数量提供了一个明确的方法。

GShard可以将MoE Transformer的规模扩展到6000亿个参数,其方案是只有MoE层被分割到多个TPU设备上,而其他层是完全重复的。

Switch Transformer则是通过将一个输入路由(routing)到一个专家,成功将模型规模扩展到数万亿的参数,甚至稀疏度更高。

https://zhuanlan.zhihu.com/p/672002822

MoEs 虽然能够高效地进行预训练并且在推理速度上超越密集型模型,但同时它们也面临一些挑战:

  • 在训练阶段:MoEs 提供了极高的计算效率预训练,但在微调过程中却面临泛化的困难,这常常导致过拟合的问题。
  • 在推理阶段:虽然 MoE 模型拥有众多参数,但在推理时只有部分参数被使用。这意味着与拥有同等参数数量的密集型模型相比,MoE 的推理速度要快得多。

然而,所有的参数都需要加载到 RAM 中,因此对内存的需求相当高。例如,对于一个像 Mixtral 8x7B 的 MoE,我们需要有足够的 VRAM 来支持一个 47B 参数的密集模型。为何是 47B 而非 8 x 7B = 56B 呢?这是因为在 MoE 模型中,只有 FFN 层被当做独立的专家,其他部分的模型参数则是共享的。

同时,假设每个令牌只用两个专家进行推理,那么推理速度(FLOPs)相当于使用一个 12B 模型(不是 14B 模型),因为它计算的是 2x7B 的矩阵乘法,但某些层是共享的

6.5. Offloading(卸载)

降低GPU Memory使用的方式之一。

将未使用的数据暂时offload到cpu或不同的设备上,然后在需要时将其读回。朴素的实现方式会大大降低训练速度,但复杂的实现方式会预先获取数据,这样设备就不需要再等待了。

这个想法的一个具体实现是ZeRO,它将参数、梯度和优化器状态分割到所有可用的硬件上,并根据实际需要再将它们具体化。

7. DeepSpeed

7.1. 原理

为了解决这些内存挑战,DeepSpeed 实施了一种被称为 ZeRO(Zero Redundancy Optimizer)的三阶段策略来管理内存:

  1. 优化器状态分区(ZeRO 阶段 1):这个初始阶段涉及将优化器状态进行分片(分区),分配给数据并行的工作节点或 GPU。每个 GPU 不再保存完整的优化器状态集,而是只存储其中的一部分,从而降低了整体内存需求。
  2. 梯度分区(ZeRO 阶段 2):在第一个阶段的基础上,ZeRO 阶段 2 还将梯度分区到数据并行的工作节点或 GPU。这意味着每个 GPU 只保存梯度的一部分,进一步减小了内存占用。
  3. 参数分区(ZeRO 阶段 3):这个最终阶段更进一步,不仅对优化器状态和梯度进行分区,还将模型参数分配给数据并行的工作节点或 GPU。现在,每个 GPU 只保存模型参数的一部分,使得更大的模型能够适应单个 GPU 机器内。

除了这些阶段之外,DeepSpeed 还引入了两种卸载策略,以降低 GPU 内存使用,作为对较慢模型训练速度的一种折衷:

  1. 优化器卸载:在 ZeRO 阶段 2 的基础上,这个策略将梯度和优化器状态卸载到 CPU 或磁盘。这有效地释放了 GPU 内存用于其他计算,进一步减少了内存使用。
  2. 参数卸载:这个策略建立在 ZeRO 阶段 3 之上,通过将模型参数卸载到 CPU 或磁盘。类似于优化器卸载,这种方法提供了额外的内存需求减少,使得即使是更大的模型也能够在可用资源下进行训练。

这张表明了在 ZeRO 的三个阶段中,通过不同的数据并行度(DP)对多个模型内存占用的减少:

简单来说,假设我们正在研究各种大小的模型,从具有75亿参数的较小模型到具有1万亿参数的庞大模型。我们可以测量这些模型使用的内存量,以 GB 为单位。

我们有不同的分区策略阶段,如“os”(代表优化器状态)、“os+g”(优化器状态加梯度)和“os+g+p”(优化器状态加梯度加参数)。当我们将这些策略与数据并行(DP)结合使用时,我们发现内存使用显著减少。

换句话说,这些策略有助于大幅减少模型所需的内存量,使得更容易处理较大的模型。

通过这些创新的内存管理策略,DeepSpeed有效地应对内存消耗的挑战,实现了对更大更复杂模型的训练。

7.2. 参数解释

https://deepspeed.readthedocs.io/en/latest/zero3.html

  1. zero_stage : 这个对应者DeepSpeed工具中的zero方式,分别是:
    1. 0:disable
    2. 1:optimizer state partitioning
    3. 2:optimizer + gradient state partitioning
    4. 3:optimizer + gradient + parameter partitioning
  2. offload : ZeRO-Offload 通过利用主机CPU上的计算和内存资源来执行优化器,从而减少此类模型的GPU计算和内存需求。
  3. local_rank : 分布式训练时的一个变量,用于标识当前 GPU 设备的本地排名(本机排名,与global-rank不同)
    1. -1:禁止分布式设置,一般单机多卡不需要分布式
    2. 0:进程通过local_rank来标识自己,local_rank为0的表示为master
  4. gradient_checkpointing : 降低深度学习模型训练过程中内存消耗的技术

在 DeepSpeed 中,local_rank 参数是用于指定当前进程在本地(节点内)的排名(rank)的参数。在分布式训练中,多个进程可能同时在一个节点上运行,每个进程都有一个唯一的本地排名。

具体来说,local_rank 参数用于标识当前进程在节点内的位置。这对于分布式训练非常重要,因为它帮助确定每个进程在整个分布式系统中的唯一标识。通过设置不同的 local_rank,可以确保每个进程都知道自己在整个训练中的位置,从而更有效地协同工作。

 

8. 推理

server side batch是推理服务的标配(以提升server侧吞吐量为目的).

8.1. 推理框架-TGI

https://www.zhihu.com/question/625415776?utm_id=0

HuggingFace推出。

8.2. 推理框架FasterTransformer

英伟达推出,后续推出了TensorRT-LLM后,就不再维护FasterTransformer了。

8.3. 选择推理框架的维度

并非有1个推理框架(例如vLLM,TensorRT-LLM,TGI,DJL等等)适合所有模型,同一个模型在不同的推理框架上可能表现不一样。

部分参考数据:

  • vllm,g5 ,LLama2-7b 跑下来 290 token/s
  • vllm的最大优势就是吞吐很好,且在batch 变大的情况下,latency的增加比较小

9. Chain of Thought

基本形态:one-shot prompt,给出一个step by step解决问题的例子:

其他形态:

  1. Zero-Shot-CoT:prompt末尾加”Let’s think step by step”。使用非常简单,但是效果不一定有few-shot好
  2. Manual-CoT:使用few-shot,在输入问题之前,手动设计一些问题和答案的样例(样例的答案给出中间推理步骤),这些问题和答案都需要手动构造,所以叫 Manual-CoT。很明显,要想模型效果好,需要投入一定人力成本
  3. Auto-CoT:Auto-CoT 其实也是受到了 Manual-CoT 的启发,既然 Manual-CoT 比 Zero-Shot-CoT 的性能要好,而且性能好的关键就在于人工设计的问题、中间推理步骤和答案的样例,那么就可以考虑将这部分进行自动化,从而节省人工成本

下面是一个Auto-CoT的例子:

Auto-CoT 包括两个主要阶段:(i)问题聚类:将给定数据集中的问题划分为几个聚类;(ii)演示抽样:从每个聚类中选择一个代表性问题,并使用简单的启发式方法使用 Zero-Shot-CoT 生成其推理链。总体过程如上图所示。

通过多样性选取有代表性的问题,对于每一个采样的问题拼接上“Let’s think step by step”(类似于 Zero-Shot-CoT )输入到语言模型,让语言模型生成中间推理步骤和答案,然后把这些所有采样的问题以及语言模型生成的中间推理步骤和答案全部拼接在一起,构成少样本学习的样例,最后再拼接上需要求解的问题一起输入到语言模型中进行续写。最终模型续写出了中间的推理步骤以及答案,并且质量非常高。

值得一提的是,在十个数据集上 Auto-CoT 是可以匹配甚至超越 Manual-CoT 的性能,也就说明自动构造的 CoT 的问题、中间推理步骤和答案样例比人工设计的还要好,而且还节省了人工成本。在 Auto-CoT 中,其实也是用到了很多个“Let’s think step by step”对每个采样的问题分别触发中间推理步骤和答案,这也是为什么叫它 “Let's think not just step by step but also one by one”。

10. Agent

10.1. Assistants API

OpenAI的Assistants API的常规工作流为:

  1. 创建1个Assistant,并指定其instructions和model。如果需要,也可以启用如Code Interpreter、Retrieval和Function calling的tools
  2. 在每个新用户开启对话时,创建一个Thread
  3. 在用户提问时,加入Messages到Thread
  4. 在Thread里运行Assistant来触发回复。这一步会自动调用相关的tools

https://cookbook.openai.com/examples/assistants_api_overview_python

本质上就是个Agent。OpenAI提供了直接集成Code Interpreter、自定义Function以及Retrieval的tools。

可以让Assistant决定调用提供的工具,例如Code Interpreter生成代码解决问题,或是提供自定义的Function来解决问题。

10.2. Function Calling

提供function的描述,让模型智能地输出一个包含了调用1个或多个function信息的JSON对象。本质上gpt不会自动调用function,而是返回调用function的JSON信息。

https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models

例如:

前面定义了function的描述,名称,功能等,然后基于用户的信息,返回调用的function名称以及传入的参数(以json的格式)。

11. vLLM

开源的大模型推理加速框架,通过Paged Attention高效地管理attention中缓存的张量,实现比HuggingFace Transformers高14-24倍的吞吐量。

11.1. LLM的性能受到内存限制

在自回归解码过程中,LLM的所有输入token都会生成attention机制的key和value的张量,并且这些张量被保留在GPU内存中,用来生成下一个token。

这些缓存的key和value的张量通常称为KV缓存。KV缓存有2个特点:

  1. 内存占用大:在LLaMA-13B中,单个序列的KV缓存占用高达1.7GB的内存
  2. 动态化:其大小取决于序列的长度,而序列长度高度易变,且不可预测

因此,有效管理KV缓存是一个重大挑战。对此,研究团队发现现有系统由于碎片化和过度保留而浪费了60%至80%的内存。

用团队的导师Joey Gonzalez的一句话来讲:GPU内存碎片化=慢。

11.2. PagedAttention机制

为了解决这个问题,引入了PagedAttention机制,它允许在非连续的内存空间中存储连续的key和value。

具体来说,PagedAttention将每个序列的KV缓存分为若干个block,每个block包含固定数量token的key和value张量。在注意力计算过程中,PagedAttention内核能够高效地识别和提取这些block。

由于这些块在内存中不需要连续,因此也就可以像操作系统的虚拟内存一样,以更灵活的方式管理key和value张量——将块看作page,token看作bytes,序列看作process。

序列的连续逻辑块通过块表映射到非连续的物理块。随着生成新的token,物理块会按需进行分配。

PagedAttention将内存浪费控制在了序列的最后一个块中。

在实践中,这带来了接近最优的内存使用——仅有不到4%的浪费。

而这种内存效率的提升,能让系统将更多的序列进行批处理,提高GPU利用率,从而显著提高吞吐量。

此外,PagedAttention还具有另一个关键优势:高效的内存共享。

比如在并行采样中,就能从相同的提示生成多个输出序列。在这种情况下,提示的计算和内存可以在输出序列之间共享。

11.3. Paged Attention高效地内存共享

比如在并行采样中,就能从相同的prompt生成多个输出序列。在这种情况下,prompt的计算和内存可以在输出序列之间共享。

PagedAttention通过块表自然地实现了内存共享。

类似于进程共享物理页的方式,PagedAttention中的不同序列可以通过将它们的逻辑块映射到相同的物理块来共享块。

为了确保安全,PagedAttention会跟踪物理块的引用计数,并实现了写时复制机制。

PagedAttention的内存共享极大减少了复杂采样算法(如并行采样和束搜索)的内存开销,将它们的内存使用量减少了高达55%。这可以将吞吐量提高多达2.2倍。

https://baijiahao.baidu.com/s?id=1769290162439240324&wfr=spider&for=pc

11.4. 部署ChatGLM2模型

在不使用vLLM时,问ChatGLM2-6b一个问题“晚上睡不着应该怎么办”,推理耗时13-14s。输出379个中文字符,token生成速度约为30 token/s 左右。

使用vLLM部署后,推理耗时7.6s左右,输出218个中文字符,token生成速度约为 28个token/s左右

INFO 02-06 09:01:09 llm_engine.py:649] Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 20.6 tokens/s, Running: 1 reqs, Swapped: 0 reqs, Pending: 0 reqs, GPU KV cache usage: 0.7%, CPU KV cache usage: 0.0%

12. topP与temperature

  • Temperature:控制选择单词的随机性。更低的值可以让生成的文本更一致、可控;更高的值给予模型更多的创造性,生成的文本一致的可能性更低。
    • 为0表示永远选择最可能的单词作为下一个输出单词
  • Top_p:考虑候选单词的数量。更高的值表示模型会考虑更多的候选单词,甚至是较为不可能的单词,让生成的文本更多样化.
    • Top_p = 0.5:这意味着仅考虑那些总概率至少达到总概率的50%的单词,排除了可能性较低的单词,并保持了一定水平的多样性响应。例如:如果您要求一本冒险书的标题,Top-p为0.5,模型可能会提出:“蓝山的神秘”。
    • Top_p = 0.9:这包括了更多的选择单词,允许更多的变化和独创性。例如:对于同样的冒险书标题和Top_p为0.9,模型可能会创建:“深渊之声:勇者的肖像”。

 

posted @ 2024-06-18 16:17  ZacksTang  阅读(213)  评论(0编辑  收藏  举报