Skip to content

【LLM相关性能优化,形状缓存机制】2.9.0

Latest
Compare
Choose a tag to compare
@jxt1234 jxt1234 released this 15 May 02:15
6a22d17

MNN-LLM 正式合入

增加Transformer相关算子与对应图优化功能

  • 编译 MNN 时打开宏 MNN_LOW_MEMORY 和 MNN_SUPPORT_TRANSFORMER_FUSE ,增加在线权重反量化、动态量化和Transformer相关算子支持
    • -DMNN_SUPPORT_TRANSFORMER_FUSE=ON -DMNN_LOW_MEMORY=ON
  • 转换模型时加上 --transformerFuse=1 开启Transformer模型相关图优化

新增 LLM 模块

使用方法

构建 LLM 文件包

  • 下载模型,推荐使用魔搭
    • pip install modelscope
    • 执行脚本(以千问7b为例)
from modelscope import snapshot_download
from transformers import AutoModelForCausalLM, AutoTokenizer

# Downloading model checkpoint to a local dir model_dir
model_dir = snapshot_download('qwen/Qwen-7B-Chat')
#model_dir = snapshot_download("modelscope/Llama-2-7b-chat-ms", revision='v1.0.5',
                              ignore_file_pattern=[r'.+\.bin$'])
#model_dir = snapshot_download('qwen/Qwen-1_8B-Chat')
print(model_dir)
  • 使用 MNN 目录下面的 transformers/llm/export/llm_export.py 进行模型导出,把 torch 模型转换成 onnx
    • 示例
      • python3 llm_export.py --embed_bin --embed_bf16 --onnx_path onnx --type Qwen-7B-Chat --path /Users/xtjiang/.cache/modelscope/hub/qwen/Qwen-7B-Chat --export_embed --export_token --mnn_path mnn --export
    • 务必加 --embed_bin 参数,将 embeding 层导出成 bin 文件
    • 完成后会产出两个文件夹
      • onnx
        • llm.onnx
        • tokenizer.txt
        • 其他外置权重数据,不用关注
      • mnn
        • embeddings_bf16.bin
  • 使用 MNNConvert 转换 onnx 模型,并进行量化
    • ./MNNConvert -f ONNX --modelFile onnx/llm.onnx llm.mnn --weightQuantBits=4 --transformerFuse=1 --saveExternalData
    • 一般需要20-30 分钟,请耐心等待
    • 产出 llm.mnn 与 llm.mnn.weight
  • 建文件夹,组合上述步骤产出的文件,这里以千问7b为例
    • 文件夹名:qwen-7b-int4
    • 包含文件
      • embeddings_bf16.bin
      • llm.mnn
      • llm.mnn.weight
      • tokenizer.txt

编译LLM引擎并使用

  • 编译MNN打开 MNN_BUILD_LLM 宏,编译 transformers/llm/engine 目录,产出 libllm 和 llm_demo

  • 使用 llm_demo 运行 llm

    • 参数:
      • llm.mnn 文件路径
      • forwardtype :0 为 CPU ,3 为 OpenCL
      • Memory | Precision :Memory * 4 + Precision ,如果 memory 和 preicsion 都为 low ,即设为 10
    • 示例:
      • ./llm_demo ../../qwen-7b-int4/llm.mnn 0 10
        • 【然后进行对话】
      • ./llm_demo ../../qwen-7b-int4/llm.mnn 0 10 prompt.txt :使用预设的问题进行测试
  • CPU / GPU

性能测试

  • 8Gen1
    | model | CPU 4线程 | | OpenCL | |
    | --- | --- | --- | --- | --- |
    | | prefill | decode | prefill | decode |
    | qwen-1.8b | 207.56 | 35.58 | 28.58 | 20.40 |
    | qwen-7b | 25.86 | 7.5 | 7.95 | 7.70 |
    | llama3-8b | 22.09 | 5.59 | 内存不足 | 内存不足 |

  • 8Gen3
    | model | CPU 4线程 | | OpenCL | |
    | --- | --- | --- | --- | --- |
    | | prefill | decode | prefill | decode |
    | qwen-1.8b | 205.70 | 47.07 | 61.25 | 21.56 |
    | qwen-7b | 40.93 | 11.01 | 20.26 | 10.60 |
    | llama3-8b | 36.44 | 7.83 | 19.10 | 2.14 |

  • 注:暂未对 llama3-8b 结构支持 Transformer 相关图优化,因此GPU性能较差

形变缓存机制

背景

