Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
365 views
in Technique[技术] by (71.8m points)

c# - ML.NET Create Prediction Engine using Dynamic Class

I have an ML.NET application where I have to create interface IDataView dynamically after compile time to be used for training. I found this thread 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 that you should create a prediction engine where you have to define both the input and output class types in order to create the engine. Something like:

mlContext.Model.CreatePredictionEngine<TSrc,TDst>(ITransformer, DataViewSchema)

where TSrc and TDst are class types that are known at compile time. My problem is that I don't know the structure of the input class type at compile time and have to create a dynamic interface for the input data source. The output class object can be defined since the parameters are known, but I'm unsure how to proceed with a dynamic input.

I thought I could maybe try to use something like GetType() on the interface but it says that "implicitly-typed variables cannot have multiple declarators". My simplified example looks like this:

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);
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It's possible to generate a runtime class that has all the fields listed in the DataViewSchema. This will allow you to create a PredictionEngine.

You won't be able to create the PredictionEngine directly, you'll have to invoke it. Here is some sample code:

        // 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 });

To actually use the PredictionEngine (dynamicPredictionEngine), use a call similar to this:

var predictMethod = dynamicPredictionEngine.GetType().GetMethod("Predict", new[] { runtimeType });
var predict = predictMethod.Invoke(dynamicPredictionEngine, new[] { inputObject });

I used modified copy (ClassFactory above) of the source from this wonderful example of creating runtime classes. My copy accepts a DataViewSchema to auto generate the requires class. That code is below:

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);
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...