Machine Learning Aplicado à Previsão de Rotatividade de Clientes

Gabriel Ribeiro Ferreira Lopes
22 min readJun 27, 2023

--

Retenção é a alma do negócio

Tenho certeza que você já ouviu que dados são o novo petróleo. Decorrendo deste fato, a construção de modelos de aprendizado de máquina para problemas reais de negócios são a maneira mais inteligente de criar estratégias robustas, tomar decisões confiantes e evitar perdas desnecessárias.

Muitas empresas sofrem devido a falta de previsibilidade com relação à rotatividade dos clientes. Muitas dependem de assinaturas, com um modelo de recorrência, para manter alguma margem de lucro nos serviços. Ainda assim, algumas empresas vivem no escuro sem conhecer a fundo alguns padrões de comportamentos próprios de seus clientes.

Os dados históricos são muito úteis na hora de analisar tais comportamentos e padrões. Neste projeto, o estudo desses dados numa análise exploratória completa, com visualização de dados dos atributos informativos, foi capaz de trazer insights valiosos e informações das métricas e serviços que mais se relacionam com a rotatividade, tanto positiva quanto negativamente.

Mas, para além disso, o projeto descrito neste artigo traz a construção de modelos de Machine Learning otimizados para a detecção de padrões de clientes propensos à rotatividade, prevendo a ocorrência desses casos com eficácia em exemplos novos.

No contexto real da empresa, isto significa a capacidade de identificar com antecedência possíveis perdas na receita líquida, de modo a tomar providências para reter mais clientes.

O que é a taxa de Churn?

No contexto de negócios, Churn se trata da taxa de rotatividade da base de clientes. Ela calcula a evasão através da comparação entre o número de clientes no início e no final de um período. A fórmula para o cálculo é a seguinte:

em que C0 é a quantidade de clientes no início do período e Cf a quantidade ao final do mesmo período.

Empresas como Netflix e Spotify medem a rotatividade dos clientes de acordo com o número de cancelamentos de assinaturas, mas outras empresas investigam os clientes que foram para o competidor ou que deixaram de usar o produto ou serviços de uma vez.

Os impactos negativos da rotatividade são observados para além da perda de receita e fluxo de caixa. Como conquistar novos clientes é mais caro do que reter os antigos, é comum uma redução no lucro por causa do custo de aquisição de novos clientes.

Outro problema é a diminuição da reserva de mercado da empresa. Uma alta taxa de rotatividade aumenta as incertezas dos investidores, fazendo o valor de mercado cair. Além disso, a reputação da empresa pode ser prejudicada, caso a evasão esteja associada a uma má experiência do cliente.

Sendo assim, fica claro a relevância desse problema que muitos negócios enfrentam, e que muitos buscam solução justamente na construção de modelos que possam prever a rotatividade, diminuindo a taxa de Churn.

Sobre o Conjunto de Dados

Os dados utilizados neste projeto foram originalmente disponibilizados na plataforma de ensino da IBM Developer, e tratam de um problema típico de uma companhia de telecomunicações. O conjunto de dados completo pode ser encontrado neste link.

O dataset possui 7043 entradas e 21 variáveis, sendo a maioria categóricas e apenas 3 numéricas. Com relação ao conteúdo dessas variáveis, temos a variável-alvo (Churn), com a informação de que determinado cliente saiu ou não da empresa. Outras variáveis estão relacionadas aos serviços oferecidos pela empresa que o cliente específico contratou, como Internet, múltiplas linhas telefônicas, suporte técnico, proteção de aparelho, etc.

Além destas, temos informações sobre as contas dos clientes, como método de pagamento, cobranças mensais e totais, e tenure, que é o tempo de permanência de determinado cliente com a empresa. Por fim, o conjunto de dados apresenta informações demográficas sobre os clientes, como idade, parceiros e dependentes.

1. Resultados da Análise Exploratória dos Dados

O objetivo desta análise exploratória, além de tratar dados ausentes e limpar o dataset, foi o de preparar os dados para interpretação dos algoritmos e ter uma noção mais clara dos atributos informativos mais relevantes para as conclusões sobre rotatividade.

1.1 Correlações

Como temos uma quantidade muito grande de variáveis categóricas, algumas binárias e outras não, foi necessário criar um DataFrame com dados processados (mais sobre essa etapa na seção 1.5). Assim, utilizou-se do LabelEncoder para as variáveis binárias, e do método .getdummies() do Pandas, para as categóricas com mais de 2 valores únicos.

