在Android手机部署LLM

“在小米手机上端侧部署Qwen2-0.5B LLM”

@徐静
发布时间:2024 年 12 月 15 日
274762204@qq.com
GitHub:https://github.com/DataXujing/Qwen2-0.5B-Android

CONTENTS

Outline

1. Windows和Android下编译MNN库

1. Windows下编译MNN

MNN团队开发了 MNN-LLM / MNN-Diffusion,合称MNN-Transformer.

环境要求

  • Microsoft Visual Studio >= 2017
  • cmake >= 3.13
  • Ninja

64位编译:在设置中找到vcvars64.bat(适用于 VS 2017 的 x64 本机工具命令提示)并单击,打开VS编译x64架构程序的虚拟环境

# https://mnn-docs.readthedocs.io/en/latest/compile/engine.html#windows-arm
cd /path/to/MNN
./schema/generate.ps1 # 非必须
mkdir build && cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DMNN_BUILD_SHARED_LIBS=OFF -DMNN_WIN_RUNTIME_MT=OFF
-DMNN_BUILD_LLM=ON
ninja
  • 若需要编译模型转换工具,cmake 命令加上 -DMNN_BUILD_CONVERTER=ON -DMNN_BUILD_SHARED_LIBS=OFF -DMNN_WIN_RUNTIME_MT=ON
  • 若需要编译 MNN CUDA,MNN_WIN_RUNTIME_MT 和 MNN_BUILD_SHARED_LIBS 需要设成 ON ,另外加上 -DMNN_CUDA=ON: cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DMNN_BUILD_SHARED_LIBS=ON -DMNN_WIN_RUNTIME_MT=ON -DMNN_CUDA=ON
徐静 MNN在Android手机部署LLM 2024年12月15日

2. 编译MNN Android库

环境要求

  • cmake >= 3.10
  • ndk

相关编译选项

  • MNN_OPENCL 是否使用OpenCL后端,OpenCL后端可以利用GPU加速
  • MNN_NNAPI 是否使用NNAPI后端,NNAPI后端会尝试使用设备上的NPU进行加速
  • MNN_ARM82 是否支持fp16推理,开启该编译选项后,在precision设成Precision_Low时,会在支持的设备(ARMv8.2 及以上架构)上启用低精度(fp16)推理,减少内存占用,提升性能
  • MNN_SUPPORT_BF16 是否支持bf16推理,开启该编译选项后,在precision设成Precision_Low_BF16 时,会启用bf16推理,减少内存占用,提升性能
徐静 MNN在Android手机部署LLM 2024年12月15日

具体步骤

  1. NDK download下载安装NDK,建议使用最新稳定版本;
  2. .bashrc 或者 .bash_profile 中设置NDK环境变量,例如:export ANDROID_NDK=/home/myuser/xujing/test/android-ndk-r21e
  3. 执行编译

命令行方式,适用 linux / mac 系统

cd /path/to/MNN
cd project/android
# 编译armv7动态库
mkdir build_32 && cd build_32 && ../build_32.sh
# 编译armv8动态库
mkdir build_64 && cd build_64 && ../build_64.sh
# 大模型或Diffusion部署需要额外宏
../build_64.sh "-DMNN_LOW_MEMORY=true -DMNN_CPU_WEIGHT_DEQUANT_GEMM=true -DMNN_BUILD_LLM=true -DMNN_SUPPORT_TRANSFORMER_FUSE=true -DMNN_ARM82=true -DMNN_OPENCL=true -DMNN_USE_LOGCAT=true"
徐静 MNN在Android手机部署LLM 2024年12月15日

2. MNN-LLM的特性

随着移动端(手机/平板等)算力、内存、磁盘空间的不断增长,在移动端部署大模型逐渐成为可能。在端侧运行大模型,可以有一系列好处:去除网络延迟,加快响应速度;降低算力成本,便于大规模应用;不需数据上传,保护用户稳私。

  • 支持各类LLM和Diffusion模型,支持加载同时加载多份LoRA;不依赖厂商NPU能力,2020年后的手机基本都能跑得动 LLM 小模型。
  • 支持int4/int8等模型量化方案,并支持在内存不足时使用磁盘空间替换,避免内存溢出风险。
  • 充分利用CPU sdot / smmla 与GPU recordable / simdgroup / GMemory 等较新特性,在8Gen1芯片上,MNN-Transformer支持 1.8b 端侧模型 35 token/s 以上的解码速度,生成 512x512的图片约 40s (2s / iter),基本上能充分利用移动端上CPU与GPU算力。
