Vorbereiten von Daten für die Modellerstellung

Erfahren Sie, wie Sie ML.NET verwenden können, um Daten für die weitere Verarbeitung oder die Erstellung eines Modells aufzubereiten.

Die Daten sind oft unsauber und haben eine geringe Dichte. Für Machine-Learning-Algorithmen in ML.NET sind Eingaben oder Features in einem einzigen numerischen Vektor erforderlich. Analog dazu muss der Wert, der vorhersagt werden soll (Label), codiert werden. Dies gilt insbesondere für kategorische Daten. Daher besteht eines der Ziele der Datenaufbereitung darin, die Daten in das von ML.NET-Algorithmen erwartete Format zu bringen.

Aufteilen von Daten in Trainings- und Testsätze

Im folgenden Abschnitt werden häufige Probleme beim Trainieren eines Modells beschrieben, das als Überanpassung und Unteranpassung bezeichnet wird. Wenn Sie Ihre Daten aufteilen und Ihre Modelle anhand eines festgelegten Satzes überprüfen, können Sie diese Probleme identifizieren und beheben.

Überanpassung und Unteranpassung

Überanpassung und Unteranpassung sind die beiden häufigsten Probleme, die beim Trainieren eines Modells auftreten. Unteranpassung bedeutet, dass der ausgewählte Trainer nicht in der Lage ist, das Trainingsdataset anzupassen, was in der Regel zu einem hohen Datenverlust während des Trainings und einer niedrigen Bewertung/Metrik für das Testdataset führt. Um dies zu beheben, müssen Sie entweder ein leistungsstärkeres Modell auswählen oder das Feature Engineering weiterführen. Überanpassung ist das Gegenteil und geschieht, wenn das Modell die Trainingsdaten zu gut lernt. Dies führt in der Regel zu einer Metrik mit geringem Verlust während des Trainings, aber zu einem hohen Verlust im Testdataset.

Eine gute Analogie zu diesen Konzepten ist das Lernen für eine Prüfung. Angenommen, Sie wussten die Fragen und Antworten im Voraus. Nach dem Lernen legen Sie den Test ab und erhalten die höchste Punktzahl. Gute Nachrichten! Wenn Sie die Prüfung jedoch erneut mit neu angeordneten Fragen und mit etwas unterschiedlichen Formulierungen erhalten, erhalten Sie eine niedrigere Punktzahl. Das deutet darauf hin, dass Sie die Antworten auswendig gelernt haben und die Inhalte der Prüfung nicht wirklich verstanden haben. Dies ist ein Beispiel für Überanpassung. Im Gegensatz dazu spiegeln bei einer Unteranpassung die Ihnen zur Verfügung gestellten Lernmaterialien nicht genau das wider, was in der Prüfung abgefragt wird. Infolgedessen fangen Sie an, die Antworten zu raten, da Ihre Kenntnisse nicht ausreichen, um richtig zu antworten.

Aufteilen von Daten

Laden Sie die folgenden Eingabedaten in eine IDataView namens data:

var homeDataList = new HomeData[]
{
    new()
    {
        NumberOfBedrooms = 1f,
        Price = 100_000f
    },
    new()
    {
        NumberOfBedrooms = 2f,
        Price = 300_000f
    },
    new()
    {
        NumberOfBedrooms = 6f,
        Price = 600_000f
    },
    new()
    {
        NumberOfBedrooms = 3f,
        Price = 300_000f
    },
    new()
    {
        NumberOfBedrooms = 2f,
        Price = 200_000f
    }
};

Verwenden Sie die TrainTestSplit(IDataView, Double, String, Nullable<Int32>)-Methode, um Daten in Trainings-/Testsätze aufzuteilen.

// Apply filter
TrainTestData dataSplit = mlContext.Data.TrainTestSplit(data, testFraction: 0.2);

Der testFraction-Parameter wird verwendet, um 0,2 oder 20 % des Datasets zu testen. Die restlichen 80 % werden für das Training verwendet.

Das Ergebnis ist DataOperationsCatalog.TrainTestData mit zwei IDataViews, auf die Sie über TrainSet und TestSet zugreifen können.

Filtern von Daten

Manchmal sind nicht alle Daten in einem Dataset für die Analyse relevant. Ein Ansatz zur Entfernung irrelevanter Daten ist das Filtern. Der DataOperationsCatalog enthält eine Reihe von Filtervorgängen, der eine IDataView mit allen Daten aufnehmen und eine IDataView zurückgeben kann, die nur die Datenpunkte von Interesse enthält. Es ist wichtig zu beachten, dass Filtervorgänge nicht als Teil einer EstimatorChain- oder -Datenaufbereitungspipeline aufgenommen werden können, da sie kein IEstimator oder ITransformer wie im TransformerChainTransformsCatalog sind.

