Partilhar via


Preparar dados para criar um modelo

Saiba como usar ML.NET para preparar dados para processamento adicional ou criação de um modelo.

Os dados são muitas vezes impuros e escassos. ML.NET algoritmos de aprendizado de máquina esperam que a entrada ou os recursos estejam em um único vetor numérico. Da mesma forma, o valor a prever (rótulo), especialmente quando são dados categóricos, tem de ser codificado. Portanto, um dos objetivos da preparação de dados é colocá-los no formato esperado por ML.NET algoritmos.

Divida os dados em conjuntos de treinamento & teste

A seção a seguir descreve problemas comuns no treino de um modelo, conhecidos como overfitting (sobreajuste) e underfitting (subajuste). Dividir seus dados e validar seus modelos usando um conjunto mantido pode ajudá-lo a identificar e mitigar esses problemas.

Sobreajuste & subajuste

Os problemas de overfitting e underfitting são dos mais comuns que se encontram ao treinar um modelo. Underfitting significa que o treinador selecionado não é capaz o suficiente para se ajustar ao conjunto de dados de treinamento e geralmente resulta em uma alta perda durante o treinamento e baixa pontuação/métrica no conjunto de dados de teste. Para resolver isso, você precisa selecionar um modelo mais poderoso ou executar mais engenharia de recursos. O sobreajustamento é o oposto, que acontece quando o modelo aprende demasiado bem os dados de treino. Isso geralmente resulta em baixa métrica de perda durante o treinamento, mas alta perda no conjunto de dados de teste.

Uma boa analogia para esses conceitos é estudar para um exame. Digamos que você soubesse as perguntas e respostas com antecedência. Depois de estudar, você faz o teste e obtém uma pontuação perfeita. Ótima notícia! No entanto, quando você recebe o exame novamente com as questões reorganizadas e com uma redação ligeiramente diferente, você obtém uma pontuação mais baixa. Isso sugere que você memorizou as respostas e não aprendeu os conceitos em que estava sendo testado. Este é um exemplo de sobreajuste. Underfitting é o oposto, onde os materiais de estudo que você recebeu não representam com precisão o que você foi avaliado para o exame. Como resultado, você recorre a adivinhar as respostas, uma vez que não tem conhecimento suficiente para responder corretamente.

Dividir dados

Pegue os seguintes dados de entrada e carregue-os em um IDataView chamado 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 os dados em conjuntos de treino/teste, use o método TrainTestSplit(IDataView, Double, String, Nullable<Int32>).

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

O parâmetro testFraction é usado para tirar 0,2 ou 20% do conjunto de dados para teste. Os restantes 80% são utilizados para treino.

O resultado é DataOperationsCatalog.TrainTestData com dois IDataViews que você pode acessar via TrainSet e TestSet.

Filtrar dados

Às vezes, nem todos os dados de um conjunto de dados são relevantes para análise. Uma abordagem para remover dados irrelevantes é a filtragem. O DataOperationsCatalog contém um conjunto de operações de filtro que recebem um IDataView que contém todos os dados e retornam um IDataView contendo apenas os pontos de dados de interesse. É importante observar que, como as operações de filtro não são IEstimator ou ITransformer como as do TransformsCatalog, elas não podem ser incluídas como parte de um fluxo de preparação de dados EstimatorChain ou TransformerChain.

Pegue os seguintes dados de entrada e carregue-os em um IDataView chamado data:

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

Para filtrar dados com base no valor de uma coluna, use o método FilterRowsByColumn.

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

A amostra acima seleciona linhas no conjunto de dados com um preço entre 200.000 e 1.000.000. O resultado da aplicação desse filtro retornaria apenas as duas últimas linhas nos dados e excluiria a primeira linha porque seu preço é 100000 e não entre o intervalo especificado.

Substituir valores em falta

Valores ausentes são uma ocorrência comum em conjuntos de dados. Uma abordagem para lidar com valores ausentes é substituí-los pelo valor padrão para o tipo dado, se houver algum ou outro valor significativo, como o valor médio nos dados.

Pegue os seguintes dados de entrada e carregue-os em um IDataView chamado data:

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

Observe que o último elemento na lista tem um valor ausente para Price. Para substituir os valores ausentes na coluna Price, use o método ReplaceMissingValues para preencher esse valor ausente.

Importante

ReplaceMissingValue só funciona com dados 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 suporta vários modos de substituição . O exemplo acima usa o modo de substituição Mean, que preenche o valor ausente com o valor médio dessa coluna. O resultado da substituição preenche a propriedade Price para o último elemento nos dados com 200.000, uma vez que é a média de 100.000 e 300.000.

Use normalizadores

de normalização é uma técnica de pré-processamento de dados usada para dimensionar recursos para estarem no mesmo intervalo, geralmente entre 0 e 1, para que possam ser processados com mais precisão por um algoritmo de aprendizado de máquina. Por exemplo, as faixas de idade e renda variam significativamente, com a idade geralmente na faixa de 0-100 e a renda geralmente na faixa de zero a milhares. Visite a página de transformações para uma lista mais detalhada e uma descrição das transformações de normalização.

Normalização Min-max

Pegue os seguintes dados de entrada e carregue-os em um IDataView chamado data:

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

A normalização pode ser aplicada a colunas com valores numéricos únicos, bem como vetores. Normalize os dados na coluna Price usando a normalização min-max com o 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);

