Physical Address:
ChongQing,China.
WebSite:
时至今日,大模型已经进入到很多人的生活中,如GPT4,通义千问等。大模型的架构也从云端开始往端云融合,最终到达All-On-Device的架构,也就是越来越多被提及的EdgeAI。而Android设备作为Edge Device中占据主导的部分,也存在将大模型部署到安卓设备的需求。今天这篇文章将为大家介绍如何在我们的Android设备中通过llama.cpp部署大模型。
llama.cpp是一套开源框架,可以让你在自己的电脑上运行大型语言模型。我们可以在Github上找到其代码仓库。llama.cpp仍旧处于积极开发的状态,社区相当活跃,当我们遇到问题时可以从社区中获取到相当多的帮助。
llama.cpp是完全由C/C++编写的大模型部署工具,这也意味着我们可以通过CMake/NDK-build工具直接将其集成到Android系统中。这里我们编译时的版本信息如下:
Commit:97bdd26eee11fe109dec00de75690ceef61c03f2
Tag:b3400
编译环境为Ubuntu 22.04.4 LTS。
1.下载源码
git clone https://github.com/ggerganov/llama.cpp
2.配置NDK环境
根据我们的运行环境,下载合适的NDK版本,具体版本的下载可以从该链接中查找:
https://github.com/android/ndk/releases
在我的试验中,我使用的NDK版本为r25c版本。
3.配置编译
$ cd llama.cpp
$ mkdir build-android
$ cd build-android
$ export NDK=<your_ndk_directory>
$ cmake -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-${PLATFORM_API} -DCMAKE_C_FLAGS=-march=armv8.4a+dotprod ..
$ make
编译完成后,我们可以在build目录下找到生成的二进制产物。
llama.cpp项目中提供了CLI命令行工具,可以让使用者快速上手。在编译完成之后我们需要将相关二进制产物移到我们的Android环境中运行。涉及到的二进制产物包括:
llama-cli:CLI命令行工具,需要推送到/system/bin/目录下
libllama.so:依赖库,需要推送到/system/lib64(64位)目录下
libggml.so:依赖库,需要推送到/system/lib64(64位)目录下
事实上除了上述比较关键的二进制产物,还有一些Android系统提供的基础依赖库,包括libc.so、libm.so、libdl.so,而这些是Android系统自带的,不需要我们自行编译。
当我们在Android系统中使用时,还需要手动将模型数据导入Android系统。关于具体的模型我们可以到huggingface中进行下载,需要说明的是并不是所有的模型llama.cpp都支持,这里我们可以llama.cpp项目README中看到其所支持的一些主流模型:LLaMa,Falcon,Phi,Gemma,Grok等。
这里我们下载llama 7B模型的4bit量化版本,其数据大小为3.8G,如下图所示:
下载以后我们通过adb push到Android设备的/data/temp/目录下,参考官方使用示例,我们加载模型并以交互模式进行测试,如下所示:
llama-cli -m /data/temp/llama-2-7b-chat.Q4KS.gguf -p “You are a helpful assistant” -cnv
这里的几个命令参数需要作一下说明:
-m:指定Model
-p:指定Prompt
-cnv:指定为conversation对话模式
指令执行过程如下:
当我们看到>出现时,就意味着我们进入了交互模式,此时我们就可以像与GPT4对话那样与其进行对话,如下所示:
当我们想结束对话时,只需要通过Ctrl+C进行终端结果,此时我们还可以看到一些大模型推理的参数指标:
llama_print_timings: load time = 8891.31 ms
llama_print_timings: sample time = 33.95 ms / 102 runs ( 0.33 ms per token, 3004.15 tokens per second)
llama_print_timings: prompt eval time = 27400.86 ms / 51 tokens ( 537.27 ms per token, 1.86 tokens per second)
llama_print_timings: eval time = 30497.56 ms / 99 runs ( 308.06 ms per token, 3.25 tokens per second)
llama_print_timings: total time = 528586.44 ms / 150 tokens
第一行打印的内容为模型加载时间,在这里模型加载花费8891ms;通常而言模型的加载时间与模型大小成线性相关;
第二行打印的信息为模型生成Token的性能指标,这里我们可以看到Token的生成速率为3000 tokens/s;
第三行打印的信息为模型处理Prompt时的性能指标;单个Token需要花费537.27ms的时间去处理;每秒钟可以处理1.86个Token;
第四行打印的信息为模型评估的性能指标,其表明每秒钟能够生成3.25个Token,这意味着其生成文本的速率是大于处理Prompt时的速率的。
llama.cpp项目原本是用于在纯CPU环境下实现大模型的推理,这对设备的CPU消耗是很大的,如下所示:
以我的Android虚拟机为例,我们为其启用了8核心,当llama-cli进行推理时,其CPU占用高达787%,这个数据对于Android设备是不可接受的。
随着项目的发展,llama.cpp也开始支持GPU等硬件加速。在Android系统中,则支持通过Vulkan API来实现GPU加速。遗憾的是,官方一直没有提供完善的文档来指导构建,这里将结合我的个人实践来教大家如何通过Vulkan支持GPU硬件加速。
首先我们需要安装Vulkan SDK,这里以Ubuntu 22为例:
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | apt-key add -
wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
apt update -y
apt-get install -y vulkan-sdk
# 确认Vulkan SDK是否安装
vulkaninfo
当我们配置好Vulkan SDK后,还需要下载Android NDK,安卓NDK的下载配置此处不赘述,可以直接从Google官方网站下载,也可以通过命令行工具sdkManager进行下载配置。
这里我下载使用的NDK版本为NDK r25版本,下载完后请设置NDK的环境变量,以便后续使用。之后我们开始编译:
mkdir build-android && cd build-android && cmake -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=latest -DCMAKE_C_FLAGS=-march=armv8.4a+dotprod -DGGML_VULKAN=1 .. && make -j4
在编译过程中,我们会遇到以下问题:
1. vulkan-shaders-gen: not found,这里的解决办法是先编译支持本地Host架构的vulkan后端,此时会在build/bin/目录下生成vulkan-shaders-gen文件,该文件会用于生成Vulkan shader。这里我们需要手动将该执行文件的路径添加到PATH环境变量,以便后续CMake在交叉编译Android系统的产物时能够找到该程序;
1.#首先开启Vulkan编译配置,注意是为Host架构进行编译而非交叉编译
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_VULKAN=1 && cmake --build build
2.#编译完成后,设置环境变量以便导入vulkan-shader-gen,LLAMA_CPP_HOME指代llama.cpp的工程项目
exoprt PATH=$PATH:${LLAMA_CPP_HOME}/build/bin
3.编译Android
mkdir build-android && cd build-android && cmake -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=latest -DCMAKE_C_FLAGS=-march=armv8.4a+dotprod -DGGML_VULKAN=1 .. && make -j4
2.llama.cpp/ggml/src/ggml-vulkan.cpp:7:10: fatal error: ‘vulkan/vulkan.hpp’ file not found,这里我们需要手动更新vulkan相关的头文件,因为Android NDK中的Vulkan SDK中是比较滞后的,因此会报头文件找不到相关的问题。这就需要我们手动更新NDK中的Vulkan头文件:
1.下载Vulkan Headers
https://github.com/KhronosGroup/Vulkan-Headers/releases/tag/vulkan-sdk-1.3.290.0
2.将Vulkan Headers更新到NDK内
${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/vk_video
${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/vulkan
重新编译。
3.将llama-cli,libggml.so,libllama.so推送到Android系统,或者将libggml.so,libllama.so相关库通过JNI方式集成到Android系统中报错libomp.so,这是因为libomp.so并非Android系统的内置库,需要我们手动推入到系统或者集成到应用中,我们可以在NDK中找到该库。
./toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/i386/libomp.so
./toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/riscv64/libomp.so
./toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/arm/libomp.so
./toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/x86_64/libomp.so
./toolchains/llvm/prebuilt/linux-x86_64/lib/clang/18/lib/linux/aarch64/libomp.so
这里需要找到对应架构的库。
以上是一些常见的问题,当我们解决以上问题后就可以开启GPU加速。
如果我们使用llama-cli,可以通过-ngl参数开启GPU加速,如果是通过JNI的方式,则需要配置n_gpu_layers参数。
在实践过程中,发现高通Adreno系列的GPU上运行会存在问题,相关报错如下:
08-10 16:06:09.099 30852 30926 I AdrenoVK-0: Failed to link shaders.
08-10 16:06:09.099 30852 30926 I AdrenoVK-0: Pipeline create failed
08-10 16:06:09.108 30852 30926 E LLama-android: llama_model_load: error loading model: vk::Device::createComputePipeline: ErrorUnknown
08-10 16:06:09.108 30852 30926 E LLama-android: llama_load_model_from_file: failed to load model