A partir disto, e com o intuito de visualizar quais variáveis se correlacionavam mais fortemente com o alvo (Churn), foi calculada a matriz de correlação, cujos resultados são dispostos no gráfico de barras da Figura 1.1.

Em tons de azul, estão as variáveis que se correlacionam positivamente com o alvo, significando que a ocorrência dessas variáveis apontam para uma maior probabilidade da classe ser Churn, com o cliente saindo da empresa.

Por outro lado, estão as variáveis que se correlacionam negativamente com o alvo, de modo que suas ocorrências indicam uma tendência do cliente a não rotacionar. É importante notar, entretanto, que à correlação não corresponde uma causalidade. Ou seja, o fato de uma variável estar mais correlacionada ao alvo não significa que a ocorrência dela gera necessariamente o efeito ou não de Churn.

Figura 1.1 — Correlação das variáveis com a rotatividade.

Contudo, essa análise é muito útil para direcionarmos a visualização dos dados e a busca por padrões que melhorariam a performance dos modelos de previsão. Fica claro, por exemplo, que clientes com contratos mensais tendem a rotacionar mais, enquanto que clientes com mais tempo na empresa e contratos mais longos tendem a permanecer.

Isto faz sentido lógico e corrobora nossa intuição inicial a respeito das relações entre as variáveis.

Outras correlações mostram implicações menos óbvias como, por exemplo, o fato de haver uma tendência maior ao Churn quando o cliente possui Internet de fibra óptica, ou de que clientes que não contratam serviços de Internet são aqueles que acabam rotacionando menos.

De qualquer modo, podemos listar a partir dessa análise os 5 atributos que mais contribuem positiva e negativamente para o Churn, respectivamente. São eles:

  1. Contrato de mês a mês;
  2. Falta de segurança online;
  3. Falta de suporte tecnológico;
  4. Serviço de Internet de fibra óptica;
  5. Método de pagamento de cheque eletrônico.

Os 5 atributos que influenciam negativamente, diminuindo a propensão à rotatividade, em direção à retenção do cliente, são:

  1. Tempo de permanência com a empresa (tenure);
  2. Contrato de dois anos;
  3. Não ter serviços de Internet;
  4. Cobranças totais;
  5. Contrato de um ano.

1.2 Variáveis Binárias

Analisamos algumas variáveis binárias para investigar como a ocorrência delas influencia na rotatividade dos clientes. Primeiramente, visualizamos as distribuições dos gêneros dos clientes e a relação com o alvo, como pode ser visto na Figura 1.2.

Figura 1.2 — Taxa de rotatividade para cada gênero

O gráfico acima mostra que o gênero do cliente não influencia na taxa de rotatividade, além de que os dados estão bem divididos entre homens e mulheres.

A Figura 1.3 mostra outras variáveis binárias como funções do Churn. Estas estão mais relacionadas a serviços contratados e dados demográficos dos clientes.

Figura 1.3 — Relações de dados demográficos e serviços com a taxa de rotatividade.

Os dois gráficos superiores mostram como é o comportamento dos clientes com respeito à rotatividade quando eles têm parceiros ou dependentes. Clientes com parceiros tendem a rotacionar menos do que os solteiros, o que indica que a estabilidade de uma união favorece um comprometimento de longo prazo com a empresa.

Clientes com dependentes também são menos propensos a sair da empresa. Apesar da taxa positiva aumentar, a taxa negativa - dos clientes que não rotacionam - aumenta proporcionalmente muito mais.

Com relação a serviços, nos gráficos inferiores, vemos que o serviço de telefone aumenta a taxa de Churn, enquanto que a conta digital diminui. Contudo, é interessante notar que o serviço de telefone está fracamente correlacionado com o Churn, como se pode observar na Figura 1.1, ao passo que a conta sem papel está relacionada positivamente, indicando maior tendência a rotatividade.

1.3 Relação do Tempo de Permanência do Cliente com a Rotatividade

O atributo que mais influencia negativamente a rotatividade é o tenure. A palavra em inglês tem raíz no latim tenere, que significa ter posse, ou o período de tempo que uma pessoa permanece na posse de algo. Logo, no contexto de negócios que estamos lidando, significa a permanência da pessoa com aqueles serviços, como cliente da empresa.

A Figura 1.4 mostra a distribuição do número de clientes como função do tempo de permanência na empresa. Note que temos um volume mais alto de clientes com até 6 meses de empresa, e com mais de 70 meses. Os primeiros são aqueles que acabaram de contratar os serviços, e o gráfico mostra que pelo menos um terço deles saem da empresa nos primeiros dois meses.

