4

Android 端侧大模型开发

理解端侧大模型特点,完成模型转换、Demo 试用与 Android 工程接入

📚

参考资料

本模块参考人工智能部门 高性能计算工程师 朱鑫炎端侧大模型能力宣讲》整理。

📎

以官方文档为准

端侧接入、API 说明与错误码以 端侧 3B SDK 接入(id=1802) 为主参考;本页是课堂实操摘要。相关文档: SDK / Demo 下载(id=1803) · 文本审核(id=1804)。模型文件、设备要求和参数取值以当届赛项说明为准。

4.1 为什么选择端侧大模型?

云端大模型需要把请求发送到远程服务器;端侧大模型直接部署在手机上,数据处理、模型推理和结果生成主要在本机完成。

端侧优势
  • 隐私安全:数据与推理主要留在本机,减少敏感信息外传
  • 节约成本:减少云端算力、接口调用和网络传输成本
  • 离线可用:弱网或断网环境下仍可提供核心能力
  • 适合处理生物识别、金融、轨迹等本地敏感数据,以及推荐、搜索、意图识别等场景
端侧挑战
  • 资源约束:手机内存、算力和功耗均有限
  • 精度损失:模型量化和压缩可能影响生成质量
  • 常用应对手段:模型量化、LoRA 微调、prompt 工程
  • 开发时需同时平衡效果、速度、内存占用与稳定性
端侧架构

图 4-1 端侧推理:请求在本机完成分析与响应,减少对云端服务器的依赖

4.2 BlueLM 端侧能力与赛事提供能力

端侧 BlueLM 3B

面向手机端部署的 30 亿参数多模态模型,可在离线状态下完成本地文本与图片理解任务。

  • 文本总结、文本理解、文本创作
  • 文本润色、情感分析
  • 图片理解与图文问答
  • 典型应用:文档总结、离线助手、端侧智能交互
赛事提供能力
  • 端侧模型与 SDK:BlueLM-3B、8K 上下文、多轮对话、指令跟随、C++ SDK 与系统硬件能力
  • 模型微调平台:上传训练数据、LoRA 微调训练/部署、模型转换与一键导出
  • 比赛专用手机:预置适配设备的基座模型与运行环境

4.3 整体开发流程

端侧作品包含「模型」和「程序」两条开发线,最后在比赛专用手机上汇合:

开发线流程产物
模型线模型微调 → 模型转换 → LoRA 下载output.zip(LoRA 配置与权重)
程序线环境准备 → 代码编辑或 Demo 二次开发 → APK 编译可安装的 APK
汇合调试安装 APK + 推送 LoRA → 初始化基座 → 加载 LoRA → 运行调试端侧应用与验证结果
🧭

推荐顺序

先用官方 Demo 验证设备、基座模型和 LoRA 产物,再接入自己的 Android 工程。这样可以分别排查「模型问题」和「工程问题」。

4.4 本地环境与比赛专用手机

📱

培训课程说明

端侧实验统一使用现场提供的 比赛专用手机(真机),不使用云真机平台。请向讲师领取已预装环境的设备;Android 模拟器或非赛项机型无法替代目标设备的 NPU 与模型环境。

将专用手机通过 USB 数据线 连到电脑,开启「开发者选项」和「USB 调试」后,用 ADB 安装并运行官方 Demo。

准备项要求如何确认
开发 IDE建议 Android Studio能打开并同步官方 Demo 工程
构建环境Android SDK、NDK、CMake、JDK、Gradle 版本按官方 Demo 要求配置优先沿用 Demo 自带 Gradle 配置,不把讲义中的历史版本作为强制要求
ADBplatform-tools 已加入 PATH,或使用完整路径调用adb version 能正常输出
目标设备比赛专用手机;arm64-v8a、API 28+(以赛项说明为准)已开启开发者模式与 USB 调试
基座模型由赛方预置,路径和文件名不得随意修改示例:/sdcard/1225/1.7.0.4_1225_mtk9500
文件权限首次启动授予读取模型目录所需权限Android 11+ 按应用提示授予「所有文件访问权限」
bash
# USB 连接专用手机后
adb version
adb devices          # 应看到 device 状态,而不是 unauthorized
adb install -r path/to/demo.apk
adb shell am start -n com.example.demo/.MainActivity

adb devices 为空:更换数据线或 USB 接口,并确认已开启 USB 调试;Windows 需安装对应 USB 驱动。 课程现场遇到问题请优先向讲师反馈,勿自行登录云真机平台占用设备资源。

检查模型转换产物

模块二完成「模型转换」后下载 output.zip。解压目录至少应包含:

