Préparer des données pour la génération d’un modèle

Découvrez comment utiliser ML.NET pour préparer des données en vue d’effectuer un traitement supplémentaire ou de générer un modèle.

Les données sont souvent dans un format incorrect et disséminées. Les algorithmes de machine learning de ML.NET s’attendent à ce que l’entrée ou les caractéristiques se trouvent dans un seul vecteur numérique. De même, la valeur à prédire (étiquette), en particulier quand il s’agit de données de catégories, doit être codée. Ainsi, un des objectifs de la préparation des données consiste à obtenir les données dans le format attendu par les algorithmes ML.NET.

Diviser les données en jeux d’entraînement et de test

La section suivante décrit les problèmes courants lors de l’entraînement d’un modèle, appelés surajustement et sous-ajustement. La division de vos données et la validation de vos modèles en utilisant un ensemble mis de côté peuvent vous aider à identifier et à atténuer ces problèmes.

Surajustement et sous-ajustement

Le surajustement et le sous-ajustement sont les deux problèmes les plus courants que vous rencontrez lors de l’entraînement d’un modèle. Le sous-ajustement signifie que l’entraîneur sélectionné n’est pas suffisamment capable de s’adapter au jeu de données d’entraînement, et aboutit généralement à une perte élevée pendant l’entraînement et un score/métrique faible sur le jeu de données de test. Pour résoudre ce problème, vous devez sélectionner un modèle plus puissant ou aller plus loin dans l’ingénierie des caractéristiques. Le surajustement est le contraire, qui se produit quand le modèle apprend trop bien les données d’entraînement. Ceci aboutit généralement à une métrique de perte faible pendant l’entraînement, mais à une perte élevée sur le jeu de données de test.

Une bonne analogie pour ces concepts est l’étude pour un examen. Supposons que vous connaissez à l’avance les questions et les réponses. Après avoir étudié, vous passez le test et vous obtenez un score parfait. Bonne nouvelle ! Cependant, quand vous repassez l’examen avec les questions réorganisées et avec une formulation légèrement différente, vous obtenez un score inférieur. Cela suggère que vous avez mémorisé les réponses et que vous n’avez pas réellement appris les concepts sur lesquels vous avez été testé. C’est un exemple de surajustement. Le sous-ajustement est le contraire : les documents à étudier qui vous ont été donnés ne représentent pas exactement ce sur quoi vous êtes évalué pour l’examen. Par conséquent, vous devez deviner les réponses, car vous n’avez pas les connaissances suffisantes pour répondre correctement.

Fractionner les données

Prenez les données d’entrée suivantes et chargez-les dans une IDataView appelée 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
    }
};

Pour diviser les données en groupes d’entraînement / test, utilisez la méthode TrainTestSplit(IDataView, Double, String, Nullable<Int32>).

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

Le paramètre testFraction est utilisé pour prendre 0,2 ou 20 % du jeu de données à des fins de test. Les 80 % restants sont utilisés pour l’entraînement.

Le résultat est DataOperationsCatalog.TrainTestData avec deux IDataViews auxquelles vous pouvez accéder via TrainSet et TestSet.

Filtrer les données

Parfois, toutes les données d’un jeu de données ne sont pas appropriées pour l’analyse. Une approche pour supprimer les données non pertinentes consiste à effectuer un filtrage. Le DataOperationsCatalog comporte un ensemble d’opérations de filtre qui acceptent un IDataView contenant toutes les données et retournent un IDataView contenant uniquement les points de données d’intérêt. Il est important de noter que les opérations de filtre, qui ne sont pas un IEstimator ou ITransformer comme dans le TransformsCatalog, ne peuvent pas être incluses dans le cadre d’un pipeline de préparation des données EstimatorChain ou TransformerChain.

Prenez les données d’entrée suivantes et chargez-les dans une IDataView appelée data :

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

Pour filtrer les données en fonction de la valeur d’une colonne, utilisez la méthode FilterRowsByColumn.

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

