Estamos desenvolvendo um projeto de aprendizado de máquina em Python. Parte 2



Um passo a passo completo do aprendizado de máquina em Python: parte dois

Juntar todas as partes de um projeto de aprendizado de máquina pode ser complicado. Nesta série de artigos, percorreremos todas as etapas da implementação do processo de aprendizado de máquina usando dados reais e descobriremos como as várias técnicas são combinadas entre si.

No primeiro artigo, limpamos e estruturamos os dados , realizamos uma análise exploratória, coletamos um conjunto de atributos para uso no modelo e definimos uma linha de base para avaliar os resultados. Com a ajuda deste artigo, aprenderemos como implementar no Python e comparar vários modelos de aprendizado de máquina, executar o ajuste hiperparamétrico para otimizar o melhor modelo e avaliar o desempenho do modelo final em um conjunto de dados de teste.

Todo o código do projeto está no GitHub , e aqui está o segundo bloco de anotações relacionado ao artigo atual. Você pode usar e modificar o código como desejar!

Avaliação e Seleção de Modelos


Lembrete: Estamos trabalhando em uma tarefa de regressão controlada, usando as informações de energia para edifícios em Nova York para criar um modelo que prediz qual Energy Star Score um edifício em particular receberá. Estamos interessados ​​tanto na precisão da previsão quanto na interpretabilidade do modelo.

Hoje você pode escolher entre os vários modelos de aprendizado de máquina disponíveis , e essa abundância pode ser intimidadora. Obviamente, existem análises comparativas na rede que o ajudarão a navegar na escolha de um algoritmo, mas prefiro tentar algumas e ver qual é melhor. Na maioria das vezes, o aprendizado de máquina é baseado em resultados empíricos e não teóricos , e é quase impossível entender com antecedência qual modelo é mais preciso .

Geralmente, é recomendável que você comece com modelos simples e interpretáveis, como regressão linear, e se os resultados forem insatisfatórios, passe para métodos mais complexos, mas geralmente mais precisos. Este gráfico (muito anticientífico) mostra a relação entre a precisão e a interpretabilidade de alguns algoritmos:


Interpretabilidade e precisão ( fonte ).

Vamos avaliar cinco modelos de graus variados de complexidade:

  • Regressão linear.
  • O método dos k-vizinhos mais próximos.
  • "Floresta aleatória".
  • Aumento de gradiente.
  • Método de vetores de suporte.

Consideraremos não o aparato teórico desses modelos, mas sua implementação. Se você estiver interessado em teoria, consulte Introdução à aprendizagem estatística (disponível gratuitamente) ou Aprendizado de máquina prático com o Scikit-Learn e o TensorFlow . Nos dois livros, a teoria é perfeitamente explicada e a eficácia do uso dos métodos mencionados nas linguagens R e Python é mostrada, respectivamente.

Preencha os valores ausentes


Embora quando limpamos os dados, descartamos as colunas nas quais mais da metade dos valores está ausente, ainda temos muitos valores. Os modelos de aprendizado de máquina não podem funcionar com dados ausentes, portanto, precisamos preenchê- los.

Primeiro, consideramos os dados e lembramos como eles se parecem:

import pandas as pd import numpy as np # Read in data into dataframes train_features = pd.read_csv('data/training_features.csv') test_features = pd.read_csv('data/testing_features.csv') train_labels = pd.read_csv('data/training_labels.csv') test_labels = pd.read_csv('data/testing_labels.csv') Training Feature Size: (6622, 64) Testing Feature Size: (2839, 64) Training Labels Size: (6622, 1) Testing Labels Size: (2839, 1) 

Cada valor NaN é um registro ausente nos dados. Você pode preenchê-los de maneiras diferentes , e usaremos o método de imputação mediana bastante simples, que substitui os dados ausentes pelos valores médios das colunas correspondentes.

No código abaixo, criaremos um objeto Scikit-Learn Imputer com uma estratégia mediana. Em seguida, treinamos nos dados de treinamento (usando imputer.fit ) e aplicamos para preencher os valores ausentes nos conjuntos de treinamento e teste (usando imputer.transform ). Ou seja, os registros ausentes nos dados de teste serão preenchidos com o valor mediano correspondente dos dados de treinamento .