text
output/
├── bluelm_mtk_llm_lora_config.json
└── lora_chunk_0.bin          # 也可能有多个 lora_chunk_*.bin
配置项作用注意事项
lora_nameLoRA 业务名称,默认示例为 lora_test必须与代码调用 startLora 时传入的名称一致
lora_rankLoRA 低秩维度端侧仅支持 rank 16 / 32
nCtx运行时上下文长度rank 16 常用 2048;rank 32 可按官方包支持 2048 / 4096 / 8192,最终以配置和赛项说明为准
lora_chunk_*.binLoRA 权重分片全部分片须与配置文件放在同一目录,不能只推送其中一个

端侧 Demo 使用参考(X300 Pro 真机)

官方文档 id=1803 下载 SDK 与 Demo 工程,或在课程现场使用讲师提供的 APK / 工程包。 推荐按「安装 APK → 授权文件访问 → 初始化基座 → 推送并加载 LoRA → 固定问题验证」的顺序操作。

端侧 Demo:模型已初始化、LoRA 已加载

图 4-2 初始化模型并加载 LoRA(vivo X300 Pro 真机截图)

  1. 安装 APK,首次启动时授予文件访问权限
  2. 按赛项说明填写基座模型路径(示例:/sdcard/1225/1.7.0.4_1225_mtk9500),选择「纯文本对话」并初始化模型
  3. output.zip 中的配置和全部权重分片推送到同一手机目录(示例:/sdcard/lora_test
  4. 在应用中加载 LoRA,确认状态显示「LoRA 已加载」
  5. 用固定 5~10 个问题分别测试基座与 LoRA,保存回复做 A/B 对比
端侧 Demo:输入问题并查看模型回复

图 4-3 输入 STEM 相关问题,查看端侧模型回复(含 LoRA 微调效果)

bash
# 安装 Demo(APK 路径以官方包或讲师提供为准)
adb install -r path/to/demo.apk

# 推送模块二转换后的 LoRA 权重(路径按实际解压目录调整)
adb shell mkdir -p /sdcard/lora_test
adb push bluelm_mtk_llm_lora_config.json /sdcard/lora_test/
adb push lora_chunk_*.bin /sdcard/lora_test/

# 核对手机目录
adb shell ls -lh /sdcard/lora_test
  1. 在比赛专用手机上完成基座初始化、LoRA 加载和一次 A/B 对比

4.7 Android 工程接入(摘自 id=1802)

建议基于官方 Demo 二次开发。以下为官方文档中的核心步骤摘要,完整代码与参数说明请查阅 id=1802

① 引入 AAR 与 native 库

id=1803 获取 llm-sdk-release.aar,放入 app/libs/。若官方包同时提供 native .so, 将对应 arm64-v8a 文件放入 app/src/main/jniLibs/arm64-v8a/;使用 CMake 的工程按官方 Demo 建立链接关系。

groovy
android.defaultConfig {
    minSdk 28
    ndk { abiFilters 'arm64-v8a' }
}
dependencies {
    implementation files('libs/llm-sdk-release.aar')
}
源码运行流程五步
  1. 基座初始化:加载纯文本 BlueLM3B 或多模态 BlueLM_V3B
  2. 加载 LoRA:调用 startLora(name, path, nCtx)
  3. 多模态分支:有图片时先完成 callVit 编码;纯文本跳过
  4. 模型推理:按模板拼接 prompt,调用 generate
  5. 释放资源:先 releaseLora,再 release

② 基座初始化与纯文本推理

注意init 必须在子线程调用;prompt 必须带官方模板。

java
LlmManager llmManager = new LlmManager();

LlmConfig config = new LlmConfig();
config.modelPath = "/sdcard/1225/1.7.0.4_1225_mtk9500";  // 以赛项预置路径为准
config.multimodal = false;
config.nCtx = 2048;
config.nPredict = 512;
config.nThreads = 4;
config.npuPower = 100;
config.temperature = 0.95f;
config.topP = 0.8f;
config.topK = 50;

new Thread(() -> {
    int ret = llmManager.init(config);  // 0 表示成功
}).start();

// 纯文本 prompt 模板(官方格式,不可省略)
String prompt = "[|Human|]:" + userInput + "\n[|AI|]:";
llmManager.generate(prompt, new TokenCallback() {
    @Override public void onToken(String t) { /* 追加到 UI */ }
    @Override public void onComplete() {}
    @Override public void onError(int code, String msg) {}
});

// Activity 销毁时释放(若加载过 LoRA,先 releaseLora)
llmManager.release();

③ 加载 LoRA(衔接模块二转换产物)

模块二「模型转换」成功后解压 output.zip,目录内需含 bluelm_mtk_llm_lora_config.jsonlora_chunk_*.bin。 配置文件中 lora_name 须与代码传入的名称一致(示例:lora_test)。

java
// 在 init 成功后、generate 之前调用
String loraName = "lora_test";       // 与配置文件 lora_name 一致
String loraPath = "/sdcard/lora_test";
int ret = llmManager.startLora(loraName, loraPath, 2048);

// 用毕或 onDestroy 时,先释放 LoRA,再释放基座模型
llmManager.releaseLora(loraName, loraPath);
llmManager.release();

④ 多模态图文(进阶)

java
config.multimodal = true;
// init 成功后,传入 RGB 字节数组(宽×高×3)
llmManager.callVit(rgbData, width, height);

// 多模态 prompt 模板
String prompt = "[|Human|]:<im_start><image><im_end>" + userInput + "\n[|AI|]:";
⚠️

多模态调用顺序不可颠倒

多模态模式必须先成功完成 callVit,再调用 generate;未完成图像编码就直接推理,可能导致调用失败或崩溃。纯文本模式不要调用 callVit

端侧 Demo:选择图片并进行多模态图片理解

图 4-4 多模态模式:选择图片后进行图片理解与图文问答(vivo X300 Pro 真机截图)

⑤ 文本审核(必接,见 id=1804)

在 Application 中初始化 CMS,对用户输入与模型输出分别调用 TextModeration,未通过则拦截展示。详见 审核文档 id=1804

模式Prompt 模板
纯文本[|Human|]:{输入}\n[|AI|]:
多模态[|Human|]:<im_start><image><im_end>{输入}\n[|AI|]:
  1. 能够说明:AAR 接入 → 子线程 init → prompt 模板 → LoRA / 多模态 → 文本审核 → release

4.8 Manifest 与权限(id=1802)

  • 存储:READ/WRITE_EXTERNAL_STORAGE(API ≤32)+ MANAGE_EXTERNAL_STORAGE(Android 11+ 读模型目录)
  • NPU:mediatek.permission.ACCESS_APU_SYS
  • 审核:vivo.aiservice.permission.AISERVICE_ACCESS
  • Native:uses-native-library 声明 libdmabufheap.solibvcap_npu_network_v1.sorequired=false
xml
<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    <uses-permission android:name="mediatek.permission.ACCESS_APU_SYS" />
    <uses-permission android:name="vivo.aiservice.permission.AISERVICE_ACCESS" />

    <application ...>
        <uses-native-library
            android:name="libdmabufheap.so"
            android:required="false" />
        <uses-native-library
            android:name="libvcap_npu_network_v1.so"
            android:required="false" />
    </application>
</manifest>

Manifest 声明不等于运行时已授权。首次启动仍需按系统版本申请必要权限并引导用户授予文件访问权限,否则无法读取 /sdcard/ 下模型文件。

4.9 高频问题排查

现象常见原因处理方式
adb devices 显示 unauthorized手机未授权当前电脑解锁手机,重新插线并在弹窗中允许 USB 调试
基座初始化失败模型路径错误、文件缺失或机型不匹配核对赛项预置路径和比赛专用手机,请勿移动或重命名模型文件
LoRA 加载失败lora_name 不一致、权重分片漏传核对配置、代码名称及手机目录中的全部 lora_chunk_*.bin
LoRA 加载后推理异常rank / nCtx 与产物不匹配只使用 rank 16 / 32,并按导出配置和官方说明选择上下文长度
回答乱码或角色混乱prompt 模板不正确严格使用纯文本或多模态官方模板
图文推理失败或崩溃未先完成 callVit确认多模态初始化成功,图像编码完成后再调用 generate
多次进入页面后内存异常LoRA 或基座资源未释放退出时按 releaseLora → release 顺序释放

4.10 常见错误码(对照 id=1802)

错误码含义排查方向
-1001config 不存在模型目录缺少 bluelm_mtk_llm_config.json
-2101基座模型加载失败DLA 与设备 NPU 不匹配,多为机型选错(须用比赛专用手机)
-2104VIT 初始化失败多模态权重缺失或损坏
-2105Tokenizer 加载失败词表 *vocab*.bin 缺失;或模型目录根本不存在
⚠️

机型必须匹配

目录名中的 mtk9500 表示 DLA 针对天玑 9500 编译。在非赛项机型(如普通 vivo 机型、模拟器)上, 常见现象是模型目录不存在或报 -2101 / -2105——请换用讲师分配的比赛专用手机。

4.11 端侧开发完成清单

  • 环境连通adb devices 显示 device,Demo 可安装启动
  • 基座验证:能在比赛专用手机上完成纯文本本地推理
  • 模型部署:完成模型转换,全部 LoRA 文件已推送到同一目录
  • 效果验证:LoRA 加载成功,用固定问题完成基座 / LoRA A/B 对比
  • 工程接入:SDK、native 库、Manifest 权限和五步调用流程均已实现
  • 多模态(若使用):完成 callVit → generate 图文问答
  • 安全与释放:输入输出均经过 TextModeration,退出时释放 LoRA 与基座资源

接口细节、参数取值与完整 Demo 以 官方文档 id=1802 为准; SDK 与 Demo 包从 id=1803 下载。