1. 大模型部署工具 llama.cpp
大模型的研究分为训练和推理两个部分。训练的过程,实际上就是在寻找模型参数,使得模型的损失函数最小化,推理结果最优化的过程。训练完成之后,模型的参数就固定了,这时候就可以使用模型进行推理,对外提供服务。
llama.cpp 主要解决的是推理过程中的性能问题。主要有两点优化:
- llama.cpp 使用的是 C 语言写的机器学习张量库 ggml
- llama.cpp 提供了模型量化的工具
计算类 Python 库的优化手段之一就是使用 C 重新实现,这部分的性能提升非常明显。另外一个是量化,量化是通过牺牲模型参数的精度,来换取模型的推理速度。llama.cpp 提供了大模型量化的工具,可以将模型参数从 32 位浮点数转换为 16 位浮点数,甚至是 8、4 位整数。
除此之外,llama.cpp 还提供了服务化组件,可以直接对外提供模型的 API 。
2. 使用 llama.cpp 量化模型
2.1 下载编译 llama.cpp
克隆代码,编译 llama.cpp
1
2
3
| git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make
|
在目录下会生成一系列可执行文件
- main:使用模型进行推理
- quantize:量化模型
- server:提供模型 API 服务
- …
2.2 准备 llamma.cpp 支持的模型
llama.cpp 支持转换的模型格式有 PyTorch 的 .pth 、huggingface 的 .safetensors 、还有之前 llamma.cpp 采用的 ggmlv3。
在 huggingface 上找到合适格式的模型,下载至 llama.cpp 的 models 目录下。
1
| git clone https://huggingface.co/4bit/Llama-2-7b-chat-hf ./models/Llama-2-7b-chat-hf
|
2.3 转换为 GGUF 格式
llama.cpp 项目下带有 requirements.txt 文件,直接安装依赖即可。
1
| pip install -r requirements.txt
|
1
2
3
4
5
6
| python convert.py ./models/Llama-2-7b-chat-hf --vocabtype spm
params = Params(n_vocab=32000, n_embd=4096, n_mult=5504, n_layer=32, n_ctx=2048, n_ff=11008, n_head=32, n_head_kv=32, f_norm_eps=1e-05, f_rope_freq_base=None, f_rope_scale=None, ftype=None, path_model=PosixPath('models/Llama-2-7b-chat-hf'))
Loading vocab file 'models/Llama-2-7b-chat-hf/tokenizer.model', type 'spm'
...
Wrote models/Llama-2-7b-chat-hf/ggml-model-f16.gguf
|
vocabtype 指定分词算法,默认值是 spm,如果是 bpe,需要显示指定。
2.4 开始量化模型
quantize 提供各种精度的量化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| ./quantize
usage: ./quantize [--help] [--allow-requantize] [--leave-output-tensor] model-f32.gguf [model-quant.gguf] type [nthreads]
--allow-requantize: Allows requantizing tensors that have already been quantized. Warning: This can severely reduce quality compared to quantizing from 16bit or 32bit
--leave-output-tensor: Will leave output.weight un(re)quantized. Increases model size but may also increase quality, especially when requantizing
Allowed quantization types:
2 or Q4_0 : 3.56G, +0.2166 ppl @ LLaMA-v1-7B
3 or Q4_1 : 3.90G, +0.1585 ppl @ LLaMA-v1-7B
8 or Q5_0 : 4.33G, +0.0683 ppl @ LLaMA-v1-7B
9 or Q5_1 : 4.70G, +0.0349 ppl @ LLaMA-v1-7B
10 or Q2_K : 2.63G, +0.6717 ppl @ LLaMA-v1-7B
12 or Q3_K : alias for Q3_K_M
11 or Q3_K_S : 2.75G, +0.5551 ppl @ LLaMA-v1-7B
12 or Q3_K_M : 3.07G, +0.2496 ppl @ LLaMA-v1-7B
13 or Q3_K_L : 3.35G, +0.1764 ppl @ LLaMA-v1-7B
15 or Q4_K : alias for Q4_K_M
14 or Q4_K_S : 3.59G, +0.0992 ppl @ LLaMA-v1-7B
15 or Q4_K_M : 3.80G, +0.0532 ppl @ LLaMA-v1-7B
17 or Q5_K : alias for Q5_K_M
16 or Q5_K_S : 4.33G, +0.0400 ppl @ LLaMA-v1-7B
17 or Q5_K_M : 4.45G, +0.0122 ppl @ LLaMA-v1-7B
18 or Q6_K : 5.15G, -0.0008 ppl @ LLaMA-v1-7B
7 or Q8_0 : 6.70G, +0.0004 ppl @ LLaMA-v1-7B
1 or F16 : 13.00G @ 7B
0 or F32 : 26.00G @ 7B
|
执行量化命令
1
2
3
4
5
| ./quantize ./models/Llama-2-7b-chat-hf/ggml-model-f16.gguf ./models/Llama-2-7b-chat-hf/ggml-model-q4_0.gguf Q4_0
llama_model_quantize_internal: model size = 12853.02 MB
llama_model_quantize_internal: quant size = 3647.87 MB
llama_model_quantize_internal: hist: 0.036 0.015 0.025 0.039 0.056 0.076 0.096 0.112 0.118 0.112 0.096 0.077 0.056 0.039 0.025 0.021
|
量化之后,模型的大小从 13G 降低到 3.6G,但模型精度从 16 位浮点数降低到 4 位整数。
3. 使用 llama.cpp 运行 GGUF 模型
由于近期 llama.cpp 项目更新,新的模型格式为 GGUF ,不兼容 GGML 格式。如果需要使用旧的 GGML 格式模型,请切换到 commit a113689。
3.1 下载模型
在 llama.cpp 项目的首页 https://github.com/ggerganov/llama.cpp 有列举支持的模型
- LLaMA 🦙
- LLaMA 2 🦙🦙
- Falcon
- Alpaca
- GPT4All
- Chinese LLaMA / Alpaca and Chinese LLaMA-2 / Alpaca-2
- Vigogne (French)
- Vicuna
- Koala
- OpenBuddy 🐶 (Multilingual)
- Pygmalion 7B / Metharme 7B
- WizardLM
- Baichuan-7B and its derivations (such as baichuan-7b-sft)
- Aquila-7B / AquilaChat-7B
去 https://huggingface.co/models 找 GGUF 格式的大模型版本,下载模型文件放在 llama.cpp 项目 models 目录下。
1
| git clone https://huggingface.co/rozek/LLaMA-2-7B-32K-Instruct_GGUF ./models/LLaMA-2-7B-32K-Instruct_GGUF
|
仓库中包含各种量化位数的模型,Q2、Q3、Q4、Q5、Q6、Q8、F16。量化模型的命名方法遵循: “Q” + 量化比特位 + 变种。
量化位数越少,对硬件资源的要求越低,但是模型的精度也越低。
3.2 大模型推理
在 llama.cpp 项目的根目录,编译源码之后,执行下面的命令,使用模型进行推理。
1
2
3
4
5
6
7
8
9
10
11
| ./main -m ./models/llama-2-7b-langchain-chat-GGUF/llama-2-7b-langchain-chat-q4_0.gguf -p "What color is the sun?" -n 1024
What color is the sun?
nobody knows. It’s not a specific color, more a range of colors. Some people say it's yellow; some say orange, while others believe it to be red or white. Ultimately, we can only imagine what color the sun might be because we can't see its exact color from this planet due to its immense distance away!
It’s fascinating how something so fundamental to our daily lives remains a mystery even after decades of scientific inquiry into its properties and behavior.” [end of text]
llama_print_timings: load time = 376.57 ms
llama_print_timings: sample time = 56.40 ms / 105 runs ( 0.54 ms per token, 1861.77 tokens per second)
llama_print_timings: prompt eval time = 366.68 ms / 7 tokens ( 52.38 ms per token, 19.09 tokens per second)
llama_print_timings: eval time = 15946.81 ms / 104 runs ( 153.33 ms per token, 6.52 tokens per second)
llama_print_timings: total time = 16401.43 ms
|
当然,也可以用上面量化的模型进行推理。
1
2
3
4
5
6
7
8
9
10
11
| ./main -m ./models/Llama-2-7b-chat-hf/ggml-model-q4_0.gguf -p "What color is the sun?" -n 1024
What color is the sun?
sierp 10, 2017 at 12:04 pm - Reply
The sun does not have a color because it emits light in all wavelengths of the visible spectrum and beyond. However, due to our atmosphere's scattering properties, the sun appears yellow or orange from Earth. This is known as Rayleigh scattering and is why the sky appears blue during the daytime. [end of text]
llama_print_timings: load time = 90612.21 ms
llama_print_timings: sample time = 52.31 ms / 91 runs ( 0.57 ms per token, 1739.76 tokens per second)
llama_print_timings: prompt eval time = 523.38 ms / 7 tokens ( 74.77 ms per token, 13.37 tokens per second)
llama_print_timings: eval time = 15266.91 ms / 90 runs ( 169.63 ms per token, 5.90 tokens per second)
llama_print_timings: total time = 15911.47 ms
|
四位量化模型,在没有 GPU 的情况下,基本能够实现实时推理。敲完命令,按回车,就能看到模型的回复。
main 命令有一系列参数可选,其中比较重要的参数有:
-ins 交互模式,可以连续对话,上下文会保留
-c 控制上下文的长度,值越大越能参考更长的对话历史(默认:512)
-n 控制回复生成的最大长度(默认:128)
–temp 温度系数,值越低回复的随机性越小
3.3 交互模式下,使用模型
1
2
3
4
5
6
7
8
9
| ./main -m ./models/llama-2-7b-langchain-chat-GGUF/llama-2-7b-langchain-chat-q4_0.gguf -ins
> 世界上最大的鱼是什么?
卡加内利亚鲨为世界最大的鱼,体长达60英尺(18)。牠们的头部相当于一只小车,身体非常丑,腹部有两个气孔,气孔之间还有一个大口径的鳃,用于进行捕食。牠们通常是从水中搴出来到陆地上抓到的小鱼,然后产生大量液体以解脱自己的身体。
> 现在还有这种鱼吗?
作者所提到的“卡加内利亚鲨”,应该是指的是“卡加内利亚鳄”。卡加内利亚鳄是一种大型淡水肉食性鱼类,分布于欧洲和非洲部分区域。这种鱼的体长最大可达60英尺(18),是世界上已知最大的鱼之一。
不过,现在这种鱼已经消失了,因为人类对戒备和保护水生生物的意识程度低下,以及环境污染等多方面原因。
|
交互模式下,以对话的形式,有上下文的连续使用大模型。
4. 提供模型 API 服务
有两种方式,一种是使用 llama.cpp 提供的 API 服务,另一种是使用第三方提供的工具包。
4.1 使用 llama.cpp server 提供 API 服务
前面编译之后,会在 llama.cpp 项目的根目录下生成一个 server 可执行文件,执行下面的命令,启动 API 服务。
1
2
3
4
5
6
7
8
9
10
| ./server -m ./models/llama-2-7b-langchain-chat-GGUF/llama-2-7b-langchain-chat-q4_0.gguf --host 0.0.0.0 --port 8080
llm_load_tensors: mem required = 3647.96 MB (+ 256.00 MB per state)
..................................................................................................
llama_new_context_with_model: kv self size = 256.00 MB
llama_new_context_with_model: compute buffer total size = 71.97 MB
llama server listening at http://0.0.0.0:8080
{"timestamp":1693789480,"level":"INFO","function":"main","line":1593,"message":"HTTP server listening","hostname":"0.0.0.0","port":8080}
|
这样就启动了一个 API 服务,可以使用 curl 命令进行测试。
1
2
3
4
5
6
| curl --request POST \
--url http://localhost:8080/completion \
--header "Content-Type: application/json" \
--data '{"prompt": "What color is the sun?","n_predict": 512}'
{"content":".....","generation_settings":{"frequency_penalty":0.0,"grammar":"","ignore_eos":false,"logit_bias":[],"mirostat":0,"mirostat_eta":0.10000000149011612,"mirostat_tau":5.0,......}}
|
4.2 使用第三方工具包提供 API 服务
在 llamm.cpp 项目的首页 https://github.com/ggerganov/llama.cpp 中有提到各种语言编写的第三方工具包,可以使用这些工具包提供 API 服务,包括 Python、Go、Node.js、Ruby、Rust、C#/.NET、Scala 3、Clojure、React Native、Java 等语言的实现。
以 Python 为例,使用 llama-cpp-python 提供 API 服务。
- 安装依赖
1
| pip install llama-cpp-python -i https://mirrors.aliyun.com/pypi/simple/
|
如果需要针对特定的硬件进行优化,就配置 “CMAKE_ARGS” 参数,详情请参数 https://github.com/abetlen/llama-cpp-python 。我本地是 CPU 环境,就没有进行额外的配置。
- 启动 API 服务
1
2
3
4
5
6
7
8
9
| python -m llama_cpp.server --model ./models/llama-2-7b-langchain-chat-GGUF/llama-2-7b-langchain-chat-q4_0.gguf
llama_new_context_with_model: kv self size = 1024.00 MB
llama_new_context_with_model: compute buffer total size = 153.47 MB
AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | SSSE3 = 1 | VSX = 0 |
INFO: Started server process [57637]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://localhost:8000 (Press CTRL+C to quit)
|
在启动的过程中,可能因缺失一些依赖导致失败,根据提示安装即可。如果提示包版本冲突,则需要单独创建一个虚拟 Python 环境,然后安装依赖。
- 使用 curl 测试 API 服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| curl -X 'POST' \
'http://localhost:8000/v1/chat/completions' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"messages": [
{
"content": "You are a helpful assistant.",
"role": "system"
},
{
"content": "Write a poem for Chinese?",
"role": "user"
}
]
}'
{"id":"chatcmpl-c3eec466-6073-41e2-817f-9d1e307ab55f","object":"chat.completion","created":1693829165,"model":"./models/llama-2-7b-langchain-chat-GGUF/llama-2-7b-langchain-chat-q4_0.gguf","choices":[{"index":0,"message":{"role":"assistant","content":"I am not programmed to write poems in different languages. How about I"},"finish_reason":"length"}],"usage":{"prompt_tokens":26,"completion_tokens":16,"total_tokens":42}}
|
- 使用 openai 调用 API 服务
1
2
3
4
5
6
7
8
9
10
11
12
| # -*- coding: utf-8 -*-
import openai
openai.api_key = 'random'
openai.api_base = 'http://localhost:8000/v1'
messages = [{'role': 'system', 'content': u'你是一个真实的人,老实回答提问,不要耍滑头'}]
messages.append({'role': 'user', 'content': u'你昨晚去哪里了'})
response = openai.ChatCompletion.create(
model='random',
messages=messages,
)
print(response['choices'][0]['message']['content'])
|
这里的 api_key
、model
可以随便填写,但是 api_base
必须指向真实服务地址 http://localhost:8000/v1
。
5. 总结
本篇文章主要是介绍 llama.cpp 这个大模型部署工具,主要解决的是推理过程中的性能问题。主要有两个优化点:
- llama.cpp 使用的是 C 语言写的机器学习张量库 ggml
- llama.cpp 提供了模型量化的工具
接着从 llama.cpp 量化模型开始,一步一步使用 llama.cpp 运行 GGUF 模型,提供模型 API 服务,最后还使用 curl 测试了 API ,使用 Python 库 openai 调用 API 服务验证其兼容 OpenAI API 接口功能。
6. 参考