Os último são os clientes mais fidelizados, que por estarem a mais tempo na empresa, tem a menor propensão em deixá-la. Entre esses dois extremos, o número de clientes se distribui de maneira homogênea após 18 meses na empresa, indicando certa estabilidade.

Figura 1.4 — Número de clientes como função do tempo de permanência dos clientes na empresa, em meses.

Avançando nossa análise, iremos filtrar agora a permanência na empresa como função dos contratos escolhidos pelos clientes. A Figura 1.5 mostra três gráficos com o número de clientes que assinaram contratos mensais, anuais e bianuais.

Fica mais claro o comportamento dos clientes com os respectivos contratos. Quem acabou de chegar na empresa, opta por contratos mensais, por ser um período mais voltado para testes e construção de confiança. À medida que o tempo passa, menores são as chances do cliente escolher o contrato mensal.

A próxima escolha mais provável é o contrato anual, e vemos de fato que ele tem uma tendência a aumentar a partir dos 20 meses do cliente na empresa. Por fim, o contrato bianual é muito mais frequente e se destaca para clientes com mais de 50 meses na empresa. Estes, por sua vez, são os mais fiéis e habituados com os serviços, possuindo, portanto, a menor propensão à rotatividade.

Figura 1.5 — Tempo de permanência dos clientes na empresa, por tipo de contrato.

Para finalizar, vamos analisar a rotatividade por tipo de contrato. As análises anteriores já apontavam que clientes mais novos, que optavam por contratos mensais, tinham maiores chances de rotacionar. Porém, agora, podemos observar claramente essa tendência.

A Figura 1.6 mostra que, dos que estão no contrato mensal, 43% rotacionam. Enquanto que essa taxa cai abruptamente para os contratos de mais longo prazo (11% para anual, e apenas 3% para bianual). Isso significa uma redução de 93% da taxa de rotatividade, apenas pelo tempo de permanência na empresa.

Figura 1.6 — Influência do tipo de contrato na taxa de rotatividade dos clientes.

Não é à toa que essa variável apresenta a maior correlação negativa com a variável-alvo. O boxplot da Figura 1.7 mostra as diferenças nas distribuições estatísticas entre as duas classes com relação à variável tenure. Note como a classe de Churn está mais concentrada nos valores menores de tenure. De fato, 50% dos clientes que saíram da empresa, o fizeram nos primeiros 10 meses e, em 30 meses, a maioria já tinha saído.

Figura 1.7 — Diferenças nas distribuições estatísticas de tenure com relação à rotatividade.

1.4 Relação entre Cobranças e Rotatividade

Para completar nossa discussão sobre os atributos que mais influenciam nos padrões de rotatividade, vamos analisar a relação com as cobranças.

Na Figura 1.8, a distribuição das cobranças mensais mostra que quanto maior for seu valor, maiores são as chances de rotatividade. Para valores maiores que US$ 60, vemos um aumento na taxa de Churn, enquanto que para cobranças menores que US$ 30 mensais, vemos que a taxa de rotatividade fica bem baixa.

Figura 1.8 — Distribuição das cobranças mensais e seus impacto sobre a rotatividade.

Na Figura 1.9, vemos outra perspectiva, agora com relação às cobranças totais, ou seja, a soma dos valores de cobrança em todo o período em que o cliente ficou com a empresa. Neste caso, temos que a taxa de rotatividade é maior para cobranças totais de menor valor. Isto é lógico, uma vez que a maioria dos clientes sai da empresa em até 10 meses, somando uma cobrança total relativamente baixa.

Os clientes mais fidelizados tem mais tempo com a empresa e, portanto, somam cobranças maiores. Por isso, vemos a prevalência da classe que não rotaciona para valores altos de cobranças totais, enquanto a classe de Churn tende a zero.

Figura 1.9 — Cobranças totais e sua influência na taxa de rotatividade.

1.5 Pré-Processamento dos Dados — Codificação das Variáveis

O procedimento de feature engineering envolve a manipulação das variáveis de forma a criar novos atributos informativos e tratar aqueles já existentes. Com isto, os algoritmos se tornam mais capazes de interpretar as informações e capturar os padrões necessários à previsão.

Um dos processos realizados com esse conjunto de dados em específico foi o de codificação das variáveis categóricas. Lidamos com variáveis categóricas, múltiplas e binárias, que precisaram ser tratadas a partir de técnicas como LabelEncoder e OneHotEncoder, ou métodos como o .getdummies().

Utilizamos o LabelEncoder (LE) para as variáveis binárias. A escolha decorre do fato de que o LE associa valores para cada uma das classes que, no caso binário, são os valores de 0 ou 1.

