ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

FTLR部署环境全流程:NPU上微调qwen2.5-7b并推理

FTLR部署环境全流程:NPU上微调qwen2.5-7b并推理 1. 为什么 FTLR 部署环境总在第一步劝退人FTLR 这个名字很多刚接触大模型部署的朋友可能没听过。我这边项目里一直用它做小规模 Transformer 模型的推理运行时全称我们内部叫 Fast Transformer Lightweight Runtime简单说就是一套面向生成式模型的轻量级推理框架特别适合在非 N 卡环境下跑大模型。配合 NPU 电脑部署深度学习环境把 qwen2.5-7b 这类开源模型拉下来微调成行业模型再走完环境配置、模型微调、模型部署、效果展示这一整条链路FTLR 这套部署环境是我实测下来最省心的一套组合。但我得实话实说部署环境这关卡掉的初学者比模型本身卡掉的还多。很多教程默认你手里有一张 N 卡装好 CUDA 就完事。可一旦换成国产 NPU 设备驱动、固件、算子库、编译器、Python 版本、依赖包版本任何一环对不上轻则报错重则直接编译失败连模型都加载不出来。我自己刚开始搭 FTLR 环境时光一个“算子不支持”的报错就折腾了两天后来才发现是 CANN 版本和框架版本不匹配。这篇文章我想把我踩过的坑、试过的版本组合、完整的实操流程全部写出来从零开始把 FTLR 部署环境这件事讲透。内容包括基础环境怎么搭、模型格式怎么转、qwen2.5-7b 怎么做行业微调、微调后怎么接进 FTLR以及最后怎么验证效果和排查问题。适合这几类人看手上拿着 NPU 设备或者普通服务器想做推理部署的算法工程师准备把开源大模型落地到具体行业的开发者还有刚入门深度学习部署、想找一份完整可复现教程的学生。2. 部署前的基础选型驱动、系统和依赖这样定2.1 硬件与驱动层先对齐部署环境的第一个大坑是很多人拿到设备就开始装 Python 包完全忽略底层驱动。NPU 设备和 GPU 不一样GPU 跑通了 CUDA 之后上层框架基本都能自适应NPU 对驱动、固件、算子库的版本要求非常严格而且驱动和算子库经常是一一绑定的。以昇腾 NPU 为例你要确认三样东西固件版本、驱动版本、CANN 版本。这三者之间有一个配套矩阵版本对不上后续安装 FTLR 依赖的 acl 运行时就会报“找不到 so 文件”之类的错。我在搭环境时参考的是昇腾官方文档里的配套表使用昇腾 910B 系列设备和 CANN 8.0 版本。这里建议你先通过命令确认当前状态。npu-smi info如果执行之后能列出 NPU 的型号、显存、驱动版本说明基础驱动没问题。如果提示命令找不到先去检查驱动是否安装成功或者确认 /usr/local/Ascend 目录下是否存在。这个目录是 CANN 的默认安装位置里面包括 driver、fwk、tools 等子目录后续很多环境变量都要指向这里。注意千万别跳过这步直接装框架。就算 PyTorch 装得再全底层驱动不对模型推理时一样会崩而且报错信息基本都看不懂。2.2 Python 环境隔离别把系统环境当试验田我第一次搭深度学习环境时图省事直接往系统 Python 里装依赖结果一个小版本升级把别人的项目环境搞坏了整个服务器上的服务全部歇菜。后来我学乖了一律用 conda 建独立环境。FTLR 这套部署环境对依赖版本的敏感度很高尤其是 numpy、protobuf、torch 这几个包版本差一位都可能导致算子编译失败或者推理结果不对。我的推荐组合是 Ubuntu 22.04 系统、conda 23.x、Python 3.10。为什么不选最新的 3.12因为 PyTorch 2.x 在 3.10 上的兼容性最好CANN 自带的一些工具和算子编译脚本对 3.10 的支持也最完整。Python 版本不是越新越好而是越“配套”越好。这样选的原因是如果你先装好了 PyTorch 再装 CANN 的 torch_npu 插件两者都需要基于 Python 3.10 编译好的 wheel 包选 3.10 基本不会踩到编译链路的坑。依赖隔离还有一层好处FTLR 本身要编译一些自定义算子编译过程中会生成临时文件、修改环境变量这些操作放在虚拟环境里就算搞砸了删掉环境重来就是不会污染系统。2.3 静态图导出 vs 动态图推理我为什么选前者在用 FTLR 之前我用 PyTorch 原生做在线推理动态图灵活是灵活但性能和内存占用都很让人头疼。每个算子都单独调度中间张量频繁写回显存模型一大就开始吃力。FTLR 的思路就不一样它先把模型转成静态图做算子融合、内存复用再跑推理。我在部署环境里坚持用静态图不是没原因的。你可以把动态图想象成现场点菜每次运行都要重新决定下一步做什么静态图更像按固定菜单出餐后厨提前把流程都排好了出餐速度自然快得多。对上线服务来说推理路径是固定的完全没必要保留动态图的灵活性。FTLR 在模型转换阶段做一次图优化运行时就只是纯执行延迟和显存占用都更可控。下面是我实际验证过比较稳的一套版本组合整理成表供你参考。组件推荐版本备注操作系统Ubuntu 22.04 LTS内核 5.15 及以上NPU 驱动适配昇腾 910B 的最新稳定版以 npu-smi info 为准CANN8.0与驱动配套包含工具链conda23.x建议安装 minicondaPython3.10FTLR 与 torch_npu 兼容性最佳torch2.1.0对 NPU 适配成熟torch_npu与 CANN 8.0 配套安装时看版本对应关系numpy1.26.4锁定版本避免自动升级3. 一步步搭建 FTLR 部署环境新手可照抄3.1 检查固件与 NPU 状态环境搭建的第一步不是安装而是检查设备状态。我这里说的检查不只是看一眼驱动版本还要确认 NPU 的算力是否正常。实操方式很简单看 npu-smi info 的输出重点关注两处一是驱动版本是否正确二是显存占用是否被其他进程占满。如果显存被占满后面加载模型时会出现逐字等待的卡顿现象报错还不明显。如果 npu-smi 命令能跑但后续安装 CANN 时提示固件版本低你需要单独下载配套固件升级包。升级固件需要 root 权限执行包里提供的升级脚本升级完必须重启机器才生效。这一步不要跳过我试过不重启直接装 CANN装完一跑模型就段错误排查到最后才发现是新固件没加载。检查完设备后顺手确认一下系统里有没有残留的旧版本 CANN。如果有建议先卸载干净再装新版本。命令如下# 查看已安装的 CANN 包 ls /usr/local/Ascend # 如果存在旧版本卸载后重装 # 具体卸载命令根据安装方式区分deb 安装用 dpkgrpm 用 rpm -e3.2 创建虚拟环境并安装核心依赖设备状态没问题后开始创建 Python 虚拟环境。我用 miniconda 管理环境命令很简单。conda create -n ftlr python3.10 -y conda activate ftlr环境激活后先把基础依赖装上。这里有一个很重要的点先装 PyTorch 再装 torch_npu顺序不要反。如果先装 torch_npu它会根据当前已有的 torch 版本去匹配一旦 torch 版本不满足要求它会自动帮你升级或者降级 torch很容易引发连锁反应。pip install torch2.1.0 # 根据 CANN 版本安装对应的 torch_npu pip install torch-npu2.1.0.post8 # 基础工具包 pip install numpy1.26.4 transformers4.44.2 accelerate0.32.0 onnx1.16.0装完之后强烈建议做一次导入验证确认 NPU 设备能被 PyTorch 感知到。import torch import torch_npu print(torch.npu.is_available()) print(torch.npu.device_count())如果输出 False 或者设备数为 0不要急着往下走先排查环境变量和 CANN 安装路径。3.3 FTLR 推理运行时的安装与编译FTLR 的安装方式和我们内部管理一致是从源码仓库拉下来编译。编译前需要先把 CANN 的环境变量注入当前 shell。CANN 自带的 set_env.sh 脚本已经帮我们整理好了不用自己去配 LD_LIBRARY_PATH。source /usr/local/Ascend/ascend-toolkit/set_env.sh # 确认环境变量生效 echo $ASCEND_HOME_PATH环境变量正常后进入 FTLR 源码目录执行编译和安装。git clone 你的 FTLR 仓库地址 ftlr cd ftlr mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease -DWITH_NPUON make -j$(nproc) make install编译过程中比较常见的报错是缺依赖库。如果提示找不到 acl/error_codes.h说明 CANN 的 include 路径没被 cmake 找到检查 set_env.sh 是否已经 source。如果提示找不到 protobuf先确认 protobuf 版本是 3.20.x 还是 4.xFTLR 对这两者的处理方式不一样通常 3.20.x 更稳。3.4 模型格式转换与校验环境搭建好后另一个关键步骤是把 HuggingFace 格式的模型转成 FTLR 能吃的静态图格式。这里我用 ONNX 作为中间格式先把 PyTorch 模型导出为 ONNX再通过 FTLR 提供的转换工具优化成内部格式。导出时的几个关键参数直接决定后面能不能跑通import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_dir ./qwen2.5-7b model AutoModelForCausalLM.from_pretrained( model_dir, torch_dtypetorch.float16, low_cpu_mem_usageTrue ).eval() # 构造一个假输入只做图结构导出 dummy_input torch.randint(0, 1000, (1, 16), dtypetorch.int64) torch.onnx.export( model, dummy_input, ./qwen2.5-7b.onnx, opset_version17, input_names[input_ids], output_names[logits], dynamic_axes{ input_ids: {0: batch, 1: seq_len}, logits: {0: batch, 1: seq_len} } )这里我特意把 batch 和 seq_len 都设成动态轴因为推理服务经常要并发请求如果固定 batch1后面做动态批处理就没戏了。opset_version 选 17 是 FTLR 验证过的版本太新可能导致算子兼容问题太旧又缺少必要的量化算子支持。导出完成后用 FTLR 自带的工具做格式转换和精度校验。ftlr-convert --input qwen2.5-7b.onnx --output qwen2.5-7b.ftlr ftlr-inspect qwen2.5-7b.ftlrftlr-inspect 会打印模型的输入输出形状、算子数量、内存占用预估。如果在转换阶段报“未支持的算子”一般有两个处理思路一是把模型中对应模块替换成等价实现二是在 FTLR 配置里加上对该算子的实现映射。4. 把 qwen2.5-7b 微调成行业模型再接入 FTLR4.1 行业数据准备从零构造一份可用的微调数据集部署环境只是一个容器真正让行业用户觉得“好用”的是模型本身能回答行业问题。拿 qwen2.5-7b 来说通用能力已经不错但问它具体的业务问题时回答经常泛泛而谈。这时候就需要微调。微调的第一步是数据。我建议直接用 sharegpt 格式组织数据Transformer 生态里对这种格式支持最完善不管是做全参微调还是 LoRA都不用额外改代码。数据格式长这样[ { conversations: [ {role: user, content: 请解释一下设备巡检中的故障代码 E401 是什么意思}, {role: assistant, content: E401 表示电机过流保护触发常见原因是负载过大或电源相序错误建议先排查机械卡阻再检查三相电流是否平衡。} ] } ]数据量上如果是特定行业的知识问答5000 组对话起步比较靠谱。我第一次做只用 1000 组效果有提升但不明显加到 5000 组之后回答的稳定性明显改善。另外要提醒一句数据质量比数量重要得多宁可少而精也不要拿一堆错误答案去训练。4.2 LoRA 微调的参数与训练配置数据准备好后我直接用 HuggingFace 的 PEFT 库做 LoRA 微调没有做全参数微调。原因很简单硬件资源有限全参微调 7B 模型对显存的要求太苛刻LoRA 只训练一小部分参数效果上完全够用。我的训练脚本关键配置如下from peft import LoraConfig, get_peft_model from transformers import AutoModelForCausalLM, TrainingArguments lora_config LoraConfig( r8, lora_alpha16, lora_dropout0.05, target_modules[q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj], task_typeCAUSAL_LM ) training_args TrainingArguments( output_dir./checkpoints, per_device_train_batch_size1, gradient_accumulation_steps8, learning_rate2e-4, num_train_epochs2, logging_steps50, save_steps500, fp16True, optimadamw_torch )几个关键的调参心得学习率设 2e-4比全参微调的 1e-5 高一个数量级因为 LoRA 本身只更新少量参数需要更大的步长才能学到东西。梯度累积设成 8相当于用 8 个 batch 的平均梯度来更新一次参数既能模拟较大 batch 的效果又不会把显存撑爆。fp16 在 NPU 上开启后训练速度提升明显但要注意如果 loss 出现剧烈波动优先检查数据里有没有异常长文本。4.3 模型合并、量化与导出训练完成后LoRA adapter 权重是单独保存的部署前必须合并回原模型。合并这一步在 PEFT 里有现成接口。from peft import PeftModel base_model AutoModelForCausalLM.from_pretrained( ./qwen2.5-7b, torch_dtypetorch.float16 ) merged_model PeftModel.from_pretrained(base_model, ./checkpoints/final) merged_model merged_model.merge_and_unload() merged_model.save_pretrained(./qwen2.5-7b-industry)合并完的模型体积比原模型大一点是正常现象。如果后续对显存有要求可以在导出 ONNX 时顺手量化到 INT8。FTLR 对 INT8 模型的支持比较完善量化后的模型体积小一半推理速度也会有提升。不过要提醒一个坑合并后必须重新做 ONNX 导出。直接拿训练前的 ONNX 加 adapter 推理是行不通的因为 ONNX 是静态图adapter 的权重不在图里。4.4 把微调结果接入 FTLR 服务模型完成合并、转换之后最后一步就是启动服务。FTLR 启动一个推理服务的过程很简单核心是加载模型和分词器然后建立 HTTP 接口。ftlr-server \ --model ./qwen2.5-7b-industry.ftlr \ --tokenizer ./qwen2.5-7b-industry \ --port 8080 \ --max-batch 4 \ --max-seq-len 2048参数含义model 指定 FTLR 格式模型tokenizer 指向 HuggingFace 格式的分词器目录max-batch 控制并发 batch 大小max-seq-len 限制最大序列长度。并发场景下 max-batch 调大有助于提升吞吐但也会增加单次请求的排队时间具体数值要按业务响应时间要求做权衡。5. 部署后的效果验证与性能调优实操5.1 正确性校验先保证对了再谈快部署环境搭完模型能跑起来第一件事不是看响应速度快不快而是确认输出对不对。我每次部署完新模型都会做一轮正确性校验拿同一段 prompt分别在原始 PyTorch 模型和 FTLR 模型上跑一遍对比输出结果的相似度。校验脚本的核心思路很简单import numpy as np # 假设分别拿到两个模型的 logits logits_ref np.load(./logits_pytorch.npy) # 原始模型输出 logits_ftlr np.load(./logits_ftlr.npy) # FTLR 模型输出 # 计算余弦相似度 cosine_sim np.dot(logits_ref.flatten(), logits_ftlr.flatten()) / ( np.linalg.norm(logits_ref) * np.linalg.norm(logits_ftlr) ) print(cosine_sim)余弦相似度在 0.99 以上基本可以确认模型转换没有问题。如果相似度偏低大概率是 ONNX 导出时某些算子的实现细节有差异建议重新导出并对比每一层输出定位是哪个环节出了问题。还有一种情况是 logits 能对上但生成文本乱码。这大概率是分词器路径不对加载的 tokenizer 和模型训练时用的不是同一个词表对不上。5.2 延迟、吞吐与 QPS 的实测方法正确性验证通过后再进入性能测试。性能测试我用的是自己写的一个小工具向服务并发发送大量请求统计响应时间和吞吐量。# 模拟 100 个并发请求每个请求 128 token ftlr-bench \ --url http://localhost:8080 \ --concurrency 100 \ --requests 500 \ --prompt-len 128 \ --max-new-tokens 64测试完重点看三个指标首 token 延迟、平均延迟、QPS。第一次跑出来的数据如果不理想不要急着调代码先看 NPU 利用率。npu-smi info如果 NPU 利用率一直很高说明计算瓶颈考虑优化模型本身如果利用率只有 30% 以下说明瓶颈在数据加载或者请求调度上优先处理预处理逻辑。5.3 性能优化三板斧对 FTLR 部署环境来说性能优化有成熟的三板斧动态批处理、静态内存复用、低精度量化。动态批处理是在服务端把多路请求攒起来合并成一个 batch 送给 NPU 计算。batch 越大单 token 的计算成本越低。前提是模型导出时设置了动态 batch 维度。静态内存复用是 FTLR 内部实现的事对使用者来说只需要在启动命令里加内存池配置参数即可。低精度量化就是前面说的 INT8 量化。我在实际项目中做过对比FP16 基础上再量化到 INT8首 token 延迟降低约 30%显存占用降低约 40%效果明显。注意量化的收益和损失需要具体业务层面权衡。如果业务场景对输出质量极其敏感建议先在测试集上做一轮量化前后对比再决定是否上线。6. FTLR 部署环境常见问题排查与避坑记录6.1 环境依赖类问题环境依赖问题是最容易遇到也最容易解决的前提是你知道真正的坑在哪。最典型的现象是装完 FTLR 后导入时报错“找不到 libascendcl.so”或者提示 protobuf 版本冲突。这类问题的排查顺序是先确认 CANN 环境变量是否生效再确认 Python 包里是否有多个 protobuf 版本。我遇到过好多次conda 环境里自带了一个 protobufpip 又装了一个不同版本两个版本路径同时出现在 sys.path 里导出 ONNX 的时候行为非常随机。解决办法是锁定版本并把当前环境的 protobuf 统一到 3.20.x。pip install protobuf3.20.36.2 驱动与算子类问题驱动和算子类问题比较隐蔽。比如编译 FTLR 时报“unsupported operator”表面看是算子不支持深层原因往往是 ONNX 导出的某些节点冗余且不在 FTLR 的算子清单里。这时候不要硬刚先查看 ONNX 计算图定位到不支持的节点。python -m onnxruntime.tools.print_model ./qwen2.5-7b.onnx定位到节点类型后去 FTLR 的算子库目录里确认是否已实现。没实现的话优先考虑修改导出策略比如把某些自定义算子改写成标准算子组合。还有一类问题出现在推理阶段报错“ACL_ERROR_RT_PARAM_INVALID”或者“run time error”多半是 input 的形状或者数据类型和静态图不一致。FP16 模型要用 FP16 的输入张量如果外部传来的是 FP32 数据必须显式转换。6.3 推理结果异常类问题部署环境没问题、模型也加载了但生成文本不合理这类问题最考验排查功底。常见的有三种表现完全乱码、反复复读、答案明显偏离业务知识。完全乱码先查词表对齐确认 tokenizer 和模型是否来自同一 checkpoint。反复复读大概率是采样参数问题温度太低或重复惩罚没设置好。答案偏离业务知识就要回看微调数据大概率是行业语料占了正例模型没充分学会。我在项目里总结了一套针对这类问题的快速排查表分享出来供参考。现象可能原因排查方向输出乱码词表不对齐检查 tokenizer 与模型目录反复复读温度过低、无重复惩罚调高 temperature 或加 repetition_penalty行业答案不准微调数据不足或噪声大增加数据量、清洗错漏样本首 token 极慢未开启动态批处理检查 max-batch 配置显存溢出max-seq-len 过大调低序列长度或量化模型6.4 一些不写进文档的部署小技巧最后分享几个我实际干活时摸索出来的小技巧这些在官方文档里基本找不到但对实际部署非常管用。第一个技巧是环境搭建阶段把整个依赖环境打包成一个镜像。我用 conda-pack 把虚拟环境直接离线打包到了新机器上三分钟就能还原不用重新下载一堆包。conda pack -n ftlr -o ftlr_env.tar.gz新机器上解压后改一下路径前缀就能直接 activate对多台 NPU 设备的批量部署特别节省时间。第二个技巧是防呆检查。每次启动 FTLR 服务之前先跑一个最小的单轮推理用例比如输入“你好”看服务是否正常返回。别嫌这一步啰嗦部署环境这种事最怕的就是服务启动成功但推理能力异常等到线上流量打进来才发现问题。第三个技巧是把 npu-smi 的显存监控和日志匹配起来。部署完服务后开一个定时任务把 NPU 显存占用和服务响应日志同步记录。当服务出现响应变慢时回看显存曲线能非常直观地判断是显存不足导致排队还是算子计算变慢。我现在的流程基本固定成环境隔离、版本锁定、正确性优先、性能后调。这套方法在多个项目里反复验证过环境问题越来越少模型迭代速度明显加快。做部署这件事真正值钱的不是那条跑通的命令而是踩过的坑和被验证过的那套流程。希望这篇 FTLR 部署环境全流程记录能让你少走点弯路。
返回列表