<jupyter_start><jupyter_text>ModelingEsse Notebook visa apresentar a etapa de definição do modelo de machine learning usado para fazer a predição da ```ICU```.Após a etapa de *Feature Engineering* os dados estão prontos para serem usados de maneira a auxiliar a classificação. Sumário   * [Importe das Bibliotecas](import)   * [Leitura dos dados](leitura)   * [Metrica de avaliação](metrics)   * [Dummy Classifier](dummy)   * [Seleção de Modelo](modsel)   * [Random Forest Classifier](rfcsf)   * [Matriz de confusão](confusion)   * [Salvando modelo](savemodel)   * [Conclusão](concl)     Importe das Bibliotecas  Bibliotecas Externas<jupyter_code>import pandas as pd
import numpy as np
from pprint import pprint
import joblib

from sklearn.model_selection import train_test_split, cross_validate, RepeatedStratifiedKFold, RandomizedSearchCV, LeaveOneOut
from sklearn.ensemble import RandomForestClassifier
from sklearn.dummy import DummyClassifier
from sklearn import tree
from sklearn.metrics import RocCurveDisplay
from sklearn.metrics import plot_confusion_matrix<jupyter_output><empty_output><jupyter_text>Bibliotecas Internas<jupyter_code>import sys
sys.path.insert(1, "../src/")
from modeling import run_model_cv, many_Lazy_Classifiers<jupyter_output><empty_output><jupyter_text>-----------------Retornar ao [Sumário](sumario)  Leitura dos dadosFaz a leitura dos dados que serão usados para a modelagem.**```df : pd.DataFrame```** é o DataFrame contendo os dados que já passaram pela etapa de *feature engineering*.<jupyter_code># Leitura do raw data desse  projeto no Github
df = pd.read_csv('../data/processed/df_featured.csv', index_col='Unnamed: 0')
df.index.names = ['PATIENT_VISIT_IDENTIFIER']

# Imprime na tela todas as colunas em vez de as 10 primeiras e 10 ultimas
pd.set_option('max_columns', df.shape[1])
df.head()<jupyter_output><empty_output><jupyter_text>Métrica de avaliação:A métrica de avaliação utilizada para determinar o modelo de machine learning será o **ROC AUC**.* **ROC** do inglês *Receiver Operating Characteristics* é uma curva plotada entre os eixos *Taxa de Verdadeiros Positivos* e a *Taxa de Falsos Positivos*. Essa métrica mede o quanto o modelo consegue separar o binômio de saída entre suas duas classes (```ICU = 0``` e ```ICU = 1```).* **AUC** do inglês *Area Under the Curve* é a maneira de resumir a curva **ROC** em um único valor real, de maneira a facilitar a comparação com outros modelos. Seria o mesmo que calcular a inegral do ROC* **ROC AUC**    * O valor será próximo de 0.5 se o modelo não tiver capacidade de separar as variáveis de saída em suas respectivas classes. Imagine que o modelo Tem 50% de verdadeiro positivo e 50% de falso positivo, ou seja, acerta em 50% das tentativas.    * Quanto mais o valor se aproxima de 1.0, mais capacidade o modelo tem de separar os dois grupos, ou classes, acertando muito mais os Verdadeiros Positivos que errando.    * O inverso também é verdadeiro. Quanto mais o valor se aproxima de 0, significa que o modelo também tem a capacidade de separar o binômio de saída em dois grupos, só que nesse caso, errando muito, pois teria um valor de Falsos Verdadeiros muito grande. Como a saída tem 2 estados, sendo 0 ou 1, poderia ser usado o inverso (NOT) da resposta e ainda assim o modelo acertaria bastante.  Dummy ClassifierEssa etapa visa criar um modelo genérico que faz predições dos dados de entrada usando regras bem simples. Ter um modelo "bobo" que usa uma regra simples é importante para avaliar o modelo final, pois se espera do modelo a ser implementado que ao menos seja melhor que escolher aleatóriamente entre as alternativas de saída, ou então "chutar" a alternativa de saída mais comum.<jupyter_code>dummy_clf = DummyClassifier()
run_model_cv('DummyClassifier',dummy_clf , df, 10, 10)<jupyter_output>DummyClassifier: AUC Mean: 0.5, AUC Std: 0.0, AUC CI: 0.5 - 0.5<jupyter_text>Vemos que o DummyClassifier encontrou um ROC AUC Médio de **0.5**, logo, essa deve ser a meta inicial a ser batida.  Seleção de ModeloA seleção de modelo será suportada pelo módulo LazyClassifier. A idéia aleatorizar os dados, dividi-los em teste e treino, e rodar a classe LazyClassifier, que vai treinar os modelos mais comuns e dar o resultado para cada um dos modelos.A função *many_Lazy_Classifiers* roda n vezes o teste e treino, trazendo como resultado a média e o desvio padrão de cada modelo, tornando assim facil a seleção de qual modelo utilizar.É importante salientar que o módulo LazyClassifier treina os dados utilizando valores padrões dos modelos, ou seja, sem 'tunar' os hiperparâmetros de cada modelo. Sabemos que esse tune pode melhorar muito o resultado de um modelo, fazendo com que algum que tivesse uma resposta não tão boa com os valores padrões, superasse o modelo com a melhor avaliação usando os parâmetros padrão.Para esse caso irá ser usado o modelo que der o melhor valor geral, e após isso, os hiperparâmetros desse modelo serão avaliados.<jupyter_code>many_Lazy_Classifiers(df, 100)<jupyter_output><empty_output><jupyter_text>Houve um empate em média e Desvio padrão entre dois modelos: **CatBoostClassifier** e **RandomForestClassifier**. Como o **RandomForestClassifier** é mais difundido e tem mais material sobre sua implementação, foi escolhido usar esse modelo.  Random Forest Classifier  imprimindo os parâmetros padrõesA idéia aqui não é fazer uma análise, e sim ter uma referência dos parâmetros *default* do modelo **RandonForestClassiier**.```std_rf_clf``` *Standard Random Forest Classifier* é o modelo criado com os valores padrões.<jupyter_code>std_rf_clf = RandomForestClassifier(random_state = 42)
# Look at parameters used by our current forest
print('Os parâmetros sendo utilizados no modelo são::\n')
pprint(std_rf_clf.get_params())<jupyter_output>Os parâmetros sendo utilizados no modelo são::