Não utilizou-se o LE para variáveis categóricas com mais de 2 valores únicos, pois ele apenas atribui valores inteiros para cada categoria na mesma coluna. Se, por exemplo, uma variável com 4 categorias únicas for codificada, teríamos valores de 0 a 3 na coluna codificada. Um algoritmo poderia interpretar, a partir desses números, alguma hierarquia de valores ou ordenamento que na realidade não existem entre as categorias.

Por esse motivo, para variáveis categóricas com mais de 2 valores únicos, utilizou-se o método .getdummies(), que distribui as diferentes categorias de uma determinada variável em novas colunas com valores binários. Portanto, se uma variável tiver três categorias distintas, o método criará três novas colunas, uma para cada categoria, onde os valores serão 0 ou 1 para indicar se a categoria está presente ou não em cada observação.

As Figuras 1.10 e 1.11 mostram como ficou o DataFrame processado, após a codificação das variáveis categóricas. O primeiro, dá foco nos atributos binários que passaram por LE, enquanto o segundo foca nos que tinham mais de 2 categorias.

Figura 1.10 — DataFrame com as variáveis binárias processadas e codificadas.

A variável Contract, por exemplo, continha três categorias — Month-to-month, Oneyear, Twoyear — que se tornaram três colunas binárias independentes.

Figura 1.11 — Porção do mesmo DataFrame com variáveis categóricas processadas pelo método .getdummies()

Clique aqui para o passo-a-passo do procedimento, com o código completo.

2. Construção dos Modelos de Machine Learning

Nesta seção, iremos construir os modelos para previsão de rotatividade, testando vários algoritmos de classificação diferentes. O objetivo é conseguir a maior taxa de recall, buscando também um equilíbrio na performance relacionada a outras métricas.

A seção está dividida como segue:

  • Balanceamento dos Dados
  • Modelo Baseline e Testes de Desempenho sem Otimização
  • Otimização de Hiperparâmetros
  • Desempenho nos Dados de Teste e Estatísticas Finais
  • Escolha do Modelo Adequado

2.1 Balanceamento dos Dados

Em uma empresa, espera-se que o número de clientes que saíram sejam uma parcela menor da população. Sendo assim, lidamos com dados desbalanceados, tendo como classe majoritária os clientes que não praticaram Churn.

De fato, a Figura 2.1 mostra claramente o desbalanceamento das classes da variável-alvo. Aproximadamente 3/4 do dataset se refere à classe majoritária e 1/4 à minoritária.

Figura 2.1 — Desbalanceamento das classes da variável-alvo no conjunto de dados.

Para realizar o balanceamento, foi escolhido a técnica de RandomUnderSampling. Este método envolve a seleção aleatória e posterior remoção de exemplos na classe majoritária, até que as duas classes estejam com a mesma proporção de 50:50.

Foi escolhida essa técnica pois, com a classe minoritária representando 1/4 do dataset, temos exemplos suficientes para compor um bom subconjuntos para testes. Sendo assim, a alternativa de oversampling, com a criação de exemplos artificiais, não é necessária nem traria melhorias de performance significativas.

Obviamente, há um trade-off envolvido nessa escolha, uma vez que toda técnica de undersampling gera uma perda de informações do conjunto de dados usado como treino.

Antes ainda do balanceamento, foi realizada uma padronização das variáveis contínuas tenure, TotalCharges e MonthlyCharges. A padronização mapeia todos os valores dessas colunas para que a distribuição tenha média zero e desvio-padrão unitário, enquanto mantém a forma de sua distribuição.

Clique aqui para o passo-a-passo do balanceamento, com o código completo.

A Figura 2.2 mostra o resultado do balanceamento após a padronização do conjunto de teste.

Figura 2.2 — Resultados das classes balanceadas através da técnica de RandomUnderSampling.

2.2 Modelo Baseline e Testes de Desempenho sem Otimização

Para podermos ter uma base de comparação dos desempenhos dos diferentes modelos que serão testados, iremos construir um baseline simples. Um modelo baseline serve como ponto de referência para medir a performance de modelos mais complexos. Ele estabelece um nível de performance mínimo que os modelos devem ter, visto que se trata da pontuação de um modelo sem otimização alguma.

Abaixo, vamos criar uma função que utiliza dos dados de treino e de um classificador para calcular a média das pontuações de recall obtidas através de validação cruzada.

Os dados de treino utilizados estão desbalanceados e não-padronizados, apenas com a codificação das variáveis categóricas.

def eval_model(X, y, clf, report = True, std = True):

