Tutorial do Scikit-Learn: como instalar e exemplos do Scikit-Learn

O que é Scikit-learn?

Scikit-learn é um open-source Python biblioteca para aprendizado de máquina. Ela suporta algoritmos de última geração, como KNN, XGBoost, random forest e SVM. Ela é construída sobre NumPy. O Scikit-learn é amplamente usado na competição Kaggle, bem como em empresas de tecnologia proeminentes. Ela ajuda no pré-processamento, redução de dimensionalidade (seleção de parâmetros), classificação, regressão, agrupamento e seleção de modelos.

Scikit-learn possui a melhor documentação de todas as bibliotecas de código aberto. Ele fornece um gráfico interativo em https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html.

Como funciona o Scikit Learn
Como funciona o Scikit Learn

O Scikit-learn não é muito difícil de usar e oferece excelentes resultados. No entanto, o scikit learn não oferece suporte a cálculos paralelos. É possível executar um algoritmo de aprendizado profundo com ele, mas não é uma solução ideal, especialmente se você souber como usar o TensorFlow.

Como baixar e instalar o Scikit-learn

Agora neste Python Tutorial do Scikit-learn, aprenderemos como baixar e instalar o Scikit-learn:

Opção 1: AWS

scikit-learn pode ser usado na AWS. Por favor referir A imagem docker que tem o scikit-learn pré-instalado.

Para usar a versão do desenvolvedor, use o comando em Jupyter

import sys
!{sys.executable} -m pip install git+git://github.com/scikit-learn/scikit-learn.git

Opção 2: Mac ou Windows usando Anaconda

Para saber mais sobre a instalação do Anaconda, consulte https://www.guru99.com/download-install-tensorflow.html

Recentemente, os desenvolvedores do scikit lançaram uma versão de desenvolvimento que aborda problemas comuns enfrentados pela versão atual. Achamos mais conveniente usar a versão do desenvolvedor em vez da versão atual.

Como instalar o scikit-learn com ambiente Conda

Se você instalou o scikit-learn com o ambiente conda, siga a etapa para atualizar para a versão 0.20

Passo 1) Ativar ambiente tensorflow

source activate hello-tf

Passo 2) Remova o scikit lean usando o comando conda

conda remove scikit-learn

Passo 3) Instale a versão do desenvolvedor.
Instale a versão do desenvolvedor do scikit learn junto com as bibliotecas necessárias.

conda install -c anaconda git
pip install Cython
pip install h5py
pip install git+git://github.com/scikit-learn/scikit-learn.git

OBSERVAÇÃO: Windows o usuário precisará instalar Microsoft visual C++ 14. Você pode obtê-lo em aqui

Exemplo do Scikit-Learn com aprendizado de máquina

Este tutorial do Scikit está dividido em duas partes:

  1. Aprendizado de máquina com scikit-learn
  2. Como confiar em seu modelo com LIME

A primeira parte detalha como construir um pipeline, criar um modelo e ajustar os hiperparâmetros, enquanto a segunda parte fornece o que há de mais moderno em termos de seleção de modelos.

Etapa 1) Importe os dados

Durante este tutorial de aprendizagem do Scikit, você usará o conjunto de dados adulto.

Para obter informações básicas sobre este conjunto de dados, consulte Se você estiver interessado em saber mais sobre as estatísticas descritivas, use as ferramentas Dive e Overview.

Consulte Neste tutorial saiba mais sobre mergulho e visão geral

Você importa o conjunto de dados com o Pandas. Observe que você precisa converter o tipo das variáveis ​​contínuas em formato flutuante.

Este conjunto de dados inclui oito variáveis ​​categóricas:

As variáveis ​​categóricas estão listadas em CATE_FEATURES

  • classe de trabalho
  • educação
  • marital
  • ocupação
  • relacionamento
  • raça
  • sexo
  • país nativo

além disso, seis variáveis ​​contínuas:

As variáveis ​​contínuas estão listadas em CONTI_FEATURES

  • idade
  • fnlwgt
  • número_educação
  • Ganho de capital
  • perda_capital
  • horas_semana

Observe que preenchemos a lista manualmente para que você tenha uma ideia melhor de quais colunas estamos usando. Uma maneira mais rápida de construir uma lista categórica ou contínua é usar:

## List Categorical
CATE_FEATURES = df_train.iloc[:,:-1].select_dtypes('object').columns
print(CATE_FEATURES)

## List continuous
CONTI_FEATURES =  df_train._get_numeric_data()
print(CONTI_FEATURES)

Aqui está o código para importar os dados:

# Import dataset
import pandas as pd

## Define path data
COLUMNS = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital',
           'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
           'hours_week', 'native_country', 'label']
### Define continuous list
CONTI_FEATURES  = ['age', 'fnlwgt','capital_gain', 'education_num', 'capital_loss', 'hours_week']
### Define categorical list
CATE_FEATURES = ['workclass', 'education', 'marital', 'occupation', 'relationship', 'race', 'sex', 'native_country']

