MNN Chat 端侧多模态大模型应用完整指南
【免费下载链接】MNNMNN: A blazing-fast, lightweight inference engine battle-tested by Alibaba, powering high-performance on-device LLMs and Edge AI.项目地址: https://gitcode.com/GitHub_Trending/mn/MNN
MNN Chat 是 MNN 开源仓库官方出品的端侧多模态大模型 Android 应用,由 MNN-LLM 推理引擎驱动,把文生文、图生文、语音转文本、文生图四类任务全部放在设备本地完成。它的差异点在于:一套 C++ 引擎统一承载四种模态,聊天记录与上传素材不经过任何云端。
项目全景
MNN Chat 不是孤立的 UI 工程,而是 MNN 推理栈最上面的一层。自底向上分四块,数据沿这条链路单向流动:
- 推理核心(
source/backend/与source/core/):多后端执行层,含 CPU(含 arm82 目录下的 ARMv8.2 fp16 内核)、OpenCL、Metal、CUDA、Vulkan、Hexagon、QNN 等,KVCache 管理在 source/core/KVCacheManager.hpp,支持把注意力缓存 mmap 到磁盘文件。 - LLM 引擎(
transformers/llm/engine/src/):llm.cpp提供Llm/Omni两个入口类,配套llmconfig.hpp(配置解析)、sampler.cpp(采样)、tokenizer/(词表与模板)、diskembedding.cpp(embedding 文件落盘加载)。 - 应用层(
apps/Android/MnnLlmChat/):Kotlin UI,modelmarket包负责模型市场的解析与渲染,ModelDownloadManager统一管理双源下载、增量热更新与进度状态。 - 模型市场数据:apps/Android/MnnLlmChat/app/src/main/assets/model_market.json,当前
version为 22,models数组含 159 个模型条目,每条带tags、categories、sources(HuggingFace 与 ModelScope 双源)。
一次对话的完整路径:用户在市场选中模型 → 按sources从 HF/ModelScope 下载到本地 → 引擎读模型目录里的llm_config.json→createLLM分发到Llm或Omni→ 前向推理出 logits →Sampler采样出 token → 回调到 Kotlin 层流式渲染。
跑通最短路径
有两条路:直接安装官方 APK(下载入口见 apps/Android/MnnLlmChat/README_CN.md 的 Releases 章节),或从源码构建。改参数、换模型、深度调试都需要走源码构建,前置条件只有三项:
- Android Studio,且 NDK 版本与 apps/Android/MnnLlmChat/app/build.gradle 的
ndkVersion一致,当前为27.2.12479018 - 一台 arm64-v8a 设备(应用只打包该架构,
minSdk 26、targetSdk 35) - 一个指向 NDK 根目录的
ANDROID_NDK环境变量
克隆仓库并导出 NDK 路径,成功后终端无报错、本地出现MNN目录:
git clone https://gitcode.com/GitHub_Trending/mn/MNN export ANDROID_NDK=${YOUR_NDK_ROOT}project/android/build_64.sh 是 Android 通用构建脚本,默认 arm64-v8a、c++_static、android-21、Release。传入下面这串开关即可开启全部多模态能力,make install完成后build_64目录生成 MNN、MNN_LLM、MNN_Audio、MNN_Diffusion 等运行库。开关按功能分三组:
- 推理核心:
MNN_LOW_MEMORY=true(低内存权重布局)、MNN_CPU_WEIGHT_DEQUANT_GEMM=true(低比特权重在 GEMM 里即时反量化)、MNN_ARM82=true(打开 fp16 计算路径)、MNN_BUILD_LLM=true(编译 LLM 引擎) - 多模态能力:
LLM_SUPPORT_VISION=true(视觉编码器)、MNN_BUILD_AUDIO=true与LLM_SUPPORT_AUDIO=true(语音链路)、MNN_BUILD_DIFFUSION=ON(文生图) - GPU 与链接:
MNN_OPENCL=true(OpenCL 后端)、MNN_SEP_BUILD=OFF(产物合并,方便应用整体打包)
cd project/android && mkdir build_64 && cd build_64 ../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_USE_LOGCAT=true -DMNN_OPENCL=true -DLLM_SUPPORT_VISION=true -DMNN_BUILD_OPENCV=true -DMNN_IMGCODECS=true -DLLM_SUPPORT_AUDIO=true -DMNN_BUILD_AUDIO=true -DMNN_BUILD_DIFFUSION=ON -DMNN_SEP_BUILD=OFF -DCMAKE_INSTALL_PREFIX=." make install最后执行安装脚本,它等价于./gradlew assembleStandardDebug加adb install打包产物,成功后设备应用列表出现 MNN Chat,首页即模型市场:
cd ../../../apps/Android/MnnLlmChat ./installDebug.sh核心机制一:llm_config.json 如何决定引擎形态
模型目录与配置的映射
引擎侧所有配置集中在 transformers/llm/engine/src/llmconfig.hpp 的LlmConfig类。它的构造函数接受一个路径,若是.json则直接解析;若是.mnn则合成{"llm_model": "xxx.mnn", "llm_weight": "xxx.mnn.weight"}兼容旧用法(llmconfig.hpp 第 58~99 行)。加载完主配置后还会读取并合并同目录的llm_config.json,因此模型目录里实际生效的文件是二者的叠加。
各配置项的默认文件名与默认值都写死在这一组 getter 里:llm_model默认llm.mnn、llm_weight默认llm.mnn.weight、visual_model默认visual.mnn、audio_model默认audio.mnn、tokenizer_file默认tokenizer.txt(第 106~144 行);后端组backend_type默认cpu、thread_num默认 4、precision默认low(第 181~203 行);内存组含use_mmap、use_cached_mmap、mmap_size(默认 1024)、kvcache_mmap(第 391~405 行)。
Llm 还是 Omni:一个分支决定引擎形态
分发逻辑在 transformers/llm/engine/src/llm.cpp 的Llm::createLLM(第 90~99 行):
if (config->is_visual() || config->is_audio() || config->has_talker()) { llm = new Omni(config); } else { llm = new Llm(config); }纯文本模型走Llm;只要llm_config.json里声明了is_visual、is_audio或has_talker任一项,就升级为Omni(多模态基类,omni.hpp),由它统一管理视觉编码器、音频前端与 talker 的加载顺序。同一个LlmConfig实例被两个类共享,意味着多模态与纯文本共用同一套后端、内存与模板配置,这是四类任务能共用一个应用的底层原因。
核心机制二:token 采样流水线是怎么拼装的
默认六级过滤链
采样配置由LlmConfig的 getter 读取(llmconfig.hpp 第 523~554 行):sampler_type默认mixed,mixed_samplers默认["topK", "tfs", "typical", "topP", "min_p", "temperature"],topK默认 40、topP默认 0.9。这套默认链与主流开源实现的差异在于把 tfs(tail free sampling)与 typical 也排进过滤链,而不仅仅是 topK/topP。
执行时发生了什么
transformers/llm/engine/src/sampler.hpp 里Sampler把每种过滤器抽象成一个SamplerStep(std::function<void(SamplerState&)>),buildPipeline按配置顺序把它们串进mPipeline向量。SamplerState持有候选 token 下标、logits 和一份懒计算的 softmax 概率缓存(ensureProbs),多个过滤器依次作用时只算一次 softmax。sample(logits)被调用时先createState,再顺序执行各 step,最后selected_token即本步输出。
sampler.hpp 第 84~86 行还有一处性能优化:mTopKPrefilter在 topK 是首个生效过滤器时启用快速预过滤,只取 top-k 的值和下标,而不是对全词表走完整管线——词表动辄十几万,这一步省掉的是一次全量排序。
参数与性能地图
下表回答三个问题:哪些编译开关决定应用能力边界、硬件门槛在哪、官方基准数据是多少。
| 构建开关 / 门槛项 | 作用 |
|---|---|
MNN_LOW_MEMORY=true | 调整权重存储布局,压低运行时内存峰值 |
MNN_CPU_WEIGHT_DEQUANT_GEMM=true | 低比特量化权重在 GEMM 算子里即时反量化,省去整体解量化 |
MNN_ARM82=true | 启用 ARMv8.2 扩展,打开 fp16 计算路径 |
MNN_BUILD_LLM / LLM_SUPPORT_VISION / MNN_BUILD_AUDIO / MNN_BUILD_DIFFUSION | 分别对应 LLM 引擎、图像理解、语音识别、文生图 |
MNN_SEP_BUILD=OFF | 产物合并打包,应用直接引用 |
minSdk 26/targetSdk 35 | Android 8.0 起步,只打包 arm64-v8a |
NDK27.2.12479018 | 构建库的 NDK 必须与应用侧一致 |
| 实测机型 | 官方仅在 OnePlus 13 与小米 14 Ultra 完成实测 |
性能基准(以 apps/Android/MnnLlmChat/README_CN.md 给出的数据为准):Android 端 qwen-7b 场景下,MNN-LLM 预填充速度是 llama.cpp 的 8.6 倍、fastllm 的 20.5 倍;解码分别快 2.3 倍与 8.9 倍。
适用边界与排查
不建议直接使用的场景:中低端设备(官方明示可能出现推理缓慢、卡顿甚至无法运行)、32 位设备(应用不含该架构)、以及任何需要把数据送云端处理的场景(MNN Chat 只做本地推理)。
典型异常对应的排查位置:
| 现象 | 排查入口 |
|---|---|
| 模型下载卡住或失败 | 用下面的命令盯进度事件;完整回归流程见 apps/Android/MnnLlmChat/docs/mobile_use.md |
| C++ 侧无错误日志 | 编译时带-DMNN_USE_LOGCAT=true,日志进 Android logcat |
| 模型显示「(有更新可用)」 | ModelDownloadManager的增量热更新机制,属正常提示,确认后重新拉取即可 |
| GPU 加速不生效 | 检查MNN_OPENCL开关与设备驱动支持,必要时回退backend_type为cpu |
抓取下载进度事件,成功后能看到onDownloadProgress回调序列:
adb logcat -d | grep "onDownloadProgress"想继续深入,两个入口:docs/transformers/llm.md 讲 LLM 引擎的模型导出与推理全流程,transformers/llm/engine/src/llm.cpp 的主推理循环(含 KV 复用、prompt cache 分支)是读源码最快的起点。
【免费下载链接】MNNMNN: A blazing-fast, lightweight inference engine battle-tested by Alibaba, powering high-performance on-device LLMs and Edge AI.项目地址: https://gitcode.com/GitHub_Trending/mn/MNN
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考