动态 training/test 类 和 ML.NET
dynamic training/test classes with ML.NET
这是此处问题的跟进
Dynamic classes/objects ML.net's PredictionMoadel<TInput, TOutput> Train()
我的系统无法在编译时使用预定义的 class,因此我尝试将动态 class 提供给 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);
}
并收到错误:“在类型 'System.Object' 中找不到名称为 'Name' 的字段或 属性”。我尝试仅使用反射生成 class 以 运行 进入相同的问题。
有解决办法吗?谢谢
现在我正在使用像这样的虚拟占位符作为解决方法
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;
}
动态 class 实际上并不创建 class 定义,而是为您提供动态对象。
我查看了 SchemaDefinition.Create()
的代码,它需要一个实际的 class 定义来构建架构。因此,您的选择是动态创建和加载 class 定义。
您可以将 class 创建为具有所有动态属性的字符串,并使用 Microsoft 编译器服务 Roslyn
对其进行编译。参见 。这将使用您的动态类型生成一个程序集(在内存中作为内存流或在文件系统上)。
现在你只完成了一半。要从动态程序集中获取动态类型,您需要将其加载到应用程序域中。见 this post。
加载程序集后,您可以使用“Activator.CreateInstance()”,如果它是同一个域,或者如果它是您的自定义域,那么您需要 yourDomain.CreateInstanceAndUnwrap()
从动态生成的 Class 中创建对象并获取类型使用 Assembly.GetType()
。
这里的示例很少,有点过时了,但如果您愿意的话,会让您重新站起来。请参阅 CompilerEngine and CompilerService 以编译和加载程序集。
其他选项: Refelection.Emit()
但它需要大量的 IL 级别编码。看到这个 post.
对于那些尝试这样做的人,我有一个工作解决方案可以创建模式并可用于动态训练数据。
首先,从我的另一个答案中获取 DynamicTypeProperty 和 DynamicType 的代码 here。
以下代码将动态创建模式:
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);
然后您需要创建包含所需数据的列表。这是按如下方式完成的:
var dynamicList = DynamicType.CreateDynamicList(dynamicType);
// get an action that will add to the list
var addAction = DynamicType.GetAddAction(dynamicList);
// call the action, with an object[] containing parameters in exact order added
addAction.Invoke(new object[] {1.1, 2.2, 3.3, 4.4});
// call add action again for each row.
然后您需要使用数据创建一个 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
。
祝你好运。
这是此处问题的跟进 Dynamic classes/objects ML.net's PredictionMoadel<TInput, TOutput> Train()
我的系统无法在编译时使用预定义的 class,因此我尝试将动态 class 提供给 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);
}
并收到错误:“在类型 'System.Object' 中找不到名称为 'Name' 的字段或 属性”。我尝试仅使用反射生成 class 以 运行 进入相同的问题。
有解决办法吗?谢谢
现在我正在使用像这样的虚拟占位符作为解决方法
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;
}
动态 class 实际上并不创建 class 定义,而是为您提供动态对象。
我查看了 SchemaDefinition.Create()
的代码,它需要一个实际的 class 定义来构建架构。因此,您的选择是动态创建和加载 class 定义。
您可以将 class 创建为具有所有动态属性的字符串,并使用 Microsoft 编译器服务 Roslyn
对其进行编译。参见
现在你只完成了一半。要从动态程序集中获取动态类型,您需要将其加载到应用程序域中。见 this post。
加载程序集后,您可以使用“Activator.CreateInstance()”,如果它是同一个域,或者如果它是您的自定义域,那么您需要 yourDomain.CreateInstanceAndUnwrap()
从动态生成的 Class 中创建对象并获取类型使用 Assembly.GetType()
。
这里的示例很少,有点过时了,但如果您愿意的话,会让您重新站起来。请参阅 CompilerEngine and CompilerService 以编译和加载程序集。
其他选项: Refelection.Emit()
但它需要大量的 IL 级别编码。看到这个 post.
对于那些尝试这样做的人,我有一个工作解决方案可以创建模式并可用于动态训练数据。
首先,从我的另一个答案中获取 DynamicTypeProperty 和 DynamicType 的代码 here。
以下代码将动态创建模式:
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);
然后您需要创建包含所需数据的列表。这是按如下方式完成的:
var dynamicList = DynamicType.CreateDynamicList(dynamicType);
// get an action that will add to the list
var addAction = DynamicType.GetAddAction(dynamicList);
// call the action, with an object[] containing parameters in exact order added
addAction.Invoke(new object[] {1.1, 2.2, 3.3, 4.4});
// call add action again for each row.
然后您需要使用数据创建一个 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
。
祝你好运。