{'bootstrap': True,
 'ccp_alpha': 0.0,
 'class_weight': None,
 'criterion': 'gini',
 'max_depth': None,
 'max_features': 'auto',
 'max_leaf_nodes': None,
 'max_samples': None,
 'min_impurity_decrease': 0.0,
 'min_impurity_split': None,
 'min_samples_leaf': 1,
 'min_samples_split': 2,
 'min_weight_fraction_leaf': 0.0,
 'n_estimators': 100,
 'n_jobs': None,
 'oob_score': False,
 'random_state': 42,
 'verbose': 0,
 'warm_start': False}<jupyter_text>Avaliação de performance com parâmetros padrãoA ideia é avaliar o desempenho do modelo **RandonForestClassifier** usando para isso os parâmetros padrão. Esse resultado vai servir de base para o *tunning* dos hiperparâmetros.Para fazer tal avaliação, pensando em minimizar o erro causado pelo baixo número de observações que se tem, será executada a função ```run_model_cv```, que usa ```RepeatedStratifiedKFold``` para estimar um intervalo de confiança do resultado.<jupyter_code>run_model_cv('RandomForestClassifier', std_rf_clf, df, 5, 30)<jupyter_output><empty_output><jupyter_text>Podemos ver então que utilizando os valores padrão temos um **AUC Médio = 80%** e um **Desvio Padrão = 5.3%**.  Seleção automática de hiperparâmetrosO método ```RandomizedSearchCV``` usa um grid de parâmetros pré configurado, e aleatóriamente utiliza esses parâmetros em seus modelos, encontrando assim a melhor combinação dos parâmetros sem necessáriamente rodar todas as combinações possíveis.A variável ```random_grid``` carrega esses parâmetros que se deseja testar.<jupyter_code>n_estimators = [int(x) for x in np.linspace(start = 500, stop = 2000, num = 4)]
