Android端部署DeepSeek

DeepSeek最近几个月很火热,很多产品以及企业都在接入DeepSeek,比如微信搜索接入,可以搜索公众号信息并总结,这个对于查一些资料还挺好用,因为现在很多都会在公众号上写一些东西,进行宣传,毕竟手机才是用户用的最多的。

既然谈到了手机,那么DeepSeek能否部署于手机之上呢,在不联网的情况下就能使用?基于这个问题,查询了相关资料,发现android端部署DeepSeek两种方法:

第一种是使用Termux。Termux 是一款终端模拟器应用程序,专为 Android 系统设计,提供完整的 Linux 环境,允许用户在 Android 设备上运行 Linux 命令和工具,无需 root 权限。然后使用ollama相关命令下载部署模型即可。目前网上基本上都是这种方式,这种方式其实跟在服务端部署差不多,所以为啥不直接用服务端部署的模型呢。

第二种就是直接将模型文件下载到手机中,应用内直接加载模型文件并运行,这种方式好处在于,可结合自身业务做一些基于大模型的离线本地私有化应用,耗费基本为0。

本文主要采用第二种方法,基于阿里MNN库进行部署。

MNN代码仓库

1 准备环境

1.1 MNN转换工具

bash 复制代码
cd MNN
mkdir build && cd build
cmake .. -DMNN_BUILD_CONVERTER=ON
make -j8

这个主要是得到MNNConvert转换工具,可用来转换onnx格式为mnn格式

1.2 大模型转换工具

安装大模型转换工具需要的一些依赖,可用conda来进行环境管理

bash 复制代码
cd path/MNN/transformers/llm/export
pip install -r requirements.txt

如果只是想试试大模型android端部署,把这些依赖下载下来,然后使用如下方式一转换模型就行了

1.3 模型下载

bash 复制代码
git lfs install
git clone https://www.modelscope.cn/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B.

1.4 模型转换

方式一:直接使用llmexport.py直接转换得到mnn格式的模型文件

bash 复制代码
python llmexport.py --path path/model/DeepSeek-R1-Distill-Qwen-1.5B --export mnn

方式二:可先转换为onnx,然后再使用MNNConvert转换为mnn格式模型

bash 复制代码
python llmexport.py --path path/model/DeepSeek-R1-Distill-Qwen-1.5B --export onnx ./MNNConvert -f ONNX --modelFile llm.onnx --MNNModel llm.mnn --bizCode biz

两种方式都尝试过,转换出来的模型都没啥问题,方式二主要可以进行其他bits数的量化

成功:

产物解释:

1.5 编译android依赖库

bash 复制代码
cd project/android
mkdir build_64 && cd build_64
../build_64.sh "-DMNN_LOW_MEMORY=true -DMNN_CPU_WEIGHT_DEQUANT_GEMM=true -DMNN_BUILD_LLM=true -DMNN_SUPPORT_TRANSFORMER_FUSE=true -DMNN_ARM82=true -DMNN_OPENCL=true -DMNN_USE_LOGCAT=true"
mkdir build_32 && cd build_32
../build_32.sh "-DMNN_LOW_MEMORY=true -DMNN_CPU_WEIGHT_DEQUANT_GEMM=true -DMNN_BUILD_LLM=true -DMNN_SUPPORT_TRANSFORMER_FUSE=true -DMNN_ARM82=true -DMNN_OPENCL=true -DMNN_USE_LOGCAT=true"

这个主要是用于编译android需要用的mnn库,编译完成后将*.so文件将其放在android工程中

⚠️:如果需要调试mnn库,需要将build_64/32.sh文件中的如下参数设置为true

2 android工程

主要介绍一下其中一些关键的点:CMakeList文件的编写、JNI文件的编写,以及简要说一下android native的实现

2.1 头文件导入

将MNN库中的头文件(要包含llm.hpp头文件),以及1.5编译的android依赖库放入android工程中,目录如下:

2.2 CMakeList

bash 复制代码
cmake_minimum_required(VERSION 3.10.2)

project("my_deep_seek")

