You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch大语言模型转ONNX Runtime可行性问询:性能与文件优化

PyTorch大语言模型转ONNX Runtime的实践经验与问题解答

成功案例与实际效果

很多开发者已经完成了Mistral、Llama、Falcon等主流PyTorch LLM到ONNX的转换,实际收益明确:

  • 文件大小缩减:通过ONNX的INT8/FP16量化支持,模型体积可比原PyTorch模型缩小30%-70%;即使不做量化,纯ONNX格式也会因去除框架冗余信息,比PyTorch的.bin权重文件略小。
  • 性能提升:ONNX Runtime的图优化、硬件加速(CUDA/TensorRT/CPU AVX2等)能带来20%-50%的推理速度提升,批量推理、长文本生成场景下优化效果更显著。

转换指导与实践经验

  1. 模型加载优化
    加载大模型时指定torch_dtype=torch.float16,大幅降低内存占用,避免导出过程中OOM:

    model = AutoModelForCausalLM.from_pretrained(
        model_name,
        use_auth_token=True,
        device_map="auto",
        torch_dtype=torch.float16
    )
    
  2. 导出参数配置

    • 优先使用opset 16及以上版本:LLM用到的新型算子(如滑动窗口注意力、分组查询注意力)在高opset版本中支持更完善,减少导出失败概率。
    • 完整定义动态轴:除input_ids外,必须添加attention_mask的动态维度,否则推理时无法适配不同长度的输入:
      dynamic_axes={
          "input_ids": {0: "batch_size", 1: "sequence_length"},
          "attention_mask": {0: "batch_size", 1: "sequence_length"}
      }
      
    • 启用常量折叠:添加do_constant_folding=True,让ONNX Runtime提前计算固定常量,提升推理效率。
  3. 量化与进一步优化
    导出后可用ONNX Runtime的量化工具做INT8/FP8量化,兼顾体积与精度:

    from onnxruntime.quantization import quantize_dynamic, QuantType
    quantized_model_path = "/kaggle/working/mistral_7b_quantized.onnx"
    quantize_dynamic(
        onnx_model_path,
        quantized_model_path,
        weight_type=QuantType.QUInt8
    )
    

    也可以用Hugging Face Optimum库的ORTOptimizer,一键完成导出、优化、量化流程,更适配LLM场景。

  4. 转换正确性验证
    导出后必须验证输出一致性,避免算子不兼容导致的逻辑错误:

    import onnxruntime as ort
    
    # 加载ONNX模型
    sess = ort.InferenceSession(onnx_model_path, providers=["CUDAExecutionProvider"])
    # 准备测试输入
    test_input = tokenizer("Hello, world!", return_tensors="pt")
    input_ids_np = test_input["input_ids"].cpu().numpy()
    attention_mask_np = test_input["attention_mask"].cpu().numpy()
    
    # ONNX推理
    ort_outputs = sess.run(["output"], {
        "input_ids": input_ids_np,
        "attention_mask": attention_mask_np
    })
    
    # PyTorch推理对比
    with torch.no_grad():
        pt_outputs = model(test_input["input_ids"].to(device), test_input["attention_mask"].to(device)).logits.cpu().numpy()
    
    # 检查误差(通常小于1e-3即为正常)
    print("输出平均误差:", abs(ort_outputs[0] - pt_outputs).mean())
    

常见挑战与解决方案

  • 自定义算子不兼容:部分LLM的自定义算子(如Mistral的滑动窗口注意力)在低opset下无法导出,解决方案是升级opset到16+,或者用Hugging Face Optimum工具自动适配算子。
  • 导出时OOM:7B以上模型直接导出容易内存不足,可切换到CPU导出,或者用device_map="cpu"加载模型,导出后再释放内存。
  • 量化精度损失:INT8量化可能导致部分LLM的生成质量下降,可采用混合精度量化(仅量化权重,激活保持FP16),或者评估后调整量化策略。
  • 动态轴影响性能:过于灵活的动态轴会限制ONNX Runtime的优化,可根据实际场景固定部分维度(比如固定batch_size为1),或者设置动态维度的范围。

针对你提供代码的优化建议

你的基础导出代码可行,但有几个关键优化点:

  1. 加载模型时添加torch_dtype=torch.float16,减少内存占用;
  2. 导出时加入attention_mask作为输入,否则推理时模型无法正确处理padding;
  3. 升级opset到16,提升算子兼容性;
  4. 增加转换后的验证步骤,确保输出正确。

优化后的代码示例:

import os
import torch
import gc
import onnxruntime as ort
from transformers import AutoTokenizer, AutoModelForCausalLM
import warnings

warnings.filterwarnings("ignore")

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model_name = "mistralai/Mistral-7B-Instruct-v0.3"

# 优化模型加载
tokenizer = AutoTokenizer.from_pretrained(model_name, use_auth_token=True)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    use_auth_token=True,
    device_map="auto",
    torch_dtype=torch.float16
)

# 准备完整的dummy输入
dummy_inputs = tokenizer("Hello, world!", return_tensors="pt").to(device)

# 清理内存
gc.collect()
torch.cuda.empty_cache()

# 导出ONNX模型
onnx_model_path = "/kaggle/working/mistral_7b.onnx"
torch.onnx.export(
    model,
    (dummy_inputs["input_ids"], dummy_inputs["attention_mask"]),
    onnx_model_path,
    input_names=["input_ids", "attention_mask"],
    output_names=["output"],
    dynamic_axes={
        "input_ids": {0: "batch_size", 1: "sequence_length"},
        "attention_mask": {0: "batch_size", 1: "sequence_length"}
    },
    opset_version=16,
    do_constant_folding=True,
    export_params=True
)

print(f"Model has been successfully converted to ONNX and saved at {onnx_model_path}")

# 验证转换正确性
sess = ort.InferenceSession(onnx_model_path, providers=["CUDAExecutionProvider" if torch.cuda.is_available() else "CPUExecutionProvider"])
test_input = tokenizer("Hello, world!", return_tensors="pt")
input_ids_np = test_input["input_ids"].cpu().numpy()
attention_mask_np = test_input["attention_mask"].cpu().numpy()

ort_outputs = sess.run(["output"], {"input_ids": input_ids_np, "attention_mask": attention_mask_np})
with torch.no_grad():
    pt_outputs = model(test_input["input_ids"].to(device), test_input["attention_mask"].to(device)).logits.cpu().numpy()

print(f"Output mean error: {abs(ort_outputs[0] - pt_outputs).mean():.6f}")

内容的提问来源于stack exchange,提问作者Haseeb Sultan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:35:03