2.9.0

alibaba/MNN2.9.0May 15, 2024by jxt1234

AI Summary

MNN 2.9.0 officially integrates the MNN-LLM module with transformer-related operators and graph optimization, introduces shape cache mechanism for speech/text models, and adds HarmonyOS support.

Key Highlights

  • MNN-LLM模块正式合入,支持Transformer相关算子与图优化
  • 形变缓存机制 - 语音/文本类模型输入维度变化时跳过形状不变算子的形变操作
  • 华为鸿蒙系统编译与运行支持
  • WinogradLevel配置支持用户设定较低的Winograd输出计算尺寸节省内存
  • ONNX external data原生支持降低大模型转换内存占用
  • Metal后端NC4HW4布局改为C4NHW4优化batch>1的卷积性能
  • Metal后端支持BatchMatMul/Gather/GatherND/GridSampler3D
  • Metal后端支持可变精度设置(fp16/fp32)
  • LayerNorm RMS模式及图优化支持
  • OpenCL后端Pooling算子性能优化和Kernel缓存机制

New Features

  • MNN-LLM模块 - 完整的LLM推理引擎
  • Transformer相关算子与图优化(MNN_LOW_MEMORY和MNN_SUPPORT_TRANSFORMER_FUSE宏)
  • 形变缓存机制 - Session_Resize_Check和Session_Resize_Fix模式
  • 华为鸿蒙系统支持
  • WinogradLevel配置
  • ONNX external data支持
  • Metal后端C4NHW4布局
  • Metal后端BatchMatMul/Gather/GatherND/GridSampler3D支持
  • Metal后端可变精度设置
  • LayerNorm RMS模式支持
  • CPU后端权值在线反量化模式修正
  • OpenCL后端Pooling优化和Kernel缓存

Full Release Notes

# 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为例)
```python
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
```cpp
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
```cpp
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 修正

- https://github.com/alibaba/MNN/issues/2589
- https://github.com/alibaba/MNN/issues/2628
- https://github.com/alibaba/MNN/issues/2657
- https://github.com/alibaba/MNN/issues/2670
- https://github.com/alibaba/MNN/issues/2689
- https://github.com/alibaba/MNN/issues/2691
- https://github.com/alibaba/MNN/issues/2734
- https://github.com/alibaba/MNN/issues/2757
- https://github.com/alibaba/MNN/issues/2763
- https://github.com/alibaba/MNN/issues/2765
- https://github.com/alibaba/MNN/issues/2814
- https://github.com/alibaba/MNN/issues/2832
- https://github.com/alibaba/MNN/issues/2843