if std == True:

# X: DataFrame, variáveis independentes
# y: Series, variável-alvo
# clf: modelo de classificador
# report: bool, indica se a função deve imprimir ou não os resultados

pipeline = make_pipeline(StandardScaler(), # Criar a pipeline com um passo de pré-processamento e com o modelo
clf)

scores = cross_val_score(pipeline, X, y, scoring = 'recall', cv = 5) # Realizar validação cruzada com 5 dobras

if report == True:
print(f'Recall {scores.mean():.2f} (+/- {scores.std():.2f})') # Imprimir resultado da média do recall

return scores.mean() # Retorna a média dos scores da validação cruzada

else:

model = clf.fit(X, y)

scores = cross_val_score(model, X, y, scoring = 'recall', cv = 5) # Realizar validação cruzada com 5 dobras

if report == True:
print(f'Recall {scores.mean():.2f} (+/- {scores.std():.2f})') # Imprimir resultado da média do recall

return scores.mean() # Retorna a média dos scores da validação cruzada
rf = RandomForestClassifier()

print('Performance do modelo de baseline')
score_baseline = eval_model(X_train, y_train, rf)

Instanciamos para o teste o RandomForestClassifier sem nenhum tipo de otimização, para termos como base a performance. O resultado para a métrica de recall foi de 0,49 (+/- 0.02), indicando uma chance de meio a meio para cada classe, nos dados desbalanceados e sem otimização.

A seguir, iremos realizar um teste de desempenho com vários modelos de classificação, buscando aqueles que melhor performam na predição de casos de rotatividade de clientes.

Dentre os modelos, iremos testar os seguintes:

  • SVC (Support Vector Classifier): classificador de vetores de suporte, algoritmo de aprendizado supervisionado;
  • SGDC (Stochastic Gradient Descent Classifier): algoritmo de classificação linear para datasets grandes, baseado na técnica de gradiente descendente;
  • DTC (Decision Tree Classifier): algoritmo de aprendizado supervisionado não-paramétrico para prever as classes através de regras de decisão;
  • Regressão Logística: algoritmo de aprendizado supervisionado para classificação binária ou multi-classe, baseado em modelo estatístico;
  • XGBC (XGBoost Classifier): algoritmo de impulsionamento de gradiente otimizado;
  • Random Forest Classifier: classificador de floresta aleatória, um método de aprendizagem conjunta que combina múltiplas árvores de decisão para melhorar a classificação;
  • LGBM Classifier (Light Gradient Boosting Machine Classifier): uma estrutura de impulsionamento de gradiente voltado para alta performance e eficiência em datasets grandes.
# Instanciar os modelos
svc = SVC()
sgdc = SGDClassifier()
dtc = DecisionTreeClassifier()
lr = LogisticRegression()
xgbc = XGBClassifier()
rf = RandomForestClassifier()
lgbm = LGBMClassifier()

# Inicializar listas vazias para armazenar scores de recall e os nomes dos respectivos modelos
recall = []
model = []

# Armazenar os modelos numa lista
models = [svc, sgdc, dtc, lr, xgbc, rf, lgbm]

for clf in models:
model.append(clf.__class__.__name__) # Armazenar os nomes dos modelos em model
recall.append(eval_model(X_rus, y_rus, clf, report = False, std = False)) # Armazenar os valores das médias de validação cruzada para cada modelo em recall

# Armazenar os resultados em um DataFrame
pd.DataFrame(data = recall, index = model, columns = ['Recall']).sort_values(by = 'Recall', ascending = False)

A Tabela 1 abaixo mostra a média das pontuações de recall para os modelos de classificadores. O recall foi escolhido porque ele mostra a proporção de positivos classificados corretamente com relação ao total de positivos. Como queremos minimizar a rotatividade, queremos um modelo que tenha o maior recall, isto é, a maior taxa de verdadeiros positivos e a menor taxa de falsos negativos.

Tabela 1 — Testes de desempenho com vários modelos de classificação.

Dessa análise, vemos que o Classificador de Vetor de Suporte (SVC) e a Regressão Logística são os modelos que apresentam maior recall.

Entretanto, queremos trabalhar com algoritmos que permitam maior flexibilidade na otimização de hiperparâmetros para melhora da performance. Por esse motivo, neste projeto foram testados os algoritmos de otimização SGDClassifier e os modelos de boost por gradiente baseados em árvores de decisão, i.e, LGBMClassifier e XGBoost Classifier.

