Preparación de los datos para la compilación de un modelo

Aprenda cómo utilizar ML.NET para preparar datos para el procesamiento adicional o la creación de un modelo.

Los datos suelen estar dispersos o sin limpiar. Los algoritmos de aprendizaje automático de ML.NET esperan que las entradas o características estén en un vector numérico único. Del mismo modo, el valor que se va a predecir (etiqueta), especialmente cuando se trata de datos categóricos, debe estar codificado. Por lo tanto, uno de los objetivos de la preparación de datos es obtener los datos en el formato esperado por los algoritmos de ML.NET.

División de los datos en conjuntos de entrenamiento y prueba

En la sección siguiente se describen los problemas comunes al entrenar un modelo conocidos como sobreajuste e infraajuste. Dividir los datos y validar los modelos mediante un conjunto mantenido puede ayudarle a identificar y mitigar estos problemas.

Sobreajuste e infraajuste

El sobreajuste y el infraajuste son los dos problemas más comunes que se producen al entrenar un modelo. El infraajuste significa que el entrenador seleccionado no es lo suficientemente capaz de ajustarse al conjunto de datos de entrenamiento y normalmente da lugar a una pérdida alta durante el entrenamiento y una puntuación o métrica baja en el conjunto de datos de prueba. Para resolverlo, debe seleccionar un modelo más eficaz o realizar más ingeniería de características. El sobreajuste es lo contrario, lo que sucede cuando el modelo aprende demasiado bien los datos de entrenamiento. Esto suele producir una métrica de baja pérdida durante el entrenamiento, pero una pérdida alta en el conjunto de datos de prueba.

Una buena analogía para estos conceptos es estudiar para un examen. Supongamos que conocía las preguntas y respuestas con antelación. Después de estudiar, hace el examen y obtiene una puntuación perfecta. Buenas noticias Pero cuando se le vuelve a dar el examen con las preguntas reorganizadas y con un texto ligeramente diferente, obtendrá una puntuación inferior. Eso sugiere que memorizó las respuestas y no aprendió realmente los conceptos sobre los que se le estaba examinando. Este es un ejemplo de sobreajuste. El infraajuste es lo contrario, cuando los materiales de estudio que se le han dado no representan con precisión lo que se evalúa en el examen. Como resultado, recurre a adivinar las respuestas, ya que no tiene suficiente conocimiento para contestar correctamente.

División de los datos

Tome los siguientes datos de entrada y cárguelos en un elemento IDataView denominado 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
    }
};

Para dividir los datos en conjuntos de entrenamiento y pruebas, use el método TrainTestSplit(IDataView, Double, String, Nullable<Int32>).

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

El parámetro testFraction se usa para tomar el 0,2 o el 20 % del conjunto de datos para hacer pruebas. El 80 % restante se usa para el entrenamiento.

El resultado es DataOperationsCatalog.TrainTestData con dos IDataViews a los que puede acceder mediante TrainSet y TestSet.

Filtrado de los datos

En ocasiones, no todos los datos de un conjunto de datos son relevantes para el análisis. Un método para quitar los datos irrelevantes es el filtrado. DataOperationsCatalog contiene un conjunto de operaciones de filtro que recibe un IDataView que contiene todos los datos y devuelve un IDataView que contiene únicamente los puntos de datos de interés. Es importante tener en cuenta que dado que las operaciones de filtro no son un IEstimator o un ITransformer como las de TransformsCatalog, no pueden incluirse como parte de una canalización de preparación de datos de EstimatorChain o TransformerChain.

Tome los siguientes datos de entrada y cárguelos en un elemento IDataView denominado data:

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

Para filtrar datos según el valor de una columna, use el método FilterRowsByColumn.

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

El ejemplo anterior toma filas del conjunto de datos con un precio entre 200 000 y 1 000 000. El resultado de aplicar este filtro podría devolver solo las dos últimas filas de los datos y excluir la primera fila porque su precio es de 100 000 y no está dentro del intervalo especificado.

Reemplazar los valores que faltan

En los conjuntos de datos suelen haber valores ausentes. Un enfoque para tratar con los valores ausentes es reemplazarlos por el valor predeterminado para el tipo especificado, si lo hay, o por otro valor significativo como, por ejemplo, el valor medio de los datos.

Tome los siguientes datos de entrada y cárguelos en un elemento IDataView denominado data:

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

Tenga en cuenta que el último elemento de nuestra lista tiene un valor ausente para Price. Para reemplazar los valores ausentes en la columna Price, use el método ReplaceMissingValues para rellenar ese valor ausente.

Importante

ReplaceMissingValue solo funciona con datos numéricos.

// 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 admite varios modos de reemplazo. El ejemplo anterior usa el modo de reemplazo Mean que rellena el valor ausente con el valor medio de esa columna. El resultado del reemplazo se rellena en la propiedad Price para el último elemento en nuestros datos con 200 000, ya que es la media de 100 000 y 300 000.

Usar normalizadores

La normalización es una técnica de procesamiento previo de datos que se usa para escalar características de forma que estén en el mismo intervalo, normalmente entre 0 y 1, para que un algoritmo de aprendizaje automático pueda procesarlas con más precisión. Por ejemplo, los intervalos de edad e ingresos varían significativamente, ya que la edad suele estar en el intervalo de 0 a 100 y los ingresos suelen estar en el intervalo de cero a miles. Visite la página de transformaciones para ver una lista más detallada y una descripción de las transformaciones de normalización.

Normalización mínima-máxima

Tome los siguientes datos de entrada y cárguelos en un elemento IDataView denominado data:

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

