记:一次接口性能优化(cpu模型推理 + ES)
优化结果
线下
线上
线上因为服务和ES都在AWS,所以速度会更快,具体结果还在测试中(后面会介绍优化后,docker部署的一个需要注意的地方)
优化过程
性能分析
在优化性能时,首先分析耗时在哪里,此次优化的是一个Python的flask接口,内部的主要分两部分——ES查询 + bert推理。此时选取一个python性能分析工具cProfile,即可获取一个分析结果
根据分析结果,我们可以看到此接口进行了50次ES查询以及10次模型推理(forward)。
优化思路
50次ES查询是否可以合并为1次来减少网络开销
模型推理有什么更快的方式
针对第一项,我们首先查看其业务逻辑,发现ES查询和模型推理互相并没有依赖关系(最开始的设想中,我误以为ES查询依赖于模型推理的结果,考虑做模型输出结果的预测,提前查询ES来进行预测命中,减少后续的查询时间)。而50次查询中可能会存在重复查询,但要对应每次查询的结果,所以我们对50次查询进行汇总去重,并保留了和原数据的映射关系,查询后再映射回去。
针对第二项,因该项目部署服务器为CPU机器(无GPU),选用GGML库,对推理模型改 ...
PagedAttention
接着上文,LLM为了增加推理速度提出了KV Cache,但KV Cache会增加显存的消耗,从而对高并发不友好,
因此提出了一些解决方案,PagedAttention就是其中一种较为高效的解决办法。
什么是PagedAttention
PagedAttention并不是一个新的网络结构,而是一种显存优化方案。
PagedAttention的原理
使用KV Cache的大模型的显存分布
其中,KV Cache中的显存主要浪费在保留显存、内部碎片、外部碎片
且因为KV Cache是动态的(相比于模型的其他部分),参考内存的处理方式,提出了显存分页。
针对解码的优化
Parallel Sampling
Beam Search
为什么显卡没有虚拟内存和内存分页
GPU设计的目的是为了并行计算,对显存要求低
GPU的显存往往都不大
GPU对带宽要求很高,虚拟内存会增加开销
扩展
这里我们自然会想到一些有趣的联想
为什么目前的LLM接口都提供一个system prompt字段?
为什么LLM接口(openai chatgpt)的收费竟然和max ...
KV Cache
自回归模型的推理过程
想看懂KV Cache部分,首先需要清晰的理解自回归模型在推理过程是怎样的。
推理过程不同于SFT部分(相比于SFT低效太多,或者说训练部分并没有体现自回归范式),每次(输入流经整个模型)得到一个token,我们首先讨论按照训练时进行推理的方式,即每次输入上一轮的输入+上一轮生成的token序列的最后一位。循环迭代直到达到停止条件。
但加入KV Cache之后,真正推理时每次仅需输入上一轮生成的token,而不是当前生成的完整输出。两种推理差异如图所示。
其中绿色的为上一轮生成的token,蓝色为之前的token,橘色的在token表示上等同于蓝色。蓝色和绿色都为需要即时计算,橘色则为缓存部分
多轮自回归的QKV的变化
第一行为第一次输入,此时缓存是空的,输入的query也需要为整个question(prompt+question)生成对应的QKV,然后将K和V缓存起来;第二行即自回归的第二轮,将第一行的输出(一个token id)再次输入,得到一个QKV(长度皆为1),然后将该KV与各自的缓存KV合并起来,再进行attention,并把此时的KV缓存 ...
闭包
闭包是什么?——一个函数与其周围状态绑定在一起就是闭包。
概述
闭包通常出现在支持头等函数、垃圾回收的编程语言中,如果函数f中定义了函数g,函数g中使用了自由变量,则函数g构成闭包。
头等函数(First-class function):指函数可以作为别的函数的参数、函数的返回值,赋值给变量或存储在数据结构中。
自由变量(Free variables):自由变量是指在一个特定作用域内未被定义但被引用的变量。
约束变量(Bound variables):约束变量是指在一个特定作用域内已经被定义的变量。
以Python代码为例
123456789101112def f(): y = 0 # 自由变量 def g(): nonlocal y x = 0 # 约束变量 y += 1 x += 1 return y return gclosure = f()print(closure()) # 1
为什么提出闭包
最初闭包是为了解决函数式编程中一些问题,包括隐藏保存一些私有数据、保存上下文(例如计数 ...
RLHF
LLM 训练步骤
预训练
监督微调
RLHF
前两部和以前的语言模型无异,我们以ChatGPT的博客,看一下RLHF做了些什么
RLHF——PPO与LM的结合
PPO属于Actor-Critic架构,Actor即策略也就是LLM本身,那么我们需要一个能对Actor输出结果进行打分的Critic值函数,因此我们先介绍Critic部分,该部分我们称之为Reward Model的训练,以dschat为例
Reward Model
Backbone采用LLM-base部分(即预训练后、监督微调前),输出头表示每个Token对应的V值。模型loss为pairwise loss
1loss -= F.logsigmoid(c_divergence_reward - r_divergence_reward).mean()
并且以last token作为终止奖励,根据模型可选的增加一个新的token
PPO
以KL Penalty Coefficient加上Reward模型的end reward作为额外的奖励值(个人感觉不是很必要,同时用了clipped advantage)
计算act ...
强化学习远程环境
远程环境无法渲染
123456789101112131415161718import gymnasium as gymfrom stable_baselines3 import A2Cenv = gym.make("CartPole-v1", render_mode="rgb_array")model = A2C("MlpPolicy", env, verbose=1)model.learn(total_timesteps=10_000)vec_env = model.get_env()obs = vec_env.reset()for i in range(1000): action, _state = model.predict(obs, deterministic=True) obs, reward, done, info = vec_env.step(action) vec_env.render("human") # VecEnv resets automatically ...
C++虚函数、纯虚函数、虚函数表
虚函数virtual ReturnType Function(){}
纯虚函数virtual ReturnType Function()=0;
虚函数与纯虚函数的区别
声明虚函数,可以定义,也可以不定义,含有虚函数的类,可以被实现。
声明纯虚函数,不可以有定义,含有纯虚函数的类(等价于抽象类),不可以被实现。
虚函数表
虚函数表是C++实现动态绑定技术(多态)的背后依赖。每个包含虚函数的类都包含一个虚表,同一个类的多个对象共享同一个虚表,也就是说每个对象前8个字节用来存储一个指针,同一类的多个对象的该指针相同,都指向该类的虚表。而虚表的构造在编译阶段就已经确定下来了。
123456789101112131415161718192021222324252627282930class A{public: virtual void func1() { cout<<"A::func1() is called"<<endl; } virtual void f ...
Python关键字import
import是在一个模块中访问另一个模块的方法,那么第一个问题就是——什么是模块?
模块(Module)
什么是模块?
模块是一个包含python的声明和定义的文件,文件名为模块名加上.py后缀,每个模块拥有自己的命名空间。
包(Package)
什么是包?为什么设计包这个概念?包与模块有什么区别?
通常来说,包是一个包含多个模块的模块,例如一个包含多个.py后缀的目录。而设计这个包的概念是为了可以层级的组织模块。包是一种特殊情况下的模块,单不是所有模块都是包,最直接的区别就是包拥有__path__属性,而模块是没有的。
同时,Python定义了两种类型的包——常规包和命名空间包。
常规包(Regular Package)
通常我们印象中的包的结构都是常规包,如下所示
12345678parent/ __init__.py one/ __init__.py two/ __init__.py three/ __init__.py
目录中包含__init__.py文件,当我们引用该包或该包中的子模块时,对应的__init__ ...
混合精度训练
浮点型
什么是浮点型
小数点位置约定在固定位置的数称为定点数,小数点位置约定为可以浮动的数称为浮点数。
深度学习中常见的浮点型格式
单精度FP32
半精度FP16
半精度BF16(安培架构)
浮点型表示方法
浮点型包含三部分,依次为符号位(sign)、指数位(阶码)(exponent)、尾数位(mantissa, trailing significand field, fraction),则一个浮点型数可以表示为
(−1)s×t×2e(-1)^s \times t \times 2^e
(−1)s×t×2e
其中符号位1为负数,0为正数,指数位采用补码,尾数位采用原码。指数位存在一个指数偏置项(移码)(exponent bias),偏置项为 2(n−1)−12^{(n-1)}-12(n−1)−1 而非通常的 2(n−1)2^{(n-1)}2(n−1) ,以FP32为例,偏置项为127,通过偏置项将无符号指数范围从1~254(全1和全0用来标记特殊值)转移到 -126~127 避免指数位同样出现一个符号位与浮点型符号位增加比值问题的复杂度。因同一个数对应的尾数有多种表示,因此IE ...
大模型评估数据集
基础常识推理
BoolQ
BoolQ is a question answering dataset for yes/no questions containing 15942 examples. These questions are naturally occurring ---they are generated in unprompted and unconstrained settings. Each example is a triplet of (question, passage, answer), with the title of the page as optional additional context. The text-pair classification setup is similar to existing natural language inference tasks.
PIQA(Physical Interaction: Question Answering)
The dataset contains 16,000 examp ...