C# 使用ML.NET动态培训/测试课程

C# 使用ML.NET动态培训/测试课程,c#,machine-learning,ml.net,C#,Machine Learning,Ml.net,这是问题的后续部分 我的系统无法在编译时使用预定义的类,因此我尝试将动态类输入到ML.NET中,如下所示 // field data type public class Field { public string FieldName { get; set; } public Type FieldType { get; set; } } // dynamic class helper public class Dy

这是问题的后续部分

我的系统无法在编译时使用预定义的类,因此我尝试将动态类输入到ML.NET中,如下所示

    // field data type
    public class Field
    {
        public string FieldName { get; set; }
        public Type FieldType { get; set; }
    }

    // dynamic class helper
    public class DynamicClass : DynamicObject
    {
        private readonly Dictionary<string, KeyValuePair<Type, object>> _fields;

        public DynamicClass(List<Field> fields)
        {
            _fields = new Dictionary<string, KeyValuePair<Type, object>>();
            fields.ForEach(x => _fields.Add(x.FieldName,
                new KeyValuePair<Type, object>(x.FieldType, null)));
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            if (_fields.ContainsKey(binder.Name))
            {
                var type = _fields[binder.Name].Key;
                if (value.GetType() == type)
                {
                    _fields[binder.Name] = new KeyValuePair<Type, object>(type, value);
                    return true;
                }
                else throw new Exception("Value " + value + " is not of type " + type.Name);
            }
            return false;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = _fields[binder.Name].Value;
            return true;
        }
    }

    private static void Main(string[] args)
    {
        var fields = new List<Field>
        {
            new Field {FieldName = "Name", FieldType = typeof(string)},
            new Field {FieldName = "Income", FieldType = typeof(float)}
        };

        dynamic obj1 = new DynamicClass(fields);
        obj1.Name = "John";
        obj1.Income = 100f;

        dynamic obj2 = new DynamicClass(fields);
        obj2.Name = "Alice";
        obj2.Income = 200f;

        var trainingData = new List<dynamic> {obj1, obj2};

        var env = new LocalEnvironment();
        var schemaDef = SchemaDefinition.Create(typeof(DynamicClass));
        schemaDef.Add(new SchemaDefinition.Column(null, "Name", TextType.Instance));
        schemaDef.Add(new SchemaDefinition.Column(null, "Income", NumberType.R4));
        var trainDataView = env.CreateStreamingDataView(trainingData, schemaDef);

        var pipeline = new CategoricalEstimator(env, "Name")
            .Append(new ConcatEstimator(env, "Features", "Name"))
            .Append(new FastTreeRegressionTrainer(env, "Income", "Features"));

        var model = pipeline.Fit(trainDataView);
    }
