Android系统中通过llama.cpp部署大模型

在Android系统中部署llama.cpp的流程与问题记录
Views: 26
1 0
Read Time:3 Minute, 49 Second

时至今日,大模型已经进入到很多人的生活中,如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
Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %
FranzKafka95
FranzKafka95

极客,文学爱好者。如果你也喜欢我,那你大可不必害羞。

文章: 86

留下评论

您的电子邮箱地址不会被公开。 必填项已用*标注

zh_CNCN