徐静 MNN在Android手机部署LLM 2024年12月15日

#c

MNN-Transformer由导出工具、量化工具、插件与引擎三个部分组成:

  • 导出工具负责将各类大模型转换为MNN格式,并构建必需的资源包;
  • 量化工具减少MNN模型大小,以降低运行时内存,加快执行速度;
  • LLM/Diffusion运行时所需要的分词、KV缓存管理、LoRA等功能由插件与引擎模块提供
徐静 MNN在Android手机部署LLM 2024年12月15日

#c

徐静 MNN在Android手机部署LLM 2024年12月15日

离线工具

  • 模型导出:将torch模型导出为onnx,然后转换为mnn模型;导出tokenizer文件,embedding等文件;(MNN提供了各大模型导出onnx的脚本)
  • 模型量化:MNN 支持4bit/8bit无输入校验量化,支持对称量化,非对称量化;同时支持channel-wise量化和block-wise量化;同时支持GPTQ量化权重导入计算。

插件与引擎

  • Attention :提供Transformer结构的 Cross-Attention / Self-Attention 等算子实现。
  • KV Manager :管理大语言模型的 KV Cache ,提供申请、扩容、量化、预加载等功能。
  • LoRA :LoRA 是为了让大模型支持多个任务,比如用同一个LLM实现多个人设。基于MNN权重共享的能力,LoRA插件可以用同一个基座大模型,以很少的内存占用(仅LoRA模型大小)和轻微的性能损失(10%以内)支持加载多个LoRA模型。
  • Tokenizer, Embedding:目前实现了 Sentencepiece 和 Tiktoken
  • Sampler :LLM的输出进行后处理,输出tokens
徐静 MNN在Android手机部署LLM 2024年12月15日

动态量化

#c

徐静 MNN在Android手机部署LLM 2024年12月15日

对大模型来说,低Bit量化是在端侧部署的必要条件,比如一个70亿参数(7b)的模型,需要约25GB的空间。如果采用4位量化,7b模型就只需大约3.5GB内存,1.8b模型不到1GB。一般而言,需要权重量化且预先计算模型中各层的量化信息,才能基于低bit直接计算。但由于大模型参数量大,使训练量化和基于KL散度的后训练量化成本都比较高,难以预先计算模型中各层的量化信息。因此,MNN新增了动态量化机制,以便直接运行仅权重量化的模型。

  • 对 CPU ,由于有 int8 计算指令,对输入进行一次数学统计,计算出量化参数(scale/bias)并进行量化,使之能与int4/int8权重的进行计算。相比于浮点计算,可以减少内存占用、减少内存访问量,提升计算效率。MNN采用Per-Batch的方案,可以在性能轻微损失的情况下,保证精度几乎无损。
  • 对 GPU ,由于 int8 计算指令效率不高,把反量化操作(int4/int8转浮点)合入计算卷积/矩阵乘的Kernel,以支持直接读取 int4/int8权重。相比于浮点计算,可以减少内存占用和内存访问量,但计算效率下降,因此,对于计算密集的情况(LLM的预填充阶段和diffusion中的卷积计算),我们仍然需要增加一个把 int4/int8 的权重反量化为浮点的过程,再使用原先浮点的卷积/矩阵乘Kernel进行计算。
徐静 MNN在Android手机部署LLM 2024年12月15日

#c

磁盘映射技术

端侧运行大模型时,常常会遇到OOM(内存溢出)的问题。但其实单独跑大模型时内存一般是足够的,只是与应用中其他模块的内存叠加后导致了溢出。为此,MNN支持了磁盘映射技术,通过mmap接口,可将模型运行内存映射到磁盘,这样在其他模块内存不足时,可以卸载模型运行内存。而由于大模型与其他模块一般并不会同时执行,所以对模型运行效率的影响较小。

徐静 MNN在Android手机部署LLM 2024年12月15日

KV量化与缓存

#c

徐静 MNN在Android手机部署LLM 2024年12月15日

在 LLM 输出句子变长后,KV Cache 的内存成为不可忽略的增量,为减少这部分内存我们实现了 KV 量化。对 CPU 后端而言,K 的增长方向 kv_seq_len 与int8矩阵乘计算的 scale 数组增加方向一致,可以使用 int8 量化及相应int8矩阵乘计算。而 V 的增长方向与 int8矩阵乘计算的 scale 数组增长方向不一致,目前采用FP8的量化方案。

