Was kann ML.NET und wie kann es verwendet werden?

21.03.2020

ML.NET ist ein open source und cross-platform Machine Learning Framework für .NET. Es bietet die Möglichkeit, Machine Learning Modelle zu trainieren oder diese auszuliefern. Zulässige Programmiersprachen sind aktuell C# und F#.

Verwendeter Datensatz

Exemplarisch wird für das Beispiel ein Datensatz der Stadt New York City verwendet. Dieser beinhaltet Daten zu Taxifahrten. Ziel des Versuch soll es sein, die Kosten für Taxifahrten vorherzusagen. Der Datensatz enthält Felder wie zum Beispiel die Anzahl der Passagiere, die Anbieternummer und natürlich den Endpreis der Fahrt.

Getting Started

Zu Beginn muss das Framework installiert werden. Es kann klassisch als NuGet-Package geladen werden.

ModelBuilder-VisualStudio Extension

ML.NET bietet eine Benutzeroberfläche für das Trainieren von Modellen. Diese Extension findet sich hier.

Über Add/Machine Learning lässt sich diese öffnen.

Problemauswahl

Zu Beginn muss die Art des Problems ausgewählt werden. Folgende Klassen sind für die Auswahl verfügbar:

  • Binary classification
  • Multiclass classification
  • Regression
  • Image classification

Falls sich zu Beginn nicht abschätzen lässt, welche Klasse von Algorithmus vermutlich am besten funktionieren wird, können unter Custom Scenario verschiedene Algorithmen durchprobiert werden. Hier findest sich eventuell die passende Auswahl.

Wir wählen für den Taxidatensatz das Regressionsproblem (Price Prediction) aus.

Screenshot Price Scenario
Eigene Darstellung

Datenauswahl

Für die Auswahl der Trainingsdaten kann aus zwei Quellen gewählt werden:

  • .csv oder .tsv
  • SQL Server Tabelle

Wir wählen für das Taxi-Beispiel den Typ File, und wählen die .csv-Datei aus.

Anschließend wählen wir für Column to Predict (Label) die Spalte aus, die den Preis der Fahrt angibt (fare_amount).

Screenshot Add data
Eigene Darstellung

Als Input Columns (Features) wähle ich folgende Spalten:

VendorID passenger_count trip_distance PULocationID DOLocationID

Training

Für den Vorgang wird die vorgeschlagene Trainingszeit von 60 Minuten genutzt.

Screenshot Train
Eigene Darstellung

Am Ende erhalten wir den Algorithmus, der am besten für unser Problem funktioniert hat.

Bewertung

Hier können stichprobenartige Tests durchgeführt werden.

Für eine Validierung wurde folgende Route ausgedacht:

Online-Taxi-Rechner
Eigene Darstellung

Lässt man diese über einen Online-Taxi-Rechner kalkulieren, erhält man folgendes Ergebnis:

Online-Taxi-Rechner

Nun die Vorhersage unseres Modells:

Screenshot Vorhersage Modell
Eigene Darstellung

Das scheint Out-of-the-Box bereit, ein akzeptables Ergebnis zu sein.

Dieses ließe sich vermutlich noch weiter verbessern, indem man den Wochentag und die Uhrzeit der Fahrt noch in die Features miteinbezieht, da diese auch Einfluss auf den Preis der Fahrt nehmen.

Export

Nun kann man das Modell in dein Projekt exportieren.

Das Modell wird in Form von auto generiertem Code exportiert und ist als neues Projekt in der Projekt-Solution verfügbar. Anpassungen sind also auch nach dem Export möglich. Es muss allerdings neu trainiert werden.

Screenshot Code
Eigene Darstellung

Mehr Informationen über das Verwenden des Modells im Projekt finden sich hier.

Plain Code

Wenn das Modell komplett manuell über Code erstellt werden soll, gibt es mehr Freiheiten als beim Nutzen der Extension. Eine detaillierte Anleitung, wie das Framework benutzt werden kann, findet sich hier.

Verfügbare Transformationsmethoden

Es sind gängige Methoden wie Normalisierung, Skalierung oder Text-Tokenization verfügbar. Infos zu vielen anderen Datentransformationsmethoden finden sich hier.


Und wie funktioniert das Thema nun im Code?

Datensatz laden

Zu beginn müssen wir unseren Trainingsdatensatz laden.

var mlContext = new MLContext();

IDataView dataSource = mlContext.Data.LoadFromTextFile<CabData>(
                "path/to/cab_data.csv",
                separatorChar: ',',
                hasHeader: true);