Os valores de preço originais [200000,100000] são convertidos em [ 1, 0.5 ] usando a fórmula de normalização MinMax que gera valores de saída no intervalo de 0-1.

Encadernação

Binning converte valores contínuos em uma representação discreta da entrada. Por exemplo, suponha que uma das suas características é a idade. Em vez de usar o valor de idade real, o binning cria intervalos para esse valor. 0-18 poderia ser um caixote, outro poderia ser 19-35 e assim por diante.

Pegue os seguintes dados de entrada e carregue-os em um IDataView chamado data:

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

Normalize os dados em compartimentos usando o método NormalizeBinning. O parâmetro maximumBinCount permite especificar o número de compartimentos necessários para classificar seus dados. Neste exemplo, os dados serão colocados em dois compartimentos.

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

O resultado do binning cria limites de intervalos de [0,200000,Infinity]. Portanto, os compartimentos resultantes são [0,1,1] porque a primeira observação está entre 0-200000 e as outras são maiores que 200000, mas menores que o infinito.

Trabalhar com dados categóricos

Um dos tipos mais comuns de dados são os dados categóricos. Os dados categóricos têm um número finito de categorias. Por exemplo, os estados dos EUA, ou uma lista dos tipos de animais encontrados em um conjunto de imagens. Quer os dados categóricos sejam recursos ou rótulos, eles devem ser mapeados em um valor numérico para que possam ser usados para gerar um modelo de aprendizado de máquina. Existem várias maneiras de trabalhar com dados categóricos em ML.NET, dependendo do problema que você está resolvendo.

Mapeamento de valores-chave

Em ML.NET, uma chave é um valor inteiro que representa uma categoria. O mapeamento de valor-chave é usado com mais freqüência para mapear rótulos de cadeia de caracteres em valores inteiros exclusivos para treinamento e, em seguida, de volta aos seus valores de cadeia de caracteres quando o modelo é usado para fazer uma previsão.

As transformações usadas para executar o mapeamento de valor de chave são MapValueToKey e MapKeyToValue.

MapValueToKey adiciona um dicionário de mapeamentos no modelo, para que MapKeyToValue possa executar a transformação inversa ao fazer uma previsão.

Uma codificação a quente

Uma codificação a quente pega um conjunto finito de valores e os mapeia em inteiros cuja representação binária tem um único valor 1 em posições exclusivas na cadeia de caracteres. Uma codificação a quente pode ser a melhor escolha se não houver uma ordenação implícita dos dados categóricos. A tabela a seguir mostra um exemplo com códigos postais como valores brutos.

Valor bruto Um valor codificado a quente
98052 00...01
98100 00...10
... ...
98109 10...00

A transformação para converter dados categóricos em números com codificação one-hot é OneHotEncoding.

Hashing

O hashing é outra maneira de converter dados categóricos em números. Uma função hash mapeia dados de um tamanho arbitrário (uma cadeia de caracteres de texto, por exemplo) em um número com um intervalo fixo. O hashing pode ser uma maneira rápida e eficiente em termos de espaço de vetorização de recursos. Um exemplo notável de hashing no aprendizado de máquina é a filtragem de spam de e-mail, onde, em vez de manter um dicionário de palavras conhecidas, cada palavra no e-mail é hashed e adicionada a um grande vetor de recurso. Usar hashing desta forma evita que a filtragem de spam malicioso seja contornada pelo uso de palavras que não estão no dicionário.

ML.NET fornece a transformação de hash para executar hashing em texto, datas e dados numéricos. Tal como no mapeamento de chave de valor, as saídas da transformação de hash são tipos de chave.

Trabalhar com dados de texto

Como os dados categóricos, os dados de texto precisam ser transformados em recursos numéricos antes de usá-los para construir um modelo de aprendizado de máquina. Visite a página de transformações no para uma lista mais detalhada e descrição das transformações de texto.

Usando dados similares aos listados abaixo que foram carregados em um 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 fornece a transformação FeaturizeText que usa o valor da cadeia de caracteres de um texto e cria um conjunto de recursos do texto, aplicando uma série de transformações individuais.

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

A transformação resultante converte os valores de texto na coluna Description em um vetor numérico semelhante à saída abaixo:

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

As transformações que compõem o FeaturizeText também podem ser aplicadas individualmente para um controle mais preciso sobre a geração de funcionalidades.

// 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 contém um subconjunto de operações executadas pelo método FeaturizeText. O benefício de um pipeline mais complexo é o controle e a visibilidade sobre as transformações aplicadas aos dados.

Usando a primeira entrada como exemplo, segue-se uma descrição detalhada dos resultados produzidos pelas etapas de transformação definidas por textEstimator:

Texto Original: Este é um bom produto

Transformar Descrição Resultado
1. NormalizeText Converte todas as letras em minúsculas por padrão Este é um bom produto
2. TokenizeWords Divide a cadeia de caracteres em palavras individuais ["este", "é", "um", "bom", "produto"]
3. RemoveDefaultStopWords Remova palavras de paragem como é e um. ["bom","produto"]
4. MapValueToKey Mapeia os valores para chaves (categorias) com base nos dados de entrada [1,2]
5. Gerar n-gramas Transforma texto em sequência de palavras consecutivas [1,1,1,0,0]
6. NormalizeLpNorm Escalonar as entradas pela sua norma lp [ 0.577350529, 0.577350529, 0.577350529, 0, 0 ]