aux_source_directory(./ SRC_LIST)

add_library(my_deep_seek SHARED ${SRC_LIST})

find_library(log-lib log)
find_library(android-lib android)

include_directories(${CMAKE_SOURCE_DIR}/mnn/include)
include_directories(${CMAKE_SOURCE_DIR}/mnn/include/expr/)
add_library(libMNN STATIC IMPORTED)
add_library(libMNN_CL STATIC IMPORTED)
add_library(libMNN_Express STATIC IMPORTED)
add_library(libllm STATIC IMPORTED)
set_target_properties(
        libMNN PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/mnn/${CMAKE_ANDROID_ARCH_ABI}/libMNN.so
)
set_target_properties(
        libMNN_CL PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/mnn/${CMAKE_ANDROID_ARCH_ABI}/libMNN_CL.so
)
set_target_properties(
        libMNN_Express PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/mnn/${CMAKE_ANDROID_ARCH_ABI}/libMNN_Express.so
)
set_target_properties(
        libllm PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/mnn/${CMAKE_ANDROID_ARCH_ABI}/libllm.so
)

message("===>>>> abi is : ${CMAKE_ANDROID_ARCH_ABI} <<<<===")

target_link_libraries(
        my_deep_seek

        ${log-lib}
        ${android-lib}
        libMNN
        libMNN_CL
        libMNN_Express
        libllm
)

2.3 JNI

kotlin 复制代码
class Chat : Serializable {
    companion object {
        init {
            System.loadLibrary("my_deep_seek")
        }
    }

    external fun Init(modelDir: String): Boolean // 加载模型
    external fun Submit(input: String): String // 输入请求
    external fun Respose(): ByteArray // 模型输出
    external fun Done()
    external fun Reset()
}
c++ 复制代码
#include <android/asset_manager_jni.h>
#include <android/bitmap.h>
#include <android/log.h>

#include <jni.h>
#include <string>
#include <vector>
#include <sstream>
#include <thread>

#include "MNN/llm.hpp"

#ifndef LOG_TAG
#define LOG_TAG "MyDeepSeek"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG ,__VA_ARGS__) // 定义LOGF类型
#endif

static std::unique_ptr<MNN::Transformer::Llm> llm(nullptr);
static std::stringstream response_buffer;

extern "C" {

// 模型加载
JNIEXPORT jboolean JNICALL
Java_com_example_mydeepseek_Chat_Init(JNIEnv *env, jobject thiz, jstring modelDir) {
    const char* model_dir = env->GetStringUTFChars(modelDir, 0);
    if (!llm.get()) {
        llm.reset(MNN::Transformer::Llm::createLLM(model_dir));
        try {
            llm->load();
        } catch (const std::exception& e) {
            LOGI("=== 异常:%s ====", e.what());
            return JNI_FALSE;
        }
    }
    return JNI_TRUE;
}

// 将问题输入模型
JNIEXPORT jstring JNICALL
Java_com_example_mydeepseek_Chat_Submit(JNIEnv *env, jobject thiz, jstring inputStr) {
    if (!llm.get()) {
        return env->NewStringUTF("Failed, Chat is not ready!");
    }
    const char* input_str = env->GetStringUTFChars(inputStr, 0);
    auto chat = [&](std::string str) {
        llm->response(str, &response_buffer, "<eop>");
    };
    std::thread chat_thread(chat, input_str); //子线程运行
    chat_thread.detach();
    jstring result = env->NewStringUTF("Submit success!");
    return result;
}

// 取出模型输出
JNIEXPORT jbyteArray JNICALL
Java_com_example_mydeepseek_Chat_Respose(JNIEnv *env, jobject thiz) {
    auto len = response_buffer.str().size();
    jbyteArray res = env->NewByteArray(len);
    env->SetByteArrayRegion(res, 0, len, (const jbyte*)response_buffer.str().c_str());
    return res;
}

JNIEXPORT void JNICALL
Java_com_example_mydeepseek_Chat_Done(JNIEnv *env, jobject thiz) {
    response_buffer.str("");
}

JNIEXPORT void JNICALL
Java_com_example_mydeepseek_Chat_Reset(JNIEnv *env, jobject thiz) {
    llm->reset();
}

} // extern "C"