max_features = ['auto', 'sqrt', 'log2']
max_depth = [int(x) for x in np.linspace(2, 30, num = 5)]
max_depth.append(None)
min_samples_split = [1, 2, 5, 10]
min_samples_leaf = [1, 2, 4]
bootstrap = [True, False]
oob_score = [True, False]
criterion = ['gini', 'entropy']

random_grid = {'n_estimators': n_estimators,
               'max_features': max_features,
               'max_depth': max_depth,
               'min_samples_split': min_samples_split,
               'min_samples_leaf': min_samples_leaf,
               'bootstrap': bootstrap,
               'oob_score' : oob_score,
               'criterion' : criterion}

pprint(random_grid)
rf = RandomForestClassifier()
rf_random = RandomizedSearchCV(estimator = rf, param_distributions = random_grid, n_iter = 750, cv = 5, verbose=2, random_state=42, n_jobs = -1)# Fit the random search model
rf_random.fit(df.drop('ICU', axis=1), df['ICU'])
rf_random.best_params_<jupyter_output><empty_output><jupyter_text>Os parâmetros selecionados são:|Parâmetro        |Valor  ||-----------------|-------||oob_score        |False  ||n_estimators     |2000   ||min_samples_split|10     ||min_samples_leaf |1      ||max_features     |auto   ||max_depth        |30     ||criterion        |entropy||bootstrap        |False  | Basta agora rodar novamente o modelo, desta vez, utilizando os parâmetros recomendados, para que seja possível avaliar o ganho relacionado com os hiperparâmetros.<jupyter_code>rf_clf = RandomForestClassifier(random_state = 1923814, n_estimators=2000, min_samples_split=10,
                                min_samples_leaf=1, max_features='auto', max_depth=30,
                                bootstrap=False, criterion='entropy')
run_model_cv('RandomForestClassifier', rf_clf, df, 5, 30)<jupyter_output>RandomForestClassifier: AUC Mean: 0.8, AUC Std: 0.052, AUC CI: 0.7 - 0.9<jupyter_text>|Parâmetro        |De   |Para ||-----------------|-----|-----||AUC ROC Mean     |80%  |80%  ||AUC ROC Std      |5.3% |5.2% |Houve uma pequena melhora, praticamente insignificante.  Matriz de confusãoA **Matriz de Confusão** mostra como estão distribuidos os erros e os acertos do modelo.Como o valor médio do ROC_AUC foi de 80%, é esperado cerca de 20% de valores incorretos, e desejavelmente devem ser o menor possível na posição: True Label = 1; Predicted Label = 0, que se refere a pessoas indicadas pelo modelo que não seriam internados na UTI e o resultado prático mostrou o contrário.<jupyter_code>import matplotlib.pyplot as plt
y = df["ICU"]
X = df.drop(["ICU"], axis=1)

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y,
                                                        shuffle=True,
                                                        test_size=0.20,
                                                        random_state=1923814)

rf_clf = RandomForestClassifier(random_state = 1923814, n_estimators=2000, min_samples_split=10,
                                min_samples_leaf=1, max_features='auto', max_depth=30,
                                bootstrap=False, criterion='entropy')

rf_clf.fit(X_train, y_train)

plot_confusion_matrix(rf_clf, X_test, y_test)
plt.savefig('../img/modeling/confusionmatrix.png')
svc_disp = RocCurveDisplay.from_estimator(rf_clf, X_test, y_test)
plt.savefig('../img/modeling/roc.png')<jupyter_output><empty_output><jupyter_text>Salvando modeloPara que seja possível utilizar o modelo sem precisar treinar todas as vezes que inicializar o sistema, é possível salvar o modelo treinado num arquivo , e quando necessário, carregar o arquivo, trazendo o modelo treinado a tona, pronto para fazer as predições.Nesse caso, foi utilizado ```compress = 3``` que traz um bom nível de compressão.<jupyter_code>joblib.dump(rf_clf, "../models/rf_compressed.joblib", compress=3)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08867170146601511, 'fraction_numerical': 0.015459795646379388, 'mean_word_length': 4.450847457627119, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '176087', 'license': 'MIT', 'n_tokens_mistral': 3978, 'n_tokens_neox': 3746, 'n_words': 1267, 'path': 'notebooks/4.0_Modeling.ipynb', 'repo_name': 'fdrigui/covid19_icu_admission_prediction', 'size': '11255'}