Realizamos o preenchimento e não treinamos o modelo nos dados como estão, para evitar o problema de vazamento dos dados de teste quando as informações do conjunto de dados de teste entram no treinamento.

 # Create an imputer object with a median filling strategy imputer = Imputer(strategy='median') # Train on the training features imputer.fit(train_features) # Transform both training data and testing data X = imputer.transform(train_features) X_test = imputer.transform(test_features) Missing values in training features: 0 Missing values in testing features: 0 

Agora todos os valores estão preenchidos, não há lacunas.

Escala de recursos


Escala é o processo geral de alterar o alcance de uma característica. Este é um passo necessário , porque os sinais são medidos em unidades diferentes, o que significa que cobrem intervalos diferentes. Isso distorce bastante os resultados de algoritmos como o método do vetor de suporte e o método vizinho k-mais próximo, que levam em consideração as distâncias entre as medições. E o dimensionamento permite evitar isso. Embora métodos como regressão linear e “floresta aleatória” não exijam escala de recursos, é melhor não negligenciar esta etapa ao comparar vários algoritmos.

Escalaremos usando cada atributo para um intervalo de 0 a 1. Pegamos todos os valores do atributo, selecionamos o mínimo e dividimos pela diferença entre o máximo e o mínimo (intervalo). Esse método de dimensionamento é geralmente chamado de normalização e a outra maneira principal é a padronização .

Esse processo é fácil de implementar manualmente, portanto, usaremos o objeto MinMaxScaler do Scikit-Learn. O código para esse método é idêntico ao código para preencher os valores ausentes, somente a escala é usada em vez de colar. Lembre-se de que aprendemos o modelo apenas no conjunto de treinamento e depois transformamos todos os dados.

 # Create the scaler object with a range of 0-1 scaler = MinMaxScaler(feature_range=(0, 1)) # Fit on the training data scaler.fit(X) # Transform both the training and testing data X = scaler.transform(X) X_test = scaler.transform(X_test) 

Agora, cada atributo tem um valor mínimo de 0 e máximo de 1. Preenchendo os valores ausentes e a escala dos atributos - esses dois estágios são necessários em quase qualquer processo de aprendizado de máquina.

Implementamos modelos de aprendizado de máquina no Scikit-Learn


Depois de todo o trabalho preparatório, o processo de criação, treinamento e execução de modelos é relativamente simples. Usaremos a biblioteca Scikit-Learn em Python, que é lindamente documentada e com sintaxe elaborada para a construção de modelos. Ao aprender como criar um modelo no Scikit-Learn, você pode implementar rapidamente todos os tipos de algoritmos.

Ilustraremos o processo de criação, treinamento ( .fit ) e teste ( .predict ) usando o aumento de gradiente:

 from sklearn.ensemble import GradientBoostingRegressor # Create the model gradient_boosted = GradientBoostingRegressor() # Fit the model on the training data gradient_boosted.fit(X, y) # Make predictions on the test data predictions = gradient_boosted.predict(X_test) # Evaluate the model mae = np.mean(abs(predictions - y_test)) print('Gradient Boosted Performance on the test set: MAE = %0.4f' % mae) Gradient Boosted Performance on the test set: MAE = 10.0132 

Apenas uma linha de código para criação, treinamento e teste. Para construir outros modelos, usamos a mesma sintaxe, alterando apenas o nome do algoritmo.



Para avaliar objetivamente os modelos, calculamos o nível base usando o valor mediano da meta e obtivemos 24,5. E os resultados foram muito melhores, para que nosso problema possa ser resolvido usando o aprendizado de máquina.

No nosso caso, o aumento do gradiente (MAE = 10,013) acabou sendo um pouco melhor que a "floresta aleatória" (10,014 MAE). Embora esses resultados não possam ser considerados completamente honestos, porque para os hiperparâmetros usamos principalmente os valores padrão. A eficácia dos modelos depende fortemente dessas configurações, especialmente no método do vetor de suporte . No entanto, com base nesses resultados, escolheremos o aumento de gradiente e começaremos a otimizá-lo.

Otimização de modelo hiperparamétrico


