Как я могу перевести пример «Hello ML. NET World» на F #? - PullRequest
2 голосов
/ 07 мая 2020

Я пытаюсь перевести пример «Hello ML. NET World» с C# на F # (код скопирован ниже), но получаю ошибку компилятора F # о несовместимых типах .

Я видел пару сообщений в блогах о ML. NET и F #, но все они используют более старый API, который включал явное создание объекта LearningPipeline. Насколько я могу судить, этот API был удален.

Проблема c строка в C# - это строка для обучения конвейера:

var pipeline = mlContext.Transforms.Concatenate("Features", new[] { "Size" })
    .Append(mlContext.Regression.Trainers.Sdca(labelColumnName: "Price", maximumNumberOfIterations: 100));

Я пытался чтобы перевести на F # вот так:

let pipeline (mlContext:MLContext) =
    mlContext.Transforms
        .Concatenate("Features", [| "Size" |])
        .Append(mlContext.Regression.Trainers.Sdca(labelColumnName = "Price", maximumNumberOfIterations = Nullable(100)))

Однако я получаю сообщение об ошибке компилятора: Type constraint mismatch: The type 'Transforms.ColumnConcatenatingEstimator' is not compatible with the type IEstimator<ITransformer>'.

Я также пробовал явно понижать преобразование ColumnConcatenatingEstimator в IEstimator:

let pipeline' (mlContext:MLContext) =
    let concat = mlContext.Transforms.Concatenate("Features", [| "Size" |])
    let scda = mlContext.Regression.Trainers.Sdca(labelColumnName = "Price", maximumNumberOfIterations = Nullable(100))

    let concatAsEstimator = concat :> IEstimator<_>
    concatAsEstimator.Append(scda)

Это немного меняет типы в ошибке компилятора. Новое сообщение указывает, что IEstimator<ColumnConcatenatingTransformer> несовместимо с IEstimator<ITransformer>.

Похоже, мне нужно явно преобразовать ColumnConcatenatingTransformer внутри generi c в ITransformer, но я не уверен, как это сделать. сделайте это на F #. Возможно ли это?

Для справки, вот полный C# код от Microsoft, который я пытаюсь адаптировать:

using System;
using Microsoft.ML;
using Microsoft.ML.Data;

class Program
{
   public class HouseData
   {
       public float Size { get; set; }
       public float Price { get; set; }
   }

   public class Prediction
   {
       [ColumnName("Score")]
       public float Price { get; set; }
   }

   static void Main(string[] args)
   {
       MLContext mlContext = new MLContext();

       // 1. Import or create training data
       HouseData[] houseData = {
           new HouseData() { Size = 1.1F, Price = 1.2F },
           new HouseData() { Size = 1.9F, Price = 2.3F },
           new HouseData() { Size = 2.8F, Price = 3.0F },
           new HouseData() { Size = 3.4F, Price = 3.7F } };
       IDataView trainingData = mlContext.Data.LoadFromEnumerable(houseData);

       // 2. Specify data preparation and model training pipeline
       var pipeline = mlContext.Transforms.Concatenate("Features", new[] { "Size" })
           .Append(mlContext.Regression.Trainers.Sdca(labelColumnName: "Price", maximumNumberOfIterations: 100));

       // 3. Train model
       var model = pipeline.Fit(trainingData);

       // 4. Make a prediction
       var size = new HouseData() { Size = 2.5F };
       var price = mlContext.Model.CreatePredictionEngine<HouseData, Prediction>(model).Predict(size);

       Console.WriteLine($"Predicted price for size: {size.Size*1000} sq ft= {price.Price*100:C}k");

       // Predicted price for size: 2500 sq ft= $261.98k
   }
}

(Edit: просто для пояснения, это не тот же вопрос, что и Как перевести вводную часть ML. NET demo в F # .) Это другой пример кода, и в нем используется более новая версия ML. NET. Ссылка Microsoft в этом ответе также теперь не работает.

Ответы [ 2 ]

2 голосов
/ 08 мая 2020

ML. NET построен с учетом C#, поэтому иногда при преобразовании в F # необходимо везде добавлять Nullable и float32. Вот моя версия, в которой я избавляюсь от PredictionEngine, я помещаю Sdca в качестве тренера и использую EstimatorChain() для добавления и создания IEstimator

open System
open Microsoft.ML
open Microsoft.ML.Data


type HouseData = 
    {
        Size  : float32
        Price : float32 
    }
let downcastPipeline (x : IEstimator<_>) = 
    match x with 
    | :? IEstimator<ITransformer> as y -> y
    | _ -> failwith "downcastPipeline: expecting a IEstimator<ITransformer>"

let mlContext = MLContext(Nullable 0)
let houseData = 
    [|
        { Size = 1.1F; Price = 1.2F }
        { Size = 1.1F; Price = 1.2F }
        { Size = 2.8F; Price = 3.0F }
        { Size = 3.4F; Price = 3.7F }
    |] |> mlContext.Data.LoadFromEnumerable 
let trainer = 
    mlContext.Regression.Trainers.Sdca(
        labelColumnName= "Label",
        featureColumnName = "Features",
        maximumNumberOfIterations = Nullable 100
        )
let pipeline = 
    EstimatorChain()
        .Append(mlContext.Transforms.Concatenate("Features", "Size"))
        .Append(mlContext.Transforms.CopyColumns("Label", "Price"))
        .Append(trainer)
    |> downcastPipeline 

let model = pipeline.Fit houseData

let newSize = [| {Size = 2.5f; Price = 0.f} |] 
let prediction = 
    newSize
    |> mlContext.Data.LoadFromEnumerable
    |> model.Transform
    |> fun x -> x.GetColumn<float32> "Score"
    |> Seq.toArray
printfn "Predicted price for size: %.0f sq ft= %.2fk" (newSize.[0].Size * 1000.f) (prediction.[0] * 100.f)

результата

Predicted price for size: 2500 sq ft= 270.69k

Видео Джона Вуда F # ML. Net также является хорошим местом для начала использования ML. Net в F #.

2 голосов
/ 07 мая 2020

Я тоже боролся с этим. Попробуйте использовать эту вспомогательную функцию:

let append (estimator : IEstimator<'a>) (pipeline : IEstimator<'b>)  =
      match pipeline with
      | :? IEstimator<ITransformer> as p ->
          p.Append estimator
      | _ -> failwith "The pipeline has to be an instance of IEstimator<ITransformer>."

let pipeline = 
    mlContext.Transforms.Concatenate("Features",[|"Size"|])
    |> append(mlContext.Regression.Trainers.Sdca(labelColumnName = "Price", maximumNumberOfIterations = Nullable(100)))
...