In der Klasse, die die Trainingsdatenstruktur modelliert, kann man über Attribute festlegen, welche Spalte aus der .csv-Datei welcher Variable zugeordnet werden soll, und wie die Spalte später benannt werden soll.
class CabData
{

private float _vendorId;
private float _passengerCount;
private float _tripDistance;
private float _pickupLocationID;
private float _dropoffLocationID;
private float _fare;

// which column to choose from csv?
[LoadColumn(0)]
// column name after loading csv
[ColumnName("VendorID")]
public float VendorId { get => _vendorId; set => _vendorId = value; }
[LoadColumn(3)]
[ColumnName("PassengerCount")]
public float PassengerCount { get => _passengerCount; set => _passengerCount = value; }
[LoadColumn(4)]
[ColumnName("TripDistance")]
public float TripDistance { get => _tripDistance; set => _tripDistance = value; }
[LoadColumn(7)]
[ColumnName("PickupLocationID")]
public float PickupLocationID { get => _pickupLocationID; set => _pickupLocationID = value; }
[LoadColumn(8)]
[ColumnName("DropoffLocationID")]
public float DropoffLocationID { get => _dropoffLocationID; set => _dropoffLocationID = value; }         [LoadColumn(10)]
[ColumnName("Label")]
public float Fare { get => _fare; set => _fare = value;
}

Training

ML.NET arbeitet mit sogenannten Pipelines. Hierbei werden bestimmte Aktionen, seien es Schritte beim Training oder bei der Datentransformation vor dem Training, in einer bestimmten Reihenfolge ausgeführt.

Im ersten Schritt wollen wir die Daten transformieren. Wir wenden eine Normalisierung auf die Passagieranzahl an und kodieren die kategorischen Werte VendorID, PickupLocationID, DropoffLocationID mit dem One Hot Encoding.

Zuletzt legen wir noch die Features (=Spalten) fest, die für die Vorhersage verwendet werden sollen.

IEstimator<ITransformer> dataTransoformPipeline =
                 // apply LogMeanVariance normalization to Column PassengerCount
                 mlContext.Transforms.NormalizeLogMeanVariance(inputColumnName: "PassengerCount", outputColumnName: "PassengerCountNormalized")
                // apply one hot encoding to Columns VendorID, PickupLocationID and DropoffLocationID
                .Append(mlContext.Transforms.Categorical.OneHotEncoding(inputColumnName: "VendorID", outputColumnName: "VendorIDEncoded"))
                .Append(mlContext.Transforms.Categorical.OneHotEncoding(inputColumnName: "PickupLocationID", outputColumnName: "PickupLocationIDEncoded"))
                .Append(mlContext.Transforms.Categorical.OneHotEncoding(inputColumnName: "DropoffLocationID", outputColumnName: "DropoffLocationIDEncoded"))
                // select features used for prediction
                .Append(mlContext.Transforms.Concatenate(
                "Features",
                "VendorIDEncoded", 
               "PassengerCountNormalized", 
"TripDistance", 
"PickupLocationIDEncoded", 
"DropoffLocationIDEncoded"));
Nun erstellen wir die Pipeline für das Training. Hierfür hängen wir an die Transformationspipeline einfach einen Regressionsalgorithmus an. Für die Regression wird hier Sdca verwendet. An dieser Stelle ließen sich bei anderen Algorithmen auch die Parameter einstellen.
IEstimator<ITransformer> trainingPipeline = dataTransoformPipeline.Append(mlContext.Regression.Trainers.Sdca(labelColumnName: "Label"));
Wir filtern noch Datensätze, die eine TripDistance von weniger als 0.01 haben, um die Vorhersage nicht zu verfälschen. Außerdem teilen wir unseren Datensatz noch in 90% Trainingsdaten und 10% Validierungsdaten.
var filteredTrainingData = mlContext.Data.FilterRowsByColumn(dataSource, columnName: "TripDistance", lowerBound: 0.01f);
// split dataset for training & validation
TrainTestData splitData = mlContext.Data.TrainTestSplit(filteredTrainingData, 0.1);
Nun können wir das Training starten.
// start training
ITransformer model = trainingPipeline.Fit(splitData.TrainSet);

Bewertung

Nach dem Abschluss des Trainings empfiehlt es sich, die Genauigkeit des Modells zu testen. Anschließend kann dieses exportiert und gespeichert werden.

Wir testen das Modell auf unseren Testdatensatz.

IDataView predictions = model.Transform(splitData.TestSet);

// check model accuracy

var metrics = mlContext.Regression.Evaluate(predictions);

 

Mit dieser Konfiguration wurde eine Genauigkeit von 87% erreicht. Diese liegt über der Genauigkeit, die vom ML.NET-ModelBuilder erzielt wurde. Man hat hier mehr Freiheiten und kann somit an mehr Parametern drehen, die bei der Nutzung der Extension schlichtweg nicht einstellbar sind.

Hier noch ein Graph, der die Genauigkeit beschreibt:

Graph zur Genauigkeit
Eigene Darstellung

Nun noch zum Vergleich zum oben genannten Preis des Taxi-Rechners.

Prediction forCabData
{
VendorId = 1,
PassengerCount = 1,
TripDistance = 14.9,
PickupLocationID = 153,
DropoffLocationID = 12
}
---------------------------------
Predicted Taxi fare: 46.66$

Somit liegt die Vorhersage des selbst erstellten Modells näher an den geschätzten 45.60$ des Taxi Rechners.

Über eine entsprechende Methode kann das Modell exportiert werden.

// save the model
mlContext.Model.Save(model, dataSource.Schema, "taxi_fare_model.zip");

Analog lassen sich bereits gespeicherte Modells später erneut laden.
MLContext loadedMlContext = new MLContext();
DataViewSchema predictionPipelineSchema;
ITransformer trainedModel = loadedMlContext.Model.Load("taxi_fare_model.zip", out predictionPipelineSchema);

Über diese Methode können auch Modelle von anderen Frameworks, wie z.B. Tensorflow geladen werden.

 

Fazit

ML.NET bietet bereits sehr viele Algorithmen und deckt sehr viele UseCases ab.

Schade ist, dass die neuronalen Netze (bisher) außen vorgelassen wurden. Es gibt noch keine Möglichkeit, neuronale Netze zu erstellen.

Laut Microsoft soll solch ein Feature auch nicht in näherer Zukunft erscheinen.

Man muss derzeit also separat ein neuronales Netz-Modell trainieren. Dieses kann dann in ML.NET geladen werden.

Co-Autor: Felix Steck

 

Zurück zur Übersicht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*Pflichtfelder

*