Laden Sie die folgenden Eingabedaten in eine IDataView namens data:

HomeData[] homeDataList = new HomeData[]
{
    new ()
    {
        NumberOfBedrooms=1f,
        Price=100000f
    },
    new ()
    {
        NumberOfBedrooms=2f,
        Price=300000f
    },
    new ()
    {
        NumberOfBedrooms=6f,
        Price=600000f
    }
};

Um Daten nach dem Wert einer Spalte zu filtern, verwenden Sie die FilterRowsByColumn-Methode.

// Apply filter
IDataView filteredData = mlContext.Data.FilterRowsByColumn(data, "Price", lowerBound: 200000, upperBound: 1000000);

Das obige Beispiel zeigt Zeilen im Dataset mit einem Preis zwischen 20.0000 und 100.000.000. Das Ergebnis der Anwendung dieses Filters würde nur die letzten beiden Zeilen in den Daten zurückgeben und die erste Zeile ausschließen, da der Preis den Wert 100000 hat und nicht im angegebenen Bereich liegt.

Ersetzen fehlender Werte

Fehlende Werte kommen in Datasets häufig vor. Ein Ansatz für den Umgang mit fehlenden Daten, besteht darin, sie durch den Standardwert für den gegebenen Typ zu ersetzen, falls vorhanden, oder durch einen anderen sinnvollen Wert, wie beispielsweise den Mittelwert der Daten.

Laden Sie die folgenden Eingabedaten in eine IDataView namens data:

HomeData[] homeDataList = new HomeData[]
{
    new ()
    {
        NumberOfBedrooms=1f,
        Price=100000f
    },
    new ()
    {
        NumberOfBedrooms=2f,
        Price=300000f
    },
    new ()
    {
        NumberOfBedrooms=6f,
        Price=float.NaN
    }
};

Beachten Sie, dass im letzten Element in unserer Liste ein Wert für Price fehlt. Um die fehlenden Werte in der Price-Spalte zu ersetzen, verwenden Sie die -Methode ReplaceMissingValues, um diesen fehlenden Wert einzugeben.

Wichtig

ReplaceMissingValue funktioniert nur mit numerischen Daten.

// Define replacement estimator
var replacementEstimator = mlContext.Transforms.ReplaceMissingValues("Price", replacementMode: MissingValueReplacingEstimator.ReplacementMode.Mean);

// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
ITransformer replacementTransformer = replacementEstimator.Fit(data);

// Transform data
IDataView transformedData = replacementTransformer.Transform(data);

ML.NET unterstützt verschiedene Ersetzungsmodi. Das obige Beispiel verwendet den Ersetzungsmodus Mean, der den fehlenden Wert mit dem Durchschnittswert dieser Spalte auffüllt. Das Ergebnis der Ersetzung trägt für die Price-Eigenschaft für das letzte Element in unseren Daten mit 200.000 ein, da dies der Durchschnitt von 100.000 und 300.000 ist.

Verwenden von Normalisierungsfunktionen

Normalisierung ist eine Datenvorverarbeitungstechnik, die verwendet wird, um Features so zu skalieren, dass sie sich im gleichen Bereich, normalerweise zwischen 0 und 1, befinden, damit sie von einem Algorithmus für maschinelles Lernen präziser verarbeitet werden können. So variieren beispielsweise die Bereiche für Alter und Einkommen stark, wobei das Alter im Allgemeinen im Bereich von 0-100 und das Einkommen im Allgemeinen im Bereich von 0 bis x-Tausende liegt. Eine detailliertere Liste und Beschreibung der Normalisierungstransformationen finden Sie auf der Seite zu Datentransformationen.

Min-Max-Normalisierung

Laden Sie die folgenden Eingabedaten in eine IDataView namens data:

HomeData[] homeDataList = new HomeData[]
{
    new ()
    {
        NumberOfBedrooms = 2f,
        Price = 200000f
    },
    new ()
    {
        NumberOfBedrooms = 1f,
        Price = 100000f
    }
};

Auf Spalten mit einzelnen numerischen Werten sowie auf Vektoren kann Normalisierung angewendet werden. Verwenden Sie die Min-Max-Normalisierung mit der NormalizeMinMax-Methode, um die Daten in der Spalte Price zu normalisieren.

// Define min-max estimator
var minMaxEstimator = mlContext.Transforms.NormalizeMinMax("Price");

// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
ITransformer minMaxTransformer = minMaxEstimator.Fit(data);

// Transform data
IDataView transformedData = minMaxTransformer.Transform(data);

Die ursprünglichen Preiswerte [200000,100000] werden unter Verwendung der Normalisierungsformel MinMax in [ 1, 0.5 ] umgewandelt, wodurch Ausgabewerte im Bereich von 0 bis 1 generiert werden.

Diskretisieren

Die Quantisierung konvertiert kontinuierliche Werte in eine diskrete Darstellung der Eingabe. Nehmen wir beispielsweise an, dass eines Ihrer Features das Alter ist. Anstatt den tatsächlichen Alterswert zu verwenden, werden durch die Quantisierung Bereiche für diesen Wert erstellt. 0 bis 18 könnte dabei einer der Bereiche sein, ein anderer könnte 19 bis 35 sein usw.

Laden Sie die folgenden Eingabedaten in eine IDataView namens data:

HomeData[] homeDataList = new HomeData[]
{
    new ()
    {
        NumberOfBedrooms=1f,
        Price=100000f
    },
    new ()
    {
        NumberOfBedrooms=2f,
        Price=300000f
    },
    new ()
    {
        NumberOfBedrooms=6f,
        Price=600000f
    }
};

Normalisieren Sie die Daten mit der NormalizeBinning-Methode in Bereiche. Mit dem maximumBinCount-Parameter können Sie die Anzahl der Bereiche angeben, die zur Klassifizierung Ihrer Daten benötigt werden. In diesem Beispiel werden Daten in zwei Bereiche aufgeteilt.

// Define binning estimator
var binningEstimator = mlContext.Transforms.NormalizeBinning("Price", maximumBinCount: 2);

// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
var binningTransformer = binningEstimator.Fit(data);

// Transform Data
IDataView transformedData = binningTransformer.Transform(data);

Durch die Quantisierung werden Bereichsbegrenzungen von [0,200000,Infinity] erstellt. Daher sind die resultierenden Bereiche [0,1,1], weil die erste beobachteten Werte zwischen 0 und 200.000 liegen und die anderen größer als 200.000, aber kleiner als unendlich sind.

Arbeiten mit Kategoriedaten

Einer der gängigsten Datentypen sind Kategoriedaten. Kategoriedaten zeichnen sich durch eine begrenzte Anzahl von Kategorien aus. Beispiele sind die Bundesstaaten der USA oder eine Liste der Tierarten, die in einer Reihe von Bildern gefunden wurden. Unabhängig davon, ob es sich bei den Kategoriedaten um Features oder Beschriftungen handelt, müssen sie einem numerischen Wert zugeordnet werden, damit sie zur Erzeugung eines Machine Learning-Modells verwendet werden können. Es gibt je nach dem zu lösenden Problem eine Reihe von Möglichkeiten, in ML.NET mit Kategoriedaten zu arbeiten.

Schlüsselwertzuordnung

In ML.NET ist ein Schlüssel ein ganzzahliger Wert, der eine Kategorie darstellt. Die Schlüsselwertzuordnung kommt meist zum Einsatz, um Zeichenfolgenbeschriftungen für das Training eindeutigen ganzzahligen Werten und dann wieder ihren Zeichenfolgenwerten zuzuordnen, sobald das Modell zum Erstellen einer Vorhersage verwendet wird.

Die für die Schlüsselwertzuordnung verwendeten Transformationen sind MapValueToKey und MapKeyToValue.

MapValueToKey fügt ein Wörterbuch von Zuordnungen im Modell hinzu, sodass MapKeyToValue bei der Erstellung einer Vorhersage die Rücktransformation vornehmen kann.

1-aus-n-Code

1-aus-n-Code verwendet eine endliche Menge von Werten und ordnet sie ganzen Zahlen zu, deren binäre Darstellung einen einzigen 1-Wert an eindeutigen Positionen in der Zeichenfolge aufweist. 1-aus-n-Code kann die beste Wahl sein, wenn es keine implizite Reihenfolge der Kategoriedaten gibt. In der folgenden Tabelle wird ein Beispiel mit Postleitzahlen als Rohwerte gezeigt.

Rohwert 1-aus-n-Code-Wert
98052 00...01
98100 00...10
... ...
98109 10...00

Die Transformation zum Konvertieren von Kategoriedaten in1-aus-n-Code-Zahlen ist OneHotEncoding.

Hashing

