Selbst lernt die Maschine

Installation und Training

Zur Nutzung von ML.NET AutoML müssen Sie lediglich die Bibliotheken Microsoft.ML und Microsoft.ML.AutoML installieren, zum Beispiel mittels NuGet in Visual Studio. Greifen Sie ruhig zur neuesten Version, auch wenn sie noch als instabil gekennzeichnet ist. In der Tat treten bei einzelnen Trainingsdurchläufen gelegentlich Exceptions auf, was aber verschmerzbar ist – durch Abfangen der Exception geht nur das Ergebnis des aktuellen Durchlaufs verloren.
Zur Demonstration der Vorgehensweise dient das Beispiel „Brustkrebsdiagnose“, das auch schon in [12] beschrieben und dort mit R Services implementiert wurde. Bei diesem Beispiel liegen Gewebebiopsie-Daten aus der Untersuchung des Tumorgewebes von Brustkrebspatientinnen vor (Tabelle 3). Basierend auf diesen Eingangswerten soll das trainierte Modell eine Entscheidung fällen, ob bei einer Person bösartiger Brustkrebs vorliegt oder nicht (binäre Klassifikation).
Listing 1 zeigt anhand einer Konsolenanwendung, wie es geht. Zuerst müssen Sie als Basis ein MLContext-Objekt erzeugen. Hier können Sie optional einen Seed (Startwert) übergeben, der sicherstellt, dass die Ergebnisse reproduzierbar sind. Danach können Sie die Trainingsdaten einlesen, zum Beispiel aus einer CSV-Datei. Dabei erfolgt eine Typisierung auf die Eingangsdaten, wie sie in der Klasse Biopsy­Data vorgegeben sind (Listing 2). Jedes Feld muss dabei mit dem LoadColumn-Attribut versehen werden. Erlaubte Datentypen innerhalb von BiopsyData sind string, float und bool, da AutoML nur mit diesen umgehen kann. Die Eingangsdaten sollten entsprechend angepasst werden (true/false statt ma­lignant/benign).
Listing 1: Automatisches Training mit Ausgabe des Gewinnermodells
using Microsoft.ML;
using Microsoft.ML.AutoML;
using Microsoft.ML.Data;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AutoMLExample
{
  public static class Program
  {
    const string trainDataFilename =
      "BiopsyTrainData.csv";
    const string modelFilename = "Model.bin";
    const uint trainingTimeInSeconds = 10;

    static List<TrainingResult> trainingResults =
      new List<TrainingResult>();

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

      IDataView trainDataView = mlContext.Data.LoadFrom
        TextFile<BiopsyData>(trainDataFilename, ';',
        true);

      var experimentSettings =
        new BinaryExperimentSettings();
      experimentSettings.MaxExperimentTimeInSeconds =
        trainingTimeInSeconds;

      var metricValues = Enum.GetValues(typeof(
        BinaryClassificationMetric));

      // Loop through all metrics
      foreach (BinaryClassificationMetric metricValue
          in metricValues)
      {
        experimentSettings.OptimizingMetric =
          metricValue;

        var experiment = mlContext.Auto().CreateBinary
          ClassificationExperiment(experimentSettings);
        ExperimentResult<BinaryClassificationMetrics>
          experimentResult;

        try
        {
          // Run training
          experimentResult = experiment.Execute(
            trainDataView, "class");
        }
        // Workaround for bug in Execute() Method
        catch (ArgumentOutOfRangeException)
        {
          continue;
        }

        var bestRun = experimentResult.BestRun;
        var metrics = bestRun.ValidationMetrics;

        trainingResults.Add(
          new TrainingResult
        {
          ExperimentTimeInSeconds =
            trainingTimeInSeconds,
          Model = bestRun.Model,
          Metric = metricValue,
          Trainer = bestRun.TrainerName,
          Accuracy = metrics.Accuracy,
          AreaUnderPrecisionRecallCurve =
            metrics.AreaUnderPrecisionRecallCurve,
          AreaUnderRocCurve =
            metrics.AreaUnderRocCurve,
          F1Score = metrics.F1Score,
          PositiveRecall = metrics.PositiveRecall,
          NegativeRecall = metrics.NegativeRecall,
          PositivePrecision =
            metrics.PositivePrecision,
          NegativePrecision =
            metrics.NegativePrecision,
          Matrix = metrics.ConfusionMatrix
        });
      }
     
      trainingResults = trainingResults
        .OrderByDescending(result =>
          result.NegativePrecision)
        .ThenByDescending(result =>
          result.PositivePrecision)
        .ToList<TrainingResult>();

      // Save best model to disk
      var bestTrainingResults = trainingResults[0];
      mlContext.Model.Save(bestTrainingResults.Model,
        trainDataView.Schema, "Model.bin");

      // Print results to console window
      Console.WriteLine($"Training Time: {
        bestTrainingResults.ExperimentTimeInSeconds}");
      Console.WriteLine($"Metric: {
        bestTrainingResults.Metric.ToString()}");
      Console.WriteLine($"Trainer: {
        bestTrainingResults.Trainer}");
      Console.WriteLine($"Accuracy: {
        bestTrainingResults.Accuracy:0.###}");
      Console.WriteLine($"AreaUnderPrecisionRecallCurve:
        {bestTrainingResults.AreaUnderPrecisionRecall
        Curve:0.###}");
      Console.WriteLine($"AreaUnderRocCurve: {
        bestTrainingResults.AreaUnderRocCurve:0.###}");
      Console.WriteLine($"F1Score: {
        bestTrainingResults.F1Score:0.###}");
      Console.WriteLine($"PositiveRecall: {
        bestTrainingResults.PositiveRecall:0.###}");
      Console.WriteLine($"NegativeRecall: {
        bestTrainingResults.NegativeRecall:0.###}");
      Console.WriteLine($"Positive Precision: {
        bestTrainingResults.PositivePrecision:0.###}");
      Console.WriteLine($"Negative Precision: {
        bestTrainingResults.NegativePrecision:0.###}");
      Console.WriteLine();
      Console.WriteLine(bestTrainingResults.Matrix.Get
        FormattedConfusionTable());
    }
  }
}
Listing 2: Eingangsfelder in einer Klasse deklarieren
using Microsoft.ML.Data;

namespace AutoMLExample
{
  public class BiopsyData
  {
    [LoadColumn(0)]  public float ID;
    [LoadColumn(1)]  public float V1;
    [LoadColumn(2)]  public float V2;
    [LoadColumn(3)]  public float V3;
    [LoadColumn(4)]  public float V4;
    [LoadColumn(5)]  public float V5;
    [LoadColumn(6)]  public float V6;
    [LoadColumn(7)]  public float V7;
    [LoadColumn(8)]  public float V8;
    [LoadColumn(9)]  public float V9;
    [LoadColumn(10)] public bool  @class;
  }
}
Im Anschluss erstellen Sie ein BinaryExperimentSettings-Objekt, mit dem das Training konfiguriert wird (Microsoft nennt das Training ein „Experiment“). Einer der Konfigura­tionswerte ist die zu verwendende Optimierungsmetrik. Um die Automatisierung noch einen Schritt weiter zu treiben, setzen Sie eine Schleife um den darauffolgenden Trainingscode und iterieren dabei durch alle verfügbaren Metriken.
Für das eigentliche Training wird ein BinaryClassificationExperiment-Objekt benötigt, das Sie aus mlContext erzeugen können. Durch Aufruf der Execute-Methode wird das Training angestossen. Legen Sie nach jedem Durchlauf das trainierte Modell mitsamt seiner Konfiguration und den ermittelten Messgrössen (Listing 3) in einer Liste ab. Am Ende picken Sie sich das geeignetste Modell durch Sortieren der Liste heraus. Im vorliegenden Beispiel wird zuerst nach negativer Genauigkeit (NegativePrecision), dann nach positiver Genauigkeit (PositivePrecision) sortiert, weil es wichtig ist, wenige falsch negative Ergebnisse zu bekommen (ein übersehener bösartiger Tumor ist viel problematischer als einige irrtümlich als bösartig erkannte gutartige Tumoren).
Listing 3: Hilfsklasse als Container für die trainierten Modelle
using Microsoft.ML;
using Microsoft.ML.AutoML;
using Microsoft.ML.Data;

namespace AutoMLExample
{
  public class TrainingResult
  {
          // Configuration
    public uint                        ExperimentTimeInSeconds        { get; set; }
    public ITransformer                Model                          { get; set; }
    public string                      Trainer                        { get; set; }
    public BinaryClassificationMetric  Metric                        { get; set; }

          // Metrics
    public double                      Accuracy                      { get; set; }
    public double                      AreaUnderPrecisionRecallCurve  { get; set; }
    public double                      AreaUnderRocCurve              { get; set; }
    public double                      F1Score                        { get; set; }
    public double                      PositiveRecall                { get; set; }
    public double                      NegativeRecall                { get; set; }
    public double                      PositivePrecision              { get; set; }
    public double                      NegativePrecision              { get; set; }

    public ConfusionMatrix            Matrix                        { get; set; }
  }
}
Im letzten Schritt persistieren Sie das Gewinnermodell in eine Binärdatei und geben einige Kenngrössen in der Konsole aus. Besonders übersichtlich ist dabei die sogenannte Confusion Matrix, die mehrere Grössen tabellenartig wiedergibt [13]. Ein typisches Ergebnis sehen Sie in Bild 1.
Ausgabe der Kenngrössen des Gewinnermodells (Bild 1)
Quelle: Autor

Martin Gossen
Autor(in) Martin Gossen




Das könnte Sie auch interessieren