## Prepare the data
features = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital',
           'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
           'hours_week', 'native_country']

PATH = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data"

df_train = pd.read_csv(PATH, skipinitialspace=True, names = COLUMNS, index_col=False)
df_train[CONTI_FEATURES] =df_train[CONTI_FEATURES].astype('float64')
df_train.describe()
idade fnlwgt número_educação Ganho de capital perda_capital horas_semana
contar 32561.000000 3.256100e + 04 32561.000000 32561.000000 32561.000000 32561.000000
significar 38.581647 1.897784e + 05 10.080679 1077.648844 87.303830 40.437456
std 13.640433 1.055500e + 05 2.572720 7385.292085 402.960219 12.347429
minutos 17.000000 1.228500e + 04 1.000000 0.000000 0.000000 1.000000
25% 28.000000 1.178270e + 05 9.000000 0.000000 0.000000 40.000000
50% 37.000000 1.783560e + 05 10.000000 0.000000 0.000000 40.000000
75% 48.000000 2.370510e + 05 12.000000 0.000000 0.000000 45.000000
max 90.000000 1.484705e + 06 16.000000 99999.000000 4356.000000 99.000000

Você pode verificar a contagem de valores exclusivos dos recursos native_country. Você pode ver que apenas uma família vem da Holanda-Holanda. Este agregado familiar não nos trará nenhuma informação, mas sim através de um erro durante o treino.

df_train.native_country.value_counts()
United-States                 29170
Mexico                          643
?                               583
Philippines                     198
Germany                         137
Canada                          121
Puerto-Rico                     114
El-Salvador                     106
India                           100
Cuba                             95
England                          90
Jamaica                          81
South                            80
China                            75
Italy                            73
Dominican-Republic               70
Vietnam                          67
Guatemala                        64
Japan                            62
Poland                           60
Columbia                         59
Taiwan                           51
Haiti                            44
Iran                             43
Portugal                         37
Nicaragua                        34
Peru                             31
France                           29
Greece                           29
Ecuador                          28
Ireland                          24
Hong                             20
Cambodia                         19
Trinadad&Tobago                  19
Thailand                         18
Laos                             18
Yugoslavia                       16
Outlying-US(Guam-USVI-etc)       14
Honduras                         13
Hungary                          13
Scotland                         12
Holand-Netherlands                1
Name: native_country, dtype: int64

Você pode excluir esta linha não informativa do conjunto de dados

## Drop Netherland, because only one row
df_train = df_train[df_train.native_country != "Holand-Netherlands"]

A seguir, você armazena a posição dos recursos contínuos em uma lista. Você precisará dele na próxima etapa para construir o pipeline.

O código abaixo irá percorrer todos os nomes de colunas em CONTI_FEATURES e obter sua localização (ou seja, seu número) e, em seguida, anexá-lo a uma lista chamada conti_features

## Get the column index of the categorical features
conti_features = []
for i in CONTI_FEATURES:
    position = df_train.columns.get_loc(i)
    conti_features.append(position)
print(conti_features)  
[0, 2, 10, 4, 11, 12]

O código abaixo faz o mesmo trabalho acima, mas para a variável categórica. O código abaixo repete o que você fez anteriormente, exceto com os recursos categóricos.

## Get the column index of the categorical features
categorical_features = []
for i in CATE_FEATURES:
    position = df_train.columns.get_loc(i)
    categorical_features.append(position)
print(categorical_features)  
[1, 3, 5, 6, 7, 8, 9, 13]

Você pode dar uma olhada no conjunto de dados. Observe que cada recurso categórico é uma string. Você não pode alimentar um modelo com um valor de string. Você precisa transformar o conjunto de dados usando uma variável fictícia.

df_train.head(5)

Na verdade, você precisa criar uma coluna para cada grupo do recurso. Primeiro, você pode executar o código abaixo para calcular a quantidade total de colunas necessárias.

print(df_train[CATE_FEATURES].nunique(),
      'There are',sum(df_train[CATE_FEATURES].nunique()), 'groups in the whole dataset')
workclass          9
education         16
marital            7
occupation        15
relationship       6
race               5
sex                2
native_country    41
dtype: int64 There are 101 groups in the whole dataset

Todo o conjunto de dados contém 101 grupos conforme mostrado acima. Por exemplo, os recursos da classe de trabalho possuem nove grupos. Você pode visualizar o nome dos grupos com os seguintes códigos

unique() retorna os valores exclusivos dos recursos categóricos.

for i in CATE_FEATURES:
    print(df_train[i].unique())
['State-gov' 'Self-emp-not-inc' 'Private' 'Federal-gov' 'Local-gov' '?'
 'Self-emp-inc' 'Without-pay' 'Never-worked']