//字段数据类型
公共类字段
{
公共字符串字段名{get;set;}
公共类型字段类型{get;set;}
}
//动态类助手
公共类DynamicClass:DynamicObject
{
专用只读字典_字段;
公共动态类(列表字段)
{
_字段=新字典();
fields.ForEach(x=>_fields.Add(x.FieldName,
新的KeyValuePair(x.FieldType,null));
}
public override bool TrySetMember(SetMemberBinder绑定器,对象值)
{
if(_fields.ContainsKey(binder.Name))
{
变量类型=_字段[binder.Name].Key;
if(value.GetType()==type)
{
_字段[binder.Name]=新的KeyValuePair(类型、值);
返回true;
}
else抛出新异常(“值”+Value+”不是类型“+type.Name”);
}
返回false;
}
公共重写bool TryGetMember(GetMemberBinder绑定器,输出对象结果)
{
结果=_字段[binder.Name]。值;
返回true;
}
}
私有静态void Main(字符串[]args)
{
变量字段=新列表
{
新字段{FieldName=“Name”,FieldType=typeof(string)},
新字段{FieldName=“Income”,FieldType=typeof(float)}
};
动态对象J1=新的动态类(字段);
obj1.Name=“John”;
obj1.收入=100f;
动态obj2=新的动态类(字段);
obj2.Name=“Alice”;
obj2.收入=200f;
var trainingData=新列表{obj1,obj2};
var env=new LocalEnvironment();
var schemaDef=SchemaDefinition.Create(typeof(DynamicClass));
Add(新的SchemaDefinition.Column(null,“Name”,TextType.Instance));
Add(新的SchemaDefinition.Column(null,“Income”,NumberType.R4));
var trainDataView=env.CreateStreamingDataView(trainingData,schemaDef);
var管道=新的分类刺激因子(环境,“名称”)
.Append(新的ConcatEstimator(env,“Features”,“Name”))
.附加(新的FastTreeRegressionTrainer(环境,“收入”、“功能”));
var模型=pipeline.Fit(trainDataView);
}
并得到错误:“'在类型'System.Object'中找不到名为'name'的字段或属性'”。我尝试使用反射生成类,但遇到了同样的问题


有解决办法吗?谢谢

现在我正在使用一个像这样的虚拟占位符作为解决方法

    public class TrainingSample
    {
        public string TextField1;
        public string TextField2;
        public string TextField3;
        public string TextField4;
        public string TextField5;

        public float FloatField1;
        public float FloatField2;
        public float FloatField3;
        public float FloatField4;
        public float FloatField5;
        public float FloatField6;
        public float FloatField7;
        public float FloatField8;
        public float FloatField9;
        public float FloatField10;
        public float FloatField11;
        public float FloatField12;
        public float FloatField13;
        public float FloatField14;
        public float FloatField15;
    }

动态类实际上并不创建类定义,而是为您提供动态对象

我查看了
SchemaDefinition.Create()
的代码,它需要一个实际的类定义来构建模式。因此,您可以选择动态创建和加载类定义

您可以将类创建为具有所有动态属性的字符串,并使用Microsoft编译器服务aka
Roslyn
对其进行编译。看见这将使用您的动态类型生成一个程序集(在内存中作为内存流或在文件系统上)

现在你只走了一半。要从动态程序集中获取动态类型,需要将其加载到应用程序域中。见下文。 加载程序集后,如果它是同一个域或自定义域,则可以使用“”,然后需要
yourDomain.CreateInstanceAndUnwrap()
从动态生成的类中创建对象并获取类型,请使用
assembly.GetType()

这里的样品很少,有点过时,但如果你愿意的话,会让你站起来的。请参阅和以编译和加载程序集


其他选项
Refelection.Emit()
,但它需要大量的IL级编码。请参见此图。

对于那些尝试这样做的人,我有一个工作解决方案,可以创建模式,并可用于动态训练数据

首先,从我的另一个答案中获取DynamicTypeProperty和DynamicType的代码

以下代码将动态创建架构:

var properties = new List<DynamicTypeProperty>()
{
    new DynamicTypeProperty("SepalLength", typeof(float)),
    new DynamicTypeProperty("SepalWidth", typeof(float)),
    new DynamicTypeProperty("PetalLength", typeof(float)),
    new DynamicTypeProperty("PetalWidth", typeof(float)),
};

// create the new type
var dynamicType = DynamicType.CreateDynamicType(properties);
var schema = SchemaDefinition.Create(dynamicType);
然后您需要使用数据创建IDataView,这需要使用反射,否则培训师将无法推断正确的类型

            var mlContext = new MLContext();
            var dataType = mlContext.Data.GetType();
            var loadMethodGeneric = dataType.GetMethods().First(method => method.Name =="LoadFromEnumerable" && method.IsGenericMethod);
            var loadMethod = loadMethodGeneric.MakeGenericMethod(dynamicType);
            var trainData = (IDataView) loadMethod.Invoke(mlContext.Data, new[] {dynamicList, schema});
然后,您应该能够通过管道运行
trainData


祝你好运。

我确实尝试了反射解决方案,但没有帮助,它将检查其他解决方案,然后你可以使用FloatFeatures向量和textfatures向量,然后只使用接受向量参数的编码估计器步骤。大多数数字功能都接受输入列的向量,对于文本功能,有一些(例如ApplyWordEmbedding)参考:您是否尝试过使用ExpandooObject?我不知道ExpandoObject,很高兴知道,谢谢。我有一个完整的例子说明如何使用运行时生成的类来实现这一点:谢谢Gary!我能够根据您的示例创建动态IDataView。不过,我有一个跟进问题。训练模型后,必须创建一个预测引擎,该引擎要求声明源类类型。但是,我不确定如何声明这个类,因为我使用的是动态类型的静态实例。我是否误解了如何使用这些课程?或者,您可以提供一个简短的示例,说明如何使用本例中创建的IDataView来实例化预测引擎?@andyopayne,看看我对另一个类似问题的回答。我发布了一些示例代码来实现这一点。
            var mlContext = new MLContext();
            var dataType = mlContext.Data.GetType();
            var loadMethodGeneric = dataType.GetMethods().First(method => method.Name =="LoadFromEnumerable" && method.IsGenericMethod);
            var loadMethod = loadMethodGeneric.MakeGenericMethod(dynamicType);
            var trainData = (IDataView) loadMethod.Invoke(mlContext.Data, new[] {dynamicList, schema});