Llama.cpp的主要目标是在各种硬件上(本地和云端)实现LLM推断,同时保持最小的设置和最先进的性能。
- 纯C/C++实现,没有任何依赖关系
- Apple芯片是一级的支持对象 - 通过ARM NEON、Accelerate和Metal框架进行优化
- 对x86架构的AVX、AVX2和AVX512支持
- 支持1.5位、2位、3位、4位、5位、6位和8位整数量化,以加快推断速度并减少内存使用
- 为在NVIDIA GPU上运行LLMs而定制的CUDA内核(通过HIP支持AMD GPU)
- Vulkan、SYCL和(部分)OpenCL后端支持
- CPU+GPU混合推断,部分加速超过总VRAM容量的模型
而M1 上有GPU 芯片,因此让我们在 MAC M1 上运行大语言模型成为可能。
1. 下载
- 下载 llama.cpp
bash
> git clone https://github.com/ggerganov/llama.cpp.git
正克隆到 'llama.cpp'...
remote: Enumerating objects: 21605, done.
remote: Counting objects: 100% (6924/6924), done.
remote: Compressing objects: 100% (293/293), done.
remote: Total 21605 (delta 6761), reused 6711 (delta 6629), pack-reused 14681
接收对象中: 100% (21605/21605), 26.16 MiB | 3.31 MiB/s, 完成.
处理 delta 中: 100% (15230/15230), 完成.
- 下载通义千问1.5-7B模型
-
安装git-lfs
brew install git-lfs
-
访问 hugging face 上 qwen 1.5 模型
这里我们使用 qwen 1.5 7B 的 chat 模型 huggingface.co/Qwen/Qwen1....
bash
git clone https://huggingface.co/Qwen/Qwen1.5-7B-Chat
如果你git下载不了 huggingface,也可以考虑浏览器下载文件放到该目录中。据了解chat模型和base模型区别是base模型只能续写,而cgat模型是在后面加了一些对话数据训练的,从而能够回复问题。下载好之后,目录如下
diff
drwxr-xr-x 17 evilkylin staff 544 4 3 11:21 .
drwxr-xr-x 10 evilkylin staff 320 4 3 11:21 ..
drwxr-xr-x 12 evilkylin staff 384 4 3 09:46 .git
-rw-r--r-- 1 evilkylin staff 1519 4 3 09:46 .gitattributes
-rw-r--r-- 1 evilkylin staff 6896 4 3 09:46 LICENSE
-rw-r--r-- 1 evilkylin staff 4338 4 3 09:46 README.md
-rw-r--r-- 1 evilkylin staff 663 4 3 09:46 config.json
-rw-r--r-- 1 evilkylin staff 243 4 3 09:46 generation_config.json
-rw-r--r-- 1 evilkylin staff 1671839 4 3 09:46 merges.txt
-rw-r--r--@ 1 evilkylin staff 3988014264 4 3 10:24 model-00001-of-00004.safetensors
-rw-r--r--@ 1 evilkylin staff 3957749080 4 3 10:23 model-00002-of-00004.safetensors
-rw-r--r--@ 1 evilkylin staff 3957749112 4 3 10:20 model-00003-of-00004.safetensors
-rw-r--r--@ 1 evilkylin staff 3539181096 4 3 10:16 model-00004-of-00004.safetensors
-rw-r--r-- 1 evilkylin staff 31696 4 3 09:46 model.safetensors.index.json
-rw-r--r-- 1 evilkylin staff 7028015 4 3 09:46 tokenizer.json
-rw-r--r-- 1 evilkylin staff 1402 4 3 09:46 tokenizer_config.json
-rw-r--r-- 1 evilkylin staff 2776833 4 3 09:46 vocab.json
2. 编译llama.cpp
csharp
> cd llama.cpp
> make
I ccache not found. Consider installing it for faster compilation.
I llama.cpp build info:
I UNAME_S: Darwin
I UNAME_P: arm
I UNAME_M: arm64
.....
cc -I. -Icommon -D_XOPEN_SOURCE=600 -D_DARWIN_C_SOURCE -DNDEBUG -DGGML_USE_ACCELERATE -DACCELERATE_NEW_LAPACK -DACCELERATE_LAPACK_ILP64 -DGGML_USE_METAL -std=c11 -fPIC -O3 -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function -Wshadow -Wstrict-prototypes -Wpointer-arith -Wmissing-prototypes -Werror=implicit-int -Werror=implicit-function-declaration -pthread -Wunreachable-code-break -Wunreachable-code-return -Wdouble-promotion -c tests/test-c.c -o tests/test-c.o
3. 安装llama 依赖
markdown
> python3 -m pip install -r requirements.txt
因为我是格式化后更新到 MAC OS 14.4 了,所以 python 有点问题。使用 homebrew 安装的 python 现在都安装在/opt/homebrew/bin/ , 这里我们要做两个链接。
shell
> ln -s /opt/homebrew/bin/python3.10 /opt/homebrew/bin/python3
> ln -s /opt/homebrew/bin/python /opt/homebrew/bin/python
> ln -sf pip3.10 /opt/homebrew/bin/pip3
> ln -sf pip3.10 /opt/homebrew/bin/pip
如果你使用 conda 也可以考虑建立虚拟环境,接下来安装依赖。
scss
> python3 -m pip install -r requirements.txt
Collecting numpy~=1.24.4 (from -r ./requirements/requirements-convert.txt (line 1))
Downloading numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl.metadata (5.6 kB)
...
Successfully installed MarkupSafe-2.1.5 certifi-2024.2.2 charset-normalizer-3.3.2 einops-0.7.0 filelock-3.13.3 fsspec-2024.3.1 gguf-0.6.0 huggingface-hub-0.22.2 idna-3.6 jinja2-3.1.3 mpmath-1.3.0 networkx-3.2.1 numpy-1.24.4 packaging-24.0 protobuf-4.25.3 pyyaml-6.0.1 regex-2023.12.25 requests-2.31.0 safetensors-0.4.2 sentencepiece-0.1.99 sympy-1.12 tokenizers-0.15.2 torch-2.1.2 tqdm-4.66.2 transformers-4.39.3 typing-extensions-4.10.0 urllib3-2.2.1
4. 转换 Qwen 模型为 GGUF
什么是 GGUF? GGUF是一种用于存储用于GGML推断和基于GGML的执行器的模型的文件格式。GGUF是一种二进制格式,旨在快速加载和保存模型,并易于阅读。传统上,模型是使用PyTorch或其他框架开发的,然后转换为GGUF以在GGML中使用。
GGUF是GGML、GGMF和GGJT的后继文件格式,旨在通过包含加载模型所需的所有信息来消除歧义。它还设计为可扩展的,因此可以向模型添加新信息而不会破坏兼容性,更多信息访问官方说明文档。
Llama.cpp上是使用 convert.py, 但有人说 qwen 得用 convert-hf-to-gguf.py。
注意:有同学在这里报错找不到文件之类的,注意执行该命令的时候python3 convert-hf-to-gguf.py ~/Projects/Qwen1.5-7B-Chat/ 后面的路径要改为你之前在第1.2节中下载Qwen1.5-7B-Chat的目录。
ini
> python3 convert-hf-to-gguf.py ~/Projects/Qwen1.5-7B-Chat/
Loading model: Qwen1.5-7B-Chat
gguf: This GGUF file is for Little Endian only
Set model parameters
gguf: context length = 32768
gguf: embedding length = 4096
gguf: feed forward length = 11008
gguf: head count = 32
gguf: key-value head count = 32
gguf: rope theta = 1000000.0
gguf: rms norm epsilon = 1e-06
gguf: file type = 1
Set model tokenizer
Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained
....
output_norm.weight, n_dims = 1, torch.bfloat16 --> float32
Model successfully exported to '/Users/xxxx/Projects/Qwen1.5-7B-Chat/ggml-model-f16.gguf'
可以看到他已经转为 F16 的 gguf 格式的模型了。
5. 量化模型
vbnet
> ./quantize ~/Projects/Qwen1.5-7B-Chat/ggml-model-f16.gguf ./models/qwen1.5-chat-ggml-model-Q4_K_M.gguf Q4_K_M
main: build = 2585 (f87f7b89)
main: built with Apple clang version 15.0.0 (clang-1500.3.9.4) for arm64-apple-darwin23.4.0
main: quantizing '/Users/evilkylin/Projects/Qwen1.5-7B-Chat/ggml-model-f16.gguf' to './models/qwen1.5-chat-ggml-model-Q4_K_M.gguf' as Q4_K_M
llama_model_loader: loaded meta data with 19 key-value pairs and 387 tensors from /Users/evilkylin/Projects/Qwen1.5-7B-Chat/ggml-model-f16.gguf (version GGUF V3 (latest))
....
llama_model_quantize_internal: model size = 14728.52 MB
llama_model_quantize_internal: quant size = 4540.59 MB
main: quantize time = 72620.01 ms
main: total time = 72620.01 ms
我们将gguf 的模型量化到INT4,这样模型会从大约 14.7G减少到 4.4GB 左右。
6. 运行测试
sql
> ./main -m ./models/mymodels/qwen1.5-chat-ggml-model-Q4_K_M.gguf -n 128
Log start
main: build = 2585 (f87f7b89)
main: built with Apple clang version 15.0.0 (clang-1500.3.9.4) for arm64-apple-darwin23.4.0
main: seed = 1712117398
llama_model_loader: loaded meta data with 20 key-value pairs and 387 tensors from ./models/mymodels/qwen1.5-chat-ggml-model-Q4_K_M.gguf (version GGUF V3 (latest))
....
压力容器的定期检查(包括外部检查、内部检查和全面检查)分为每()年1次和每3~6年1次两种。
A. 1
B. 2
C. 3
D. 5 答案:D微量元素在生物体内虽少,但对生物体的生命活动起着非常重要的作用,下列选项中都属于微量元素的是( ) A. 钙、铁、锌 B. 钾、镁、氟 C. 锌、硒、碘 D. 碳、氢、氧
钙、钾、
llama_print_timings: load time = 8845.23 ms
llama_print_timings: sample time = 31.94 ms / 128 runs ( 0.25 ms per token, 4007.51 tokens per second)
llama_print_timings: prompt eval time = 0.00 ms / 1 tokens ( 0.00 ms per token, inf tokens per second)
llama_print_timings: eval time = 10612.83 ms / 128 runs ( 82.91 ms per token, 12.06 tokens per second)
llama_print_timings: total time = 10899.90 ms / 129 tokens
ggml_metal_free: deallocating
Log end
看起来是自动化跑了个测试, 看输出显示大约 12.06 tokens 每秒,也就是说应该每秒至少 12 个字,但由于编码原因有可能字更多。 接下来我们进入对话模型。如何启动呢?我们这里查看example/alpaca.sh 的启动方式,来编写启动 Qwen 模型命令。
bash
#!/bin/bash
#
# Temporary script - will be removed in the future
#
cd `dirname $0`
cd ..
./main -m ./models/alpaca.13b.ggmlv3.q8_0.bin \
--color \
-f ./prompts/alpaca.txt \
--ctx_size 2048 \
-n -1 \
-ins -b 256 \
--top_k 10000 \
--temp 0.2 \
--repeat_penalty 1.1 \
-t 7
那么 qwen 就是这样了,具体的参数可以参看官方说明。实际上 Qwen1.5官方文档的context window是标明支持大小32768。但我测试将ctx size 设置为32768会无法启动,大概是内存不够用,还是自己不断尝试修改吧。另外top k默认 40,这个参数的意思用来增加随机性的,因为预测下一个token 时候的top k个选择,一般默认就行。我估计应该和 temp 有关系吧。-t是设置并发线程数,根据自己 cpu 核数设置。
vbnet
> ./main -m ./models/mymodels/qwen1.5-chat-ggml-model-Q4_K_M.gguf --color --ctx_size 2048 -n -1 -ins -b 256 --top_k 30 --temp 0.2 --repeat_penalty 1.1 -t 7
Log start
main: build = 2585 (f87f7b89)
main: built with Apple clang version 15.0.0 (clang-1500.3.9.4) for arm64-apple-darwin23.4.0
main: seed = 1712120436
llama_model_loader: loaded meta data with 20 key-value pairs and 387 tensors from ./models/mymodels/qwen1.5-chat-ggml-model-Q4_K_M.gguf (version GGUF V3 (latest))
....
== Running in interactive mode. ==
- Press Ctrl+C to interject at any time.
- Press Return to return control to LLaMa.
- To return control without starting a new line, end your input with '/'.
- If you want to submit another line, end your input with '\'.
You are a helpful assistant.
> 你是谁
我是阿里云研发的大规模语言模型,我叫通义千问。
实际测试下来,输出确实很快,一点也不卡。 但是多次重复启动后,会直接退出,据说是 llama.cpp 出问题了,因此重新 make clean
和 make
就好了。
7. 像OpenAI一样输出
llama.cpp内置了一个c++写的快速,轻量级的http server,提供 OpenAI API一样的输入输出,还提供一个简单 web 前端来和 llama.cpp 交互。使用以下命令开启http server,测试了一下qwen 1.5 context window 开到 16384 在 m1 上会报内存不足,据了解token数量本身也会占用不少内存,和transformer本身的设计有关,具体原理就不展开了。当前开到 8192个token GPU显存就完全跑满了。
bash
./server -m ./models/mymodels/qwen1.5-chat-ggml-model-Q4_K_M.gguf -c 8192 -n -1 -t 7
- chat completion接口
request
vbnet
curl --location 'http://localhost:8080/v1/chat/completions' \
--header 'accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "you are a helpful assitant"
},
{
"role": "user",
"content": "写一个笑话"
}
],
"stream": false
}'
response
swift
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"content": " Why did the tomato turn red?\n\nBecause it saw the salad dressing!\n\nI hope that brought a smile to your face. If you have any other questions or need assistance with something, feel free to ask!",
"role": "assistant"
}
}
],
"created": 1713099144,
"model": "gpt-3.5-turbo",
"object": "chat.completion",
"usage": {
"completion_tokens": 47,
"prompt_tokens": 20,
"total_tokens": 67
},
"id": "chatcmpl-jItjibb9dXIOO0YgrqwZHNxSeBfdBmlR"
}
- embeddings
要返回 embedings需要在开启 server 的时候加上flag --embeddings
bash
./server -m ./models/mymodels/qwen1.5-chat-ggml-model-Q4_K_M.gguf -c 8192 -n -1 -t 7 --embeddings
request
css
curl --location 'http://localhost:8080/v1/embeddings' \
--header 'Authorization: Bearer $OPENAI_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"input": "The food was delicious and the waiter...",
"model": "text-embedding-ada-002",
"encoding_format": "float"
}'
response
json
{
"model": "text-embedding-ada-002",
"object": "list",
"usage": {
"prompt_tokens": 0,
"total_tokens": 0
},
"data": [
{
"embedding": [
-0.005755452439188957,
....
0.01070545706897974,
0.011975807137787342
],
"index": 0,
"object": "embedding"
}
]
}
- Tokenize
request
css
curl --location 'http://localhost:8080/tokenize' \
--header 'Content-Type: application/json' \
--data '{
"content": "hello are you ok"
}'
response
yaml
{
"tokens": [
6312,
28709,
460,
368,
3614
]
}
- detokenize
request
css
curl --location 'http://localhost:8080/detokenize' \
--header 'Content-Type: application/json' \
--data '{
"tokens": [
6312,
28709,
460,
368,
3614
]
}'
response
css
{
"content": " hello are you ok"
}