Hashing ist eine weitere Möglichkeit, um Kategoriedaten in Zahlen umzuwandeln. Eine Hashfunktion ordnet Daten beliebiger Größe (z. B. eine Textzeichenfolge) einer Zahl mit einem festen Bereich zu. Hashing kann eine schnelle und speichereffiziente Methode zur Vektorisierung von Features sein. Ein nennenswertes Beispiel für Hashing beim maschinellen Lernen sind E-Mail-Spamfilter, bei denen anstelle eines Wörterbuchs bekannter Wörter jedes Wort in der E-Mail gehasht und zu einem großen Featurevektor hinzugefügt wird. Durch den Einsatz von Hashing wird das Problem der Umgehung des Spamfilters durch die Verwendung von Wörtern vermieden, die nicht im Wörterbuch stehen.

ML.NET bietet eine Hash-Transformation zur Durchführung des Hashings für Text, Datumsangaben und numerische Daten. Wie bei der Schlüsselwertzuordnung sind die Ausgaben der Hashtransformation Schlüsseltypen.

Arbeiten mit Textdaten

Wie Kategoriedaten müssen Textdaten in numerische Features umgewandelt werden, bevor sie zum Erstellen eines Machine Learning-Modells verwendet werden können. Eine detailliertere Liste und Beschreibung der Texttransformationen finden Sie auf der Seite zu Datentransformationen.

Verwenden Sie Daten wie die Daten unten, die in eine IDataView geladen wurden:

ReviewData[] reviews = new ReviewData[]
{
    new ReviewData
    {
        Description="This is a good product",
        Rating=4.7f
    },
    new ReviewData
    {
        Description="This is a bad product",
        Rating=2.3f
    }
};

ML.NET bietet die FeaturizeText-Transformation, die den Zeichenfolgenwert eines Texts verwendet und durch Anwenden einer Reihe einzelner Transformationen eine Reihe von Features anhand des Texts erstellt.

// Define text transform estimator
var textEstimator  = mlContext.Transforms.Text.FeaturizeText("Description");

// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
ITransformer textTransformer = textEstimator.Fit(data);

// Transform data
IDataView transformedData = textTransformer.Transform(data);

Die resultierende Transformation konvertiert die Textwerte in der Spalte Description in einen numerischen Vektor, der der folgenden Ausgabe ähnelt:

[ 0.2041241, 0.2041241, 0.2041241, 0.4082483, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0, 0, 0, 0, 0.4472136, 0.4472136, 0.4472136, 0.4472136, 0.4472136, 0 ]

Die Transformationen, aus denen sich FeaturizeText zusammensetzt, können auch für eine präzisere Steuerung der Generierung von Features einzeln angewendet werden.

// Define text transform estimator
var textEstimator = mlContext.Transforms.Text.NormalizeText("Description")
    .Append(mlContext.Transforms.Text.TokenizeIntoWords("Description"))
    .Append(mlContext.Transforms.Text.RemoveDefaultStopWords("Description"))
    .Append(mlContext.Transforms.Conversion.MapValueToKey("Description"))
    .Append(mlContext.Transforms.Text.ProduceNgrams("Description"))
    .Append(mlContext.Transforms.NormalizeLpNorm("Description"));

textEstimator enthält eine Teilmenge von Vorgängen, die von der FeaturizeText-Methode durchgeführt werden. Der Vorteil einer komplexeren Pipeline ist die Steuerung und Sichtbarkeit hinsichtlich der Transformationen, die für die Daten durchgeführt werden.

Im Folgenden finden Sie am Beispiel des ersten Eintrags eine detaillierte Beschreibung der Ergebnisse, die durch die vom textEstimator definierten Transformationsschritte generiert wurden:

Originaltext: Das ist ein gutes Produkt

Transformieren BESCHREIBUNG Ergebnis
1. NormalizeText Konvertiert standardmäßig alle Buchstaben in Kleinbuchstaben das ist ein gutes produkt
2. TokenizeWords Teilt die Zeichenfolge in einzelne Wörter auf [„das“, „ist“, „ein“, „gutes“, „produkt“]
3. RemoveDefaultStopWords Entfernt Stoppwörter wie ist und ein. [„gutes“, „produkt“]
4. MapValueToKey Ordnet die Werte den Schlüsseln (Kategorien) basierend auf den Eingabedaten zu [1,2]
5. ProduceNGrams Transformiert Text in eine Folge von aufeinander folgenden Wörtern [1,1,1,0,0]
6. NormalizeLpNorm Skaliert Eingaben durch ihre Lp-Norm [ 0,577350529, 0,577350529, 0,577350529, 0, 0 ]