Qwen1.5模型导出ONNX与TFLite的完整实战流程
2026/8/27 5:50:33 网站建设 项目流程

简介:大语言模型部署的核心在于将训练好的模型高效迁移到目标环境,而PyTorch权重在线上场景常面临依赖环境复杂、推理性能受限、跨平台移植困难等三大问题。针对这些挑战,ONNX Runtime通过算子优化显著提升CPU/GPU推理速度,TFLite则凭借轻量化和移动生态支持成为端侧部署的主流方案。本文从Transformer基础架构出发,系统讲解Qwen1.5模型从PyTorch权重分别导出为ONNX和TFLite的完整步骤,涵盖动态轴配置、量化策略、序列长度固定等关键技术细节,并对比两种格式在服务器与移动端的适用场景。无论你是准备将大模型部署到云端服务,还是尝试在边缘设备上运行轻量级模型,这套流程都能提供可直接复用的工程参考。 训练出模型只是项目的前半程,后半程是把它装进目标环境里跑起来。这句话在我准备部署Qwen1.5大语言模型时,体会特别深。很多人拿到Qwen1.5的PyTorch权重后,习惯直接调AutoModel.from_pretrained,本地验证效果自然很快,可真要推到服务器、手机或者边缘设备上,环境依赖、推理性能、跨平台移植这些问题会像连环套一样涌出来。

这次做的小型实战项目,核心目标只有一个:把Qwen1.5大语言模型完整导出为ONNX和TFLite两种格式,并整合出可复用的源码流程教程。ONNX用来解决服务器端和通用推理框架的落地问题,TFLite主要面向移动端和边缘设备。文章会按实际操作的顺序展开,环境搭建、模型导出、量化处理、推理验证和排错过程都会拆开讲透。如果你之后也要做Transformer结构的大模型导出,这套流程的参考价值同样成立。

1. 为什么要把Qwen1.5导出成ONNX和TFLite

导出这一步,看起来只是换了一种模型文件格式,本质上解决的却是三个非常实际的部署问题:依赖环境、推理性能、跨平台能力。

1.1 先认清PyTorch权重在线上环境的三道坎

第一道坎是环境依赖。PyTorch权重本身不能脱离框架运行,想用Transformers库推理,机器上必须完整安装PyTorch、tokenizers、transformers等一整套Python依赖。这些库对版本的要求非常严格,Python 3.8、torch 2.0、transformers 4.39,一步版本不对就可能跑不起来。到生产环境换一台机器,往往就是半天环境问题。

第二道坎是推理性能。Transformers库的代码为了兼顾开发灵活性,在运行时会有很多动态分支和Python层面的调度开销。单次推理还好,一旦做成并发服务,Python的GIL会直接限制吞吐,延迟曲线也会变得很难看。把模型导出成ONNX后,可以用ONNX Runtime的高性能算子执行推理,CPU上通常能拿到立竿见影的速度提升,GPU上也可以无缝切到CUDA或者TensorRT。

第三道坎是跨平台能力。手机端、嵌入式设备上根本跑不了完整的Python环境,也不可能装一个PyTorch进去。像安卓设备要用NNAPI,iOS要用Core ML,这些场景下模型必须是轻量的、能被对应运行时托管的格式。TFLite就是目前移动端支持最广泛、生态最成熟的一类格式。

1.2 ONNX与TFLite的定位差异

把这一步想清楚,后面就不会在格式选择上来回纠结。这两者不是竞争关系,而是面向不同目标场景的部署格式。

对比项ONNXTFLite
主要目标服务器、PC、云端推理移动端、嵌入式、边缘设备
推理运行时ONNX Runtime、TensorRT、OpenVINOLiteRT(TFLite)、NNAPI、Core ML
量化支持动态/静态INT8,FP16等动态范围量化、INT8全整型、FP16
体积比原始PyTorch略小更紧凑,适合包体受限场景
算子覆盖覆盖面更广,LLM相关算子较完整算子相对受限,导出时需适配

从Transformer架构的角度看,Qwen1.5由Embedding、RMS Norm、Self-Attention、MLP、LayerNorm、线性层组成。ONNX对这些基础算子的覆盖已经相当齐全,所以把Qwen1.5导出成ONNX整体是顺的。TFLite的算子集相对有限,需要做一些适配,这也是为什么我在TFLite部分会强调固定序列长度和量化策略,这两个都是移动端模型落地的关键。

2. 环境与模型选型,这步定了后面才省事

2.1 硬件与依赖

先给一份我自己验证过的硬件配置参考。导出过程本身对算力要求并不极端,CPU推理只在验证阶段使用。

推荐配置:

  • CPU:Intel i5/Ryzen 5以上,负责ONNX导出与验证
  • 内存:16GB以上,导出7B以上模型建议32GB
  • 硬盘:预留30GB左右空间,模型文件加转换缓存
  • GPU(可选):NVIDIA CUDA显卡,用于FP16导出和TensorRT加速