L’exemple ci-dessus prend les lignes dans le jeu de données ayant un prix entre 200 000 et 1 000 000. L’application de ce filtre retourne uniquement les deux dernières lignes dans les données et exclut la première ligne, car son prix, 1 00 000, n’appartient pas à la plage spécifiée.

Remplacer des valeurs manquantes

Les valeurs manquantes sont courantes dans les jeux de données. Pour traiter les valeurs manquantes, une approche consiste à les remplacer par la valeur par défaut du type donné éventuel ou par une autre valeur significative telle que la valeur moyenne dans les données.

Prenez les données d’entrée suivantes et chargez-les dans une IDataView appelée data :

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

Notez que le dernier élément dans notre liste a une valeur manquante pour Price. Pour remplacer la valeur manquante dans la colonne Price, utilisez la méthode ReplaceMissingValues afin de remplir cette valeur manquante.

Important

ReplaceMissingValue fonctionne uniquement avec des données numériques.

// 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 prend en charge divers modes de remplacement. L’exemple ci-dessus utilise le mode de remplacement Mean, qui remplit la valeur manquante avec la valeur moyenne de la colonne concernée. Le remplacement se traduit par le remplissage de la propriété Price pour le dernier élément dans les données avec la valeur 200 000, soit la moyenne de 100 000 et 300 000.

Utiliser des normaliseurs

La normalisation est une technique de prétraitement des données utilisée pour mettre à l’échelle les caractéristiques afin qu’elles se trouvent dans la même plage, généralement comprise entre 0 et 1, pour qu’elles puissent être traitées de façon plus juste par un algorithme de machine learning. Par exemple, les plages pour l’âge et le revenu varient de façon importante, l’âge étant généralement compris entre 0 et 100, et le revenu entre zéro et plusieurs milliers. Consultez la page sur les transformations pour obtenir une liste et une description plus détaillées des transformations de normalisation.

Normalisation min-max

Prenez les données d’entrée suivantes et chargez-les dans une IDataView appelée data :

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

La normalisation peut être appliquée aux colonnes avec des valeurs numériques uniques, ainsi qu’aux vecteurs. Normalisez les données dans la colonne Price à l’aide de la normalisation min-max avec la méthode NormalizeMinMax.

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

Les valeurs de prix d’origine [200000,100000] sont converties en [ 1, 0.5 ] en utilisant la formule de normalisation MinMax, qui génère des valeurs de sortie appartenant dans la plage de 0-1.

Quantification

La quantification convertit des valeurs continues en une représentation discrète de l’entrée. Par exemple, supposons qu’une de vos caractéristiques est l’âge. Au lieu d’utiliser la valeur de l’âge réel, le binning crée des plages pour cette valeur. 0-18 pourrait être une cellule, de même que 19-35, et ainsi de suite.

Prenez les données d’entrée suivantes et chargez-les dans une IDataView appelée data :

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

Normalisez les données en cellules à l’aide de la méthode NormalizeBinning. Le paramètre maximumBinCount vous permet de spécifier le nombre de cellules nécessaires pour classifier vos données. Dans cet exemple, les données sont placées dans deux cellules.

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

La quantification crée des bornes de cellule de [0,200000,Infinity]. Ainsi, les cellules obtenues sont [0,1,1], car la première observation est comprise entre 0 et 200 000, et les autres sont supérieures à 200 000 mais inférieures à l’infini.

Utiliser des données de catégorie

Les données de catégorie sont un des types de données les plus courants. Les données de catégorie ont un nombre fini de catégories. Par exemple, les états des États-Unis ou une liste des types d’animaux trouvés dans un ensemble de photos. Que les données de catégorie soient des caractéristiques ou des étiquettes, elles doivent être mappées à une valeur numérique afin de pouvoir être utilisées pour générer un modèle Machine Learning. Il existe plusieurs façons d’utiliser des données de catégorie dans ML.NET, en fonction du problème que vous résolvez.

Mappage des valeurs de clé