2.4 android native

app实现方面比较简单,使用recycleview来显示与模型的对话,其他就调用jni接口即可

(1)加载模型

将1.4节模型转换得到的那些文件将其放到手机的/data/local/tmp/DeepSeek-R1-Distill-Qwen-1.5B目录下,然后mModelDir就为/data/local/tmp/DeepSeek-R1-Distill-Qwen-1.5B/llm.mnn

kotlin 复制代码
Thread {
    mChat = Chat()
    if (mChat?.Init(mModelDir) == true) {
        runOnUiThread {
            mIntent?.putExtra("chat", mChat)
            startActivityForResult(mIntent, 100)
        }
    } else {
        Toast.makeText(this,"加载模型失败", Toast.LENGTH_SHORT).show()
    }
}.start()

(2)向模型输入

kotlin 复制代码
mChat?.Submit(input)

(3)得到模型输出

kotlin 复制代码
Thread {
    mChat?.Submit(input) // 输入
    var lastResponse = ""
    while (!lastResponse.contains("<eop>")) { // 模型输出结束标志"<eop>"
        try {
            Thread.sleep(50) // 等模型输出一点信息
            val response: String = String(mChat?.Respose() ?: ByteArray(0))
            if (response != lastResponse) {
                lastResponse = response
                lifecycleScope.launch {
                    updateBotResponse(
                        response.replaceFirst(
                            "<eop>".toRegex(),
                            ""
                        )
                    )
                }
            }
        } catch (e: InterruptedException) {
            Thread.currentThread().interrupt()
        }
    }
    mChat?.Done()
}

3 总结

1.5B模型,运行内存最高1.5G,推理时1G,占用内存还挺多,且模型性能相比云端模型还是差很多。但需向前看,最近阿里的QwQ-32B模型不是达到了DeepSeek 671B模型的性能了吗,甚至某些方面还超越DeepSeek,发展还是很快的。说不定后面不到1B参数的模型,性能可比肩671B模型,狠狠期待一下。

做为一个小小的开发,或许只能积累以及积极传递技术,让更多的人参与进来,这样技术和应用才能更加繁荣,或许才能在浪潮之下享受技术带来的福利。

参考资料

1\] [github.com/alibaba/MNN...](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Falibaba%2FMNN%2Ftree%2Fmaster "https://github.com/alibaba/MNN/tree/master")

相关推荐
moonless02221 分钟前
【AGI】Dify框架从入门到入土(01—本地化部署Dify)
人工智能·docker·openai
数据猎手小K2 分钟前
GAEA-1.6M:一个包含 80万张图像和约 160 万问答对的大型对话式地理定位数据集,目的提升模型的地理定位和对话能力。
人工智能
tracyZhang3 分钟前
NativeAllocationRegistry----通过绑定Java对象辅助回收native对象内存的机制
android
BuluAI算力云10 分钟前
发布即封神!DeepSeek V3「暗度陈仓」更新:编程能力直逼 Claude 3.7,MIT 开源引爆开发者圈
人工智能
vv啊vv10 分钟前
使用android studio 开发app笔记
android·笔记·android studio
后端小肥肠14 分钟前
RAG回答准确率暴涨300%!用Coze工作流进行数据结构化(附完整提示词)
人工智能·coze·deepseek
阿里云大数据AI技术21 分钟前
DistilQwen2.5-R1发布:知识蒸馏助推小模型深度思考
人工智能·机器学习
Mapmost40 分钟前
【数据可视化艺术·进阶篇】热力图探秘:用色彩演绎场馆和景区的人流奥秘
前端·人工智能·数据可视化
冯浩(grow up)1 小时前
使用vs code终端访问mysql报错解决
android·数据库·mysql
知梦EDA2 小时前
【今日EDA行业分析】2025年3月24日
大数据·人工智能·eda·半导体·行业分析