La normalización se puede aplicar a las columnas con valores numéricos individuales, así como a los vectores. Normalice los datos de la columna Price mediante la normalización mínima-máxima con el método 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);

Los valores originales del precio [200000,100000] se convierten en [ 1, 0.5 ] mediante la fórmula de normalización MinMax que genera valores de salida en el intervalo entre 0 y 1.

Discretización

La discretización convierte valores continuos en una representación discreta de la entrada. Por ejemplo, suponga que una de sus características es la edad. En lugar de usar el valor de edad real, la discretización crea intervalos para ese valor. 0-18 puede ser un rango, otro podría ser 19-35 y así sucesivamente.

Tome los siguientes datos de entrada y cárguelos en un elemento IDataView denominado data:

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

Normalice los datos en rangos con el método NormalizeBinning. El parámetro maximumBinCount permite especificar el número de rangos necesarios para clasificar los datos. En este ejemplo, los datos se colocarán en dos rangos.

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

El resultado de la discretización crea límites de rangos de [0,200000,Infinity]. Por lo tanto, los rangos resultantes son [0,1,1] porque la primera observación está comprendida entre 0 y 200 000 y las demás son mayores que 200 000, pero menores que infinito.

Trabajar con datos categóricos

Uno de los tipos de datos más comunes son los datos categóricos. Los datos categóricos tienen un número finito de categorías. Por ejemplo, los estados de EE. UU. o una lista de los tipos de animales que se encuentran en un conjunto de imágenes. Si los datos categóricos son características o etiquetas, deben asignarse a un valor numérico para que se puedan usar para generar un modelo de aprendizaje automático. Hay varias maneras de trabajar con datos categóricos en ML.NET, en función del problema que quiera resolver.

Asignación de valores de clave

En ML.NET, una clave es un valor entero que representa una categoría. La asignación de valores de clave se usa con más frecuencia para asignar etiquetas de cadena en valores enteros únicos para el entrenamiento y, después, volver a sus valores de cadena cuando el modelo se utilice para realizar una predicción.

Las transformaciones utilizadas para realizar la asignación de valores de clave son MapValueToKey y MapKeyToValue.

MapValueToKey agrega un diccionario de asignaciones en el modelo, de modo que MapKeyToValue pueda realizar la transformación inversa al realizar una predicción.

Codificación frecuente

Una codificación frecuente toma un conjunto finito de valores y los asigna a enteros cuya representación binaria tiene un valor 1 único en posiciones únicas en la cadena. Una codificación frecuente puede ser la mejor opción si no hay una ordenación implícita de los datos categóricos. En la tabla siguiente se muestra un ejemplo con códigos postales como valores sin formato.

Valor sin formato Valor codificado frecuente
98052 00...01
98100 00...10
... ...
98109 10...00

La transformación para convertir los datos categóricos en números codificados de acceso frecuente es OneHotEncoding.

Aplicación de algoritmo hash

La aplicación de algoritmo hash es otra manera de convertir los datos categóricos en números. Una función hash asigna datos de tamaño arbitrario (una cadena de texto, por ejemplo) a un número con un intervalo fijo. La aplicación de algoritmo hash puede ser una forma rápida y eficaz de vectorizar las características. Un ejemplo importante de aplicación de algoritmo hash en el aprendizaje automático es el filtrado de correo no deseado, donde, en lugar de mantener un diccionario de palabras conocidas, se aplica un algoritmo hash a cada palabra del correo electrónico y se agrega a un vector de características grande. Este uso de la aplicación de algoritmo hash evita el problema de la elusión de filtrado del correo no deseado malintencionado mediante el uso de palabras que no están en el diccionario.

ML.NET proporciona transformación hash para realizar operaciones de aplicación de algoritmo hash en texto, fechas y datos numéricos. Al igual que la asignación de claves de valor, las salidas de la transformación hash son tipos de clave.

Trabajar con datos de texto

Al igual que los datos categóricos, los datos de texto deben transformarse en características numéricas antes de usarlos para crear un modelo de aprendizaje automático. Visite la página de transformaciones para ver una lista más detallada y una descripción de las transformaciones de texto.

Uso de datos, como los datos siguientes que se han cargado en 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 proporciona la transformación FeaturizeText, que toma el valor de cadena de un texto y crea un conjunto de características a partir del texto, aplicando una serie de transformaciones individuales.

// 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 transformación resultante convierte los valores de texto en la columna Description en un vector numérico que es similar a la salida siguiente:

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

Las transformaciones que componen FeaturizeText también pueden aplicarse individualmente para conseguir un mayor control de la generación de características.

// 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 contiene un subconjunto de operaciones realizadas por el método FeaturizeText. La ventaja de una canalización más compleja es el control y la visibilidad sobre las transformaciones aplicadas a los datos.

Con la primera entrada como ejemplo, la siguiente es una descripción detallada de los resultados producidos por los pasos de transformación definidos por textEstimator:

Texto original: Se trata de un buen producto

Transformación Descripción Resultado
1. NormalizeText Convierte todas las letras en minúsculas de forma predeterminada este es un buen producto
2. TokenizeWords Divide la cadena en palabras individuales ["este","es","un","buen","producto"]
3. RemoveDefaultStopWords Quita las palabras irrelevantes, como es y un. ["buen","producto"]
4. MapValueToKey Asigna los valores a las claves (categorías), según los datos de entrada [1,2]
5. ProduceNGrams Transforma texto en secuencia de palabras consecutivas [1,1,1,0,0]
6. NormalizeLpNorm Escala las entradas por su valor lp-norm [ 0.577350529, 0.577350529, 0.577350529, 0, 0 ]