Depois de escolher um modelo, você pode otimizá-lo para a tarefa a ser resolvida ajustando os hiper parâmetros.

Mas antes de tudo, vamos entender o que são hiperparâmetros e como eles diferem dos parâmetros comuns ?

  • Os hiperparâmetros do modelo podem ser considerados as configurações do algoritmo, que definimos antes do início de seu treinamento. Por exemplo, o hiperparâmetro é o número de árvores na "floresta aleatória" ou o número de vizinhos no método k-vizinhos mais próximos.
  • Parâmetros do modelo - o que ela aprende durante o treinamento, por exemplo, pesos em regressão linear.

Ao controlar o hiperparâmetro, influenciamos os resultados do modelo, alterando o equilíbrio entre a falta de educação e a reciclagem . Sob a aprendizagem, há uma situação em que o modelo não é complexo o suficiente (possui poucos graus de liberdade) para estudar a correspondência de sinais e objetivos. Um modelo pouco treinado tem um alto viés, que pode ser corrigido complicando o modelo.

A reciclagem é uma situação em que o modelo essencialmente se lembra dos dados de treinamento. O modelo reciclado possui uma alta variação, que pode ser ajustada limitando a complexidade do modelo através da regularização. Os modelos mal treinados e reciclados não serão capazes de generalizar bem os dados de teste.

A dificuldade em escolher os hiperparâmetros certos é que, para cada tarefa, haverá um conjunto ideal exclusivo. Portanto, a única maneira de escolher as melhores configurações é tentar combinações diferentes no novo conjunto de dados. Felizmente, o Scikit-Learn possui vários métodos que permitem avaliar efetivamente os hiperparâmetros. Além disso, projetos como o TPOT estão tentando otimizar a busca por hiperparâmetros usando abordagens como a programação genética . Neste artigo, nos restringimos ao uso do Scikit-Learn.

Pesquisa aleatória cruzada


Vamos implementar um método de ajuste de hiperparâmetro chamado pesquisas aleatórias de validação cruzada:

  • Pesquisa aleatória - uma técnica para selecionar hiperparâmetros. Definimos uma grade e, em seguida, selecionamos aleatoriamente várias combinações, em contraste com a pesquisa de grade, na qual tentamos sucessivamente cada combinação. A propósito, a pesquisa aleatória funciona quase tão bem quanto a pesquisa em grade , mas muito mais rápido.
  • A verificação cruzada é uma maneira de avaliar a combinação selecionada de hiperparâmetros. Em vez de dividir os dados em conjuntos de treinamento e teste, o que reduz a quantidade de dados disponíveis para treinamento, usaremos a validação cruzada do bloco k (validação cruzada do K-Fold). Para fazer isso, dividiremos os dados de treinamento em k blocos e, em seguida, executaremos o processo iterativo, durante o qual treinamos primeiro o modelo em blocos k-1 e, em seguida, comparamos o resultado ao aprender no k-ésimo bloco. Repetiremos o processo k vezes e, no final, obteremos o valor médio do erro para cada iteração. Essa será a avaliação final.

Aqui está uma ilustração gráfica da validação cruzada do bloco k em k = 5:



Todo o processo de pesquisa aleatória de validação cruzada se parece com o seguinte:

  1. Estabelecemos uma grade de hiperparâmetros.
  2. Selecione aleatoriamente uma combinação de hiperparâmetros.
  3. Crie um modelo usando essa combinação.
  4. Avaliamos o resultado do modelo usando a validação cruzada do bloco k.
  5. Decidimos quais hiperparâmetros dão o melhor resultado.

Obviamente, tudo isso é feito não manualmente, mas usando o RandomizedSearchCV do Scikit-Learn!

Pequena digressão: métodos de aumento de gradiente


Usaremos um modelo de regressão baseado em aumento de gradiente. Este é um método coletivo, ou seja, o modelo consiste em numerosos "alunos fracos", neste caso, de árvores de decisão separadas. Se os alunos aprendem em algoritmos paralelos como “floresta aleatória” e o resultado da previsão é selecionado por votação, em algoritmos de otimização como aumento de gradiente, os alunos aprendem em sequência e cada um deles “se concentra” nos erros cometidos por seus antecessores.