['Bachelors' 'HS-grad' '11th' 'Masters' '9th' 'Some-college' 'Assoc-acdm'
 'Assoc-voc' '7th-8th' 'Doctorate' 'Prof-school' '5th-6th' '10th'
 '1st-4th' 'Preschool' '12th']
['Never-married' 'Married-civ-spouse' 'Divorced' 'Married-spouse-absent'
 'Separated' 'Married-AF-spouse' 'Widowed']
['Adm-clerical' 'Exec-managerial' 'Handlers-cleaners' 'Prof-specialty'
 'Other-service' 'Sales' 'Craft-repair' 'Transport-moving'
 'Farming-fishing' 'Machine-op-inspct' 'Tech-support' '?'
 'Protective-serv' 'Armed-Forces' 'Priv-house-serv']
['Not-in-family' 'Husband' 'Wife' 'Own-child' 'Unmarried' 'Other-relative']
['White' 'Black' 'Asian-Pac-Islander' 'Amer-Indian-Eskimo' 'Other']
['Male' 'Female']
['United-States' 'Cuba' 'Jamaica' 'India' '?' 'Mexico' 'South'
 'Puerto-Rico' 'Honduras' 'England' 'Canada' 'Germany' 'Iran'
 'Philippines' 'Italy' 'Poland' 'Columbia' 'Cambodia' 'Thailand' 'Ecuador'
 'Laos' 'Taiwan' 'Haiti' 'Portugal' 'Dominican-Republic' 'El-Salvador'
 'France' 'Guatemala' 'China' 'Japan' 'Yugoslavia' 'Peru'
 'Outlying-US(Guam-USVI-etc)' 'Scotland' 'Trinadad&Tobago' 'Greece'
 'Nicaragua' 'Vietnam' 'Hong' 'Ireland' 'Hungary']

Portanto, o conjunto de dados de treinamento conterá 101 + 7 colunas. As últimas sete colunas são os recursos contínuos.

O Scikit-learn pode cuidar da conversão. Isso é feito em duas etapas:

  • Primeiro, você precisa converter a string em ID. Por exemplo, State-gov terá o ID 1, Self-emp-not-inc ID 2 e assim por diante. A função LabelEncoder faz isso para você
  • Transponha cada ID para uma nova coluna. Conforme mencionado anteriormente, o conjunto de dados possui 101 IDs de grupo. Portanto, haverá 101 colunas capturando todos os grupos de recursos categóricos. Scikit-learn possui uma função chamada OneHotEncoder que realiza esta operação

Etapa 2) Crie o conjunto de treinamento/teste

Agora que o conjunto de dados está pronto, podemos dividi-lo em 80/20.

80% para o conjunto de treinamento e 20% para o conjunto de teste.

Você pode usar train_test_split. O primeiro argumento é o dataframe são os recursos e o segundo argumento é o rótulo dataframe. Você pode especificar o tamanho do conjunto de testes com test_size.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(df_train[features],
                                                    df_train.label,
                                                    test_size = 0.2,
                                                    random_state=0)
X_train.head(5)
print(X_train.shape, X_test.shape)
(26048, 14) (6512, 14)

Etapa 3) Construir o pipeline

O pipeline facilita alimentar o modelo com dados consistentes.

A ideia por trás é colocar os dados brutos em um ‘pipeline’ para realizar operações.

Por exemplo, com o conjunto de dados atual, você precisa padronizar as variáveis ​​contínuas e converter os dados categóricos. Observe que você pode realizar qualquer operação dentro do pipeline. Por exemplo, se você tiver 'NA's' no conjunto de dados, poderá substituí-los pela média ou mediana. Você também pode criar novas variáveis.

Você tem a escolha; codifique os dois processos ou crie um pipeline. A primeira escolha pode levar ao vazamento de dados e criar inconsistências ao longo do tempo. Uma opção melhor é usar o pipeline.

from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
from sklearn.compose import ColumnTransformer, make_column_transformer
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression

O pipeline realizará duas operações antes de alimentar o classificador logístico:

  1. Padronize a variável: `StandardScaler()“
  2. Converta os recursos categóricos: OneHotEncoder(sparse=False)

Você pode executar as duas etapas usando make_column_transformer. Esta função não está disponível na versão atual do scikit-learn (0.19). Não é possível com a versão atual realizar o codificador de rótulo e um codificador ativo no pipeline. Esse é um dos motivos pelos quais decidimos usar a versão de desenvolvedor.

make_column_transformer é fácil de usar. Você precisa definir quais colunas aplicar a transformação e qual transformação operar. Por exemplo, para padronizar o recurso contínuo, você pode fazer:

  • conti_features, StandardScaler() dentro de make_column_transformer.
    • conti_features: lista com a variável contínua
    • StandardScaler: padroniza a variável

O objeto OneHotEncoder dentro de make_column_transformer codifica automaticamente o rótulo.

preprocess = make_column_transformer(
    (conti_features, StandardScaler()),
    ### Need to be numeric not string to specify columns name 
    (categorical_features, OneHotEncoder(sparse=False))
)

Você pode testar se o pipeline funciona com fit_transform. O conjunto de dados deve ter o seguinte formato: 26048, 107

preprocess.fit_transform(X_train).shape
(26048, 107)

O transformador de dados está pronto para uso. Você pode criar o pipeline com make_pipeline. Depois que os dados forem transformados, você poderá alimentar a regressão logística.

model = make_pipeline(
    preprocess,
    LogisticRegression())

Treinar um modelo com o scikit-learn é trivial. Você precisa usar o ajuste do objeto precedido pelo pipeline, ou seja, modelo. Você pode imprimir a precisão com o objeto score da biblioteca scikit-learn

model.fit(X_train, y_train)
print("logistic regression score: %f" % model.score(X_test, y_test))
logistic regression score: 0.850891

Finalmente, você pode prever as classes com predizer_proba. Ele retorna a probabilidade para cada classe. Observe que isso soma um.

model.predict_proba(X_test)
array([[0.83576663, 0.16423337],
       [0.94582765, 0.05417235],
       [0.64760587, 0.35239413],
       ...,
       [0.99639252, 0.00360748],
       [0.02072181, 0.97927819],
       [0.56781353, 0.43218647]])

Etapa 4) Usando nosso pipeline em uma pesquisa em grade

Ajustar o hiperparâmetro (variáveis ​​que determinam a estrutura da rede como unidades ocultas) pode ser tedioso e exaustivo.

Uma forma de avaliar o modelo poderia ser alterar o tamanho do conjunto de treinamento e avaliar o desempenho.

Você pode repetir esse método dez vezes para ver as métricas de pontuação. No entanto, é muito trabalhoso.

Em vez disso, o scikit-learn fornece uma função para realizar ajuste de parâmetros e validação cruzada.

Validação cruzada

Validação cruzada significa que durante o treinamento, o conjunto de treinamento desliza n número de vezes em dobras e então avalia o modelo n vezes. Por exemplo, se cv for definido como 10, o conjunto de treinamento é treinado e avaliado dez vezes. A cada rodada, o classificador escolhe aleatoriamente nove dobras para treinar o modelo, sendo a 10ª dobra destinada à avaliação.

Pesquisa de grade

Cada classificador possui hiperparâmetros para ajustar. Você pode tentar valores diferentes ou definir uma grade de parâmetros. Se você acessar o site oficial do scikit-learn, verá que o classificador logístico tem diferentes parâmetros para ajustar. Para tornar o treinamento mais rápido, você escolhe ajustar o parâmetro C. Ele controla o parâmetro de regularização. Deveria ser positivo. Um valor pequeno dá mais peso ao regularizador.

Você pode usar o objeto GridSearchCV. Você precisa criar um dicionário contendo os hiperparâmetros a serem ajustados.

Você lista os hiperparâmetros seguidos dos valores que deseja testar. Por exemplo, para ajustar o parâmetro C, você usa:

  • 'logisticregression__C': [0.1, 1.0, 1.0]: O parâmetro é precedido do nome, em letras minúsculas, do classificador e dois sublinhados.

O modelo tentará quatro valores diferentes: 0.001, 0.01, 0.1 e 1.

Você treina o modelo usando 10 dobras: cv=10

from sklearn.model_selection import GridSearchCV
# Construct the parameter grid
param_grid = {
    'logisticregression__C': [0.001, 0.01,0.1, 1.0],
    }

Você pode treinar o modelo usando GridSearchCV com os parâmetros gri e cv.

# Train the model
grid_clf = GridSearchCV(model,
                        param_grid,
                        cv=10,
                        iid=False)
grid_clf.fit(X_train, y_train)

SAÍDA

GridSearchCV(cv=10, error_score='raise-deprecating',
       estimator=Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...ty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False))]),
       fit_params=None, iid=False, n_jobs=1,
       param_grid={'logisticregression__C': [0.001, 0.01, 0.1, 1.0]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=0)

Para acessar os melhores parâmetros, você usa best_params_

grid_clf.best_params_

SAÍDA

{'logisticregression__C': 1.0}

Depois de treinar o modelo com quatro valores de regularização diferentes, o parâmetro ideal é

print("best logistic regression from grid search: %f" % grid_clf.best_estimator_.score(X_test, y_test))

melhor regressão logística da pesquisa em grade: 0.850891

Para acessar as probabilidades previstas:

grid_clf.best_estimator_.predict_proba(X_test)
array([[0.83576677, 0.16423323],
       [0.9458291 , 0.0541709 ],
       [0.64760416, 0.35239584],
       ...,
       [0.99639224, 0.00360776],
       [0.02072033, 0.97927967],
       [0.56782222, 0.43217778]])

Modelo XGBoost com scikit-learn

Vamos tentar exemplos do Scikit-learn para treinar um dos melhores classificadores do mercado. XGBoost é uma melhoria em relação à floresta aleatória. A base teórica do classificador fora do escopo deste Python Tutorial do Scikit. Tenha em mente que o XGBoost ganhou muitas competições kaggle. Com um tamanho médio de conjunto de dados, ele pode ter um desempenho tão bom quanto um algoritmo de aprendizado profundo ou até melhor.

O classificador é difícil de treinar porque possui um grande número de parâmetros para ajustar. Você pode, é claro, usar GridSearchCV para escolher o parâmetro para você.

Em vez disso, vamos ver como usar uma maneira melhor de encontrar os parâmetros ideais. GridSearchCV pode ser entediante e muito demorado para treinar se você passar muitos valores. O espaço de busca cresce junto com o número de parâmetros. Uma solução preferível é usar RandomizedSearchCV. Este método consiste em escolher aleatoriamente os valores de cada hiperparâmetro após cada iteração. Por exemplo, se o classificador for treinado em 1000 iterações, então 1000 combinações serão avaliadas. Funciona mais ou menos assim. GridSearchCV

Você precisa importar o xgboost. Se a biblioteca não estiver instalada, use pip3 install xgboost ou

use import sys
!{sys.executable} -m pip install xgboost

In Jupyter meio Ambiente

Em seguida,

import xgboost
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import StratifiedKFold

A próxima etapa neste Scikit Python O tutorial inclui a especificação dos parâmetros a serem ajustados. Você pode consultar a documentação oficial para ver todos os parâmetros a serem ajustados. Por uma questão de Python Tutorial do Sklearn, você escolhe apenas dois hiperparâmetros com dois valores cada. O XGBoost leva muito tempo para treinar; quanto mais hiperparâmetros na grade, mais tempo você precisa esperar.

params = {
        'xgbclassifier__gamma': [0.5, 1],
        'xgbclassifier__max_depth': [3, 4]
        }

Você constrói um novo pipeline com o classificador XGBoost. Você escolhe definir 600 estimadores. Observe que n_estimators são um parâmetro que você pode ajustar. Um valor alto pode levar ao overfitting. Você pode tentar valores diferentes por conta própria, mas esteja ciente de que isso pode levar horas. Você usa o valor padrão para os outros parâmetros

model_xgb = make_pipeline(
    preprocess,
    xgboost.XGBClassifier(
                          n_estimators=600,
                          objective='binary:logistic',
                          silent=True,
                          nthread=1)
)

Você pode melhorar a validação cruzada com o validador cruzado Stratified K-Folds. Você constrói apenas três dobras aqui para acelerar o cálculo, mas diminuindo a qualidade. Aumente esse valor para 5 ou 10 em casa para melhorar os resultados.

Você escolhe treinar o modelo em quatro iterações.

skf = StratifiedKFold(n_splits=3,
                      shuffle = True,
                      random_state = 1001)

random_search = RandomizedSearchCV(model_xgb,
                                   param_distributions=params,
                                   n_iter=4,
                                   scoring='accuracy',
                                   n_jobs=4,
                                   cv=skf.split(X_train, y_train),
                                   verbose=3,
                                   random_state=1001)

A pesquisa aleatória está pronta para uso, você pode treinar o modelo

#grid_xgb = GridSearchCV(model_xgb, params, cv=10, iid=False)
random_search.fit(X_train, y_train)
Fitting 3 folds for each of 4 candidates, totalling 12 fits
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8759645283888057, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8729701715996775, total= 1.0min
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8706519235199263, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8735460094437406, total= 1.3min
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8722791661868018, total=  57.7s
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8753886905447426, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8697304768486523, total= 1.3min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8740066797189912, total= 1.4min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8707671043538355, total= 1.0min
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8729701715996775, total= 1.2min
[Parallel(n_jobs=4)]: Done  10 out of  12 | elapsed:  3.6min remaining:   43.5s
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8736611770125533, total= 1.2min
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8692697535130154, total= 1.2min
[Parallel(n_jobs=4)]: Done  12 out of  12 | elapsed:  3.6min finished
/Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/model_selection/_search.py:737: DeprecationWarning: The default of the `iid` parameter will change from True to False in version 0.22 and will be removed in 0.24. This will change numeric results when test-set sizes are unequal. DeprecationWarning)
RandomizedSearchCV(cv=<generator object _BaseKFold.split at 0x1101eb830>,
          error_score='raise-deprecating',
          estimator=Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...
       reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
       silent=True, subsample=1))]),
          fit_params=None, iid='warn', n_iter=4, n_jobs=4,
          param_distributions={'xgbclassifier__gamma': [0.5, 1], 'xgbclassifier__max_depth': [3, 4]},
          pre_dispatch='2*n_jobs', random_state=1001, refit=True,
          return_train_score='warn', scoring='accuracy', verbose=3)

Como você pode ver, o XGBoost tem uma pontuação melhor do que a regressão logística anterior.

print("Melhor parameter", random_search.best_params_)
print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
Melhor parameter {'xgbclassifier__max_depth': 3, 'xgbclassifier__gamma': 0.5}
best logistic regression from grid search: 0.873157
random_search.best_estimator_.predict(X_test)
array(['<=50K', '<=50K', '<=50K', ..., '<=50K', '>50K', '<=50K'],      dtype=object)

Crie DNN com MLPClassifier no scikit-learn

Finalmente, você pode treinar um algoritmo de aprendizado profundo com o scikit-learn. O método é igual ao outro classificador. O classificador está disponível em MLPClassifier.

from sklearn.neural_network import MLPClassifier

Você define o seguinte algoritmo de aprendizado profundo:

  • Solucionador de Adam
  • Função de ativação Relu
  • Alfa = 0.0001
  • tamanho do lote de 150
  • Duas camadas ocultas com 100 e 50 neurônios respectivamente
model_dnn = make_pipeline(
    preprocess,
    MLPClassifier(solver='adam',
                  alpha=0.0001,
                  activation='relu',
                    batch_size=150,
                    hidden_layer_sizes=(200, 100),
                    random_state=1))

Você pode alterar o número de camadas para melhorar o modelo

model_dnn.fit(X_train, y_train)
  print("DNN regression score: %f" % model_dnn.score(X_test, y_test))

Pontuação de regressão DNN: 0.821253

LIME: Confie no seu modelo

Agora que você tem um bom modelo, precisa de uma ferramenta para confiar nele. Aprendizado de máquinas algoritmos, especialmente florestas aleatórias e redes neurais, são conhecidos como algoritmos de caixa preta. Diga o contrário, funciona, mas ninguém sabe por quê.

Três pesquisadores criaram uma ótima ferramenta para ver como o computador faz uma previsão. O artigo se chama Por que devo confiar em você?

Eles desenvolveram um algoritmo chamado Explicações agnósticas de modelos interpretáveis ​​locais (LIME).

Veja um exemplo:

às vezes você não sabe se pode confiar em uma previsão de aprendizado de máquina:

Um médico, por exemplo, não pode confiar num diagnóstico só porque um computador o disse. Você também precisa saber se pode confiar no modelo antes de colocá-lo em produção.

Imagine que podemos entender por que qualquer classificador está fazendo uma previsão até mesmo de modelos incrivelmente complicados, como redes neurais, florestas aleatórias ou svms com qualquer kernel

Será mais fácil confiar em uma previsão se pudermos compreender as razões por trás dela. A partir do exemplo do médico, se a modelo lhe dissesse quais sintomas são essenciais, você confiaria nela, também seria mais fácil descobrir se você não deveria confiar na modelo.

O Lime pode dizer quais recursos afetam as decisões do classificador

Preparação de dados

São algumas coisas que você precisa alterar para executar o LIME python. Em primeiro lugar, é necessário instalar cal no terminal. Você pode usar pip install lime

O Lime faz uso do objeto LimeTabularExplainer para aproximar o modelo localmente. Este objeto requer:

  • um conjunto de dados em formato numpy
  • O nome dos recursos: feature_names
  • O nome das classes: class_names
  • O índice da coluna dos recursos categóricos: categorical_features
  • O nome do grupo para cada recurso categórico: categorical_names

Crie um conjunto de trem entorpecido

Você pode copiar e converter df_train de pandas para numpy muito facilmente

df_train.head(5)
# Create numpy data
df_lime = df_train
df_lime.head(3)

Obtenha o nome da classe O rótulo é acessível com o objeto unique(). Você deveria ver:

  • '<= 50K'
  • '> 50K'
# Get the class name
class_names = df_lime.label.unique()
class_names
array(['<=50K', '>50K'], dtype=object)

índice da coluna dos recursos categóricos

Você pode usar o método que você usou antes para obter o nome do grupo. Você codifica o rótulo com LabelEncoder. Você repete a operação em todos os recursos categóricos.

## 
import sklearn.preprocessing as preprocessing
categorical_names = {}
for feature in CATE_FEATURES:
    le = preprocessing.LabelEncoder()
    le.fit(df_lime[feature])
    df_lime[feature] = le.transform(df_lime[feature])
    categorical_names[feature] = le.classes_
print(categorical_names)    
{'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private',
       'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'],
      dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th',
       'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad',
       'Masters', 'Preschool', 'Prof-school', 'Some-college'],
      dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse',
       'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'],
      dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair',
       'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners',
       'Machine-op-inspct', 'Other-service', 'Priv-house-serv',
       'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support',
       'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child',
       'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other',
       'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba',
       'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England',
       'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras',
       'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica',
       'Japan', 'Laos', 'Mexico', 'Nicaragua',
       'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland',
       'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan',
       'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam',
       'Yugoslavia'], dtype=object)}

df_lime.dtypes
age               float64
workclass           int64
fnlwgt            float64
education           int64
education_num     float64
marital             int64
occupation          int64
relationship        int64
race                int64
sex                 int64
capital_gain      float64
capital_loss      float64
hours_week        float64
native_country      int64
label              object
dtype: object

Agora que o conjunto de dados está pronto, você pode construir os diferentes conjuntos de dados, conforme mostrado nos exemplos de aprendizado do Scikit abaixo. Na verdade, você transforma os dados fora do pipeline para evitar erros com o LIME. O conjunto de treinamento no LimeTabularExplainer deve ser um array numpy sem string. Com o método acima, você já tem um conjunto de dados de treinamento convertido.

from sklearn.model_selection import train_test_split
X_train_lime, X_test_lime, y_train_lime, y_test_lime = train_test_split(df_lime[features],
                                                    df_lime.label,
                                                    test_size = 0.2,
                                                    random_state=0)
X_train_lime.head(5)

Você pode fazer o pipeline com os parâmetros ideais do XGBoost

model_xgb = make_pipeline(
    preprocess,
    xgboost.XGBClassifier(max_depth = 3,
                          gamma = 0.5,
                          n_estimators=600,
                          objective='binary:logistic',
                          silent=True,
                          nthread=1))

model_xgb.fit(X_train_lime, y_train_lime)
/Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/preprocessing/_encoders.py:351: FutureWarning: The handling of integer data will change in version 0.22. Currently, the categories are determined based on the range [0, max(values)], while in the future they will be determined based on the unique values.
If you want the future behavior and silence this warning, you can specify "categories='auto'."In case you used a LabelEncoder before this OneHotEncoder to convert the categories to integers, then you can now use the OneHotEncoder directly.
  warnings.warn(msg, FutureWarning)
Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...
       reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
       silent=True, subsample=1))])

Você recebe um aviso. O aviso explica que não é necessário criar um codificador de rótulo antes do pipeline. Se não quiser usar LIME, você pode usar o método da primeira parte do tutorial Machine Learning with Scikit-learn. Caso contrário, você pode continuar com este método, primeiro crie um conjunto de dados codificado e defina o codificador mais quente dentro do pipeline.

print("best logistic regression from grid search: %f" % model_xgb.score(X_test_lime, y_test_lime))
best logistic regression from grid search: 0.873157
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01],
       [9.5173013e-01, 4.8269872e-02],
       [7.9344827e-01, 2.0655173e-01],
       ...,
       [9.9031430e-01, 9.6856682e-03],
       [6.4581633e-04, 9.9935418e-01],
       [9.7104281e-01, 2.8957171e-02]], dtype=float32)

Antes de usar o LIME em ação, vamos criar um array numpy com os recursos da classificação errada. Você pode usar essa lista posteriormente para ter uma ideia do que enganou o classificador.

temp = pd.concat([X_test_lime, y_test_lime], axis= 1)
temp['predicted'] = model_xgb.predict(X_test_lime)
temp['wrong']=  temp['label'] != temp['predicted']
temp = temp.query('wrong==True').drop('wrong', axis=1)
temp= temp.sort_values(by=['label'])
temp.shape

(826, 16)

Você cria uma função lambda para recuperar a previsão do modelo com os novos dados. Você precisará disso em breve.

predict_fn = lambda x: model_xgb.predict_proba(x).astype(float)
X_test_lime.dtypes
age               float64
workclass           int64
fnlwgt            float64
education           int64
education_num     float64
marital             int64
occupation          int64
relationship        int64
race                int64
sex                 int64
capital_gain      float64
capital_loss      float64
hours_week        float64
native_country      int64
dtype: object
predict_fn(X_test_lime)
array([[7.96461046e-01, 2.03538969e-01],
       [9.51730132e-01, 4.82698716e-02],
       [7.93448269e-01, 2.06551731e-01],
       ...,
       [9.90314305e-01, 9.68566816e-03],
       [6.45816326e-04, 9.99354184e-01],
       [9.71042812e-01, 2.89571714e-02]])

Você converte o dataframe do pandas em um array numpy

X_train_lime = X_train_lime.values
X_test_lime = X_test_lime.values
X_test_lime
array([[4.00000e+01, 5.00000e+00, 1.93524e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       [2.70000e+01, 4.00000e+00, 2.16481e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       [2.50000e+01, 4.00000e+00, 2.56263e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       ...,
       [2.80000e+01, 6.00000e+00, 2.11032e+05, ..., 0.00000e+00,
        4.00000e+01, 2.50000e+01],
       [4.40000e+01, 4.00000e+00, 1.67005e+05, ..., 0.00000e+00,
        6.00000e+01, 3.80000e+01],
       [5.30000e+01, 4.00000e+00, 2.57940e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01]])
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01],
       [9.5173013e-01, 4.8269872e-02],
       [7.9344827e-01, 2.0655173e-01],
       ...,
       [9.9031430e-01, 9.6856682e-03],
       [6.4581633e-04, 9.9935418e-01],
       [9.7104281e-01, 2.8957171e-02]], dtype=float32)
print(features,
      class_names,
      categorical_features,
      categorical_names)
['age', 'workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country'] ['<=50K' '>50K'] [1, 3, 5, 6, 7, 8, 9, 13] {'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private',
       'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'],
      dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th',
       'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad',
       'Masters', 'Preschool', 'Prof-school', 'Some-college'],
      dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse',
       'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'],
      dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair',
       'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners',
       'Machine-op-inspct', 'Other-service', 'Priv-house-serv',
       'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support',
       'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child',
       'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other',
       'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba',
       'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England',
       'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras',
       'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica',
       'Japan', 'Laos', 'Mexico', 'Nicaragua',
       'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland',
       'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan',
       'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam',
       'Yugoslavia'], dtype=object)}
import lime
import lime.lime_tabular
### Train should be label encoded not one hot encoded
explainer = lime.lime_tabular.LimeTabularExplainer(X_train_lime ,
                                                   feature_names = features,
                                                   class_names=class_names,
                                                   categorical_features=categorical_features, 
                                                   categorical_names=categorical_names,
                                                   kernel_width=3)

Vamos escolher um domicílio aleatório do conjunto de teste e ver a previsão do modelo e como o computador fez sua escolha.

import numpy as np
np.random.seed(1)
i = 100
print(y_test_lime.iloc[i])
>50K
X_test_lime[i]
array([4.20000e+01, 4.00000e+00, 1.76286e+05, 7.00000e+00, 1.20000e+01,
       2.00000e+00, 4.00000e+00, 0.00000e+00, 4.00000e+00, 1.00000e+00,
       0.00000e+00, 0.00000e+00, 4.00000e+01, 3.80000e+01])

Você pode usar o explicador com explica_instance para verificar a explicação por trás do modelo

exp = explainer.explain_instance(X_test_lime[i], predict_fn, num_features=6)
exp.show_in_notebook(show_all=False)

Preparação de dados

Podemos ver que o classificador previu o agregado familiar corretamente. A renda está, de fato, acima de 50 mil.

A primeira coisa que podemos dizer é que o classificador não tem tanta certeza sobre as probabilidades previstas. A máquina prevê que a família tenha uma renda superior a 50 mil com probabilidade de 64%. Esses 64% são compostos por Ganho de Capital e Conjugal. A cor azul contribui negativamente para a classe positiva e a linha laranja, positivamente.

O classificador fica confuso porque o ganho de capital desta família é nulo, enquanto o ganho de capital é normalmente um bom preditor de riqueza. Além disso, o agregado familiar trabalha menos de 40 horas por semana. Idade, ocupação e sexo contribuem positivamente para o classificador.

Se o estado civil fosse solteiro, o classificador teria previsto uma renda abaixo de 50 mil (0.64-0.18 = 0.46)

Podemos tentar com outra família que tenha sido classificada incorretamente

temp.head(3)
temp.iloc[1,:-2]
age                  58
workclass             4
fnlwgt            68624
education            11
education_num         9
marital               2
occupation            4
relationship          0
race                  4
sex                   1
capital_gain          0
capital_loss          0
hours_week           45
native_country       38
Name: 20931, dtype: object
i = 1
print('This observation is', temp.iloc[i,-2:])
This observation is label        <=50K
predicted     >50K
Name: 20931, dtype: object
exp = explainer.explain_instance(temp.iloc[1,:-2], predict_fn, num_features=6)
exp.show_in_notebook(show_all=False)

Preparação de dados

O classificador previu uma renda abaixo de 50 mil, embora isso não seja verdade. Esta casa parece estranha. Não possui ganho de capital, nem perda de capital. Ele é divorciado e tem 60 anos, e é uma pessoa instruída, ou seja, número_educação > 12. De acordo com o padrão geral, este agregado familiar deveria, tal como explicado pelo classificador, obter um rendimento inferior a 50k.

Você tenta brincar com o LIME. Você notará erros grosseiros do classificador.

Você pode verificar o GitHub do proprietário da biblioteca. Eles fornecem documentação extra para classificação de imagens e textos.

Resumo

Abaixo está uma lista de alguns comandos úteis com a versão scikit learn >=0.20

criar conjunto de dados de treinamento/teste estagiários divididos
Construir um pipeline
selecione a coluna e aplique a transformação fazercolunatransformador
tipo de transformação
estandardizar StandardScaler
mínimo máximo MinMax Scaler
Normalizar Normalizador
Imputar valor ausente imputar
Converter categórico OneHotEncoder
Ajuste e transforme os dados ajuste_transform
Faça o pipeline make_pipeline
Modelo básico
regressão logística LogísticaRegressão
XGBoostName Classificador XGB
Rede neural Classificador MLP
Pesquisa de grade GradeSearchCV
Pesquisa aleatória CV de pesquisa randomizado