如何用C#.NET从零训练神经网络并通过ONNX在ML.NET中运行?
解决方案
方案1:借助PyTorch Python脚本完成ONNX导出(最直接可行)
TorchSharp保存的模型格式与PyTorch完全兼容,可通过以下步骤完成格式转换:
TorchSharp端保存完整模型
训练完成后,使用model.Save("your_model.pt")保存完整模型(不要仅保存state_dict,避免后续加载权重时结构不匹配)。Python脚本加载模型并导出ONNX
安装PyTorch后,编写与TorchSharp端结构完全一致的模型代码,加载权重并导出:
import torch # 必须与TorchSharp中的模型结构完全匹配(层数量、参数、激活顺序都要一致) class CustomModel(torch.nn.Module): def __init__(self): super().__init__() self.fc1 = torch.nn.Linear(784, 256) self.relu = torch.nn.ReLU() self.fc2 = torch.nn.Linear(256, 10) def forward(self, x): x = self.fc1(x) x = self.relu(x) x = self.fc2(x) return x # 加载TorchSharp保存的模型权重 model = CustomModel() model.load_state_dict(torch.load("your_model.pt")) model.eval() # 构造与模型输入维度一致的示例张量(需匹配实际输入尺寸) dummy_input = torch.randn(1, 784) # 导出ONNX模型 torch.onnx.export(model, dummy_input, "your_model.onnx", opset_version=12)
- ML.NET导入ONNX模型并统一接口
使用OnnxTransformer加载导出的模型,与现有ML.NET代码整合:
var mlContext = new MLContext(); // 定义输入输出数据类 public class ModelInput { [ColumnName("input")] [VectorType(784)] public float[] Features { get; set; } } public class ModelOutput { [ColumnName("output")] public float[] PredictedLabels { get; set; } } // 构建ML.NET管道,添加ONNX转换节点 var pipeline = mlContext.Transforms.ApplyOnnxModel( outputColumnNames: new[] { "output" }, inputColumnNames: new[] { "input" }, modelFile: "your_model.onnx"); // 拟合空数据完成管道初始化(模型已训练完成,仅需构建推理管道) var emptyData = mlContext.Data.LoadFromEnumerable(new List<ModelInput>()); var inferenceModel = pipeline.Fit(emptyData); // 创建预测引擎,与现有ML.NET接口统一 var predictionEngine = mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(inferenceModel); var input = new ModelInput { Features = new float[784] }; // 替换为实际输入数据 var prediction = predictionEngine.Predict(input);
方案2:纯.NET环境下的替代方案(无需Python)
如果不想依赖Python,可尝试以下两种方式:
- 使用TorchSharp底层ONNX导出API(实验性)
部分最新版TorchSharp提供了未公开的底层导出接口,可尝试调用:
using TorchSharp; using static TorchSharp.torch; // 模型切换为评估模式 model.eval(); // 构造与实际输入维度一致的示例张量 var dummyInput = torch.randn(1, 784); // 调用底层导出方法 model.ToONNX("your_model.onnx", dummyInput, opset_version: 12);
注意:该方法仅在部分TorchSharp预览版中可用,稳定性需自行验证。
- 封装TorchSharp模型为ML.NET自定义Transformer
直接将TorchSharp模型封装为ML.NET的ITransformer实现,无需转换格式即可统一接口:
public class TorchSharpTransformer : ITransformer { private readonly Module _model; private readonly MLContext _mlContext; public TorchSharpTransformer(Module model, MLContext mlContext) { _model = model.eval(); _mlContext = mlContext; } public DataViewSchema InputSchema { get; } public DataViewSchema OutputSchema { get; } public IDataView Transform(IDataView input) { // 将ML.NET数据转换为TorchSharp张量 var inputData = _mlContext.Data.CreateEnumerable<ModelInput>(input, reuseRowObject: false).ToList(); var featureArray = inputData.SelectMany(x => x.Features).ToArray(); var inputTensor = torch.tensor(featureArray, dtype: torch.float32).view(inputData.Count, 784); // 执行模型推理 using var outputTensor = _model.forward(inputTensor); var outputArray = outputTensor.data<float>().ToArray(); // 将推理结果转换为ML.NET数据格式 var outputData = inputData.Select((item, index) => new ModelOutput { PredictedLabels = outputArray.Skip(index * 10).Take(10).ToArray() }); return _mlContext.Data.LoadFromEnumerable(outputData); } // 按需实现ITransformer接口的其他方法(如Fit、GetRowToRowMapper等) }
使用时直接将该Transformer加入现有ML.NET管道即可实现接口统一。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