Nos últimos anos, os algoritmos de otimização tornaram-se populares e geralmente vencem em competições de aprendizado de máquina. O aumento de gradiente é uma das implementações nas quais o Gradient Descent é usado para minimizar o custo da função. A implementação do aumento de gradiente no Scikit-Learn é considerada não tão eficaz quanto em outras bibliotecas, por exemplo, no XGBoost , mas funciona bem em pequenos conjuntos de dados e fornece previsões bastante precisas.

Voltar para a configuração hiperparamétrica


Na regressão usando o aumento de gradiente, há muitos hiperparâmetros que precisam ser configurados. Para obter detalhes, refiro-o à documentação do Scikit-Learn. Vamos otimizar:

  • loss : minimização da função de perda;
  • n_estimators : o número de árvores de decisão fracas usadas (árvores de decisão);
  • max_depth : profundidade máxima de cada árvore de decisão;
  • min_samples_leaf : o número mínimo de exemplos que devem estar no nó folha da árvore de decisão;
  • min_samples_split : o número mínimo de exemplos necessários para dividir o nó da árvore de decisão;
  • max_features : o número máximo de recursos usados ​​para separar nós.

Não tenho certeza se alguém realmente entende como tudo funciona, e a única maneira de encontrar a melhor combinação é tentar opções diferentes.

Nesse código, criamos uma grade de hiperparâmetros, depois criamos um objeto RandomizedSearchCV e pesquisamos usando a validação cruzada em quatro blocos para 25 combinações diferentes de hiperparâmetros:

 # Loss function to be optimized loss = ['ls', 'lad', 'huber'] # Number of trees used in the boosting process n_estimators = [100, 500, 900, 1100, 1500] # Maximum depth of each tree max_depth = [2, 3, 5, 10, 15] # Minimum number of samples per leaf min_samples_leaf = [1, 2, 4, 6, 8] # Minimum number of samples to split a node min_samples_split = [2, 4, 6, 10] # Maximum number of features to consider for making splits max_features = ['auto', 'sqrt', 'log2', None] # Define the grid of hyperparameters to search hyperparameter_grid = {'loss': loss, 'n_estimators': n_estimators, 'max_depth': max_depth, 'min_samples_leaf': min_samples_leaf, 'min_samples_split': min_samples_split, 'max_features': max_features} # Create the model to use for hyperparameter tuning model = GradientBoostingRegressor(random_state = 42) # Set up the random search with 4-fold cross validation random_cv = RandomizedSearchCV(estimator=model, param_distributions=hyperparameter_grid, cv=4, n_iter=25, scoring = 'neg_mean_absolute_error', n_jobs = -1, verbose = 1, return_train_score = True, random_state=42) # Fit on the training data random_cv.fit(X, y) After performing the search, we can inspect the RandomizedSearchCV object to find the best model: # Find the best combination of settings random_cv.best_estimator_ GradientBoostingRegressor(loss='lad', max_depth=5, max_features=None, min_samples_leaf=6, min_samples_split=6, n_estimators=500) 

Você pode usar esses resultados para uma pesquisa em grade, selecionando parâmetros para a grade que estão próximos desses valores ótimos. Porém, é improvável que ajustes adicionais melhorem significativamente o modelo. Existe uma regra geral: a construção competente de recursos terá um impacto muito maior na precisão do modelo do que na configuração mais cara do hiperparâmetro. Esta é a lei da diminuição da lucratividade em relação ao aprendizado de máquina : o design de atributos oferece o maior retorno e o ajuste hiperparamétrico traz apenas benefícios modestos.

Para alterar o número de estimadores (árvores de decisão) e preservar os valores de outros hiperparâmetros, pode ser realizado um experimento que demonstrará o papel dessa configuração. A implementação é dada aqui , mas aqui está o resultado:



À medida que o número de árvores usadas pelo modelo aumenta, o nível de erros durante o treinamento e o teste diminui. Mas os erros de aprendizado diminuem muito mais rapidamente e, como resultado, o modelo é treinado novamente: mostra excelentes resultados nos dados de treinamento, mas funciona pior nos dados de teste.