O SGDClassifier é um classificador linear otimizado com a técnica de gradiente descendente estocástico (SGD). Ele tem como objetivo minimizar a função de perda de modelos de classificação linear, como Regressão Logística e SVC. Justamente por trabalhar com esse método de otimização, ele é uma escolha melhor para problemas de larga escala, em datasets maiores.

A busca por hiperparâmetros foi feita utilizando o GridSearchCV, que combina a técnica de validação cruzada para retornar medidas de desempenho mais precisas, realizadas sobre dobras de treino e validação balanceadas e randomizadas. Isto traz maior consistência ao teste dos modelos e sua posterior avaliação.

2.3 Otimização de Hiperparâmetros

A construção de modelos de Machine Learning nunca se apoia em apenas um algoritmo. O processo envolve a escolha de uma série de algoritmos que de alguma forma se relacionam com o problema, fornecendo uma solução. A partir da avaliação do desempenho de cada modelo, segundo uma métrica específica, escolhemos aquele mais adequado para lidar com o problema.

Com o modelo mais adequado definido, é possível melhorar ainda mais sua performance aplicando métodos que o testam sob diversas configurações de hiperparâmetros, selecionando aqueles que resultarem na melhor performance. Disto se trata a otimização de hiperparâmetros nesta seção.

2.3.1 Procedimento Realizado

Durante a otimização, foi selecionado, para cada um dos três modelos, alguns dos hiperparâmetros mais comuns e com maior impacto na performance dos respectivos algoritmos.

Em seguida, instanciamos o modelo sem nenhum tipo de otimização e criamos o primeiro dicionário de hiperparâmetros para serem utilizados no GridSearchCV.

# Instanciar o SGD Classifier padrão
sgdc = SGDClassifier(learning_rate = 'optimal')

A otimização ocorreu por partes, criando dicionários com poucos hiperparâmetros. A razão disso foi que, com a realização de testes, notou-se uma tendência a overfitting sempre que todos os hiperparâmetros eram passados de uma vez para o GridSearchCV.

Cada vez que se repetia o procedimento, os dados de treino eram embaralhados utilizando a função KFold, que prepara os dados para validação cruzada utilizada no GridSearchCV. A melhor performance do modelo era exibida, juntamente com a configuração otimizada para o respectivo hiperparâmetro.

sgdc = SGDClassifier(learning_rate = 'optimal')

sgdc_grid = [
{'alpha': [0.001, 0.01, 0.1, 1, 10, 100, 1000]}
]

# Estabelecer número de dobras randomizadas com as mesmas proporções de classes
kfold = KFold(n_splits = 5, shuffle = True)
# Instanciar o objeto GridSearchCV
grid = GridSearchCV(sgdc, sgdc_grid, scoring = 'recall', cv = kfold, n_jobs = -1)
# Realizar a busca por hiperparâmetros nas variáveis de treino
grid_res = grid.fit(X_rus, y_rus)

# Imprimir melhores resultados
print(f'Melhor performance: {grid_res.best_score_:.3f}',
f'\nHiperparâmetros: {grid_res.best_params_}', '\n')

O modelo era então instanciado novamente, já com o valor para o hiperparâmetro otimizado definido. Assim, o GridSearchCV era rodado para uma nova otimização.

sgdc = SGDClassifier(learning_rate = 'optimal', alpha = 1)

sgdc_grid = [
{'penalty': ['l1', 'l2', 'elasticnet']}
]

# Estabelecer número de dobras randomizadas com as mesmas proporções de classes
kfold = KFold(n_splits = 5, shuffle = True)
# Instanciar o objeto GridSearchCV
grid = GridSearchCV(sgdc, sgdc_grid, scoring = 'recall', cv = kfold, n_jobs = -1)
# Realizar a busca por hiperparâmetros nas variáveis de treino
grid_res = grid.fit(X_rus, y_rus)

# Imprimir melhores resultados
print(f'Melhor performance: {grid_res.best_score_:.3f}',
f'\nHiperparâmetros: {grid_res.best_params_}', '\n')

Clique aqui para o passo-a-passo da otimização, com o código completo.

Com todos os hiperparâmetros de determinado modelo otimizados, o modelo era ajustado aos dados balanceados de treino e, então, realizava-se outra validação cruzada para determinar a média de performance do modelo, com cálculo de erro por desvio-padrão.

sgdc_final = SGDClassifier(learning_rate = 'optimal', alpha = 1,
penalty = 'l2', loss = 'hinge', max_iter = 200)

# Ajuste do modelo
sgdc_final.fit(X_rus, y_rus)

