fusion-llm-demo / deployment /export_onnx.py
zhan1206
fix: comprehensive audit fixes - Thinking Dial unification, deployment scripts, README, bilingual filter, data download
bce487e
Raw
History Blame
4.55 kB
"""
ONNX 部署 — 将 FusionModel 导出为 ONNX 格式
使用方式:
python deployment/export_onnx.py --checkpoint output/mini_model --output output/onnx/model.onnx
python deployment/export_onnx.py --checkpoint output/mini_model --output output/onnx/ --dynamic-batch
依赖: onnx, onnxruntime (pip install onnx onnxruntime)
"""
import sys
import os
import argparse
from pathlib import Path
sys.path.insert(0, '.')
def export_to_onnx(model, output_path, dynamic_batch=False, dynamic_seq=True, opset=14):
"""Export FusionModel to ONNX format.
Args:
model: FusionModel instance
output_path: Output .onnx file path
dynamic_batch: Enable dynamic batch dimension
dynamic_seq: Enable dynamic sequence length
opset: ONNX opset version
"""
try:
import onnx
import onnxruntime as ort
except ImportError:
print("[ERROR] onnx and onnxruntime required. Install with:")
print(" pip install onnx onnxruntime")
return False
import torch
model.eval()
output_path = Path(output_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
# Create dummy input
batch_size = 1
seq_len = 16
dummy_input_ids = torch.randint(0, model.config.vocab_size, (batch_size, seq_len))
dummy_attention_mask = torch.ones(batch_size, seq_len, dtype=torch.long)
# Dynamic axes
dynamic_axes = {}
if dynamic_batch or dynamic_seq:
input_axes = {}
if dynamic_batch:
input_axes[0] = "batch_size"
if dynamic_seq:
input_axes[1] = "seq_len"
dynamic_axes = {
"input_ids": input_axes,
"attention_mask": input_axes,
"logits": input_axes,
}
# Export
print(f"[EXPORT] Exporting to ONNX (opset={opset})...")
torch.onnx.export(
model,
(dummy_input_ids, dummy_attention_mask),
str(output_path),
input_names=["input_ids", "attention_mask"],
output_names=["logits"],
dynamic_axes=dynamic_axes if dynamic_axes else None,
opset_version=opset,
do_constant_folding=True,
)
# Verify
print("[VERIFY] Checking ONNX model...")
onnx_model = onnx.load(str(output_path))
onnx.checker.check_model(onnx_model)
# Test with ONNX Runtime
print("[VERIFY] Running ONNX Runtime inference test...")
session = ort.InferenceSession(str(output_path))
ort_inputs = {
"input_ids": dummy_input_ids.numpy(),
"attention_mask": dummy_attention_mask.numpy(),
}
ort_outputs = session.run(None, ort_inputs)
# Compare with PyTorch output
with torch.no_grad():
pt_outputs = model(input_ids=dummy_input_ids, attention_mask=dummy_attention_mask, return_dict=True)
import numpy as np
max_diff = np.max(np.abs(ort_outputs[0] - pt_outputs.logits.numpy()))
print(f"[VERIFY] Max diff between PyTorch and ONNX Runtime: {max_diff:.6e}")
if max_diff < 1e-4:
print(f"[PASS] ONNX export verified: {output_path}")
else:
print(f"[WARN] Diff {max_diff:.6e} exceeds 1e-4, check model compatibility")
# Save model metadata
metadata = {
"model_type": "fusion",
"vocab_size": model.config.vocab_size,
"hidden_size": model.config.hidden_size,
"num_hidden_layers": model.config.num_hidden_layers,
"num_attention_heads": model.config.num_attention_heads,
"dynamic_batch": dynamic_batch,
"dynamic_seq": dynamic_seq,
"onnx_opset": opset,
}
import json
meta_path = output_path.with_suffix(".meta.json")
with open(meta_path, "w", encoding="utf-8") as f:
json.dump(metadata, f, indent=2)
print(f"[DONE] ONNX export complete: {output_path}")
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Export FusionModel to ONNX format")
parser.add_argument("--checkpoint", required=True, help="Path to model checkpoint")
parser.add_argument("--output", default="output/onnx/model.onnx", help="Output ONNX path")
parser.add_argument("--dynamic-batch", action="store_true", help="Enable dynamic batch dim")
parser.add_argument("--opset", type=int, default=14, help="ONNX opset version")
args = parser.parse_args()
from models.fusion_model import FusionModel
model = FusionModel.from_pretrained(args.checkpoint)
export_to_onnx(model, args.output, args.dynamic_batch, opset=args.opset)