Nos dados de teste, a precisão sempre diminui (porque o modelo vê as respostas corretas para o conjunto de dados de treinamento), mas uma queda significativa indica reciclagem . Esse problema pode ser resolvido aumentando a quantidade de dados de treinamento ou reduzindo a complexidade do modelo usando hiperparâmetros . Aqui não abordaremos os hiperparâmetros, mas recomendo que você sempre preste atenção ao problema da reciclagem.

Para o nosso modelo final, levaremos 800 avaliadores, pois isso nos dará o menor nível de erro na validação cruzada. Agora teste o modelo!

Avaliação usando dados de teste


Como pessoas responsáveis, garantimos que nosso modelo não tivesse acesso aos dados de teste durante o treinamento. Portanto, podemos usar a precisão ao trabalhar com dados de teste como um indicador de qualidade do modelo quando ele é admitido em tarefas reais.

Alimentamos os dados de teste do modelo e calculamos o erro. Aqui está uma comparação dos resultados do algoritmo padrão de aumento de gradiente e nosso modelo personalizado:

 # Make predictions on the test set using default and final model default_pred = default_model.predict(X_test) final_pred = final_model.predict(X_test) Default model performance on the test set: MAE = 10.0118. Final model performance on the test set: MAE = 9.0446. 

O ajuste hiperparamétrico ajudou a melhorar a precisão do modelo em cerca de 10%. Dependendo da situação, isso pode ser uma melhoria muito significativa, mas leva muito tempo.

Você pode comparar o tempo de treinamento para ambos os modelos usando o %timeit magic %timeit nos cadernos Jupyter. Primeiro, meça a duração padrão do modelo:

 %%timeit -n 1 -r 5 default_model.fit(X, y) 1.09 s ± 153 ms per loop (mean ± std. dev. of 5 runs, 1 loop each) 

Um segundo para estudar é muito decente. Mas o modelo ajustado não é tão rápido:

 %%timeit -n 1 -r 5 final_model.fit(X, y) 12.1 s ± 1.33 s per loop (mean ± std. dev. of 5 runs, 1 loop each) 

Essa situação ilustra o aspecto fundamental do aprendizado de máquina: trata-se de compromissos . É constantemente necessário escolher um equilíbrio entre precisão e interpretabilidade, entre deslocamento e dispersão , entre precisão e tempo de operação, e assim por diante. A combinação certa é completamente determinada pela tarefa específica. No nosso caso, um aumento de 12 vezes na duração do trabalho em termos relativos é grande, mas em termos absolutos é insignificante.

Obtivemos os resultados finais da previsão, agora vamos analisá-los e descobrir se há algum desvio perceptível. À esquerda, há um gráfico da densidade da previsão e dos valores reais, à direita, um histograma do erro



A previsão do modelo repete bem a distribuição dos valores reais, enquanto nos dados de treinamento, o pico de densidade está localizado mais próximo do valor mediano (66) do que o pico de densidade real (cerca de 100). Os erros têm uma distribuição quase normal, embora existam vários valores negativos grandes quando a previsão do modelo é muito diferente dos dados reais. No próximo artigo, examinaremos mais detalhadamente a interpretação dos resultados.

Conclusão


Neste artigo, examinamos várias etapas da solução do problema de aprendizado de máquina:

  • Preenchendo valores ausentes e recursos de dimensionamento.
  • Avaliação e comparação dos resultados de vários modelos.
  • Ajuste hiperparamétrico usando pesquisa aleatória em grade e validação cruzada.
  • Avaliação do melhor modelo usando dados de teste.

Os resultados indicam que podemos usar o aprendizado de máquina para prever o Energy Star Score com base nas estatísticas disponíveis. Com a ajuda do aumento de gradiente, foi obtido um erro de 9,1 nos dados de teste. O ajuste hiperparamétrico pode melhorar muito os resultados, mas ao custo de uma desaceleração significativa. Esse é um dos muitos compromissos a serem considerados no aprendizado de máquina.

No próximo artigo, tentaremos descobrir como nosso modelo funciona. Também veremos os principais fatores que influenciam o Energy Star Score. Se soubermos que o modelo é preciso, tentaremos entender por que ele prediz dessa maneira e o que isso nos diz sobre o problema em si.

Source: https://habr.com/ru/post/pt425907/


All Articles