# Validação cruzada em 5 dobras e 10 repetições
kfold = RepeatedKFold()
scores = cross_validate(sgdc_final, X_rus, y_rus, scoring = ['recall', 'roc_auc'], cv = kfold)

# Imprimir resultados das métricas de recall e AUC
print('Performance SGD - Treino',
'\nRecall: {:.3f}'.format(scores['test_recall'].mean()), '+/-', '{:.3f}'.format(scores['test_recall'].std()),
'\nAUC: {:.3f}'.format(scores['test_roc_auc'].mean()), '+/-', '{:.3f}'.format(scores['test_roc_auc'].std()), '\n')

Isto se fez necessário para que pudéssemos lidar com a natureza estocástica dos algoritmos de classificação sendo utilizados. Como, a cada vez que se roda o programa, temos uma nova divisão e randomização dos dados, os modelos resultam em performances ligeiramente diferentes, que precisam estar compreendidas dentro de um intervalo de valores esperados.

Assim, para cada modelo, foi possível determinar a partir dos dados de treino este intervalo que leva em conta a randomização natural que existe nos próprios dados, que é carregada para o pré-processamento e que também está presente na estrutura dos algoritmos.

Dessa forma, para a métrica recall que nos interessa, obtivemos os seguintes intervalos para cada um dos modelos com hiperparâmetros otimizados:

SGD Classifier

# Modelo SGD Final
sgdc_final = SGDClassifier(learning_rate = 'optimal', alpha = 1,
penalty = 'l2', loss = 'hinge', max_iter = 200)

Recall: 0.826 ± 0.018

LightGBM Classifier

# Modelo final de LightGBM
lgbm_final = LGBMClassifier(random_state = 100,
n_estimators = 50, learning_rate = 0.01,
subsample = 0.7, min_child_samples = 1,
min_child_weight = 0.001, max_depth = 5)

Recall: 0.784 ± 0.022

XGBoost Classifier

# Modelo XGBoost Final
xgb_final = XGBClassifier(learning_rate = 0.001,
n_estimators = 50, max_depth = 1,
min_child_weight = 1, gamma = 0.0)

Recall: 0.890 ± 0.014

2.4 Desempenho nos Dados de Teste e Estatísticas Finais

Ao aplicar os modelos sobre os dados de teste, buscamos ter o mesmo cuidado quanto a natureza estocástica dos algoritmos e a randomização natural dos dados.

Os hiperparâmetros otimizados que obtivemos no treinamento dos modelos apontam um caminho. Porém, a melhor performance para o objetivo que buscamos pode representar a variação de algum hiperparâmetro para aquele conjunto de dados específico.

Por isso, nos testes, tomou-se o cuidado de também realizar repetições, porém variando-se alguns hiperparâmetros. As repetições foram feitas manualmente e somaram um total de 10, de modo que tivéssemos uma boa amostragem para realizar o cálculo das estatísticas de média e desvio-padrão.

Alguns modelos, como o XGBoost Classifier, manteve muita consistência entre os hiperparâmetros otimizados durante o treino e durante o teste. O LightGBM e o SGD, por outro lado, tinha 2 ou 3 hiperparâmetros que variavam a cada rodada.

Contudo, essa metodologia foi capaz de reduzir as incertezas e providenciar intervalos de performance que representam robustamente cada um dos modelos. A intenção era de capturar a variabilidade intrínseca que ocorre nos algoritmos e nos dados.

Assim, após 10 repetições, obtivemos os seguintes intervalos de performance para a métrica recall nos dados de teste:

SGD Classifier

Recall: 0.835 ± 0.019

A Figura 2.3 mostra a matriz de confusão para o modelo SGD nos dados de teste, em uma das repetições.

Figura 2.3 — Matriz de Confusão para o SGD Classifier

LightGBM Classifier

Recall: 0.793 ± 0.019

A Figura 2.4 mostra a matriz de confusão para o LightGBM Classifier em uma das repetições realizadas.

Figura 2.4 — Matriz de Confusão para o LightGBM Classifier

XGBoost Classifier

Recall: 0.883 ± 0.022

A Figura 2.5 mostra a matriz de confusão para o XGBoost Classifier em uma das repetições nos dados de teste.

Figura 2.5 — Matriz de Confusão do XGBoost Classifier

Estes valores capturam o quanto o modelo pode variar, como função da mudança de alguns hiperparâmetros e de dados novos. Temos, portanto, resultados mais confiáveis considerando-se a natureza estocástica dos algoritmos que suportam os modelos.

Se compararmos os intervalos obtidos no treino e no teste, vamos notar que eles se sobrepõem, reafirmando a robustez da metodologia.