软件依赖:

pip install torch torchvision transformers optimum onnx onnxruntime pip install ai-edge-torch # TFLite导出用 pip install onnx2tf # ONNX转TFLite备选

Qwen1.5系列基于Transformers库的AutoModelForCausalLM接口,所以底层依赖与标准HuggingFace生态一致。optimum是用来做ONNX导出的关键库,ai-edge-torch是Google维护的PyTorch到TFLite直转工具,后面会用到。

2.2 选哪个尺寸的Qwen1.5

Qwen1.5系列从0.5B到72B都有。导出ONNX这个环节,只要内存足够,多大都可以导出,但TFLite就要特别考虑算力和存储。

我的建议很明确:

  • ONNX导出Demo:选Qwen1.5-1.8B-Chat,体积适中,CPU跑得动,效果也能体现大模型能力。
  • TFLite导出Demo:选Qwen1.5-0.5B-Chat,量化后500MB左右,至少在移动端有安装可行性。
  • 只做流程验证:选0.5B更合适,导出速度快,迭代成本低。
  • 做线上服务:视显存选7B或14B,再配合Runtime的并行能力上生产。

记住一个原则:TFLite导出不等于所有参数都能塞进手机,模型尺寸和最终设备算力必须匹配,否则导出来也只是一个没法用的静态文件。

3. 导出ONNX的完整流程:两条路线都亲自走了一遍

3.1 用Optimum快速导出:一条命令干完核心事

Optimum库封装了HuggingFace模型与ONNX Runtime之间的转换,对于因果语言模型(CausalLM)的支持做得相当成熟,包括past_key_values的处理和LM头等复杂结构。

先加载模型并导出:

from optimum.onnxruntime import ORTModelForCausalLM from transformers import AutoTokenizer model_id = "Qwen/Qwen1.5-1.8B-Chat" export_dir = "./onnx/qwen1.5-1.8b-chat" model = ORTModelForCausalLM.from_pretrained(model_id, export=True) tokenizer = AutoTokenizer.from_pretrained(model_id) model.save_pretrained(export_dir) tokenizer.save_pretrained(export_dir)

这段代码走完,export_dir下会生成model.onnxmodel.onnx_data(大权重备份)和config.json。如果权重超过2GB,会拆分成model.onnx_data,这是ONNX的标准做法,后面推理时ONNX Runtime会自动加载,不用手动处理。

这里重点说一下ORTModelForCausalLM和普通AutoModel的区别:后者返回的是PyTorch模型,前者是ONNX Runtime封装后的模型,导出后直接就能用generate发起推理,不用自己写采样循环。这也是实际项目里更推荐的方式。

验证一次完整生成:

from optimum.onnxruntime import ORTModelForCausalLM from transformers import AutoTokenizer model_dir = "./onnx/qwen1.5-1.8b-chat" tokenizer = AutoTokenizer.from_pretrained(model_dir) model = ORTModelForCausalLM.from_pretrained(model_dir) prompt = "请用一句话介绍你自己" inputs = tokenizer(prompt, return_tensors="pt") outputs = model.generate(**inputs, max_new_tokens=64, do_sample=True) print(tokenizer.decode(outputs[0], skip_special_tokens=True))

用Optimum导出有个隐形好处:它会同时导出带past_key_values输入的版本,生成时能走KV Cache路径,对话场景下推理效率比每步全量重算高一个量级。这个细节在纯手工导出时容易被忽略。

3.2 手动导出:torch.onnx.export与dynamic_axes配置

有些场景中Optimum不支持某个模型结构,或者你想精确控制输入输出的名字和形状,就必须手动走了。手动导出的套路其实也很固定。

import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_id = "Qwen/Qwen1.5-1.8B-Chat" model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.float32).eval() tokenizer = AutoTokenizer.from_pretrained(model_id) dummy_input = tokenizer("你好,请做一下自我介绍", return_tensors="pt") with torch.no_grad(): torch.onnx.export( model, (dummy_input["input_ids"], dummy_input["attention_mask"]), "qwen1.5-1.8b-chat.onnx", opset_version=17, input_names=["input_ids", "attention_mask"], output_names=["logits"], dynamic_axes={ "input_ids": {0: "batch", 1: "seq_len"}, "attention_mask": {0: "batch", 1: "seq_len"}, "logits": {0: "batch", 1: "seq_len"}, }, do_constant_folding=True, )

这个例子特别注意三点。

一,torch_dtype必须设为float32,不能直接用bfloat16继续导出。ONNX Runtime对多种精度的支持范围不如PyTorch广,默认FP32最稳妥。导出后再在推理时通过Runtime的enable_auto_mixed_precision或指定精度来提速,比在导出阶段锁死精度灵活。

二,dynamic_axes配置的是batch维度和序列长度维度。模型允许输入

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询