徐静 MNN在Android手机部署LLM 2024年12月15日

性能测试

#c

LLM 的推理过程可分为两个阶段:

  • Prefill (预填充阶段):LLM 计算并存储初始输入 token 的 KV 缓存,并生成第一个输出 token。
  • Decode:LLM 使用 KV 缓存逐个生成输出 token,然后使用新生成 token 的 (K) - (V) 对,进行更新。

Prefill 性能决定响应时间,Decode 则一般决定总回答时长,都是 LLM 推理性能衡量的重要指标

测试配置:

  • 测试设备:Android Mi 14
  • 测试模型:Qwen2-1.5B 输出长度1024
徐静 MNN在Android手机部署LLM 2024年12月15日

3. Qwen2-0.5B大模型转MNN

模型转MNN

MNN大模型部署的方案基于ONNX,MNN提供了大部分大模型转ONNX以及转MNN的的代码

cd MNN-3.0.0\transformers\llm\export
pip install -r requirements.txt

# 下载Qwen2-0.5B-Instruct权重
git clone https://www.modelscope.cn/qwen/Qwen2-0.5B-Instruct.git

# 导出onnx或mnn
# 导出模型,tokenizer和embedding,并导出对应的mnn模型,并做int4量化
python llmexport.py \
        --path /path/to/Qwen2-0.5B-Instruct \
        --export mnn \
        -quant_bit 4 --quant_block 128

徐静 MNN在Android手机部署LLM 2024年12月15日

模型导出功能(llmexport.py)

  • 支持将模型转为onnx或mnn模型,使用--export onnx--export mnn
  • 支持对模型进行对话测试,使用--test $query会返回llm的回复内容
  • 默认会使用onnx-slim对onnx模型进行优化,跳过该步骤使用--skip_slim
  • 支持合并lora权重后导出,指定lora权重的目录使用--lora_path
  • 制定量化bit数使用--quant_bit;量化的block大小使用--quant_block
  • 使用--lm_quant_bit来制定lm_head层权重的量化bit数,不指定则使用--quant_bit的量化bit数
  • 支持使用自己编译的MNNConvert,使用--mnnconvert

#c

徐静 MNN在Android手机部署LLM 2024年12月15日

4. Android下实现MNN部署Qwen-0.5B

QT中导入Android库

unix:!macx: LIBS += -L$$PWD/../mnn_3.0.0_android_armv7_8/build_32/ -lMNN
unix:!macx: LIBS += -L$$PWD/../mnn_3.0.0_android_armv7_8/build_32/ -lMNN_CL
unix:!macx: LIBS += -L$$PWD/../mnn_3.0.0_android_armv7_8/build_32/ -lMNN_Express
unix:!macx: LIBS += -L$$PWD/../mnn_3.0.0_android_armv7_8/build_32/ -lllm

INCLUDEPATH += $$PWD/../MNN-3.0.0/include
DEPENDPATH += $$PWD/../MNN-3.0.0/include

INCLUDEPATH += $$PWD/../MNN-3.0.0/transformers\llm\engine\include
DEPENDPATH += $$PWD/../MNN-3.0.0/transformers\llm\engine\include

关于QT中Android环境的配置请参考:https://github.com/DataXujing/Qt_NCNN_NanoDet

徐静 MNN在Android手机部署LLM 2024年12月15日

#c

徐静 MNN在Android手机部署LLM 2024年12月15日

模型加载与调用

using namespace MNN::Transformer;
using namespace MNN;

std::unique_ptr<Llm> llm(nullptr);
llm.reset(Llm::createLLM("models/config.json"));
llm->load();
qDebug() <<"Qwen2-0.5B模型加载成功!";

//调用
std::string output_str;
std::string prompt_str = prompt.toStdString();
output_str = llm->response(prompt_str);
llm->reset(); // 清空history,暂时不要多伦对话,真实可以固定多伦对话关联的轮次

徐静 MNN在Android手机部署LLM 2024年12月15日

5. Demo Time

成功在小米Redmi3成功部署了Qwen2-0.5B-Instruct

#center

#center

徐静 MNN在Android手机部署LLM 2024年12月15日

adb调试日志

#c

徐静 MNN在Android手机部署LLM 2024年12月15日
The End
Thanks for Your Attention!
    • 邮箱:274762204@qq.com
    • 微信:花开富贵
    • GitHub:DataXujing