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.

Dividir dados em conjuntos de treinamento e teste

A seção a seguir descreve problemas comuns ao treinar um modelo conhecido como overfitting e underfitting. Dividir seus dados e validar seus modelos usando um conjunto mantido pode ajudá-lo a identificar e mitigar esses problemas.

Overfitting ou underfitting

Overfitting e underfitting são os dois problemas mais comuns que você encontra 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 overfitting é o oposto, que acontece quando o modelo aprende muito bem os dados de treinamento. 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. Boas notícias! 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 TrainTestSplit(IDataView, Double, String, Nullable<Int32>) método.

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

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

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 contendo todos os dados e retornam um IDataView contendo apenas os pontos de interesse de dados. É importante observar que, como as TransformsCatalogoperações de filtro não são uma IEstimator ou ITransformer como as do , elas não podem ser incluídas como parte de um EstimatorChain pipeline de preparação de dados 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 FilterRowsByColumn método.

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

A amostra acima usa linhas no conjunto de dados com um preço entre 200000 e 1000000. 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 em nossa lista tem um valor ausente para Price. Para substituir os valores ausentes na Price coluna, use o ReplaceMissingValues método 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 Mean modo de substituição, que preenche o valor ausente com o valor médio dessa coluna. O resultado da substituição preenche a Price propriedade para o último elemento em nossos dados com 200.000, uma vez que é a média de 100.000 e 300.000.

Use normalizadores

A normalização é uma técnica de pré-processamento de dados usada para dimensionar recursos para que estejam 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 obter 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 usando a Price normalização min-max com o NormalizeMinMax método.

// 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 [200000,100000] de preço originais são convertidos para [ 1, 0.5 ] usar a MinMax fórmula de normalização 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 NormalizeBinning método. O maximumBinCount parâmetro 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 da encadernação cria limites de compartimento 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 1 valor 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 codificados a quente é 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 o problema de filtragem de spam malicioso contornar pelo uso de palavras que não estão no dicionário.

ML.NET fornece transformação de hash para executar hashing em texto, datas e dados numéricos. Como o 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 para obter uma lista mais detalhada e uma descrição das transformações de texto.

Usando dados como os dados 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 que usa o FeaturizeText valor da cadeia de caracteres de um texto e cria um conjunto de recursos a partir 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 Description coluna 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 FeaturizeText também podem ser aplicadas individualmente para um controle de grão mais fino sobre a geração de recursos.

// 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 realizadas pelo FeaturizeText método. 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

Transformação Description 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 ["isto","é","a","bom","produto"]
3. RemoveDefaultStopWords Remove palavras de parada como é e a. ["bom","produto"]
4. MapValueToKey Mapeia os valores para chaves (categorias) com base nos dados de entrada [1,2]
5. Produza NGrams Transforma texto em sequência de palavras consecutivas [1,1,1,0,0]
6. NormalizeLpNorm Dimensionar entradas por sua norma-lp [ 0.577350529, 0.577350529, 0.577350529, 0, 0 ]