2024年 第17篇
LLM推理端是什么
工作流程
数学计算流程
工程优化
Large Language Model,大语言模型。典型代表ChatGPT。
推理端:模型训练出来后,用于模型应用和部署的interface。
推理端实现了本地环境中部署大语言模型。可以实现LLM的基本功能,包括生成文本、自动摘要、语言翻译、代码生成、问答系统等。
Llamma.cpp是Llama推理端的C++实现库。https://github.com/ggerganov/llama.cpp ,MIT开源协议
Llama(Large Language Model Meta AI)是Meta2023年2月发布的开源大语言模型。
论文:https://arxiv.org/pdf/2302.13971.pdf
Gemma.cpp是Gemma推理端的C++实现库。https://github.com/google/gemma.cpp ,Apache开源协议
Gemma是Google基于Gemini推出的开源LLM。https://blog.google/technology/developers/gemma-open-models/
论1b-212-215.above.com:2181
LLAMA.cpp用于在各种硬件平台上高效地运行大型语言模型(LLMs)。可以使大型语言模型能够在资源受限的环境中运行,例如在个人电脑、智能手机甚至树莓派等设备上。
Gemma.cpp用于在各种硬件平台上高效运行Gemma模型。
LLM推理端的工作流程大致相同,我们以Llama.cpp为例。
Llama.cpp 工程目录
|-- example
| |-- main
| |-- main.cpp # 推理llama 2的主函数
|-- ggml-alloc.c #内存分配管理
|-- ggml-alloc.h
|-- llama.cpp # 整个llama 2的核心文件,包含所有重要interface
|-- llama.h
|-- ggml.c # 机器学习计算库.定义一些框架的基础数据结构和函数等
|-- ggml.h
|-- ggml-cuda.cu #cuda版本的llama2 中的kernel实现与调用
|-- ggml-cuda.h
|-- ggml-opencl.cpp #opencl版本的llama2 中的kernel实现与调用
|-- ggml-opencl.h
|-- ... #其他
Llama.cpp 的工作流程
步骤一:解析和设置参数
参数可以控制推理过程的行为。例如,可以设置批量大小来提高推理速度,或者设置温度来控制生成文本的多样性。
解析参数
gpt_params_parse()
。可设置的用户参数:
--model 模型地址
--color 颜色区分生成和输入
--interactive 启用交互模式
--prompt 输入的提示词
--file 提示词文件
步骤二:加载模型
加载模型是 LLAMA.cpp 工作流程的第一步。模型文件包含模型的参数和权重,是推理过程所必需的。
根据输入参数生成模型参数,
llama_model_params
使用
llama_load_model()
函数加载模型文件。
步骤三:创建llama_context
llama_context
是整个llama.cpp 重要数据结构。存储了各种参数,模型,prompt,backend(cpu/gpu),推理的计算结果。
llama_new_context_with_model()
步骤四:处理输入的prompt
将输入的文本数据转换为模型可以理解的格式。也就是文本转化为词向量。
Tokenization:
使用
llama_tokenize()
对输入文本进行分词(Tokenization),将文本拆分为token(词元)。再输出为一个整数类型
token_id
。Embedding:
将分词后的文本转换为词嵌入向量(Embeddings),这通常涉及到查找预训练的词嵌入矩阵(词汇表)。词向量的长度为hidden_dim,是LLM模型训练后固定的。把每个词元
token_id
转化为一个向量。对于某些模型,可能还需要进行位置编码(Positional Encoding)以保留序列中单词的顺序信息。
输出为(词元数 * hidden_dim)的矩阵。
步骤五:推理
这是LLAMA.cpp 工作流程的核心步骤。在这个步骤中,模型会根据输入数据生成预测结果。
推理包括两个部分逻辑
Prompt:将转化后的所有词向量进行Transformer-decode计算。
Generate:根据Prompt的结果和前一次生成的结果,进行Transformer-decode计算。最后返回预测结果
下面比较重要的函数
llama_decode()
开始推理llama_build_graph()
构建计算图llama_graph_compute()
根据计算图调用对应算子,开始计算。
计算的结果是一组概率数组,表示预测的结果,存放在llama_context
中。
步骤六:输出结果
对预测结果进行进一步处理。
根据需要对预测结果进行采样,获取到要输出的token_id。采样(sample)是从模型预测的概率分布中生成下一个词或标记的过程。
将token_id 转换回文本字符,
llama_token_to_piece()
。输出结果给用户。
步骤七:释放资源
释放资源是 LLAMA.cpp 工作流程的最后一步。释放模型句柄可以回收内存和计算资源。
调用 llama_free_model()
等函数释放模型资源。
以下是一些 LLAMA.cpp 工作流程的示例代码:
//解析参数
gpt_params params;
gpt_params_parse(argc, argv, params);
// 加载模型
llama_model_t *model = llama_load_model("llama-2-7b-chat");
//创建llama_context
llama_context * ctx = llama_new_context_with_model(model, params);
// 处理输入prompt
const char *input = "This is an example input.";
vector<llama_token> *embd_input = llama_tokenize(ctx, input);
// 推理
llama_decode(ctx, embd_input);
// 输出结果
cllama_token id = llama_sampling_sample(llama_sampling_context, ctx);
string token_str = llama_token_to_piece(ctx, id)
printf("%s\n", token_str);
// 释放资源
llama_free(ctx);
llama_free_model(model);
「 3.1 模型架构 」
Llama的基础模型架构是Transformer,出自Attention Is All You Need。Transformer目前是大部分生成式人工智能的基础模型架构。
Llama的实现 - LLaMA: Open and Efficient Foundation Language Models
Llama相比基础Transformer,主要优化工作有下面几点:
使用RMSNorm作为标准函数
使用SwiGLU作为激活函数
使用RoPE作为位置编码函数
LLM的核心是Attention的计算。
Attention是计算每个token相对其他token的重要程度,结果是一组权重向量。
"算子"(Operator)通常指的是执行特定数学运算的函数或指令。类似于c/c++的operator关键字,
在机器学习中算子的对象是张量(Tensor)。
张量是用于数学计算的数据结构,它的基本构成是多维数组。
下面是Llama.cpp定义的算子
enum ggml_op {
GGML_OP_NONE = 0,
GGML_OP_DUP,
GGML_OP_ADD,
GGML_OP_ADD1,
GGML_OP_ACC,
GGML_OP_SUB,
GGML_OP_MUL,
GGML_OP_DIV,
GGML_OP_SQR,
GGML_OP_SQRT,
GGML_OP_LOG,
GGML_OP_SUM,
GGML_OP_SUM_ROWS,
GGML_OP_MEAN,
GGML_OP_ARGMAX,
GGML_OP_REPEAT,
GGML_OP_REPEAT_BACK,
GGML_OP_CONCAT,
GGML_OP_SILU_BACK,
GGML_OP_NORM, // normalize
GGML_OP_RMS_NORM,
GGML_OP_RMS_NORM_BACK,
GGML_OP_GROUP_NORM,
GGML_OP_MUL_MAT,
GGML_OP_MUL_MAT_ID,
GGML_OP_OUT_PROD,
GGML_OP_SCALE,
GGML_OP_SET,
GGML_OP_CPY,
GGML_OP_CONT,
GGML_OP_RESHAPE,
GGML_OP_VIEW,
GGML_OP_PERMUTE,
GGML_OP_TRANSPOSE,
GGML_OP_GET_ROWS,
GGML_OP_GET_ROWS_BACK,
GGML_OP_DIAG,
GGML_OP_DIAG_MASK_INF,
GGML_OP_DIAG_MASK_ZERO,
GGML_OP_SOFT_MAX,
GGML_OP_SOFT_MAX_BACK,
GGML_OP_ROPE,
GGML_OP_ROPE_BACK,
GGML_OP_ALIBI,
GGML_OP_CLAMP,
GGML_OP_CONV_TRANSPOSE_1D,
GGML_OP_IM2COL,
GGML_OP_CONV_TRANSPOSE_2D,
GGML_OP_POOL_1D,
GGML_OP_POOL_2D,
GGML_OP_UPSCALE, // nearest interpolate
GGML_OP_PAD,
GGML_OP_ARGSORT,
GGML_OP_LEAKY_RELU,
GGML_OP_FLASH_ATTN,
GGML_OP_FLASH_FF,
GGML_OP_FLASH_ATTN_BACK,
GGML_OP_WIN_PART,
GGML_OP_WIN_UNPART,
GGML_OP_GET_REL_POS,
GGML_OP_ADD_REL_POS,
GGML_OP_UNARY,
GGML_OP_MAP_UNARY,
GGML_OP_MAP_BINARY,
GGML_OP_MAP_CUSTOM1_F32,
GGML_OP_MAP_CUSTOM2_F32,
GGML_OP_MAP_CUSTOM3_F32,
GGML_OP_MAP_CUSTOM1,
GGML_OP_MAP_CUSTOM2,
GGML_OP_MAP_CUSTOM3,
GGML_OP_CROSS_ENTROPY_LOSS,
GGML_OP_CROSS_ENTROPY_LOSS_BACK,
GGML_OP_COUNT,
};
标记为红色的算子是计算Attention中会用到的。
「 3.2 Attention计算步骤和对应的算子 」
CUDA是LLM计算中最常采用的计算平台,我们以CUDA算子举例。
CUDA是NVIDIA推出的基于GPU的科学计算软件平台和代码模型,大范围用于机器学习领域的训练和推理的计算工作。它提供了一套API和工具,使得程序员可以编写能够在GPU上执行的并行程序。
Llama.cpp的所有CUDA核函数实现全部在ggml-cuda.cu
算法流程
把上面算法流程进行拆分
Q*K - MatMul - 矩阵乘法
CUDA算子:mul_mat_p021_f16_f32
static __global__ void mul_mat_p021_f16_f32(
const void * __restrict__ vx, const float * __restrict__ y, float * __restrict__ dst,
const int ncols_x, const int nrows_x, const int nchannels_x, const int nchannels_y)
{
}
CUDA算子:scale_f32
static __global__ void scale_f32(const float * x, float * dst, const float scale, const int k) {
const int i = blockDim.x*blockIdx.x + threadIdx.x;
if (i >= k) {
return;
}
dst[i] = scale * x[i];
}
CUDA算子:diag_mask_inf_f32
static __global__ void diag_mask_inf_f32(const float * x, float * dst, const int ncols, const int rows_per_channel, const int n_past) {
const int col = blockDim.y*blockIdx.y + threadIdx.y;
const int row = blockDim.x*blockIdx.x + threadIdx.x;
if (col >= ncols) {
return;
}
const int i = row*ncols + col;
//dst[i] = col > (n_past + row % rows_per_channel) ? -INFINITY : x[i];
//dst[i] = x[i] - (col > n_past + row % rows_per_channel) * INT_MAX; // equivalent within rounding error but slightly faster on GPU
dst[i] = x[i] - (col > n_past + row % rows_per_channel) * FLT_MAX;
}
CUDA算子:soft_max_f32
static __global__ void soft_max_f32(const float * x, const float * mask, const float * pos, float * dst, const int ncols_par, const int nrows_y, const float scale, const float max_bias, const float m0, const float m1, uint32_t n_head_log2)
{
}
CUDA算子:mul_mat_vec_nc_f16_f32
static __global__ void mul_mat_vec_nc_f16_f32( // nc == non-contiguous
const void * __restrict__ vx, const float * __restrict__ y, float * __restrict__ dst, const int ncols_x, const int nrows_x,
const int row_stride_x, const int channel_stride_x, const int channel_x_divisor)
{
}
LLAMA.cpp使LLM能够在资源受限的环境中运行,例如在个人电脑、智能手机甚至树莓派等设备上。
已经对各种硬件环境进行了优化。
主要包括:
宏定义 | 计算优化项 |
GGML_USE_MPI | MPI并行计算优化 |
GGML_USE_METAL | METAL GPU计算优化 |
GGML_USE_CUBLAS | CUDA GPU计算优化 |
GGML_USE_VULKAN | VULKAN GPU计算优化 |
GGML_USE_SYCL | SYCL 并行计算优化 |
GGML_USE_KOMPUTE | KOMPUTE GPU计算优化 |
LLAMA_AVX,LLAMA_AVX2, LLAMA_AVX512 | CPU指令集优化 |
推理过程中llama_graph_compute()
等函数中调用不同平台优化算子。
static void ggml_compute_forward(struct ggml_compute_params * params, struct ggml_tensor * tensor) {
GGML_ASSERT(params);
if (tensor->op == GGML_OP_NONE) {
return;
}
#ifdef GGML_USE_CUBLAS
bool skip_cpu = ggml_cuda_compute_forward(params, tensor);//调用ggml-cuda.cu的CUDA算子
if (skip_cpu) {
return;
}
GGML_ASSERT(tensor->src[0] == NULL || tensor->src[0]->backend == GGML_BACKEND_TYPE_CPU);
GGML_ASSERT(tensor->src[1] == NULL || tensor->src[1]->backend == GGML_BACKEND_TYPE_CPU);
#elif defined(GGML_USE_VULKAN)
const bool skip_cpu = ggml_vk_compute_forward_cpu_assist(params, tensor);
#ifdef GGML_VULKAN_CHECK_RESULTS
if (skip_cpu) {
ggml_vk_check_results_1_cpu_assist(params, tensor);
}
#endif
if (skip_cpu) {
return;
}
GGML_ASSERT(tensor->src[0] == NULL || tensor->src[0]->backend == GGML_BACKEND_TYPE_CPU);
GGML_ASSERT(tensor->src[1] == NULL || tensor->src[1]->backend == GGML_BACKEND_TYPE_CPU);
#endif // GGML_USE_CUBLAS
#ifdef GGML_USE_SYCL
bool skip_cpu = ggml_sycl_compute_forward(params, tensor);
if (skip_cpu) {
return;
}
#endif // GGML_USE_SYCL
算法优化 Lora - (Low-Rank Adaptation)优化和调整大型预训练语言模型的技术。能够减少计算资源和内存需求。
KVcache - 缓存下Attention计算中产生的Key和Value张量,减少计算量。
量化 - 量化通常涉及将浮点数(如32位的FP32)转换为低精度的表示,如8位的整数(INT8)或其他低精度格式。这种转换可以在不显著影响模型性能的情况下,显著降低模型的内存占用和计算需求。(这段由moonshot 生成)
Ps. 推理过程需要的显存
Llama 7B模型为例,使用FP16数据类型,每个FP16占2个字节。hidden_dim为4096,每个K和V 都要储存。一共32个Transformer Layer。
FP16 * hidden_dim * K,V * 32Layer * context_length = 2 * 4096 * 2 * 32 * context_length(取1024) = 512M
如果输入有1024个token就需要512M显存。
「三翼鸟数字化技术平台-场景设计交互平台」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。
_________________ END__________________