Android端部署DeepSeek的詳細(xì)教程
DeepSeek最近幾個(gè)月很火熱,很多產(chǎn)品以及企業(yè)都在接入DeepSeek,比如微信搜索接入,可以搜索公眾號(hào)信息并總結(jié),這個(gè)對(duì)于查一些資料還挺好用,因?yàn)楝F(xiàn)在很多都會(huì)在公眾號(hào)上寫一些東西,進(jìn)行宣傳,畢竟手機(jī)才是用戶用的最多的。
既然談到了手機(jī),那么DeepSeek能否部署于手機(jī)之上呢,在不聯(lián)網(wǎng)的情況下就能使用?基于這個(gè)問(wèn)題,查詢了相關(guān)資料,發(fā)現(xiàn)android端部署DeepSeek兩種方法:
第一種是使用Termux。Termux 是一款終端模擬器應(yīng)用程序,專為 Android 系統(tǒng)設(shè)計(jì),提供完整的 Linux 環(huán)境,允許用戶在 Android 設(shè)備上運(yùn)行 Linux 命令和工具,無(wú)需 root 權(quán)限。然后使用ollama相關(guān)命令下載部署模型即可。目前網(wǎng)上基本上都是這種方式,這種方式其實(shí)跟在服務(wù)端部署差不多,所以為啥不直接用服務(wù)端部署的模型呢。
第二種就是直接將模型文件下載到手機(jī)中,應(yīng)用內(nèi)直接加載模型文件并運(yùn)行,這種方式好處在于,可結(jié)合自身業(yè)務(wù)做一些基于大模型的離線本地私有化應(yīng)用,耗費(fèi)基本為0。
本文主要采用第二種方法,基于阿里MNN庫(kù)進(jìn)行部署。
1 準(zhǔn)備環(huán)境
1.1 MNN轉(zhuǎn)換工具
cd MNN mkdir build && cd build cmake .. -DMNN_BUILD_CONVERTER=ON make -j8
這個(gè)主要是得到MNNConvert轉(zhuǎn)換工具,可用來(lái)轉(zhuǎn)換onnx格式為mnn格式
1.2 大模型轉(zhuǎn)換工具
安裝大模型轉(zhuǎn)換工具需要的一些依賴,可用conda來(lái)進(jìn)行環(huán)境管理
cd path/MNN/transformers/llm/export pip install -r requirements.txt
如果只是想試試大模型android端部署,把這些依賴下載下來(lái),然后使用如下方式一轉(zhuǎn)換模型就行了
1.3 模型下載
git lfs install git clone https://www.modelscope.cn/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B.
1.4 模型轉(zhuǎn)換
方式一:直接使用llmexport.py直接轉(zhuǎn)換得到mnn格式的模型文件
python llmexport.py --path path/model/DeepSeek-R1-Distill-Qwen-1.5B --export mnn
方式二:可先轉(zhuǎn)換為onnx,然后再使用MNNConvert轉(zhuǎn)換為mnn格式模型
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
兩種方式都嘗試過(guò),轉(zhuǎn)換出來(lái)的模型都沒(méi)啥問(wèn)題,方式二主要可以進(jìn)行其他bits數(shù)的量化
成功:


產(chǎn)物解釋:

1.5 編譯android依賴庫(kù)
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"
這個(gè)主要是用于編譯android需要用的mnn庫(kù),編譯完成后將*.so文件將其放在android工程中
提示:如果需要調(diào)試mnn庫(kù),需要將build_64/32.sh文件中的如下參數(shù)設(shè)置為true

2 android工程
主要介紹一下其中一些關(guān)鍵的點(diǎn):CMakeList文件的編寫、JNI文件的編寫,以及簡(jiǎn)要說(shuō)一下android native的實(shí)現(xiàn)
2.1 頭文件導(dǎo)入
將MNN庫(kù)中的頭文件(要包含llm.hpp頭文件),以及1.5編譯的android依賴庫(kù)放入android工程中,目錄如下:

2.2 CMakeList
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
class Chat : Serializable {
companion object {
init {
System.loadLibrary("my_deep_seek")
}
}
external fun Init(modelDir: String): Boolean // 加載模型
external fun Submit(input: String): String // 輸入請(qǐng)求
external fun Respose(): ByteArray // 模型輸出
external fun Done()
external fun Reset()
}
#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;
}
// 將問(wèn)題輸入模型
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); //子線程運(yùn)行
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實(shí)現(xiàn)方面比較簡(jiǎn)單,使用recycleview來(lái)顯示與模型的對(duì)話,其他就調(diào)用jni接口即可
(1)加載模型
將1.4節(jié)模型轉(zhuǎn)換得到的那些文件將其放到手機(jī)的/data/local/tmp/DeepSeek-R1-Distill-Qwen-1.5B目錄下,然后mModelDir就為/data/local/tmp/DeepSeek-R1-Distill-Qwen-1.5B/llm.mnn
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)向模型輸入
mChat?.Submit(input)
(3)得到模型輸出
Thread {
mChat?.Submit(input) // 輸入
var lastResponse = ""
while (!lastResponse.contains("<eop>")) { // 模型輸出結(jié)束標(biāo)志"<eop>"
try {
Thread.sleep(50) // 等模型輸出一點(diǎn)信息
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 總結(jié)
1.5B模型,運(yùn)行內(nèi)存最高1.5G,推理時(shí)1G,占用內(nèi)存還挺多,且模型性能相比云端模型還是差很多。但需向前看,最近阿里的QwQ-32B模型不是達(dá)到了DeepSeek 671B模型的性能了嗎,甚至某些方面還超越DeepSeek,發(fā)展還是很快的。說(shuō)不定后面不到1B參數(shù)的模型,性能可比肩671B模型,狠狠期待一下。
以上就是Android端部署DeepSeek的詳細(xì)教程的詳細(xì)內(nèi)容,更多關(guān)于Android端部署DeepSeek的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android Manifest中meta-data擴(kuò)展元素?cái)?shù)據(jù)的配置與獲取方式
這篇文章主要介紹了Android Manifest中meta-data擴(kuò)展元素?cái)?shù)據(jù)的配置與獲取方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android獲取WiFi網(wǎng)絡(luò)列表的流程步驟
在Android開(kāi)發(fā)中,我們經(jīng)常需要獲取設(shè)備附近可用的Wi-Fi網(wǎng)絡(luò)列表,這對(duì)于開(kāi)發(fā)需要基于Wi-Fi網(wǎng)絡(luò)進(jìn)行功能或者與其他設(shè)備進(jìn)行通信的應(yīng)用程序非常重要,本文將介紹如何在Android應(yīng)用程序中獲取Wi-Fi網(wǎng)絡(luò)列表,需要的朋友可以參考下2024-11-11
關(guān)于Android Studio安裝完后activity_main.xml前幾行報(bào)錯(cuò)的解決建議
這篇文章主要介紹了關(guān)于Android Studio安裝完后activity_main.xml前幾行報(bào)錯(cuò)的解決建議,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android仿QQ微信實(shí)時(shí)監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài)
這篇文章主要為大家詳細(xì)介紹了Android仿QQ微信實(shí)時(shí)監(jiān)測(cè)網(wǎng)絡(luò)狀態(tài),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
Android利用屬性動(dòng)畫實(shí)現(xiàn)優(yōu)酷菜單
這篇文章主要為大家詳細(xì)介紹了Android利用屬性動(dòng)畫實(shí)現(xiàn)優(yōu)酷菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Android實(shí)現(xiàn)快遞物流跟蹤布局效果
本篇文章主要介紹了Android實(shí)現(xiàn)快遞跟蹤布局效果,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
Android 浮動(dòng)編輯框的具體實(shí)現(xiàn)代碼
本篇文章主要介紹了Android 浮動(dòng)編輯框的具體實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
Android?hid發(fā)送apdu格式數(shù)據(jù)示例詳解
這篇文章主要介紹了Android?hid發(fā)送apdu格式數(shù)據(jù),在?Android?中,如果你想通過(guò)?HID(Human?Interface?Device)發(fā)送?APDU?格式的數(shù)據(jù),通常會(huì)涉及?USB?HID?設(shè)備或藍(lán)牙?HID?設(shè)備,本文給大家講解的非常詳細(xì),需要的朋友可以參考下2023-08-08