Dans ML.NET, une clé est une valeur entière qui représente une catégorie. Le mappage de valeurs de clé est le plus souvent utilisé pour mapper des étiquettes de type chaîne en valeurs entières uniques pour l’entraînement, puis pour les rétablir à leurs valeurs de type chaîne quand le modèle est utilisé pour faire une prédiction.

Les transformations utilisées pour effectuer le mappage de valeurs de clé sont MapValueToKey et MapKeyToValue.

MapValueToKey ajoute un dictionnaire des mappages dans le modèle, pour que MapKeyToValue puisse effectuer la transformation inverse lors d’une prédiction.

Encodage 1 parmi n (one-hot)

Un encodage 1 parmi n prend un ensemble fini de valeurs et les mappe sur des entiers dont la représentation binaire a une seule valeur 1 dans des positions uniques de la chaîne. Un encodage 1 parmi n peut être le meilleur choix s’il n’y a pas d’ordre implicite des données de catégorie. Le tableau suivant montre un exemple avec des codes postaux comme valeurs brutes.

Valeur brute Valeur encodée 1 parmi n
98052 00...01
98100 00...10
... ...
98109 10...00

La transformation pour convertir des données de catégorie en nombres encodés 1 parmi n est OneHotEncoding.

Hashing

Le hachage est un autre moyen de convertir des données de catégorie en nombres. Une fonction de hachage mappe des données d’une taille arbitraire (par exemple une chaîne de texte) à un nombre avec une plage fixe. Le hachage peut être un moyen rapide et efficace de vectoriser des caractéristiques. Un exemple notable de hachage dans le machine learning est le filtrage du courrier indésirable où, au lieu de gérer un dictionnaire de mots connus, chaque mot de l’e-mail est haché et ajouté à un grand vecteur de caractéristiques. L’utilisation du hachage de cette façon évite le problème du contournement du courrier indésirable malveillant par l’utilisation de mots qui ne figurent pas dans le dictionnaire.

ML.NET fournit une transformation de hachage pour effectuer le hachage sur du texte, des dates et des données numériques. Comme le mappage de clés de valeur, les sorties de la transformation de hachage sont des types de clé.

Utiliser des données texte

Comme les données de catégorie, les données texte doivent être transformées en caractéristiques numériques avant d’être utilisées pour générer un modèle Machine Learning. Consultez la page sur les transformations pour obtenir une liste et une description plus détaillées des transformations de texte.

Supposons que nous utilisons des données telles que celles ci-dessous qui ont été chargées dans un IDataView :

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 fournit la transformation FeaturizeText, qui prend la valeur de chaîne d’un texte et crée un ensemble de caractéristiques à partir du texte, en appliquant une série de transformations individuelles.

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

La transformation qui en résulte convertit les valeurs de texte dans la colonne Description en un vecteur numérique qui est similaire à la sortie ci-dessous :

[ 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 ]

Les transformations qui composent FeaturizeText peuvent également être appliquées individuellement pour un contrôle plus fin sur la génération des caractéristiques.

// 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 contient un sous-ensemble des opérations effectuées par la méthode FeaturizeText. L’avantage d’un pipeline plus complexe est qu’il procure une visibilité et un contrôle des transformations appliquées aux données.

Si, par exemple, nous utilisons la première entrée, voici une description détaillée des résultats produits par les étapes de transformation définies par textEstimator :

Texte d’origine : C’est un bon produit

Transformation Description Résultats
1. NormalizeText Convertit toutes les lettres en minuscules par défaut this is a good product
2. TokenizeWords Fractionne la chaîne en mots individuels ["this","is","a","good","product"]
3. RemoveDefaultStopWords Supprime les mots vides comme est et un. ["good","product"]
4. MapValueToKey Mappe les valeurs sur des clés (catégories) en fonction des données d’entrée [1,2]
5. ProduceNGrams Transforme le texte en une séquence de mots consécutifs [1,1,1,0,0]
6. NormalizeLpNorm Applique une mise à l’échelle aux entrées d’après leur norme IP [ 0.577350529, 0.577350529, 0.577350529, 0, 0 ]