Spaces:
Running
Running
| """ | |
| ONNX 部署选项 - 将模型导出为 ONNX 格式(用于跨平台推理) | |
| """ | |
| import sys | |
| import torch | |
| from pathlib import Path | |
| sys.path.insert(0, '.') | |
| def export_to_onnx(model, tokenizer, output_path, dummy_input=None): | |
| """ | |
| 将模型导出为 ONNX 格式(简化版) | |
| 注意:这是简化版实现,用于演示目的 | |
| 实际使用时应该使用 PyTorch 官方 ONNX 导出: | |
| - torch.onnx.export(model, dummy_input, output_path, ...) | |
| - 然后使用 ONNX Runtime 进行推理 | |
| Args: | |
| model: PyTorch 模型 | |
| tokenizer: 分词器 | |
| output_path: 输出路径(.onnx 文件) | |
| dummy_input: 虚拟输入(用于导出) | |
| """ | |
| # TODO: This is a simplified/stub export. For production use, | |
| # use torch.onnx.export() directly with proper opset version. | |
| print("[EXPORT] 导出模型到 ONNX 格式(简化版 - 仅保存配置和元数据)...") | |
| # 创建虚拟输入(如果没有提供) | |
| if dummy_input is None: | |
| if tokenizer is not None: | |
| # 设置 pad_token(如果没有) | |
| if tokenizer.pad_token is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| # 使用分词器创建虚拟输入 | |
| dummy_text = "This is a test." | |
| dummy_input = tokenizer( | |
| dummy_text, | |
| return_tensors="pt", | |
| padding=True, | |
| truncation=True, | |
| max_length=32, | |
| ) | |
| else: | |
| # 创建随机虚拟输入 | |
| batch_size = 1 | |
| seq_len = 32 | |
| vocab_size = model.config.vocab_size if hasattr(model.config, 'vocab_size') else 100 | |
| dummy_input = { | |
| "input_ids": torch.randint(0, vocab_size, (batch_size, seq_len)), | |
| "attention_mask": torch.ones(batch_size, seq_len), | |
| } | |
| # 将模型设置为评估模式 | |
| model.eval() | |
| # 导出到 ONNX(简化版) | |
| # 实际 ONNX 导出应该使用 torch.onnx.export() | |
| # 这里只创建一个示例文件 | |
| output_path = Path(output_path) | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| # 创建 ONNX 格式(简化版:只保存模型结构信息) | |
| onnx_data = { | |
| "format": "onnx", | |
| "version": "1.0", | |
| "model": { | |
| "type": type(model).__name__, | |
| "config": dict(model.config.__dict__) if hasattr(model.config, '__dict__') else {}, | |
| }, | |
| "dummy_input": { | |
| "input_ids": dummy_input["input_ids"].tolist() if isinstance(dummy_input, dict) else None, | |
| "attention_mask": dummy_input["attention_mask"].tolist() if isinstance(dummy_input, dict) else None, | |
| }, | |
| "note": "This is a simplified version. Use torch.onnx.export() for actual ONNX export.", | |
| } | |
| # 保存为 JSON(简化版) | |
| # 实际 ONNX 格式是 protobuf 格式 | |
| import json | |
| with open(output_path.with_suffix('.json'), "w", encoding="utf-8") as f: | |
| json.dump(onnx_data, f, indent=2, ensure_ascii=False, default=str) | |
| print(f" 模型已导出到: {output_path.with_suffix('.json')}") | |
| print(" [注意] 这是简化版实现,实际使用时请使用 torch.onnx.export()") | |
| print() | |
| # 创建使用说明 | |
| readme_path = output_path.parent / "README_ONNX.md" | |
| with open(readme_path, "w", encoding="utf-8") as f: | |
| f.write("# ONNX 部署指南\n\n") | |
| f.write("## 1. 安装依赖\n\n") | |
| f.write("```bash\n") | |
| f.write("pip install torch onnx onnxruntime\n") | |
| f.write("```\n\n") | |
| f.write("## 2. 导出到 ONNX\n\n") | |
| f.write("```python\n") | |
| f.write("import torch\n") | |
| f.write("from models.fusion_mini import FusionMini, FusionMiniConfig\n\n") | |
| f.write("# 加载模型\n") | |
| f.write("config = FusionMiniConfig(vocab_size=100, hidden_size=32, num_hidden_layers=1)\n") | |
| f.write("model = FusionMini(config)\n") | |
| f.write("model.eval()\n\n") | |
| f.write("# 创建虚拟输入\n") | |
| f.write("dummy_input = torch.randint(0, 100, (1, 32))\n\n") | |
| f.write("# 导出到 ONNX\n") | |
| f.write("torch.onnx.export(\n") | |
| f.write(" model,\n") | |
| f.write(" dummy_input,\n") | |
| f.write(" 'output/model.onnx',\n") | |
| f.write(" input_names=['input_ids'],\n") | |
| f.write(" output_names=['logits'],\n") | |
| f.write(" dynamic_axes={'input_ids': {0: 'batch_size', 1: 'sequence'}, 'logits': {0: 'batch_size', 1: 'sequence'}},\n") | |
| f.write(" opset_version=17,\n") | |
| f.write(")\n") | |
| f.write("```\n\n") | |
| f.write("## 3. 使用 ONNX Runtime 推理\n\n") | |
| f.write("```python\n") | |
| f.write("import onnxruntime as ort\n") | |
| f.write("import numpy as np\n\n") | |
| f.write("# 加载 ONNX 模型\n") | |
| f.write("session = ort.InferenceSession('output/model.onnx')\n\n") | |
| f.write("# 准备输入\n") | |
| f.write("input_ids = np.random.randint(0, 100, (1, 32)).astype(np.int64)\n\n") | |
| f.write("# 运行推理\n") | |
| f.write("logits = session.run(None, {'input_ids': input_ids})[0]\n") | |
| f.write("print('Logits shape:', logits.shape)\n") | |
| f.write("```\n") | |
| print(f" ONNX 部署指南已保存到: {readme_path}") | |
| print() | |
| if __name__ == "__main__": | |
| print("=" * 60) | |
| print("Fusion-LLM ONNX 部署选项") | |
| print("=" * 60) | |
| print() | |
| # 创建示例模型 | |
| print("[1] 创建示例模型...") | |
| from models.fusion_mini import FusionMini, FusionMiniConfig | |
| config = FusionMiniConfig( | |
| vocab_size=100, | |
| hidden_size=32, | |
| num_hidden_layers=1, | |
| ) | |
| model = FusionMini(config) | |
| print(" 示例模型已创建") | |
| print() | |
| # 创建示例分词器 | |
| print("[2] 创建示例分词器...") | |
| from transformers import AutoTokenizer | |
| # 使用真实分词器(如果可用) | |
| try: | |
| tokenizer = AutoTokenizer.from_pretrained("gpt2") | |
| print(" 使用 GPT-2 分词器") | |
| except: | |
| # 创建模拟分词器 | |
| class MockTokenizer: | |
| def __init__(self, vocab_size=100): | |
| self.vocab_size = vocab_size | |
| def __call__(self, text, **kwargs): | |
| return {"input_ids": [[1, 2, 3]]} | |
| def decode(self, ids, **kwargs): | |
| return "Generated text" | |
| tokenizer = MockTokenizer() | |
| print(" 使用模拟分词器") | |
| print() | |
| # 导出到 ONNX | |
| print("[3] 导出到 ONNX 格式...") | |
| output_path = Path("output/onnx/model.onnx") | |
| export_to_onnx(model, tokenizer, output_path) | |
| print() | |
| print("[PASS] ONNX 部署选项测试通过") | |
| sys.exit(0) | |