fastllm源码解析

fastllm源码解析

文件结构

  1. include: 头文件,包含utils, basellm,chatglm, factoryllm, fastllm, fastllm-cuda, moss, vicuna
  2. src: 源码文件, 包含chatglm, fastllm, fastllm-cuda, moss, vicuna
  3. tools: 工具类,包含模型导出工具,chatglm-export, moss-export, quant

继承与组合关系

main中有一个factoryllm工厂,用以从中生成各个llm模型的具体实例。
basellm为各个llm的基类,包含了一些常用的虚方法和模型参数,与fastllm使用相同的命名空间。
fastllm:基础类,定义了数据格式、权重map,各个基础算子的操作方法等

main
--factoryllm
--chatglm
--basellm
--moss
--basellm
--vicuna
--basellm
--basellm
--fastllm

核心类属性分析

fastllm

void SetThreads(int t); 设置线程
void SetLowMemMode(bool m); 设置低内存模式

struct LowBitConfig 包含bit,min,max,scale,zeroPoint,包含量化与反量化方法 // 可优化点:内存对齐
enum DataType 包含float int8 int4等
enum DataDevice 包含CPU和CUDA
enum WeightType 包含LINEAR、EMBEDDING和None
struct Data 包括形状、大小、扩容后字节、扩容后大小、预扩容的形状,数据指针等, 量化的配置等,有复制、分配、预扩容、分配、回收、resize、Permute等函数,CalcWeightSum计算权重和。
void ToDevice 切换为cuda
struct Tokenizer 包含TrieNode链表和tokenToStringDict字典, 有Insert插入词和对应id,encode、decode函数
struct WeightMap 包含了模型名称、模型名称到数据内存,存在LoadFromFile、SaveLowBitModel保存低位数量化权重。

void Embedding(const Data &input, Data &weight, Data &output); 根据输入和
void RMSNorm(const Data &input, const Data &weight, float eps, Data &output);
void LayerNorm(const Data &input, Data &gamma, Data &beta, int axis, Data &output);
void Linear(Data &input, Data &weight, const Data &bias, Data &output);
void Split(const Data &input, int axis, int start, int end, Data &output);
void Cat(const Data &input0, const Data &input1, int axis, Data &output);
void CatDirect(Data &input0, const Data &input1, int axis); // 直接把input1的数据拷贝到input0后面(需要input0提前扩容了足够的空间)
void CatDirectAxis0(Data &input0, const Data &input1); // 直接把input1的数据拷贝到input0后面(axis = 0的Cat操作,需要input0提前扩容了足够的空间)
void MatMulTransB(const Data &input0, const Data &input1, Data &output, float alpha = 1.0);
void Softmax(const Data &input, Data &output, int axis);
void Silu(const fastllm::Data &input, fastllm::Data &output);
void GeluNew(const Data &input, Data &output);
void Mul(const Data &input, float v, Data &output);
void MulTo(Data &input0, const Data &input1); // input0 *= input1
void AddTo(Data &input0, const Data &input1); // input0 += input1
void AddTo(Data &input0, const Data &input1, float alpha); // input0 += input1 * alpha
void AttentionMask(Data &input, const Data &mask, float maskValue); // 把input里对应位置mask中为1的部分变成maskValue
void Permute(const Data &input, const std::vector &axis, Data &output); // 转置

basellm

继承自fastllm, llm的抽象类

公有函数
void LoadFromFile(const std::string &fileName) 纯虚函数,加载权重纯虚函数,
int Forward 纯虚函数,进行模型推理
std::string Response 纯虚函数,接受字符串问答并给出回复
void SaveLowBitModel 虚函数 保存低比特
void WarmUp 虚函数 热身

私有函数
void RotatePosition2D 旋转位置二维位置编码
CausalMask 因果mask

chatglm

继承自basellm, llm抽象的chatglm具体实现,函数基本与basellm一致
void LoadFromFile 虚函数,从文件中加载权重
int Forward 虚函数,模型推理
std::string Response 虚函数,根据给出的提示输出问答
void SaveLowBitModel 虚函数,保存低比特的模型
void WarmUp 虚函数,热身

私有函数
void RotatePosition2D 旋转位置二维位置编码
CausalMask 因果mask

核心实现分析

chatglm

ChatGLMModel()初始化:
1-1/10000的invFreq
sin[i][j] = ::sin((float)i * invFreq[j]);
cos[i][j] = ::cos((float)i * invFreq[j]);

RotatePosition2D():
把key和value切成两半,分别和positionIds[0]和positionIds[1]旋转
d[j] = a * cos[j] - b * sin[j];
d[j + m / 4] = a * sin[j] + b * cos[j];

LoadFromFile:
使用weight.LoadFromFile加载文件

Forward模型实际推理函数:
参数:inputIds(文本编码后的id),attentionMask(attention时的mask),positionIds(位置编码), pastKeyValues(以往的kv值)
首先经过Embedding操作,得到inputs的embedding向量,
接下来28层循环:

  1. 经过LayerNorm->Linear->shape->split等操作区分出qkv矩阵
  2. 调用RotatePosition2D->expansion操作旋转qkv矩阵
  3. matmulTransB->Mul-->Softmax->MatMulTransB Attention结构
  4. linear->Mul->Addto 残差
  5. layernorm->linear->Gelu->Linear->Addto FNN残差结果
    最后使用layerNorm->linear输出结果

