<jupyter_start><jupyter_text>Prevendo a Satisfação dos clientes do SantanderNeste projeto treinaremos um modelo de Machine Learning, com o objetivo de prever a satisfação dos clientes do Santander Relevância do projeto:Prever a satisfação dos clientes é crucial para qualquer empresa, uma vez que o cliente é o chefe que pode demitir todos os colabordaores da empresa, inclusive o CEO.Dessa forma, mantermos os nossos clientes satisfeitos é uma vantagem competitiva vital no mercado atual.  Características gerais:Os dados foram obtidos na plataforma do Kaggle (https://www.kaggle.com/c/santander-customer-satisfaction/data), uma vez que os mesmos foram objetos de uma competição da mesma plataforma.O dataset é composto por:  - 76020 instâncias  - 371   variáveis ObjetivoObjetivo do projeto é atingir uma métrica de roc_auc de 90%, utilizando modelos simples de machine learning. EstratégiaTreinaremos baselines dos seguintes modelos: LogisticRegession, LDA, DecisionTree, RandomForest, Sthocastic Gradient Descent, dos quais serão escolhidos os dois melhores para seremotimizados.Foram utilizadas as bibliotecas do Pydata stack, tais como: Pandas Numpy e Scikit-learn. Porém, a maior parte do código foi modulado em funções e por sua vez em classes e pacote específico para esse projeto foi desenvolvido. Para acessar o código, favor acessar este link: (https://github.com/bruno154/Data_Science_Python/tree/master/Machine_Learning_Projects/Projeto_1_Santander_Customers/src)<jupyter_code>#MDefinindo o diretorio de trabalho
import os
os.chdir('C:/1.Portfolio/Data_Science_Python/Machine_Learning_Projects/Projeto_1_Santander_Customers')
#Importação de bibliotecas
import numpy as np
import pandas as pd
import pickle
import matplotlib.pyplot as plt
from src.d01_Data import collect_data, load_data
from src.d02_Processing import processing_data, load_processed_data, save_processed_data
from src.d03_Modelling import model_selection, learning_curves
from src.d03_Modelling import Random_Forest
from src.d04_ModelEvaluation import class_report
from src.d04_ModelEvaluation import evaluation
from src.d05_Viz import visualization


import warnings
warnings.filterwarnings("ignore")<jupyter_output><empty_output><jupyter_text>Coleta de DadosAbaixo carregamos a base de dados com 76020 instâncias (Linhas) e 371 variáveis (Colunas). A base não possui valores missing, no entanto, suas variáveis se encontram em escalas diferentes e fortemente desbalanciado favorecendo a classe 0 (clientes satisfeitos).  Dessa forma, padronizaremos as escalas, o que será muito benéfico para a aplicação do algaritmo PCA para a redução da grande dimensionalidade. Outra medida será o balanceamento do dataset, aplicaremos a técnica SMOTE, a principal vantagem é qua a mesma cria pontos de dados sintéticos, o que diminui a propensão ao overfitting.<jupyter_code>"""
Carregando os dados de um diretório desejado.
"""

X, y, treino, teste = collect_data('train.csv', 'test.csv')
"""
Checando se os dados de teste foram carregados!!!
"""
teste.head()
"""
Checando se os dados de treino foram carregados!!!
"""
treino.head()
""" Checando por valores missing - Não temos valores missing """

print(treino.isnull().values.any())
print(treino.isnull().sum().sum())
print(treino.shape)
"""Checando a porpocinalidade da variável target
   Precisaremos balancear o nosso dataset, pois temos muitos exemplos da classe 0. """

visualization.check_balance(dados = treino,target = 'TARGET')<jupyter_output>As classes do dataset são: [0 1]<jupyter_text>Processamento dos DadosRealizaremos o processamento dos dados preparando-os para o treinamento em três etapas: 1. Balanceamento do DatasetO desbalanceamento do dataset é um problema comum que devemos enfrentar quando trabalhamos com dados. O nosso dataset se encontra fortemente desbalanceado para a classe 0 - clientes satisfeitos - sendo assim, se treinarmos um modelo dessa forma, nosso modelo terá uma boa acurácia, uma vez que o modelo iria prever todos os 0 de forma correta, porém, não 'aprenderia' a prever a classe de interesse 1 - clientes insatisfeitos - devido ao baixo número de exemplos desta classe.Aplicaremos uma técnica chamada de SMOTE para resolver este problema. Para detalhes sobre essa técnica, por favor acessem este link - https://medium.com/analytics-vidhya/balance-your-data-using-smote-98e4d79fcddb - de um excelente artigo sobre o tema. 2. Aplicar escalonamento através da classe MinMaxScalerO MinMaxScale nada mais é do que a subtração de cada elemento da variável x pelo seu menor valor (min(x)) e divisão pelo range (max(x) - min(x)) da variável. Xnew = x - min(X)/(max(x) - min(x))Essa técnica não altera o shape das distribuições de dados, assim como também não altera a informação passada pelos dados originais. Essas são as principais razões da mesma ter sido escolhida. 3. Aplicar redução de dimensionalidade - PCAO PCA é um algaritmo de Machine Learning de aprendizagem não supervisionada que comprime as variáveis em componentes independentes, evitando assim a perda de informação dos dados originais. Algumas outras técnicas, como RFE, preveem a eliminação das variáveis menos importantes, acarretando na perda de informação para o modelo e eliminando também a multi-colinearidade, uma vez que os componentes são independentes entre si. Para mais detalhes da matemática/estatística envolvida no algaritmo favor referênciar a este artigo https://towardsdatascience.com/a-one-stop-shop-for-principal-component-analysis-5582fb7e0a9c .<jupyter_code>"""
Foram executados os procediemntos abaixo:

      1. Balanciar Dataset
      2. Aplicar MinMaxScaler
      3. Aplicar a redução de dimensionalidade - PCA
"""
X_treino, X_teste, y_treino, y_teste = processing_data(X,y)
save_processed_data(['X_treino.csv','X_teste.csv', 'y_treino.csv', 'y_teste.csv'],[X_treino, X_teste, y_treino, y_teste])<jupyter_output>Por favor adicione o diretório de trabalho? C:\1.Portfolio\Data_Science_Python\Machine_Learning_Projects\Projeto_1_Santander_Customers\data\02_processed<jupyter_text>Seleção do ModeloComo uma primeira etapa treinamos cinco modelos, sem nenhum tunnig de parâmetros, desta etapa foram escolhidos três modelos com a maior ROCAUC, são eles:- Regressão Logística - LR - com ROUCAUC de 0.91- Sthocastic Gradiente Descent Classifier - SGD - com ROCAUC de 0.91- Random Forest - RF - com ROCAUC de 0.97Logo após, analisaremos as Learning Curves para cada modelo a fim de elegermos o modelo que irá ser otimizado. 1. Regressão Logística:O modelo de regressão logística demonstrou um comportamento relativamente estável a medida que as 'training instances' vão crescendo, variando entre 0.9130 e 0.9150 de ROCAUC, é observada também pouca diferença entre a performance sobre os dados de treino e de validação, o que indica que o modelo tem uma boa capacidade de generalização. Porém, devido a convergência das curvas durante todo o espaço de hipóteses (diferentes valores de training instances), o modelo não parece ter um espaço muito grande para evoluir a partir do range de 0.9130 e 0.9150. 2. Sthocastic Gradient Descent:O modelo sthocastic gradient descent, demonstra um comportamento mais errático como é típico do modelo, apresenta também as curvas tanto de treino quanto de validação já convergentes com uma direção lateral indicando pouco espaço de ganho de performance além do patamar apresentado.  3. Random Forest:Já o modelo de Random Forest apresenta uma configuração diferente dos demais, as curvas estão bem espaçadas com a curva de treino em overfitting com ROCAUC 1.0 e da validação suavemente convergindo atingindo o patamar de aproxidamente 0.97(bem acima dos demais) ao final da curva. Essa configuração demonstra que caso obtivéssemos uma maior quantidade de dados convergiríamos a curva de validação à curva de treino em um patamar de ROCAUC entre 0.98 a 0.99(excelentes números), como estamos falando de dados de clientes seria uma questão de tempo para obtermos esses novos dados.De forma mais imediata, poderíamos tentar diminuir a complexidade do modelo (realizando o tunning dos parâmetros) afim de obter um patamar mais generalizável e comparar com os demais. Dessa forma, iremos otimizar o Random Forest.<jupyter_code># Treinando diversos modelos
models = model_selection(X_treino,y_treino, X_teste, y_teste)
#Gerando as Learnings Curves dos 3 melhores modelos
models_curve = [models[0][1], models[3][1], models[4][1]]
learning_curves(models_curve, X_treino, y_treino)<jupyter_output><empty_output><jupyter_text>ModelagemUtilizamos a técnica de Randomized Search para encontrar os melhores parâmetros possíveis, apessar dessa técnica não exaurir todas as possíveis combinações de parâmetros do espaço de hipóteses, como a Grid Search, ela é mais performática alcançando resultados satisfatórios em um menor intervalo de tempo.O processo de tunning dos parâmetros, assim como o treinamento do modelo, durarão 16.5 minutos resultando em uma ROCAUC cross validada de 0.96, mesmo patamar da nossa baseline, ou seja, a técnica de Randomized Search não gerou grandes ganhos em nossa métrica de avaliação do modelo.Abaixo iremos testar algumas hipóteses a fim de tentarmos elevar a ROCAUC, apesar de já termos alcançado o objetivo na definição do problema de 0.90.<jupyter_code># Modelando Random Forest
trained_model, _, best_params = Random_Forest.random_RF(X_treino, y_treino,5,'rf_model.sav')
class_report(trained_model, X_treino, y_treino, X_teste, y_teste)
#Melhores parametros
best_params
# Learning Curve do modelo estimado com o RandomSearch
model = [trained_model]
learning_curves(model, X_treino, y_treino)<jupyter_output><empty_output><jupyter_text>Alterando principais parâmetros Primeira hipótese: Iremos testar o parametro max_depth, que representa a quantidade de camadas(profundidade) da nossa árvore de decisão.Na hipótese nula o max_depth é igual a 13. (Valor encontrado pelo Randomized Search).Já na hipótese alternativa iremos testar valores de 0 a 40 e avaliar a ROCAUC para cada valor do parâmetro. Conforme curva abaixo, podemos observar que o valor da nossa métrica se estabiliza em max_depth = 20, dessa forma atualizaremos o valor do max_depth para 20 no próximo treinamento. Segunda hipótese:Testaremos o número de estimadores, o valor encontrado pelo Randomized Search foi de 250 estimadores. Testando a quantidade de 200 a 1000 estimadores, observamos que a quantidade de estimadores não gera ganho algum de performance na curva de validação então iremos manter o parâmetro n_estimators em 250.<jupyter_code># Primeira Hipotese
Random_Forest.validation_curve(trained_model,X_treino, y_treino, param ='max_depth' , rang=np.linspace(0,40) , cv=5)
#Segunda Hipotese
Random_Forest.validation_curve(trained_model,X_treino, y_treino, param ='n_estimators' , rang=np.linspace(200,1000,dtype='int') , cv=5)<jupyter_output><empty_output><jupyter_text>Treinando modelo finalA partir dos resultados dos testes realizados acima, treinamos o nosso modelo final como os seguintes parâmetros.- n_estimatos: 250- max_depth: 20- criterion: gini- min_samples_split: 2- min_samples_leaf: 2Observamos a partir de uma simples observação das curvas de aprendizagem que obtivemos um pequeno ganho de 0,05 na curva de validação.<jupyter_code># Treinando modelo final
tunnig_model = Random_Forest.fitting_model_rf(X_treino,y_treino,'rf_model_final.sav',n_estimators=250, max_depth=20 ,
                               criterion='gini' , min_samples_split=2, min_samples_leaf=2)
#Learning curve e classification report do novo modelo
model1 = [tunnig_model]
learning_curves(model1, X_treino, y_treino)<jupyter_output><empty_output><jupyter_text>Análise do Modelo FinalA fim de realizarmos uma análise um pouco mais profunda acerca do nosso modelo final, carregaremos e avaliaremos três métodos diferentes:- As curvas de aprendizagem- Matriz de confusão / Classification Report- ROC CURVEPrimeiramente carregamos o nosso modelo final treinado, utilizando o pickle, assim como, os dados processados.  Analisando o Classification Report em conjunto com a matriz de confusão podemos notar o bom desempenho do modelo atingindo execelentes níveis de precision (TP/TP+FP) e recall (TP/TP+FN) para ambas as classes.A Curva de apredizagem esta idêntica a que já analisamos acima, onde alcançamos uma pequena melhora na nossa métrica de 0.05, além disso, a curva de validação esta suavimente convergindo para a curva de treino a medida que aumentamos a quantidade de instâncias de treino, ou seja, de dados.A ROC CURVE, nada mais é que plotagem em um gráfico do TPR (True Positive Ratio) contra o FPR (False Positive Ratio), onde quanto menor a área que esta sobre a curva melhor o desempenho do modelo. Em geral, a utilização da AUC - Area Under the Curve ou ROCAUC (Nomenclatura da api do scikit se refere a métrica) é bastante difundida devida a simplicidade de entendimento do seu conceito.<jupyter_code># Carregando o modelo 
def load_model(model):
    os.chdir(input("Qual o diretorio do modelo?"))
    file = open(model, 'rb')
    model = pickle.load(file)
    return model

model = load_model('rf_model_final.sav')
# desenvolver np.load
def load_processed_data(X_treino, y_treino, X_teste, y_teste):

    """
    :param X_treino: Arquivo com os dados de treino, variaveis independentes. str, .csv:
    :param y_treino: Arquivo com os dados de treino, variavel dependente. str, .csv
    :param X_teste:  Arquivo com os dados de teste, variaveis independentes. str, .csv: 
    :param y_teste:  Arquivo com os dados de teste, variavel dependente. str, .csv
    :return: Pandas Dataframe das variáveis X e da variável y divididos em dados de treino e teste.

    """
    path = input('Por favor adicione o diretório onde estão os dados?')
    for dirname, _, filename in os.walk(path, topdown = True):
        for filename in filename:

            if filename == X_treino:
                X = np.loadtxt(os.path.join(dirname,filename),  delimiter=',')
            elif filename == y_treino:
                Y = np.loadtxt(os.path.join(dirname,filename),  delimiter=',')
            elif filename == X_teste:
                x = np.loadtxt(os.path.join(dirname,filename),  delimiter=',')
            elif filename == y_teste:
                y = np.loadtxt(os.path.join(dirname,filename),  delimiter=',')
            else:
                pass
        print("Carregamento Finalizado!!!")


    return X,Y,x,y

X_treino, y_treino, X_teste, y_teste = load_processed_data('X_treino.csv','y_treino.csv','X_teste.csv','y_teste.csv')
#ClassReport
class_report(model,X_treino,y_treino,X_teste, y_teste)
#Evaluation

evaluation(model,X_treino,y_treino,X_teste, y_teste)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.059260774686306604, 'fraction_numerical': 0.012479541734860884, 'mean_word_length': 5.227176220806794, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 4, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '292244', 'license': 'MIT', 'n_tokens_mistral': 5023, 'n_tokens_neox': 4758, 'n_words': 1762, 'path': 'artefato_final/pipeline_final.ipynb', 'repo_name': 'bruno154/project-1-santander-customers', 'size': '14664'}