ML.NET 使用动态创建预测引擎 Class
ML.NET Create Prediction Engine using Dynamic Class
我有一个 ML.NET 应用程序,我必须在编译时间后动态创建接口 IDataView 以用于训练。我发现这个 and I've been able to successfully create a dynamic interface for the training data set and then use it to train a model. My problem comes in when I try to use that same interface in order to create a prediction using that trained model. The docs show 你应该创建一个预测引擎,你必须在其中定义输入和输出 class 类型才能创建引擎。类似于:
mlContext.Model.CreatePredictionEngine<TSrc,TDst>(ITransformer, DataViewSchema)
其中 TSrc 和 TDst 是class 编译时已知的类型。我的问题是我在编译时不知道输入 class 类型的结构,必须为输入数据源创建一个动态接口。由于参数已知,因此可以定义输出 class 对象,但我不确定如何进行动态输入。
我想我也许可以尝试在界面上使用类似 GetType() 的东西,但它说“隐式类型的变量不能有多个声明符”。我的简化示例如下所示:
public class ModelOutput
{
public string PredictedLabel { get; set; }
public float[] Score { get; set; }
}
public class MakePrediction
{
protected void Solve(IDataView data, ITransformer model)
{
var mlContext = new MLContext();
var engine = mlContext.Model.CreatePredictionEngine<data.GetType(), ModelOutput>(model, data.Schema);
}
}
查找MethodInfo.MakeGenericMethod()
可以生成具有 DataViewSchema 中列出的所有字段的运行时 class。这将允许您创建一个 PredictionEngine。
您将无法直接创建 PredictionEngine,您必须调用它。这是一些示例代码:
// Create runtime type from fields and types in a DataViewSchema
var runtimeType = ClassFactory.CreateType(dataViewSchema);
dynamic dynamicPredictionEngine;
var genericPredictionMethod = mlContext.Model.GetType().GetMethod("CreatePredictionEngine", new[] { typeof(ITransformer), typeof(DataViewSchema) });
var predictionMethod = genericPredictionMethod.MakeGenericMethod(runtimeType, typeof(PricePrediction));
dynamicPredictionEngine = predictionMethod.Invoke(mlContext.Model, new object[] { model, dataViewSchema });
要实际使用 PredictionEngine (dynamicPredictionEngine),请使用与此类似的调用:
var predictMethod = dynamicPredictionEngine.GetType().GetMethod("Predict", new[] { runtimeType });
var predict = predictMethod.Invoke(dynamicPredictionEngine, new[] { inputObject });
我使用了来自 this wonderful example of creating runtime classes 的源代码的修改副本(上面的 ClassFactory)。我的副本接受 DataViewSchema 来自动生成需求 class。该代码如下:
using Microsoft.ML;
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
public static class ClassFactory
{
private static AssemblyName _assemblyName;
public static object CreateObject(string[] PropertyNames, Type[] Types)
{
_assemblyName = new AssemblyName("DynamicInput");
if (PropertyNames.Length != Types.Length)
{
Console.WriteLine("The number of property names should match their corresponding types number");
}
TypeBuilder DynamicClass = CreateTypeBuilder();
CreateConstructor(DynamicClass);
for (int ind = 0; ind < PropertyNames.Count(); ind++)
CreateProperty(DynamicClass, PropertyNames[ind], Types[ind]);
Type type = DynamicClass.CreateType();
return Activator.CreateInstance(type);
}
public static Type CreateType(DataViewSchema dataViewSchema)
{
_assemblyName = new AssemblyName("DynamicInput");
TypeBuilder DynamicClass = CreateTypeBuilder();
CreateConstructor(DynamicClass);
foreach (var item in dataViewSchema)
{
CreateProperty(DynamicClass, item.Name, item.Type.RawType);
}
return DynamicClass.CreateType();
}
private static TypeBuilder CreateTypeBuilder()
{
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder typeBuilder = moduleBuilder.DefineType(_assemblyName.FullName
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return typeBuilder;
}
private static void CreateConstructor(TypeBuilder typeBuilder)
{
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
}
private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
我有一个 ML.NET 应用程序,我必须在编译时间后动态创建接口 IDataView 以用于训练。我发现这个
mlContext.Model.CreatePredictionEngine<TSrc,TDst>(ITransformer, DataViewSchema)
其中 TSrc 和 TDst 是class 编译时已知的类型。我的问题是我在编译时不知道输入 class 类型的结构,必须为输入数据源创建一个动态接口。由于参数已知,因此可以定义输出 class 对象,但我不确定如何进行动态输入。
我想我也许可以尝试在界面上使用类似 GetType() 的东西,但它说“隐式类型的变量不能有多个声明符”。我的简化示例如下所示:
public class ModelOutput
{
public string PredictedLabel { get; set; }
public float[] Score { get; set; }
}
public class MakePrediction
{
protected void Solve(IDataView data, ITransformer model)
{
var mlContext = new MLContext();
var engine = mlContext.Model.CreatePredictionEngine<data.GetType(), ModelOutput>(model, data.Schema);
}
}
查找MethodInfo.MakeGenericMethod()
可以生成具有 DataViewSchema 中列出的所有字段的运行时 class。这将允许您创建一个 PredictionEngine。
您将无法直接创建 PredictionEngine,您必须调用它。这是一些示例代码:
// Create runtime type from fields and types in a DataViewSchema
var runtimeType = ClassFactory.CreateType(dataViewSchema);
dynamic dynamicPredictionEngine;
var genericPredictionMethod = mlContext.Model.GetType().GetMethod("CreatePredictionEngine", new[] { typeof(ITransformer), typeof(DataViewSchema) });
var predictionMethod = genericPredictionMethod.MakeGenericMethod(runtimeType, typeof(PricePrediction));
dynamicPredictionEngine = predictionMethod.Invoke(mlContext.Model, new object[] { model, dataViewSchema });
要实际使用 PredictionEngine (dynamicPredictionEngine),请使用与此类似的调用:
var predictMethod = dynamicPredictionEngine.GetType().GetMethod("Predict", new[] { runtimeType });
var predict = predictMethod.Invoke(dynamicPredictionEngine, new[] { inputObject });
我使用了来自 this wonderful example of creating runtime classes 的源代码的修改副本(上面的 ClassFactory)。我的副本接受 DataViewSchema 来自动生成需求 class。该代码如下:
using Microsoft.ML;
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
public static class ClassFactory
{
private static AssemblyName _assemblyName;
public static object CreateObject(string[] PropertyNames, Type[] Types)
{
_assemblyName = new AssemblyName("DynamicInput");
if (PropertyNames.Length != Types.Length)
{
Console.WriteLine("The number of property names should match their corresponding types number");
}
TypeBuilder DynamicClass = CreateTypeBuilder();
CreateConstructor(DynamicClass);
for (int ind = 0; ind < PropertyNames.Count(); ind++)
CreateProperty(DynamicClass, PropertyNames[ind], Types[ind]);
Type type = DynamicClass.CreateType();
return Activator.CreateInstance(type);
}
public static Type CreateType(DataViewSchema dataViewSchema)
{
_assemblyName = new AssemblyName("DynamicInput");
TypeBuilder DynamicClass = CreateTypeBuilder();
CreateConstructor(DynamicClass);
foreach (var item in dataViewSchema)
{
CreateProperty(DynamicClass, item.Name, item.Type.RawType);
}
return DynamicClass.CreateType();
}
private static TypeBuilder CreateTypeBuilder()
{
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder typeBuilder = moduleBuilder.DefineType(_assemblyName.FullName
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return typeBuilder;
}
private static void CreateConstructor(TypeBuilder typeBuilder)
{
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
}
private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}