Response()接受文本数据,封装forward函数:
经过tokenizer的encode进行分词并将词语转化为对应的inputs_id,初始化位置向量,mask向量,pastKV向量。
while Trye轮训不断将当前当前输出作为下一次输入,直到遇到

WarmUp():
热身函数,构造输入运行Forward

SaveLowBitModel():保存量化权重
调用了weight的低比特量化保存

Moss

Moss()初始化
1-1/10000的invFreq
sin[i][j] = ::sin((float)i * invFreq[j]);
cos[i][j] = ::cos((float)i * invFreq[j]);

LoadFromFile:
使用weight.LoadFromFile加载文件

CausalMask:
将start后的所有值都填入一个极小值

RotatePosition2D():
把key和value切成两半,分别和positionIds[0]和positionIds[1]旋转
d[j] = a * cos[j] - b * sin[j];
d[j + m / 4] = a * sin[j] + b * cos[j];

Forward模型推理:
首先调用Embedding将inputs_id转化为embedding向量
接下来循环34层:

  1. LayerNorm->Linear->reshape->split 分割出qkv
  2. 调用RotatePosition2D->cat操作旋转qkv矩阵,累积pastKeyValue
  3. MatMulTransB->CausalMask->AttentionMask->Softmax->MatMulTransB Attention结构
  4. Linear 得到中间残差结果
  5. Linear->GeluNew->Linear->AddTo 与输入以及第4步中的残差结果相加
    最后调用LayerNorm->Linear->sort找到最大值

Response()接受文本数据,封装forward函数:
经过tokenizer的encode进行分词并将词语转化为对应的inputs_id,初始化位置向量,mask向量,pastKV向量。
while Trye轮训不断将当前当前输出作为下一次输入,直到遇到

WarmUp():
热身函数,构造输入运行Forward

SaveLowBitModel():保存量化权重
调用了weight的低比特量化保存

vicuna

vicuna()初始化
1-1/10000的invFreq
sin[i][j] = ::sin((float)i * invFreq[j]);
cos[i][j] = ::cos((float)i * invFreq[j]);

LoadFromFile:
使用weight.LoadFromFile加载文件

Forward:
首先经过Embedding,将inputs_id转化为embedding向量

  1. RMSNorm->Linear 得到qkv矩阵
  2. RotatePosition2D 对kv进行位置旋转,CatDirect得到pastkv
  3. MatMulTransB->Softmax->MatMulTransB->Reshape->Linear->AddTo Attention的QKV操作,并进行残差操作
  4. RMSNorm->Linear->Linear->Silu->MulTo->Linear-AddTo FFN操作,并进行残差相加
    最后经过RMSNorm和Linear得到最终输出

Response()接受文本数据,封装forward函数:
经过tokenizer的encode进行分词并将词语转化为对应的inputs_id,初始化位置向量,mask向量,pastKV向量。
while Trye轮训不断将当前当前输出作为下一次输入,直到遇到

WarmUp():
热身函数,构造输入运行Forward

SaveLowBitModel():保存量化权重
调用了weight的低比特量化保存

fastllm

结构体及属性:
threads
lowMemMode
FileBuffer:
读取int、读取float、读取string、读取bytes
FileWriter
写int、写float、写string、写bytes

Data数据的操作:
CopyFrom 数据拷贝
Count 统计第i维的数量
Resize 扩容
Reshape 形状转置
GetBytes 得到存储空间大小
MallocSpace 内存分配
FreeSpace 内存释放
Allocate 释放内存并重新分配
Expansion 维度扩充
Print打印
Permute 转换通道
CalcWeightSum 计算权重和

Tokenizer方法:
初始化为一条链表
clear: 将数据都放在栈上,对象随栈的结束而消亡
insert:字典树的插入方式
encode:从字典树中找到对应的单词id
Decode: 查表的方式找到数据到单词的映射

WeightMap的方法:
LoadFromFile:从文件中读取字典加载到权重map中
SaveLowBitModel:保存低比特量化后的权重

fastllm的方法:
Transpose 矩阵转置
Permute通道转换
ToDevice将内存中的数据移动到GPU
Multiply:int8×int8得到int32的数据
MultiplyInt4: int4下的矩阵乘法
MultiplyMultiThread: 多线程
MultiplyInt4MultiThread: 多线程
Embedding:根据inputs_id找打对应的embedding向量
RMSNorm: 除以L2范数乘以权重
LayerNorm:(input-mean)/var * a + b
FloatLinearPart: 浮点数矩阵乘法
Float16LinearPart:Float16
Int8LinearPart:int8
Int4LinearPart:int4
Linear:多线程操作,根据type选择不同的矩阵乘法
Split:按数轴进行均分
Cat: 将数据按照axis轴进行拼接
CatDirect:原地cat
CatDirectAxis0:在0轴cat
MatMulSingle:单线程下的矩阵乘积运算
MatMulTransB:多线程转置矩阵乘法
Softmax:激活函数
Silu:silu激活函数
GeluNew:Gelu激活函数
Mul:矩阵和浮点数的乘法
MulTo:点乘
AddTo:点加(带alpha和不带alpha)
AttentionMask:大于等于1的数,值为mask

posted @ 2023-08-19 11:53  wildkid1024  阅读(337)  评论(0编辑  收藏  举报