我正在开发一个 ML.NET 项目,我将数据加载到 IDataView 中,应用 ONNX 模型转换,然后访问转换后的输出。 我的输入类与我的输出类和模型类位于同一程序集中。我将我的输入类标记为
internal
。我将我的输入类属性标记为 internal
并且出现错误。当我将属性标记为 public
(类仍然是 internal
)时,它就可以工作了。
我认为,如果类被标记为
internal
,如果属性/方法是internal
或public
,则不会有没有区别(引用:“内部类的公共成员实际上是内部的。”) 。显然确实如此。
这是一个最小的可重复示例:
using Microsoft.ML;
using Microsoft.ML.Data;
namespace ConsoleApp1
{
internal class InputData
{
[ColumnName("onnx::Concat_0")]
[VectorType(342)]
// public float[] XA1 { get; set; } = new float[342]; // Works
internal float[] XA1 { get; set; } = new float[342]; // Fails
}
public class OutputData
{
[ColumnName("onnx::Concat_0")]
[VectorType(342)]
public float[] XA1 { get; set; }
}
class Program
{
static void Main()
{
var mlContext = new MLContext();
var inputData = new[]
{
new InputData { XA1 = new float[342] }
};
IDataView inputDataView = mlContext.Data.LoadFromEnumerable(inputData);
// Simulate an ONNX transformation pipeline
var pipeline = mlContext.Transforms.CopyColumns(
"onnx::Concat_0",
"onnx::Concat_0"
);
var model = pipeline.Fit(inputDataView);
IDataView transformedData = model.Transform(inputDataView);
var transformed = mlContext.Data.CreateEnumerable<OutputData>(transformedData, reuseRowObject: false);
foreach (var item in transformed)
{
Console.WriteLine($"XA1 Length: {item.XA1.Length}");
}
}
}
}
如果有帮助:
net8.0
。JetBrains Rider 2024.2.6
。事实上,在“正常”上下文中,内部成员和公共成员之间没有区别,但是在反射上下文中,存在区别,这就是导致此行为的原因。
如果您在 ML.NET 代码中导航1,当您执行
mlContext.Data.LoadFromEnumerable(inputData);
时,ML.NET 会在 SchemaDefinition.GetMemberInfos(Type, Direction)
中构建有关类的字段和属性的信息,以下是其中的相关代码:
var fieldInfos = userType.GetFields(BindingFlags.Public | BindingFlags.Instance);
var propertyInfos = userType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
...
BindingFlags.Public
,这意味着仅采用公共属性/字段。如果他们想要处理内部(或者更确切地说,所有非公共)属性/字段,他们会使用 BindingFlags.NonPublic
(或者 BindingFlags.Public | BindingFlags.NonPublic
,如果他们想要公共和非公共)。
这种基于反射的操作通常只关心公共成员,因为正如您链接的问题的“接受的答案”所说,非公共成员是实现细节。 当您执行
pipeline.Fit(inputDataView)
时,它会查找带有
[ColumnName("onnx::Concat_0")]
的属性,但由于它没有处理您的内部属性,因此它找不到任何属性并抛出该异常。
1 从调用 LoadFromEnumerable
到进行反射的代码的整个路径是:
DataOperationsCatalog.LoadFromEnumerable<TRow>(IEnumerable<TRow>, SchemaDefinition)
DataViewConstructionUtils.CreateFromEnumerable<TRow>(IHostEnvironment, IEnumerable<TRow>, SchemaDefinition)
InternalSchemaDefinition.Create(Type, SchemaDefinition.Direction)
SchemaDefinition.Create(Type, Direction)
SchemaDefinition.GetMemberInfos(Type, Direction)