O resultado são três modelos de previsão de rotatividade que podem ser utilizados para diferentes objetivos, se adequando às circunstâncias impostas pelo problema de negócio em questão.

2.5 Escolha do Modelo Adequado

  • O SGD Classifier apresentou uma performance equilibrada, providenciando uma boa previsão na rotatividade, com uma taxa de Falsos Negativos de aproximadamente 16% e uma taxa de Falsos Positivos entre 30 e 35%.
  • O LightGBM não performa tão bem quanto os outros dois na previsão de rotatividade, com uma taxa de Falsos Negativos de aproximadamente 20%. Porém, ele é melhor na distinção correta das classes, com o maior AUC dentre todos os modelos. Isso torna sua taxa de FP a menor dentre os três. Se a empresa tem a maior preocupação em não incomodar clientes já fidelizados com estratégias anti-Churn, então este é o modelo mais indicado.
  • Por fim, o XGBoost apresenta a melhor previsão de rotatividade dos modelos, com um recall médio de 0,88 nos testes. Isso significa que se a maior fonte de perdas de lucro da empresa for realmente do Churn, e ela não se importar em oferecer estratégias anti-Churn para cliente fiéis, então este é o modelo mais correto. Isto porque ele é mais assertivo na detecção de Churn, com uma baixa taxa de FN, mas uma alta taxa de FP em contrapartida.

3. Conclusão

Este projeto buscou conhecer melhor os motivos para as taxas de rotatividade em uma empresa de telecomunicações, utilizando-se de dados históricos de seus clientes.

O Churn é fonte de muita preocupação para empresas dos mais diversos nichos, desde entretenimento até prestações de serviços, pois se relaciona diretamente com a perda de lucro devido à saída de clientes.

Por este motivo, é relevante utilizar dos dados para ter uma consciência situacional maior dos motivos por trás da saída de um cliente. Com a construção de modelos de Machine Learning através de dados históricos, a empresa é capaz de identificar os perfis dos clientes mais propensos à rotatividade e, a partir disso, tomar iniciativas voltadas para mantê-los e fidelizá-los.

A análise exploratória de dados, juntamente com a visualização, destacou os atributos mais informativos, providenciando insights e jogando luz na correlação entre as variáveis.

O pré-processamento foi necessário para codificar as muitas variáveis categóricas presentes no dataset, preparando-as para que os algoritmos de Machine Learning tenham uma melhor performance. O balanceamento dos dados foi realizado utilizando a técnica de RandomUnderSampling, igualando as proporções entre as classes.

Na construção dos modelos, foram testadas várias alternativas a partir de uma performance de baseline para a métrica recall. Três modelos foram escolhidos para otimização, segundo a capacidade de manipulação de seus hiperparâmetros e os objetivos do projeto. Devido a natureza estocástica dos algoritmos, fez-se necessário um cuidado estatístico com relação aos resultados decorrentes de cada treinamento, considerando cada configuração de hiperparâmetros através do GridSearchCV. Utilizou-se de cálculos estatísticos e da técnica de validação cruzada.

Tanto no treinamento quanto nos testes, as repetições asseguraram que os modelos apresentassem valores para a métrica de recall dentro de um intervalo confiável, garantindo mais robustez de resultados e clareza de escolha.

Os três modelos preveem a rotatividade de maneira eficiente, porém com diferenças que podem ser proveitosas segundo as circunstâncias específicas da empresa e do problema de negócios.

  • O SGD Classifier possui uma performance equilibrada, com uma boa previsão de rotatividade, e uma taxa razoável de Falsos Negativos.
  • O LightGBM Classifier é o menos capaz de prever a rotatividade, com uma taxa de Falsos Negativos de aproximadamente 20%. Porém, ele é o melhor na distinção de classes e sua taxa de Falsos Positivos é a mais baixa.
  • Por último, o XGBoost Classifier é o melhor modelo para previsão de rotatividade, com uma taxa de quase 90%. Contudo, é o que pior performa na taxa de Falsos Positivos.

Cada um dos modelos possuem pós e contras que devem ser levados em conta ao considerar a implementação num problema de negócios. Porém, o projeto mostrou um grande potencial para, a partir de dados históricos, construir modelos que preveem a rotatividade eficientemente.

Obrigado pela leitura!

Me acompanhe nas redes para mais artigos, insights e projetos.

--

--

Gabriel Ribeiro Ferreira Lopes
Gabriel Ribeiro Ferreira Lopes

Written by Gabriel Ribeiro Ferreira Lopes

Data Scientist | AI Engineer | MsC. in Physics

Responses (1)