Android SDK

Demo App

下面的演示 APK 是为搭载骁龙 8 Gen 2 芯片的三星 S23 构建的。

https://seeklogo.com/images/D/download-android-apk-badge-logo-D074C6882B-seeklogo.com.png

准备

Rust (安装) 是必要的,用于将 HuggingFace 分词器交叉编译到 Android。确保 rustc、cargo 和 rustup 在 $PATH 中可用。

Android Studio (安装) 需要安装 NDK 和 CMake。要安装 NDK 和 CMake,在 Android Studio 欢迎页面,点击 "Projects → SDK Manager → SDK Tools"。如果您已经在开发环境中安装了 NDK,请更新您的 NDK 以避免构建 Android 包失败(#2696)。当前的演示 Android APK 是用 NDK 27.0.11718014 构建的。一旦您安装或更新了 NDK,请设置以下环境变量:

  • ANDROID_NDK 以便 $ANDROID_NDK/build/cmake/android.toolchain.cmake 可用。

  • TVM_NDK_CC 指向NDK的clang编译器。

# Example on macOS
ANDROID_NDK: $HOME/Library/Android/sdk/ndk/27.0.11718014
TVM_NDK_CC: $ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang
# Example on Linux
ANDROID_NDK: $HOME/Android/Sdk/ndk/27.0.11718014
TVM_NDK_CC: $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang
# Example on Windows
ANDROID_NDK: %HOME%/AppData/Local/Android/Sdk/ndk/27.0.11718014
TVM_NDK_CC: %ANDROID_NDK%/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android24-clang

JDK,如OpenJDK >= 17,用于编译TVM Unity运行时的Java绑定。强烈建议将 JAVA_HOME 设置为Android Studio捆绑的JDK。例如,对于macOS,export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home。对于Linux,export JAVA_HOME=/opt/android-studio/jbr。按照 这里 <https://developer.android.com/build/jdks> 的推荐使用Android Studio的JBR捆绑包将减少JNI编译中潜在错误的机会。设置以下环境变量:

  • export JAVA_HOME=/path/to/java_home 然后您可以交叉检查并确保 $JAVA_HOME/bin/java 存在。

请确保Android Studio和JAVA_HOME的JDK版本相同。

TVM Unity运行时 位于MLC LLM的 3rdparty/tvm 下,因此无需额外安装任何东西。设置以下环境变量:

  • export TVM_SOURCE_DIR=/path/to/mlc-llm/3rdparty/tvm.

请按照 安装 MLC LLM Python 包 获取mlc_llm包的二进制构建。请注意,这与我们用于Android包构建的mlc-llm源代码是独立的。一旦您安装了这个包,您就不需要从源代码构建mlc llm。

备注

❗ 每当使用Python时,强烈建议使用 conda 来管理隔离的Python环境,以避免缺少依赖、版本不兼容和包冲突。

检查 环境变量 是否已正确设置为最后的检查。确保这一点的一种方法是将它们放在 $HOME/.zshrc$HOME/.bashrc 或环境管理工具中。

source $HOME/.cargo/env # Rust
export ANDROID_NDK=...  # Android NDK toolchain
export TVM_NDK_CC=...   # Android NDK clang
export JAVA_HOME=...    # Java
export TVM_SOURCE_DIR=...     # TVM Unity runtime

Windows用户的额外指南

在Windows下为Android构建仍然是实验性的;请确保您首先完成了上述指南,然后阅读并遵循本节中的说明。如果您使用的是Windows,请确保使用conda安装cmake和Ninja。

conda install -c conda-forge cmake ninja git git-lfs zstd

Windows Java发现带有空格的环境变量存在问题。请确保您获取的Java副本位于没有空格的路径中。最简单的方法是将Android Studio的JBR捆绑包复制到没有任何空格的目录中。如果您的Android Studio安装在 C:\Program Files\Android\Android Studio\,您可以尝试执行以下操作

cp -r "C:\Program Files\Android\Android Studio\jbr" C:\any-path-without-space
set JAVA_HOME=C:\any-path-without-space

你可以在正确设置这些步骤后继续接下来的步骤。

从源代码构建安卓应用

本节展示了如何从源代码构建应用程序。

第一步:安装构建依赖项

首先,请克隆 MLC LLM GitHub 仓库。克隆完成后,进入 android/ 目录。

git clone https://github.com/mlc-ai/mlc-llm.git
cd mlc-llm
git submodule update --init --recursive
cd android

第二步:构建运行时和模型库

为安卓应用程序构建的模型在 MLCChat/mlc-package-config.json 中指定:在 model_list 中,model 指向 Hugging Face 仓库,该仓库...

  • model 指向包含预转换模型权重的 Hugging Face 仓库。安卓应用程序将从 Hugging Face 的 URL 下载模型权重。

  • model_id 是模型的唯一标识符。

  • estimated_vram_bytes 是对模型在运行时占用的 vRAM 的估计。

  • "bundle_weight": true 表示在构建时,模型的权重将被捆绑到应用程序中。

  • overrides 指定了一些模型配置参数的覆盖值。

有一行命令可以构建并准备所有模型库:

cd /path/to/MLCChat  # e.g., "android/MLCChat"
export MLC_LLM_SOURCE_DIR=/path/to/mlc-llm  # has to be absolute path, ../.. does not work
mlc_llm package

该命令主要执行以下两个步骤:

  1. 编译模型。MLCChat/mlc-package-config.jsonmodel_list 的每个模型编译成二进制模型库。

  2. 构建运行时和分词器。 除了模型本身外,还需要轻量级的运行时和分词器来实际运行大语言模型。

该命令会创建包含运行时和模型构建输出的 ./dist/ 目录。请确保以下所有文件都存在于 ./dist/ 中。

dist
└── lib
    └── mlc4j
        ├── build.gradle
        ├── output
        │   ├── arm64-v8a
        │   │   └── libtvm4j_runtime_packed.so
        │   └── tvm4j_core.jar
        └── src
            ├── cpp
            │   └── tvm_runtime.h
            └── main
                ├── AndroidManifest.xml
                ├── assets
                │   └── mlc-app-config.json
                └── java
                    └── ...

移动 GPU 中的模型执行逻辑被整合到 libtvm4j_runtime_packed.so 中,而 tvm4j_core.jar 是轻量级(约 60 kb)的 Java 绑定dist/lib/mlc4j 是 gradle 子项目,您应将其包含在您的应用程序中,以便 Android 项目可以引用 mlc4j(MLC LLM Java 库)。该库打包了依赖的模型库和运行模型所需的运行时。

include ':mlc4j'
project(':mlc4j').projectDir = file('dist/lib/mlc4j')

备注

利用本地 JIT 缓存来避免重复编译相同的输入。然而,有时当我们有新的编译器更新或缓存库出现问题时,强制重新构建是有帮助的。您可以通过设置环境变量 MLC_JIT_POLICY=REDO 来实现这一点。

MLC_JIT_POLICY=REDO mlc_llm package

第三步:构建安卓应用程序

打开文件夹 ./android/MLCChat 作为 Android Studio 项目。将您的安卓设备连接到计算机。在 Android Studio 的菜单栏中,点击 "Build → Make Project"。构建完成后,点击 "Run → Run 'app'",您将看到应用程序在手机上启动。

备注

❗ 此应用程序无法在模拟器中运行,因此需要一部实体手机,因为 MLC LLM 需要实际的移动 GPU 才能以加速的速度有意义地运行。

自定义 App

可以通过自定义 MLCChat/mlc-package-config.json 来定制安卓应用程序中构建的模型。在此介绍 JSON 文件的每个字段。

JSON 文件的 "model_list" 中的每个条目包含以下字段:

model

(必填)要构建到应用程序中的 MLC 转换模型的路径。它是 Hugging Face URL(例如 "model": "HF://mlc-ai/phi-2-q4f16_1-MLC"`),其中包含预转换的模型权重。

model_id

(必需)用于标识模型的唯一本地标识符。它可以是任意值。

estimated_vram_bytes

(必需)运行模型所需的 vRAM 估计值。

bundle_weight

(可选)布尔标志,指示是否将模型权重捆绑到应用程序中。请参阅下面的 捆绑模型权重

overrides

(可选)字典,用于覆盖默认的模型上下文窗口大小(以限制 KV 缓存大小)和预填充块大小(以限制模型的临时执行内存)。例如:

{
   "device": "android",
   "model_list": [
      {
            "model": "HF://mlc-ai/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC",
            "model_id": "RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC",
            "estimated_vram_bytes": 1948348579,
            "overrides": {
               "context_window_size": 512,
               "prefill_chunk_size": 128
            }
      }
   ]
}
model_lib

(可选)字符串,指定用于模型的系统库前缀。通常,当您希望将具有相同架构的多个模型变体构建到应用程序中时,会使用此字段。此字段不会影响任何应用程序功能。 下面介绍的 "model_lib_path_for_prepare_libs" 也与此相关。例如:

{
   "device": "android",
   "model_list": [
      {
            "model": "HF://mlc-ai/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC",
            "model_id": "RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC",
            "estimated_vram_bytes": 1948348579,
            "model_lib": "gpt_neox_q4f16_1"
      }
   ]
}

除了 MLCChat/mlc-package-config.json 中的 model_list 外,您还可以 选择性地 指定 "model_lib_path_for_prepare_libs" 字典,如果您希望使用手动编译的模型库。此字典的键应为模型列表中指定的 model_lib,而字典的值是手动编译的模型库的路径(绝对路径或相对路径)。在运行 mlc_llm package 时,"model_lib_path_for_prepare_libs" 中指定的模型库将被构建到应用程序中。例如:

{
   "device": "android",
   "model_list": [
      {
            "model": "HF://mlc-ai/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC",
            "model_id": "RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC",
            "estimated_vram_bytes": 1948348579,
            "model_lib": "gpt_neox_q4f16_1"
      }
   ],
   "model_lib_path_for_prepare_libs": {
      "gpt_neox_q4f16_1": "../../dist/lib/RedPajama-INCITE-Chat-3B-v1-q4f16_1-android.tar"
   }
}

捆绑模型权重

前面的部分已经提供了使用 MLC LLM 构建安卓应用程序的说明,但根据 MLCChat/mlc-package-config.json 中的配置,它需要从 HuggingFace 下载运行时权重。然而,将权重捆绑到应用程序中以避免通过网络下载可能是更理想的选择。在本节中,我们提供了基于 ADB 的简单教程,希望能对进一步开发有所帮助。

启用权重捆绑。对于您希望在 MLCChat/mlc-package-config.json 中捆绑权重的任何模型,设置字段 "bundle_weight": true,然后再次运行 mlc_llm package。以下是示例:

{
   "device": "android",
   "model_list": [
      {
         "model": "HF://mlc-ai/gemma-2b-it-q4f16_1-MLC",
         "model_id": "gemma-2b-q4f16_1-MLC",
         "estimated_vram_bytes": 3000000000,
         "bundle_weight": true
      }
   ]
}

运行 mlc_llm package 的结果应如下所示:

dist
├── bundle
│   ├── gemma-2b-q4f16_1   # The model weights that will be bundled into the app.
│   └── mlc-app-config.json
└── ...

生成 APK。进入 Android Studio,点击 "Build → Generate Signed Bundle/APK" 以构建用于发布的 APK。如果这是您第一次生成 APK,您需要根据 Android 官方指南 创建密钥。此 APK 将放置在 android/MLCChat/app/release/app-release.apk 下。

安装 ADB 并启用 USB 调试。在手机设置的开发者模式中启用“USB 调试”。在“SDK 管理器 - SDK 工具”中,安装 Android SDK 平台工具。将平台工具的路径添加到环境变量 PATH 中(在 macOS 上,路径为 $HOME/Library/Android/sdk/platform-tools)。运行以下命令,如果 ADB 安装正确,您的手机将显示为设备:

adb devices

将 APK 和权重安装到您的手机。运行以下命令以安装应用程序,并将本地权重推送到设备上的应用程序数据目录。完成后,您可以在设备上启动 MLCChat 应用程序。将 bundle_weight 设置为 true 的模型的权重将已经存在于设备上。

cd /path/to/MLCChat  # e.g., "android/MLCChat"
python bundle_weight.py --apk-path app/release/app-release.apk