使用C#版ONNXRuntime导入WinMLTools导出的RandomForestClassifier模型遇内存访问错误
我之前处理类似问题时也踩过这个坑,你遇到的System.AccessViolationException本质是第二个输出"probabilities"的类型信息无法被正确转换为Tensor类型,导致空指针访问触发了内存错误。下面是具体的分析和可行的解决方案:
问题根源
WinMLTools在导出scikit-learn的RandomForestClassifier时,默认会把predict_proba的概率输出导出为非Tensor类型(比如Map<int64, float>),但你的C#代码尝试将其当作Tensor处理,调用OrtCastTypeInfoToTensorInfo时因为类型不匹配返回了空指针,后续访问这个空指针就引发了内存异常。而第一个类别输出是标准的Tensor,所以处理完全正常。
解决方案
1. 先确认模型输出类型(用Netron)
先把问题坐实:用Netron打开你的ONNX模型,查看两个输出的类型。如果"probabilities"不是Tensor(float)而是Map或者其他类型,那就能100%确认是类型不匹配的问题。
2. 改用skl2onnx导出模型(最靠谱的解法)
WinMLTools对scikit-learn的支持比较有限,专门的skl2onnx库能更好地控制输出类型。步骤如下:
- 先安装依赖:
pip install skl2onnx onnx - 导出时一定要加
zipmap=False,强制让概率输出变成二维Tensor:
from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType import onnx from sklearn.ensemble import RandomForestClassifier # 假设你已经训练好模型clf,n_features是输入特征的数量 clf = RandomForestClassifier() # ... 这里是你的模型训练代码 ... # 定义输入的Tensor类型 initial_type = [('input', FloatTensorType([None, n_features]))] # 导出模型,关键是options里的zipmap=False onnx_model = convert_sklearn( clf, initial_types=initial_type, options={type(clf): {'zipmap': False}} ) # 保存模型到本地 onnx.save_model(onnx_model, "rf_classifier.onnx")
3. 升级ONNXRuntime到最新版
如果你的ONNXRuntime C#版本比较老,可能存在对某些ONNX类型的兼容性bug。去NuGet把Microsoft.ML.OnnxRuntime更到最新稳定版,再试试导入模型。
4. 适配非Tensor输出(万不得已的情况)
如果没法重新导出模型,且确认"probabilities"是Map类型,可以修改C#代码来正确处理Map类型:
var outputMetadata = session.OutputMetadata; foreach (var output in outputMetadata) { if (output.Key == "probabilities") { // 检查是不是Map类型 if (output.Value.TypeInfo is MapTypeInfo mapType) { // 这里写处理Map类型的逻辑 var keyType = mapType.KeyType; var valueType = mapType.ValueType; // ... 后续的取值和处理代码 ... } } }
验证步骤
重新导出模型后,用Netron确认"probabilities"是Tensor(float)类型,再用C#的ONNXRuntime导入,应该就能正常获取输出的类型信息,不会再触发内存访问错误了。
内容的提问来源于stack exchange,提问作者Nissim

