
最近朋友扔给我一台天玑平台的安卓设备让我在上面把Qwen2.5跑起来。第一次听到这个需求我也愣了一下不就是在手机里放个大模型吗可真正动手之后才发现MTK平台跟PC上的部署完全是两回事。算子支持、内存带宽、大小核调度、量化格式每一步都可能卡住。这篇文章就是我这段时间踩坑的记录从HuggingFace权重一路走到设备端推理把关键链路拆开讲清楚。在MTK设备上本地运行Qwen2.5解决的是离线推理和隐私两件事。没有网络也能对话数据不出设备这只是表面。往深了说端侧推理意味着可以在开发板、智能终端、边缘盒子上做AI应用不用依赖云端API也不受网络抖动影响。再实际一点能用1.8B这样的小模型完成大部分对话、摘要、分类任务成本比云端调用低得多。适合谁看手里有天玑手机或MTK开发板、想把Qwen2.5这类开源模型跑起来的开发者不管是做产品原型、研究边缘推理还是单纯想折腾这篇都能给你省几天时间。1. 先想清楚MTK平台跑Qwen2.5方案到底怎么选1.1 为什么在MTK平台上部署大模型要先理清平台差异MTK平台和高通最大的不同在于AI加速单元的成熟度。高通的Hexagon DSP上有相对完整的LLM部署工具链而MTK的APU也常被叫NPU虽然算力不低但公开的SDK和社区案例主要集中在图像分类、目标检测这类CV模型上。想做LLM尤其是Qwen2.5这种带GQA、RoPE、KV Cache的生成式模型想直接靠NPU加速目前并不现实。这不是说MTK不能做。天玑9300/9400这类旗舰的CPU和内存带宽其实非常强LPDDR5T的带宽能做到60GB/s以上这是端侧LLM推理最关键的硬件指标。LLM推理是内存带宽密集型任务权重读取速度决定了速度上限CPU多核算力足够喂饱权重读取所以MTK平台上用CPU跑量化后的Qwen2.5反而是最稳、最可控的方案。所以我的建议是别一开始就想NPU先把llama.cpp这套CPU推理链路跑通。它会成为你的基线。后面有精力再研究NeuroPilot但基线必须要稳。1.2 模型尺寸选择0.5B、1.8B还是7BQwen2.5系列有不少尺寸但在MTK设备上真正有意思的是0.5B、1.8B和7B三个档位。0.5B适合跑在IoT级平台上或者做简单的文本分类、关键词提取1.8B是端侧性价比之王日常对话、摘要、指令跟随都有可用性7B则是旗舰手机或开发板的天花板挑战能跑但速度、内存占用都要精打细算。量化到Q4_K_M之后三个档位的大小大致是模型参数量Q4_K_M文件大小内存要求含上下文适合平台Qwen2.5-0.5B-Instruct约0.5B约400MB1GB以上Genio入门级、IoT设备Qwen2.5-1.8B-Instruct约1.8B约1.1GB2GB以上天玑中端/旗舰、开发板Qwen2.5-7B-Instruct约7.6B约4.4GB6GB以上天玑旗舰、8GB内存设备这里强调内存要求不只是模型文件大小还包括上下文KV Cache和推理时的临时缓冲区。7B模型配4K上下文KV Cache可能再吃掉几百MB8GB内存的设备跑起来会比较紧张需要把系统后台清一清。如果只有6GB内存建议优先考虑1.8B。这不是看不起小模型而是端侧AI本来就是“在约束下做取舍”模型选大了连系统都保不住反而得不偿失。1.3 三条部署路线GGUF、ONNX、TFLite怎么选把Qwen2.5从HuggingFace权重变成能在MTK设备上跑的形式主流有三条路路线转换流程运行后端适合场景推荐度GGUF llama.cppPyTorch - GGUF - 量化CPU/GPUVulkan通用、跨平台、社区成熟首选ONNX onnxruntimePyTorch - ONNX - INT8量化CPU/NPUNeuroPilot EP需要接入现有ONNX服务有条件可用TFLite NeuroPilotPyTorch - ONNX - TFLiteCPU/APUNNAPI小模型、深度适配NPU实验性质GGUF是llama.cpp定义的格式本质上是把模型权重、tokenizer、超参数打包成一个文件还能直接量化。其他格式处理不好动态shape的问题GGUF天生规避了因为推理逻辑就是llama.cpp自己实现不依赖第三方runtime的算子库。所以跑Qwen2.5我强烈推荐GGUF。ONNX路线看着美好但Qwen2.5导出ONNX时GQA这种分组注意力算子在很多runtime后端上兼容性一般加上动态batch和动态sequence转换过程会踩不少坑。TFLite路线则更折腾MTK NeuroPilot对TFLite的算子支持偏向CV模型Transformer的算子覆盖率不算理想最后大概率回退到CPU性能还不如llama.cpp。我自己在0.5B模型上试过TFLite路线折腾两天最终还是回到了GGUF。2. 模型转换实操从HuggingFace权重到GGUF量化2.1 准备环境与下载Qwen2.5权重转换工作在PC上完成。建议用一台Linux机器需要Python 3.10以上安装transformers和huggingface_hub。这里有个小建议llama.cpp的convert脚本依赖transformers来读取模型结构和tokenizer所以transformers版本不要太老。我踩过一次坑transformers 4.40时代解析Qwen2.5的config有点吃力换成4.45以上就好了。下载模型用huggingface-cli指定本地目录huggingface-cli download Qwen/Qwen2.5-1.8B-Instruct --local-dir ./qwen2.5-1.8b网络不方便的也可以去ModelScope镜像站下载文件结构是一样的。下载完一定要检查目录里有config.json、tokenizer.json、model.safetensors这几个关键文件。我遇到过下载中断少了tokenizer.json的情况转出来之后输出全是乱码排查了半天才发现是文件不完整。建议下完用ls -lh看一眼文件大小跟仓库页面对一下别省这一步。2.2 用llama.cpp的convert脚本转GGUF拿到原始权重之后接下来就交给llama.cpp。先把工具链拉下来git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp python3 -m venv venv source venv/bin/activate pip install -r requirements.txt然后执行转换python3 convert_hf_to_gguf.py ../qwen2.5-1.8b --outfile qwen2.5-1.8b-f16.gguf --outtype f16convert脚本会把模型权重重新排列输出一个f16精度的GGUF文件。为什么要先转成f16而不是直接转量化因为量化命令是独立工具转出f16模型后你可以用同一份f16原文件反复尝试不同量化级别不需要每次重新解析safetensors。1.8B的f16文件大概3.5GB7B的接近15GB临时磁盘空间留够。这里有个关键点convert脚本对Qwen2.5的RoPE、GQA等结构支持得很完善一般不会报错。如果报错多半是transformers版本或者模型文件缺东西。另外建议使用llama.cpp的最新稳定版不要太老否则某些新模型架构识别不了。我试过用一个半年前的分支结果Script直接不认识Qwen2.5的某些字段换回主线就好。2.3 量化等级怎么选Q4_K_M为什么是甜点GGUF文件转出来后用llama-quantize工具量化llama-quantize qwen2.5-1.8b-f16.gguf qwen2.5-1.8b-q4_k_m.gguf Q4_K_Mllama.cpp的量化等级很多。常用的几个量化等级平均每个权重bit数7B文件大小效果对比f16Q4_K_M4.8 bit左右约4.4GB质量损失很小Q5_K_M5.5 bit左右约4.8GB更接近原版Q8_08 bit约7.2GB几乎无损F1616 bit约15GB原始精度在MTK设备上我建议默认用Q4_K_M。它把7B模型压到4.4GB左右速度和精度的平衡最好。日常对话、文本生成这种任务Q4_K_M的输出质量和原版差别很小完全够用。只有当你做RAG、知识抽取这类对数值敏感的任务时才考虑上Q5_K_M甚至Q8_0代价是内存占用和推理速度明显上升。顺便说一句Q4_0这种对称量化虽然文件更小但质量波动较大不建议用于对话模型。Q4_K_M是K-quant系列里带attention等敏感部分保护的选择实测最稳。我自己对比过同一段提示词在Q4_0和Q4_K_M下的输出Q4_0偶尔会冒出不自然的重复词Q4_K_M基本没有。2.4 转出来后先在PC上验证量化完不要急着拷到设备上先在PC上跑一遍确认模型没问题。用llama-server启动个HTTP服务llama-server -m qwen2.5-1.8b-q4_k_m.gguf -c 2048 --port 8080然后用任何HTTP客户端调一下/v1/chat/completions接口发一句“你好请介绍一下你自己”看返回是否正常。这一步能提前过滤掉80%的转换问题。如果PC上输出正常再考虑部署到MTK设备。如果在PC上就已经乱码或者重复问题通常在转换阶段不用白跑设备端。3. 在MTK设备上跑推理编译、参数与性能调优3.1 把llama.cpp弄到MTK设备上三种方式目标设备形态不同方式也不一样。第一种MTK Genio这类Linux开发板。直接SSH上去用apt装好cmake、build-essential走标准编译流程git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease make -j$(nproc)第二种安卓手机/盒子用Termux。装上Termux后pkg install git cmake clang然后在Termux里同样编译。Termux的好处是不需要电脑直接在手机上完成坏处是编译时间较长。1.8B的llama.cpp全量编译在天玑旗舰上大概要十几分钟能接受。第三种安卓系统集成比如做产品APK。那就用NDK交叉编译在PC上编出arm64-v8a的动态库或可执行文件再push到设备。交叉编译命令cmake -B build \ -DCMAKE_TOOLCHAIN_FILE$NDK/build/cmake/android.toolchain.cmake \ -DANDROID_ABIarm64-v8a \ -DANDROID_PLATFORMandroid-28 \ -DCMAKE_BUILD_TYPERelease cmake --build build -j8NDK路径换成你自己的。编出来的可执行文件在build/bin目录下比如llama-server、llama-cliADB push到/data/local/tmp/再chmod x就能跑。注意Android 10以后对执行文件的限制临时目录选/data/local/tmp/最稳。如果设备有SELinux限制可能还要先用setenforce 0或者给文件打上正确的安全上下文标签这个坑在MTK盒子上特别常见。3.2 跑起来的核心参数这些是实打实的经验在MTK设备上启动llama-server我常用的命令./llama-server \ -m qwen2.5-1.8b-q4_k_m.gguf \ -c 2048 \ -t 4 \ --mlock几个参数值得展开。-c是上下文长度。2048够大多数对话场景。如果设得太高比如8192KV Cache会吃掉大量内存8GB内存的设备很容易被系统杀进程。MTK平台的内存管理比PC保守后台进程压力一大进程就会被Kill。建议先从1024或2048开始。-t是线程数。不要无脑设成8或16。MTK是大小核架构小核参与推理反而会拖慢速度。天玑9300这种134结构尽量把线程绑到大核和性能中核上一般设4到5个线程效果最好。没有root的安卓环境没法改CPU亲和性那就试不同-t值观察t/s变化取最高值。有过一次经历默认-t 8跑1.8B只有15 token/s改成-t 4反而到30 token/s就是因为默认把一堆小核拉进来干活反而帮倒忙。--mlock是把模型锁在内存里避免被换页到磁盘。安卓的swap机制有时候会把模型页换出去导致推理速度骤降。内存够用的情况下强烈建议开启。如果开了之后报错或设备明显卡顿说明内存不够改为--no-mmap或降低模型档位。3.3 内存带宽定生死算一算你的速度上限LLM推理的decode阶段每一步都要读取全部模型权重。假设Qwen2.5-7B的Q4_K_M文件是4.4GB每次生成一个token内存系统至少要读出4.4GB数据。如果平台内存带宽是60GB/s那么理论上限就是60/4.4约13.6 token/s。实际还要算上KV Cache读写、计算指令、系统调度开销能跑到8到11 token/s就已经不错了。这个计算逻辑对MTK选型非常重要。你可以拿这个公式估算任何平台的LLM能力token/s上限约等于内存带宽GB/s除以模型文件大小GB。LPDDR5和LPDDR4X的带宽差了一倍多所以同样一个7B模型在老设备上可能只有3到4 token/s在新旗舰上能到10以上。如果实测速度远低于这个估算值优先排查是不是线程设置不对、内存换了页、或者系统降频了。我这里给一组实测参考数据是在天玑9300级别平台、Q4_K_M量化、mmap开启、8GB以上内存的条件下的成绩模型上下文长度线程数实测decode速度Qwen2.5-0.5B-Instruct2048440-60 token/sQwen2.5-1.8B-Instruct2048425-35 token/sQwen2.5-7B-Instruct102447-10 token/s注意这是decode速度prefill会更快一些。不同机型、不同固件、环境温度不同数据会有浮动。但如果你的成绩比这个范围低一半以上多半是配置有问题继续往下看排查章节。3.4 还能不能更快Vulkan GPU和NPU的现状MTK的Mali/Immortalis GPU支持Vulkanllama.cpp的Vulkan后端可以把部分矩阵计算放到GPU上。这条路值得试但我的经验是收益不稳定。GPU Offload会占用显存而且llama.cpp的Vulkan实现和MTK驱动之间的兼容性在不同机型上天差地别。有些机型能提速10%-20%有些反而更慢。如果你想试编译时加-DGGML_VULKANON运行加-ngl 99。注意观察GPU内存占用如果设备明显发烫或者系统杀掉进程就乖乖用CPU。至于NPUMTK NeuroPilot要走TFLite或ONNX模型目前对Qwen2.5这类模型的支持还很原始我试过把Qwen2.5-0.5B转成TFLite喂给NeuroPilot大量算子在APU上跑不了回退到CPU后整体速度反而不如llama.cpp。所以现阶段MTK平台上跑Qwen2.5CPU加GGUF就是最现实的高效组合。4. 常见问题与排查技巧实录4.1 转换阶段问题大多出在环境和文件完整性我转换Qwen2.5时遇到的第一个坑是transformers版本太旧导致convert脚本解析tokenizer失败。具体报错记不太清了大概是指向tokenizer_config.json里的字段不认识。升级transformers就解决了。所以装依赖时建议直接用pip install -U transformers不要按旧文档锁版本。还有一个高频问题模型没下全。HuggingFace仓库里safetensors可能分成多个分片有些下载工具会漏。检查一下模型目录里的文件数量跟仓库页面对一下。漏文件的情况下convert脚本不一定立刻报错可能在某个权重加载位置才挂。我建议下载完顺手校验一下别省这一步。4.2 设备端运行慢、卡、被杀的常见原因在设备上最典型的三个表现一个是慢得离谱。如果7B Q4模型跑出来只有2 token/s先看线程数再看是否发生了内存换页。不开--mlock的情况下安卓后台活动可能把模型页挤出去导致每次推理都要从存储重新读权重速度能掉一个数量级。开启--mlock后立刻恢复我遇到过不止一次。第二个是进程被系统杀掉。MTK设备内存不够时系统会优先杀掉后台大内存进程。这通常发生在4K甚至8K上下文场景。解决思路降低-c或者换更小的模型。还有一个冷门技巧把Termux或你的App在系统设置里设为“不受电池优化限制”被杀概率会低一些。第三个是生成质量异常。Qwen2.5在端侧跑如果出现重复、混乱、记不住上下文不少人是怀疑量化精度但更常见的原因是采样参数和上下文设置。temperature不要太高建议0.6到0.7repeat_penalty保持1.1以上上下文长度不够导致“失忆”也容易表现为答非所问。先在2048上下文、默认采样下测一轮再逐步调整参数。4.3 问题速查表直接抄作业现象常见原因解决方式convert脚本报错transformer版本过旧/文件缺失升级transformers检查模型文件完整性设备端mmap失败存储空间不足或权限限制检查/data/local/tmp剩余空间改用--no-mmap推理速度极慢线程过多/内存换页/降频调-t开--mlock清理后台应用生成乱码量化文件损坏/tokenizer缺失重新转换确认下载完整上下文记不住-c太小/KV Cache截断提高上下文长度但注意内存占用进程被杀内存不足降模型档位降上下文关后台温度过高触发降频长时间高负载加散热措施降低线程数这张表基本覆盖了我遇到的90%问题。排障思路建议大家按“存储-内存-线程-采样参数”的顺序查不要一上来就怀疑量化等级那样大概率白折腾。第一次在天玑设备上看到Qwen2.5-1.8B以30 token/s的速度流畅对话时我的第一反应不是兴奋而是松了一口气。这条路看着长实际上关键就三步转GGUF、量化、用llama.cpp跑。真正花时间的反而是调线程数和解决内存被杀的问题。如果让我给新手一个建议先别碰7B。把Qwen2.5-1.8B完整跑通理解每条命令和每个参数对结果的影响再去挑战7B。7B不是1.8B的简单放大内存、散热、调度都要重新权衡体感完全不一样。另外llama.cpp版本请尽量保持更新这个项目迭代太快新版对Qwen2.5的支持和优化经常带来实际收益旧版本跑不动的问题换新版可能直接就解决了。最后分享一个自己常用的检查技巧跑之前用free -m看一眼可用内存跑的时候开个top观察CPU频率和内存占用。确认模型指纹、量化等级、参数设置都心里有数再开始调参。这些不起眼的动作往往就是快速定位问题的钥匙。大家遇到奇怪问题也希望把平台型号、llama.cpp版本、参数贴出来一起讨论排障能快很多。