ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

L3-InternLM 1.8B 模型 Android 端侧部署实践:用 TaoToken 统一 Key 打通模型转换与 mlc-llm 编译链路

L3-InternLM 1.8B 模型 Android 端侧部署实践:用 TaoToken 统一 Key 打通模型转换与 mlc-llm 编译链路 1. 为什么要在 Android 上跑 InternLM 1.8B端侧推理的真实场景InternLM 1.8B 是上海人工智能实验室开源的小参数对话模型1.8B 的体量在量化到 q4f16_1 之后权重文件大约 1GB 出头推理时峰值内存落在 2GB 到 3GB 之间。这个数字意味着什么意味着它刚好卡在中端 Android 机型的可用区间里——8GB 内存的手机跑起来不会把系统挤爆而 4GB 内存的老机型大概率会在加载阶段就被系统杀掉进程。所以「InternLM 1.8B 模型 Android 端侧部署」这件事本质上是在验证一条链路模型转换能不能在开发机上顺利完成、mlc-llm 编译出来的 so 库能不能在 arm64 上正常加载、真机推理的首 token 延迟和内存占用到底是多少。端侧部署和调云端 API 是两种完全不同的体验。云端 API 你关心的是网络往返和并发配额端侧你关心的是内存峰值、线程调度、GPU 后端是否可用。InternLM 1.8B 适合谁适合想验证「离线可用对话助手」这个产品形态的开发者适合做隐私敏感场景数据不出设备的团队也适合单纯想搞明白 mlc-llm 这套编译链路怎么走通的人。它不适合追求大模型能力上限的场景1.8B 的知识广度和推理深度有限但作为端侧链路的验证载体它的体积和效果平衡得不错。整条链路可以拆成三段第一段在 x86 开发机上用 mlc_llm 的 convert_weight 把 HuggingFace 格式的权重转成 MLC 格式并量化第二段用 mlc_llm compile 把计算图编译成目标平台的动态库Android 上就是 arm64 的 so第三段把模型和库打进 APK或者通过 adb 推到设备存储里让 App 加载。这三段里最容易卡住的是第二段的 NDK 工具链配置和第三段的签名与 gradle 配置后面会逐个给可复制的命令。调试期还有一个绕不开的问题你在开发机上验证转换结果、对比不同量化参数的效果、或者临时调一下云端模型做对照这些接口调用如果每个服务都单独配一套 Key管理起来很碎。用 TaoToken 统一 Key 的好处是转换和编译阶段用本地算力验证和对照阶段走同一个 API 通道Base URL 和 Key 只维护一份切换模型只改 Model ID。这样整条链路的调试成本会低很多尤其是你需要反复对比「端侧 q4f16_1 的输出」和「云端同系列模型的输出」时。2. 前置准备Rust、Android Studio 与 mlc-llm 环境搭建这一节的目标是把开发机上的工具链装齐让后面 convert_weight 和 compile 能跑起来。我试过在纯净的 Ubuntu 环境从零装一遍踩过的坑主要集中在 Rust 镜像源和 NDK 路径上下面按顺序来。先装 Rust。国内网络直接拉 rustup 容易超时换成中科大的镜像会稳很多。三条命令依次执行安装脚本里出现选项直接回车用默认值export RUSTUP_DIST_SERVERhttps://mirrors.ustc.edu.cn/rust-static export RUSTUP_UPDATE_ROOThttps://mirrors.ustc.edu.cn/rust-static/rustup curl --proto https --tlsv1.2 -sSf https://mirrors.ustc.edu.cn/misc/rustup-install.sh | sh装完之后记得 source 一下环境否则后面 mlc_llm 编译时找不到 cargo. $HOME/.cargo/env rustc --version接着装 Android Studio 和命令行工具。这里不需要真的打开图形界面我们只用它的 SDK、NDK 和 JBRJetBrains Runtime自带 JDK。下载解压到 /root/android 下mkdir -p /root/android cd /root/android wget https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2024.1.1.12/android-studio-2024.1.1.12-linux.tar.gz tar -xvzf android-studio-2024.1.1.12-linux.tar.gz cd android-studio wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip?hlzh-cn unzip commandlinetools-linux-11076708_latest.zip\?hl\zh-cn然后用 sdkmanager 装 NDK、CMake 和对应的 platform。NDK 版本建议锁 27.0.12077973这个版本和 mlc-llm 的编译脚本兼容性最好export JAVA_HOME/root/android/android-studio/jbr cmdline-tools/bin/sdkmanager ndk;27.0.12077973 cmake;3.22.1 platforms;android-34 build-tools;33.0.1 --sdk_rootsdk环境变量是这一节的关键写错一个后面编译就报找不到编译器。把下面这段加到 ~/.bashrc 里然后 source 生效export ANDROID_NDK/root/android/android-studio/sdk/ndk/27.0.12077973 export TVM_NDK_CC$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang export JAVA_HOME/root/android/android-studio/jbr export ANDROID_HOME/root/android/android-studio/sdk export PATH/root/android/android-studio/sdk/cmake/3.22.1/bin:$PATHTVM_NDK_CC 这个变量指向的是 aarch64 的 clang 交叉编译器mlc-llm 编译 Android 库时会读它。如果这个路径写错编译阶段会报aarch64-linux-android24-clang: not found这是最常见的报错之一。然后是 mlc-llm 本体。建议用 conda 建独立环境Python 版本锁 3.11conda create --name mlc-prebuilt python3.11 conda activate mlc-prebuilt conda install -c conda-forge git-lfsmlc-llm 的 nightly wheel 从官方源装可能慢可以先下载到本地再 pip install。注意 wheel 链接会随版本更新变化装之前去 mlc.ai/wheels 确认当前版本号wget https://github.com/mlc-ai/package/releases/download/v0.9.dev0/mlc_llm_nightly_cu122-0.1.dev1519-cp311-cp311-manylinux_2_28_x86_64.whl wget https://github.com/mlc-ai/package/releases/download/v0.9.dev0/mlc_ai_nightly_cu122-0.15.dev559-cp311-cp311-manylinux_2_28_x86_64.whl pip install mlc_llm_nightly_cu122-0.1.dev1519-cp311-cp311-manylinux_2_28_x86_64.whl pip install mlc_ai_nightly_cu122-0.15.dev559-cp311-cp311-manylinux_2_28_x86_64.whl验证安装是否成功能打印出模块路径就说明没问题python -c import mlc_llm; print(mlc_llm)最后克隆 mlc-llm 仓库注意要拉子模块TVM 在 3rdparty 目录下git clone https://github.com/mlc-ai/mlc-llm.git cd mlc-llm git submodule update --init --recursive到这里环境就齐了。这一步的检查清单rustc 能输出版本、ANDROID_NDK 路径存在、TVM_NDK_CC 指向的 clang 可执行、mlc_llm 能 import、mlc-llm 仓库子模块拉全。五个都过再往下走。3. 模型转换与 mlc-llm 编译可复制的配置与命令这一节是整条链路的核心分两步convert_weight 把原始权重转成 MLC 格式并量化gen_config 生成运行时配置。先准备模型目录把 InternLM 1.8B 的原始权重放好mkdir -p /root/models/ ln -s /share/new_models/Shanghai_AI_Laboratory/internlm2_5-1_8b-chat /root/models/internlm2_5-1_8b-chat转换命令的关键参数是--quantization q4f16_1这个量化方案把权重量化到 4bit、激活保持 fp16在端侧是精度和体积的平衡点。输出目录用-o指定cd android/MLCChat export TVM_SOURCE_DIR/root/android/mlc-llm/3rdparty/tvm export MLC_LLM_SOURCE_DIR/root/android/mlc-llm mlc_llm convert_weight /root/models/internlm2_5-1_8b-chat/ \ --quantization q4f16_1 \ -o dist/internlm2_5-1_8b-chat-q4f16_1-MLC转换完成后生成配置。conv-template 用 chatml因为 InternLM 2.5 的对话模板就是 chatml 格式写错会导致模型输出乱掉mlc_llm gen_config /root/models/internlm2_5-1_8b-chat/ \ --quantization q4f16_1 --conv-template chatml \ -o dist/internlm2_5-1_8b-chat-q4f16_1-MLCgen_config 这一步容易报缺包把这三个装上基本就解决pip install transformers sentencepiece protobuf生成的 mlc-chat-config.json 是运行时读的配置文件里面记录了模型架构、量化方式、上下文长度、对话模板等。你可以打开看一眼确认model_type是 internlm2、quantization是 q4f16_1。这个文件后面打包时会一起进 APK。打包前先本地验证转换结果把计算图编译成 CUDA 库在开发机上跑一遍。这一步能提前暴露模型转换的问题避免推到手机上才发现mkdir -p dist/libs mlc_llm compile ./dist/internlm2_5-1_8b-chat-q4f16_1-MLC/mlc-chat-config.json \ --device cuda -o dist/libs/internlm2_5-1_8b-chat-q4f16_1-MLC-cuda.so然后用 MLCEngine 加载跑一次对话确认输出正常from mlc_llm import MLCEngine engine MLCEngine( model./dist/internlm2_5-1_8b-chat-q4f16_1-MLC, model_lib./dist/libs/internlm2_5-1_8b-chat-q4f16_1-MLC-cuda.so ) print(engine) for response in engine.chat.completions.create( messages[{role: user, content: 你是谁}], streamTrue ): for choice in response.choices: print(choice.delta.content, end, flushTrue) print(\n) engine.terminate()开发机验证通过后改打包配置。mlc-package-config.json 里列出要打进 APK 的模型model 字段用 HF:// 前缀指向 HuggingFace 仓库model_id 是 App 里显示的标识{ device: android, model_list: [ { model: HF://timws/internlm2_5-1_8b-chat-q4f16_1-MLC, estimated_vram_bytes: 3980990464, model_id: internlm2_5-1_8b-chat-q4f16_1-MLC } ] }这里有个路径坑要注意打包命令必须在 MLCChat 目录下执行如果你之前把 mlc-llm 克隆到了 android-studio 里面先移出来cd /root/android/android-studio mv mlc-llm /root/android cd /root/android/mlc-llm/android/MLCChat mlc_llm packagemlc_llm package会读取上面的 json把模型权重和编译好的库一起打进 App 的 assets。这一步需要能访问 HuggingFace 拉取模型元数据如果网络不通会卡在下载阶段。调试期如果你需要对照云端模型的输出比如想看看同一个 prompt 在更大参数模型上的表现可以用 TaoToken 的 API 通道。Base URL 填https://taotoken.net/apiKey 在控制台生成Model ID 按需选。这样端侧和云端的对照实验在同一个 Key 下管理不用来回切配置。生成 Key 的入口在 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 。4. 真机验证签名、gradle 配置与 adb 安装打包出来的 APK 要装到手机上签名和 gradle 配置是绕不过去的。服务器上构建没有 Android Studio 的图形界面必须用命令行 keytool 生成签名文件cd /root/android/mlc-llm/android/MLCChat /root/android/android-studio/jbr/bin/keytool -genkey -v \ -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000交互式问答里密码自己设一个记住姓名和组织随便填国家代码填 CN。生成后得到 my-release-key.jks。然后改 app/build.gradle核心是加 signingConfigs 段并把 release 构建指向它。注意 storeFile 的路径要写绝对路径密码和 alias 和你 keytool 时填的一致signingConfigs { release { storeFile file(/root/android/mlc-llm/android/MLCChat/my-release-key.jks) storePassword 123456 keyAlias mykey keyPassword 123456 } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro signingConfig signingConfigs.release } }同时把 defaultConfig 里原来的 signingConfig 引用删掉避免冲突。改完执行编译./gradlew assembleRelease编译成功后 APK 在app/build/outputs/apk/release/app-release.apk。用 adb 装到手机上adb install -r app/build/outputs/apk/release/app-release.apk装完打开 App首次启动会从 HuggingFace 下载模型权重大约 1GB 多需要设备能访问外网。如果不想让 App 联网下载可以用 bundle 方式把模型数据通过 adb 推到设备存储具体路径参考 mlc-llm 文档里的 bundle 说明。真机跑起来之后重点记录两个指标首 token 延迟和内存占用。首 token 延迟可以用 App 内的日志或者 adb logcat 抓内存占用用adb shell dumpsys meminfo ai.mlc.mlcchat看 PSS 值。中端机型骁龙 7 系、8GB 内存上q4f16_1 量化的 InternLM 1.8B 首 token 延迟大概在几百毫秒到 1 秒多取决于 CPU 调度和是否用上了 GPU 后端。内存峰值落在 2GB 到 3GB 之间如果超过 3.5GB 就要考虑换更激进的量化或者缩短上下文。运行闪退的话先看 logcat 里有没有UnsatisfiedLinkError这通常是 so 库架构不对或者没打进 APK如果是加载模型时被杀看dumpsys meminfo的峰值多半是内存不够。模型下载不完整也会闪退删掉 App 数据重新下载一次。5. 常见报错排查从 401 到 local proxy failed这一节把链路上高频出现的报错列出来对照着查能省不少时间。报错一aarch64-linux-android24-clang: not found这是 TVM_NDK_CC 没设或者路径写错。检查echo $TVM_NDK_CC输出的路径下有没有这个可执行文件没有的话确认 NDK 版本是不是 27.0.12077973路径里的linux-x86_64和aarch64-linux-android24-clang拼写要对。报错二ModuleNotFoundError: No module named transformersgen_config 阶段缺包pip install transformers sentencepiece protobuf装上即可。如果装了还报确认当前 conda 环境是 mlc-prebuilt。报错三401 Unauthorized这个在调云端 API 做对照实验时出现说明 Key 没传或者传错。检查请求头里的 Authorization 字段格式是不是Bearer 你的KeyKey 有没有多余空格。TaoToken 的 Key 在控制台生成如果 Key 被删了或者过期重新生成一个。Base URL 确认是https://taotoken.net/api不要带多余的路径后缀。报错四local proxy failed或连接超时这个通常出现在 mlc_llm package 拉 HuggingFace 模型元数据时。检查开发机的网络能不能访问 HuggingFace如果不行可以先把模型权重下载到本地用本地路径替代 HF:// 前缀。注意这里说的是正常的网络连通性问题不涉及任何特殊网络工具。报错五Error reading choices或流式输出解析失败这个在 Python 脚本里调云端 API 时出现多半是 stream 模式下响应格式和解析代码不匹配。检查response.choices的结构有些兼容层返回的是response.choices[0].delta.content有些是response.choices[0].message.content。打印一下原始 response 看结构。报错六OAuth相关错误如果用的是需要 OAuth 的接入方式token 过期会报这个。重新走一遍授权流程拿新 token。用 API Key 方式的话不会遇到这个问题。报错七gradle 编译报签名冲突SigningConfig release is missing required property或者Keystore was tampered with。前者是 build.gradle 里 signingConfigs 段没配对后者是密码填错。确认 storePassword 和 keyPassword 和 keytool 时设的一致storeFile 路径是绝对路径且文件存在。报错八App 启动后白屏或立即退出先adb logcat | grep mlc看有没有 native 层报错。如果是dlopen failed说明 so 库没打进 APK 或者 ABI 不匹配。检查 app/build.gradle 里的 ndk abiFilters 是否包含 arm64-v8a以及 mlc4j 模块有没有正确编译。排查的顺序建议是先确认环境变量再确认模型转换产物完整然后确认 APK 里的 so 和模型文件最后看运行时日志。大部分问题在前两步就能定位。6. 把 Key 管理收拢端侧调试期的接口调用实践端侧部署的调试期你其实同时在跟两套东西打交道本地编译出来的模型库和云端用来做对照的 API。本地这套不用管 Key但云端这套如果每个服务单独配切换模型、对比输出、验证 prompt 效果的时候会很碎。用 TaoToken 统一 Key 的价值就在这里——Base URL 固定https://taotoken.net/apiKey 一份Model ID 按需换。具体怎么用比如你想对比端侧 q4f16_1 量化的 InternLM 1.8B 和云端更大参数模型的输出差异写个脚本同时调两边端侧走本地 MLCEngine云端走 OpenAI 兼容接口from openai import OpenAI client OpenAI( base_urlhttps://taotoken.net/api, api_key你的TaoToken Key ) resp client.chat.completions.create( modelinternlm2_5-1_8b-chat, messages[{role: user, content: 用一句话解释端侧推理}], streamFalse ) print(resp.choices[0].message.content)这样端侧和云端的对照实验在同一个脚本里完成Key 只维护一份。如果你在做长期的编码类 Agent 调试或者需要反复跑大量 prompt 做效果对比Coding Plan 会比按量调用更划算入口在 https://taotoken.net/coding-plan 。模型对话的在线验证入口在 https://taotoken.net/models 接入文档在 https://taotoken.net/doc API Key 管理在 https://taotoken.net/api-keys 。回到端侧本身跑通之后的优化方向有几个一是换量化方案q4f16_1 之外还有 q4f32_1 和 q3f16_1精度和体积的取舍不同二是调线程数mlc-llm 的运行时可以配 num_threads中端机型上 4 线程通常比 8 线程更稳因为大核数量有限三是缩短上下文长度mlc-chat-config.json 里的 context_window_size 调小能显著降内存。这几个参数调完首 token 延迟和内存占用会有明显变化建议每次只改一个变量记录数据对比。真机验证的时候adb shell dumpsys meminfo ai.mlc.mlcchat | grep TOTAL能直接看到 PSS 总量配合 logcat 里的首 token 时间戳两个指标一起记。中端机型上如果首 token 超过 2 秒优先检查是不是没用上 GPU 后端mlc-llm 在 Android 上支持 OpenCL配置对了能快不少。
返回列表