对于语音/文本类的模型,往往涉及张量某个维度的逐步变化,这种情况下每次都会进行几何计算、申请内存等操作,导致不可忽略的性能损耗。考虑到输入单个维度变化的情况下,网络中会存在部分算子形状不变,这部分计算是可以去除的。
为了对这种情况进行优化,MNN新增了形变缓存机制,使模型输入形状变化时,形状不变的算子跳过形变相关操作,以提升性能。

原理

  • MNN 通过两个状态去优化Resize过程,先在 Check 状态下寻找不变形状的算子,然后切换到 Fix 状态,分离易变形状与不变形状算子进行处理。对应于 Interpreter.hpp 里面增加两个 SessionMode
    • Session_Resize_Check
    • Session_Resize_Fix
  • Check 状态
    • 存储或检查输入的形状是否与上次一致,对应跳过几何计算阶段,但资源分配阶段不跳过
  • Fix 状态
    • 根据算子的形状不变标志,跳过几何计算与资源分配阶段

基于 Module API 使用

对应 API

  • **Module::**traceOrOptimize

示例

  • 网络输入为 speech ,输出为 out0 ,out1 ,输入大小为: 1,3,X
std::shared_ptr<MNN::Express::Module> net(MNN::Express::Module::load(fileName, {"speech"}, {"out0", "out1"}), MNN::Express::Module::destroy);
net->traceOrOptimize(MNN::Interpreter::Session_Resize_Check);
int validShape[] = {64, 112};
for (int i=0; i<2; ++i) {
    auto varp = MNN::Express::_Input({1, 3, validShape[i]}, MNN::Express::NCHW);
    varp->writeMap<float>();
    auto outputs = net->onForward({varp});
}
net->traceOrOptimize(MNN::Interpreter::Session_Resize_Fix);
// Forward and use output

基于 Intrepreter - Session API使用

相关API

  • Interpreter::setSessionMode

示例

  • 网络输入为 speech ,输出为 out0 ,out1 ,输入大小为: 1,3,X
std::shared_ptr<MNN::Interpreter> net(MNN::Interpreter::createFromFile(fileName), MNN::Interpreter::destroy);
MNN::ScheduleConfig config;
/*Set up config
......
*/
MNN::Session* session = net->createSession(config);
auto input = net->getSessionInput(session, "speech");
auto out0 = net->getSessionOutput(session, "out0");
auto out1 = net->getSessionOutput(session, "out1");
net->setSessionMode(Interpreter::Session_Resize_Check);
/*Resize Session for each valid size Begin*/
/*Demo: */
net->resizeTensor(input, {1, 3, 112});
net->resizeSession(session);
net->resizeTensor(input, {1, 3, 64});
net->resizeSession(session);
/*Resize Session for each valid size End*/
net->setSessionMode(Interpreter::Session_Resize_Fix);

/*Fill input and run session
......
*/

鸿蒙系统支持、功能完善与Github Issue 修正

功能完善

  • 支持华为鸿蒙系统上的编译与运行
    • 参考 MNN/project/harmony 下面的脚本进行编译
  • 增加 WinogradLevel 配置,支持用户设定较低的Winograd 输出计算尺寸 ,以节省内存
    • setSessionHint(WINOGRAD_MEMORY_LEVEL, 0) :使用最低计算尺寸
    • setSessionHint(WINOGRAD_MEMORY_LEVEL, 3) :使用默认计算尺寸
  • 修正 onnx onehot 算子在输入尺寸未知时转换出错问题
  • 修正 Sigmoid / Tanh 等算子量化后出错的问题
  • 优化 Tile 算子的几何计算分解,降低产出的Region数,以减少对应GPU后端的初始化耗时
  • 支持 LayerNorm RMS 模式及相应的图优化
  • 原生支持onnx-external data ,降低 Onnx 大模型转换所需要内存
  • Metal 后端 NC4HW4 布局模式修改为C4NHW4,以优化 batch > 1 的卷积性能
  • Metal 后端支持 BatchMatMul / Gather / GatherND 等对应生成的 Loop 算子
  • Metal 后端支持 GridSampler3D
  • Metal 后端支持可变精度设置
    • precision 为 low / normal 时使用 fp16 存储
    • precision 为 high 时使用 fp32 存储
    • 计算一律使用 fp32
  • CPU 后端修正权值在线反量化模式下,输入输出通道不对齐时出错的问题
  • OpenCL 后端优化 Pooling 算子在 KernelSize 很大时的性能问题
  • OpenCL 后端增加 Kernel 缓存机制,降低部分设备上 resize 耗时
  • 其他Bugfix与优化

相应 Issue 修正