Spaces:
Sleeping
Sleeping
Update model_utils.py
Browse files"Fixed SyntaxError by separating f-string and raw string parts for LaTeX percentage formatting; logo diagnostic check."
- model_utils.py +56 -74
model_utils.py
CHANGED
|
@@ -276,10 +276,10 @@ class ChurnModelPipeline:
|
|
| 276 |
latex_story.append(NoEscape(sample_display_df.to_latex(index=False, caption='Características do Cliente Simulado', label='tab:sim_customer', longtable=False)))
|
| 277 |
|
| 278 |
markdown_story.append(f"**Resultado da Simulação:** O cliente **{churn_status_sample}** (Probabilidade de Churn: **{prob_sample:.2%}**)\n")
|
| 279 |
-
latex_story.append(fr'\textbf{{Resultado da Simulação:}} O cliente \textbf{{{churn_status_sample}}} (Probabilidade de Churn: \textbf{{{prob_sample:.2f}\%}})\n\n')
|
| 280 |
else:
|
| 281 |
markdown_story.append("Não foi possível realizar uma simulação pois o DataFrame de teste ou dados interativos não estão disponíveis.\n")
|
| 282 |
-
latex_story.append(r'Não foi possível realizar uma simulação pois o DataFrame de teste ou dados interativos não estão disponíveis.\n\n')
|
| 283 |
|
| 284 |
# --- 2. Detalhes do Processo de Treinamento ---
|
| 285 |
markdown_story.append("## 2. Detalhes do Processo de Treinamento")
|
|
@@ -320,7 +320,7 @@ class ChurnModelPipeline:
|
|
| 320 |
latex_story.append(NoEscape(training_details_latex))
|
| 321 |
else:
|
| 322 |
markdown_story.append("Nenhum detalhe de treinamento disponível.\n")
|
| 323 |
-
latex_story.append(r'Nenhum detalhe de treinamento disponível.\n\n')
|
| 324 |
|
| 325 |
|
| 326 |
# --- 3. Descrição do Modelo e Metodologia ---
|
|
@@ -328,22 +328,22 @@ class ChurnModelPipeline:
|
|
| 328 |
latex_story.append(Section(NoEscape(r'Descrição do Modelo e Metodologia'), False))
|
| 329 |
|
| 330 |
markdown_story.append("O modelo utiliza **Regressão Logística** para classificar a probabilidade de um cliente sair (churn). Foram aplicadas as seguintes etapas para garantir robustez e tratar as características dos dados:\n")
|
| 331 |
-
latex_story.append(r'O modelo utiliza \textbf{Regressão Logística} para classificar a probabilidade de um cliente sair (churn). Foram aplicadas as seguintes etapas para garantir robustez e tratar as características dos dados:\n\n')
|
| 332 |
|
| 333 |
markdown_story.append("- **Pré-processamento de Dados:**\n - **Features Numéricas:** Imputação de valores ausentes (mediana) e escalonamento (`StandardScaler`) para padronização.\n - **Features Categóricas:** Imputação de valores ausentes (moda) e codificação One-Hot (`OneHotEncoder`) para transformar categorias em formato numérico.\n")
|
| 334 |
-
latex_story.append(r'\begin{itemize}' + '\n')
|
| 335 |
-
latex_story.append(r'\item \textbf{Pré-processamento de Dados:}' + '\n')
|
| 336 |
-
latex_story.append(r'\begin{itemize}' + '\n')
|
| 337 |
-
latex_story.append(r'\item \textbf{Features Numéricas:} Imputação de valores ausentes (mediana) e escalonamento (\texttt{StandardScaler}) para padronização.' + '\n')
|
| 338 |
-
latex_story.append(r'\item \textbf{Features Categóricas:} Imputação de valores ausentes (moda) e codificação One-Hot (\texttt{OneHotEncoder}) para transformar categorias em formato numérico.' + '\n')
|
| 339 |
-
latex_story.append(r'\end{itemize}' + '\n')
|
| 340 |
|
| 341 |
markdown_story.append("- **Balanceamento de Classes (SMOTE):** O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (`Exited`). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem).\n")
|
| 342 |
-
latex_story.append(r'\item \textbf{Balanceamento de Classes (SMOTE):} O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (\texttt{Exited}). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem).' + '\n')
|
| 343 |
|
| 344 |
markdown_story.append("- **Regularização (L2):** A Regressão Logística foi configurada com um parâmetro `C=0.1` (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.\n")
|
| 345 |
-
latex_story.append(r'\item \textbf{Regularização (L2):} A Regressão Logística foi configurada com um parâmetro \texttt{C=0.1} (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.' + '\n')
|
| 346 |
-
latex_story.append(r'\end{itemize}' + '\n\n')
|
| 347 |
|
| 348 |
|
| 349 |
# --- 4. Como a Probabilidade de Churn é Calculada ---
|
|
@@ -351,17 +351,17 @@ class ChurnModelPipeline:
|
|
| 351 |
latex_story.append(Section(NoEscape(r'Como a Probabilidade de Churn é Calculada'), False))
|
| 352 |
|
| 353 |
markdown_story.append("A Regressão Logística é um modelo de classificação que estima a probabilidade de um evento (neste caso, o churn do cliente) ocorrer. Ao contrário da regressão linear, que prevê um valor contínuo, a regressão logística utiliza a **função sigmoide** para mapear qualquer valor real para um valor entre 0 e 1, que pode ser interpretado como probabilidade.\n")
|
| 354 |
-
latex_story.append(r'A Regressão Logística é um modelo de classificação que estima a probabilidade de um evento (neste caso, o churn do cliente) ocorrer. Ao contrário da regressão linear, que prevê um valor contínuo, a regressão logística utiliza a \textbf{função sigmoide} para mapear qualquer valor real para um valor entre 0 e 1, que pode ser interpretado como probabilidade.\n\n')
|
| 355 |
|
| 356 |
markdown_story.append("A equação básica de um modelo linear (`L`) é:\n`L = β₀ + β₁X₁ + β₂X₂ + ... + βₙXₙ`\nOnde `β` são os coeficientes (pesos) das features (`X`).\n")
|
| 357 |
latex_story.append(Math(data=[NoEscape(r'L = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \dots + \beta_n X_n')]))
|
| 358 |
-
latex_story.append(r'\nOnde $\beta$ são os coeficientes (pesos) das features ($X$).\n\n')
|
| 359 |
|
| 360 |
markdown_story.append("A probabilidade (`P`) de churn é então calculada aplicando-se a função sigmoide (σ) a `L`:\n`P(Churn) = σ(L) = 1 / (1 + e⁻ᴸ)`\n")
|
| 361 |
latex_story.append(Math(data=[NoEscape(r'P(\text{Churn}) = \sigma(L) = \frac{1}{1 + e^{-L}}')]))
|
| 362 |
|
| 363 |
markdown_story.append("Esta função garante que a saída esteja sempre entre 0 e 1, representando a probabilidade de o cliente pertencer à classe 'Churn' (ou seja, `Exited = 1`). Se `P(Churn)` for maior que um determinado limiar (geralmente 0.5), o cliente é classificado como provável churn.\n")
|
| 364 |
-
latex_story.append(r'\nEsta função garante que a saída esteja sempre entre 0 e 1, representando a probabilidade de o cliente pertencer à classe `Churn` (ou seja, \texttt{Exited = 1}). Se $P(\text{Churn})$ for maior que um determinado limiar (geralmente 0.5), o cliente é classificado como provável churn.\n\n')
|
| 365 |
|
| 366 |
# --- Subseção: Exemplo de Simulação Numérica (AGORA COM DADOS REAIS DA SIMULAÇÃO) ---
|
| 367 |
markdown_story.append("### Exemplo de Simulação Numérica com Cliente Simulado")
|
|
@@ -370,7 +370,7 @@ class ChurnModelPipeline:
|
|
| 370 |
if sample_customer_df is not None:
|
| 371 |
# Reutilizamos os valores de logit_sample e prob_sample calculados anteriormente para o cliente simulado
|
| 372 |
markdown_story.append("Para ilustrar o cálculo, vamos usar as características do cliente simulado acima (ou o último cliente da Previsão Interativa) e os coeficientes do modelo treinado. Note que as características numéricas são **escalonadas** e as categóricas **one-hot encoded** antes de serem multiplicadas pelos coeficientes. \n")
|
| 373 |
-
latex_story.append(r'Para ilustrar o cálculo, vamos usar as características do cliente simulado acima (ou o último cliente da Previsão Interativa) e os coeficientes do modelo treinado. Note que as características numéricas são \textbf{escalonadas} e as categóricas \textbf{one-hot encoded} antes de serem multiplicadas pelos coeficientes. \n\n')
|
| 374 |
|
| 375 |
markdown_story.append(f"**Características do Cliente 'Simulado':**\n" + sample_display_df.to_markdown(index=False) + "\n")
|
| 376 |
latex_story.append(NoEscape(sample_display_df.to_latex(index=False, caption='Características do Cliente Simulado', label='tab:sim_customer_example', longtable=False)))
|
|
@@ -384,33 +384,31 @@ class ChurnModelPipeline:
|
|
| 384 |
markdown_story.append(f"1. **Calcular o Logit (L):** O Logit é a soma ponderada de todas as características do cliente (já processadas pelo pré-processador do modelo) multiplicadas por seus respectivos coeficientes, mais o intercepto do modelo. Para o cliente simulado, o modelo calculou um Logit de:\n`L = {logit_sample_formatted}`\n")
|
| 385 |
|
| 386 |
# LaTeX for Logit calculation
|
| 387 |
-
latex_story.append(r'\textbf{Passos do Cálculo para o Cliente "Simulado":}\n')
|
| 388 |
-
latex_story.append(r'\begin{enumerate}')
|
| 389 |
-
latex_story.append(fr'\item \textbf{{Calcular o Logit (L):}} O Logit é a soma ponderada de todas as características do cliente (já processadas pelo pré-processador do modelo) multiplicadas por seus respectivos coeficientes, mais o intercepto do modelo. Para o cliente simulado, o modelo calculou um Logit de:')
|
| 390 |
latex_story.append(Math(data=[NoEscape(fr'L = {logit_sample_formatted}')]))
|
| 391 |
|
| 392 |
markdown_story.append(f"2. **Calcular a Probabilidade de Churn (P) usando a função Sigmoide:** A probabilidade é obtida aplicando-se a função sigmoide ao valor de `L`:\n`P(Churn) = 1 / (1 + e^(-L))`\n`P(Churn) = 1 / (1 + e^(-({logit_sample_formatted})))`\n`P(Churn) = 1 / (1 + e^{{-{logit_sample_formatted}}})`\n`P(Churn) ≈ {prob_sample_formatted}`\n")
|
| 393 |
|
| 394 |
# LaTeX for Probability calculation
|
| 395 |
-
latex_story.append(r'\item \textbf{Calcular a Probabilidade de Churn (P) usando a função Sigmoide:} A probabilidade é obtida aplicando-se a função sigmoide ao valor de $L$:')
|
| 396 |
latex_story.append(Math(data=[NoEscape(r'P(\text{Churn}) = \frac{1}{1 + e^{-L}}')]))
|
| 397 |
-
|
| 398 |
-
latex_story.append(Math(data=[NoEscape(fr'P(\text{Churn}) = \frac{{1}}{{1 + e^{{-{logit_sample_formatted}}}}}')]))
|
| 399 |
-
# LINHA 405 CORRIGIDA: Idem.
|
| 400 |
-
latex_story.append(Math(data=[NoEscape(fr'P(\text{Churn}) = \frac{{1}}{{1 + e^{{-{logit_sample_formatted}}}}}')]))
|
| 401 |
latex_story.append(Math(data=[NoEscape(fr'P(\text{Churn}) \approx {prob_sample_formatted}')]))
|
| 402 |
-
latex_story.append(r'\end{enumerate}\n')
|
| 403 |
|
| 404 |
markdown_story.append(f"**Resultado da Simulação para o Cliente 'Simulado':**\n")
|
| 405 |
markdown_story.append(f"A probabilidade de Churn para este cliente específico é de **{prob_sample_formatted}**, ou seja, **{prob_sample_percent_formatted}**.\n")
|
| 406 |
markdown_story.append(f"Este resultado indica que o cliente possui uma probabilidade de churn de {prob_sample_percent_formatted}, guiando a interpretação do risco.\n")
|
| 407 |
|
| 408 |
-
latex_story.append(r'\textbf{Resultado da Simulação para o Cliente "Simulado":}\n')
|
| 409 |
-
latex_story.append(fr'A probabilidade de Churn para este cliente específico é de \textbf{{{prob_sample_formatted}}}, ou seja, \textbf{{{prob_sample_percent_formatted}}}. ')
|
| 410 |
-
latex_story.append(fr'Este resultado indica que o cliente possui uma probabilidade de churn de {prob_sample_percent_formatted}, guiando a interpretação do risco.\n\n')
|
| 411 |
else:
|
| 412 |
markdown_story.append("Não foi possível gerar o exemplo de simulação numérica, pois nenhum cliente simulado foi fornecido.\n")
|
| 413 |
-
latex_story.append(r'Não foi possível gerar o exemplo de simulação numérica, pois nenhum cliente simulado foi fornecido.\n\n')
|
| 414 |
|
| 415 |
# --- Fim da Subseção de Exemplo ---
|
| 416 |
|
|
@@ -419,64 +417,48 @@ class ChurnModelPipeline:
|
|
| 419 |
latex_story.append(Section(NoEscape(r'Importância das Variáveis (Coeficientes e Odds Ratio)'), False))
|
| 420 |
|
| 421 |
markdown_story.append("A análise dos coeficientes do modelo de Regressão Logística, transformados em Odds Ratios, nos permite entender a influência de cada característica na probabilidade de Churn. Um Odds Ratio maior que 1 indica que o aumento daquela feature (ou pertencer àquela categoria) aumenta as chances de Churn, enquanto um valor menor que 1 diminui.\n")
|
| 422 |
-
latex_story.append(r'A análise dos coeficientes do modelo de Regressão Logística, transformados em Odds Ratios, nos permite entender a influência de cada característica na probabilidade de Churn. Um Odds Ratio maior que 1 indica que o aumento daquela feature (ou pertencer àquela categoria) aumenta as chances de Churn, enquanto um valor menor que 1 diminui.\n\n')
|
| 423 |
|
| 424 |
if not self.coefficients_df.empty:
|
| 425 |
markdown_story.append(self.coefficients_df.to_markdown(index=False) + "\n")
|
| 426 |
latex_story.append(NoEscape(self.coefficients_df.to_latex(index=False, caption='Coeficientes e Odds Ratios das Variáveis', label='tab:coefficients', longtable=False)))
|
| 427 |
-
|
| 428 |
-
markdown_story.append("### **Interpretação Estratégica:**\n- **Odds Ratio > 1:** Features como `Age`, `NumOfProducts`, `Balance`, ou certas categorias de `Geography` ou `Gender` (dependendo dos valores) que possuem um Odds Ratio alto, indicam que o cliente tem **maior chance de churn** à medida que essas características aumentam ou são verdadeiras. O banco pode focar em clientes com essas características para ações proativas de retenção.\n- **Odds Ratio < 1:** Features como `IsActiveMember` (se for 1 para ativo) ou `CreditScore` (se um score mais alto diminuir o churn) que possuem Odds Ratio baixo, indicam que a característica está associada a uma **menor chance de churn**.\n")
|
| 429 |
-
latex_story.append(r'\textbf{Interpretação Estratégica:}\n\n')
|
| 430 |
-
latex_story.append(r'\begin{itemize}' + '\n')
|
| 431 |
-
latex_story.append(r'\item \textbf{Odds Ratio > 1:} Features como \texttt{Age}, \texttt{NumOfProducts}, \texttt{Balance}, ou certas categorias de \texttt{Geography} ou \texttt{Gender} (dependendo dos valores) que possuem um Odds Ratio alto, indicam que o cliente tem \textbf{maior chance de churn} à medida que essas características aumentam ou são verdadeiras. O banco pode focar em clientes com essas características para ações proativas de retenção.' + '\n')
|
| 432 |
-
latex_story.append(r'\item \textbf{Odds Ratio < 1:} Features como \texttt{IsActiveMember} (se for 1 para ativo) ou \texttt{CreditScore} (se um score mais alto diminuir o churn) que possuem Odds Ratio baixo, indicam que a característica está associada a uma \textbf{menor chance de churn}.' + '\n')
|
| 433 |
-
latex_story.append(r'\end{itemize}' + '\n\n')
|
| 434 |
-
else:
|
| 435 |
-
markdown_story.append("Nenhum coeficiente disponível. O modelo pode não ter sido treinado ou não possui coeficientes acessíveis.\n")
|
| 436 |
-
latex_story.append(r'Nenhum coeficiente disponível. O modelo pode não ter sido treinado ou não possui coeficientes acessíveis.\n\n')
|
| 437 |
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
markdown_story.append(f"- **Precisão ({self.metrics_dict.get('Precisão', 0):.2%}):** Das previsões de churn (`1`), quantos realmente foram churn. É importante para o banco não abordar clientes que não iriam dar churn (reduzir falsos positivos). Um valor de {self.metrics_dict.get('Precisão', 0):.2%} significa que das vezes que o modelo previu churn, essa porcentagem estava correta.\n")
|
| 461 |
-
latex_story.append(fr'\item \textbf{{Precisão ({self.metrics_dict.get("Precisão", 0):.2f}\%)}}: Das previsões de churn (\texttt{1}), quantos realmente foram churn. É importante para o banco não abordar clientes que não iriam dar churn (reduzir falsos positivos). Um valor de {self.metrics_dict.get("Precisão", 0):.2f}\% significa que das vezes que o modelo previu churn, essa porcentagem estava correta.' + '\n')
|
| 462 |
-
|
| 463 |
-
markdown_story.append(f"- **Recall (Sensibilidade) ({self.metrics_dict.get('Recall (Sensibilidade)', 0):.2%}):** Dos clientes que realmente deram churn (`1`), quantos o modelo identificou. É crucial para o banco identificar o máximo de clientes em risco (reduzir falsos negativos). Um valor de {self.metrics_dict.get('Recall (Sensibilidade)', 0):.2%} significa que essa porcentagem de clientes que de fato deram churn foi corretamente identificada pelo modelo.\n")
|
| 464 |
-
latex_story.append(fr'\item \textbf{{Recall (Sensibilidade) ({self.metrics_dict.get("Recall (Sensibilidade)", 0):.2f}\%)}}: Dos clientes que realmente deram churn (\texttt{1}), quantos o modelo identificou. É crucial para o banco identificar o máximo de clientes em risco (reduzir falsos negativos). Um valor de {self.metrics_dict.get("Recall (Sensibilidade)", 0):.2f}\% significa que essa porcentagem de clientes que de fato deram churn foi corretamente identificada pelo modelo.' + '\n'))
|
| 465 |
|
| 466 |
markdown_story.append(f"- **F1-Score ({self.metrics_dict.get('F1-Score', 0):.4f}):** É a média harmônica entre Precisão e Recall, útil quando há um desequilíbrio de classes e você precisa de um balanço entre identificar corretamente e não levantar falsos alarmes.\n")
|
| 467 |
-
latex_story.append(fr'\item \textbf{{F1-Score ({self.metrics_dict.get("F1-Score", 0):.4f})}}: É a média harmônica entre Precisão e Recall, útil quando há um desequilíbrio de classes e você precisa de um balanço entre identificar corretamente e não levantar falsos alarmes.' + '\n'))
|
| 468 |
|
| 469 |
-
latex_story.append(r'\end{itemize}' + '\n\n')
|
| 470 |
else:
|
| 471 |
-
markdown_story.append("
|
| 472 |
-
latex_story.append(r'Nenhum dado de avaliação disponível. O modelo pode não ter sido treinado ou avaliado.\n\n')
|
| 473 |
|
| 474 |
# --- 7. Conclusão e Próximos Passos ---
|
| 475 |
markdown_story.append("## 7. Conclusão e Próximos Passos")
|
| 476 |
latex_story.append(Section(NoEscape(r'Conclusão e Próximos Passos'), False))
|
| 477 |
|
| 478 |
markdown_story.append("O modelo de Regressão Logística provê uma base sólida para a previsão de churn. As variáveis identificadas como mais influentes (pelos Odds Ratios) devem ser o foco para o planejamento estratégico de retenção. Por exemplo, campanhas de marketing direcionadas a grupos de maior risco ou ofertas personalizadas podem ser desenvolvidas com base nas características que aumentam a probabilidade de churn.\nPara aprimoramento contínuo, sugere-se a exploração de outros modelos, engenharia de novas features, e reavaliação periódica do modelo com dados mais recentes.")
|
| 479 |
-
latex_story.append(r'O modelo de Regressão Logística provê uma base sólida para a previsão de churn. As variáveis identificadas como mais influentes (pelos Odds Ratios) devem ser o foco para o planejamento estratégico de retenção. Por exemplo, campanhas de marketing direcionadas a grupos de maior risco ou ofertas personalizadas podem ser desenvolvidas com base nas características que aumentam a probabilidade de churn.\n\nPara aprimoramento contínuo, sugere-se a exploração de outros modelos, engenharia de novas features, e reavaliação periódica do modelo com dados mais recentes.')
|
| 480 |
|
| 481 |
return "\n".join(markdown_story), latex_story, self.plot_paths
|
| 482 |
|
|
@@ -518,7 +500,7 @@ class ChurnModelPipeline:
|
|
| 518 |
shutil.copy2(logo_filename, logo_target_path) # Copia o logo para o diretório temporário do LaTeX
|
| 519 |
with doc.create(Figure(position='h!')) as logo_fig:
|
| 520 |
# Referencia pelo nome do arquivo, pois está no mesmo diretório do .tex
|
| 521 |
-
logo_fig.add_image(os.path.basename(logo_target_path), width='0.25\textwidth')
|
| 522 |
logo_fig.add_caption(NoEscape(r'\vspace{-0.5cm}'))
|
| 523 |
else:
|
| 524 |
doc.append(Command('textbf', 'AVISO: Logo da UnB não encontrado! Certifique-se de que "MARCADOR.png" esteja na raiz do seu Hugging Face Space.'))
|
|
|
|
| 276 |
latex_story.append(NoEscape(sample_display_df.to_latex(index=False, caption='Características do Cliente Simulado', label='tab:sim_customer', longtable=False)))
|
| 277 |
|
| 278 |
markdown_story.append(f"**Resultado da Simulação:** O cliente **{churn_status_sample}** (Probabilidade de Churn: **{prob_sample:.2%}**)\n")
|
| 279 |
+
latex_story.append(NoEscape(fr'\textbf{{Resultado da Simulação:}} O cliente \textbf{{{churn_status_sample}}} (Probabilidade de Churn: \textbf{{{prob_sample:.2f}\%}})\n\n'))
|
| 280 |
else:
|
| 281 |
markdown_story.append("Não foi possível realizar uma simulação pois o DataFrame de teste ou dados interativos não estão disponíveis.\n")
|
| 282 |
+
latex_story.append(NoEscape(r'Não foi possível realizar uma simulação pois o DataFrame de teste ou dados interativos não estão disponíveis.\n\n'))
|
| 283 |
|
| 284 |
# --- 2. Detalhes do Processo de Treinamento ---
|
| 285 |
markdown_story.append("## 2. Detalhes do Processo de Treinamento")
|
|
|
|
| 320 |
latex_story.append(NoEscape(training_details_latex))
|
| 321 |
else:
|
| 322 |
markdown_story.append("Nenhum detalhe de treinamento disponível.\n")
|
| 323 |
+
latex_story.append(NoEscape(r'Nenhum detalhe de treinamento disponível.\n\n'))
|
| 324 |
|
| 325 |
|
| 326 |
# --- 3. Descrição do Modelo e Metodologia ---
|
|
|
|
| 328 |
latex_story.append(Section(NoEscape(r'Descrição do Modelo e Metodologia'), False))
|
| 329 |
|
| 330 |
markdown_story.append("O modelo utiliza **Regressão Logística** para classificar a probabilidade de um cliente sair (churn). Foram aplicadas as seguintes etapas para garantir robustez e tratar as características dos dados:\n")
|
| 331 |
+
latex_story.append(NoEscape(r'O modelo utiliza \textbf{Regressão Logística} para classificar a probabilidade de um cliente sair (churn). Foram aplicadas as seguintes etapas para garantir robustez e tratar as características dos dados:\n\n'))
|
| 332 |
|
| 333 |
markdown_story.append("- **Pré-processamento de Dados:**\n - **Features Numéricas:** Imputação de valores ausentes (mediana) e escalonamento (`StandardScaler`) para padronização.\n - **Features Categóricas:** Imputação de valores ausentes (moda) e codificação One-Hot (`OneHotEncoder`) para transformar categorias em formato numérico.\n")
|
| 334 |
+
latex_story.append(NoEscape(r'\begin{itemize}' + '\n'))
|
| 335 |
+
latex_story.append(NoEscape(r'\item \textbf{Pré-processamento de Dados:}' + '\n'))
|
| 336 |
+
latex_story.append(NoEscape(r'\begin{itemize}' + '\n'))
|
| 337 |
+
latex_story.append(NoEscape(r'\item \textbf{Features Numéricas:} Imputação de valores ausentes (mediana) e escalonamento (\texttt{StandardScaler}) para padronização.' + '\n'))
|
| 338 |
+
latex_story.append(NoEscape(r'\item \textbf{Features Categóricas:} Imputação de valores ausentes (moda) e codificação One-Hot (\texttt{OneHotEncoder}) para transformar categorias em formato numérico.' + '\n'))
|
| 339 |
+
latex_story.append(NoEscape(r'\end{itemize}' + '\n'))
|
| 340 |
|
| 341 |
markdown_story.append("- **Balanceamento de Classes (SMOTE):** O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (`Exited`). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem).\n")
|
| 342 |
+
latex_story.append(NoEscape(r'\item \textbf{Balanceamento de Classes (SMOTE):} O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (\texttt{Exited}). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem).' + '\n'))
|
| 343 |
|
| 344 |
markdown_story.append("- **Regularização (L2):** A Regressão Logística foi configurada com um parâmetro `C=0.1` (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.\n")
|
| 345 |
+
latex_story.append(NoEscape(r'\item \textbf{Regularização (L2):} A Regressão Logística foi configurada com um parâmetro \texttt{C=0.1} (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.' + '\n'))
|
| 346 |
+
latex_story.append(NoEscape(r'\end{itemize}' + '\n\n'))
|
| 347 |
|
| 348 |
|
| 349 |
# --- 4. Como a Probabilidade de Churn é Calculada ---
|
|
|
|
| 351 |
latex_story.append(Section(NoEscape(r'Como a Probabilidade de Churn é Calculada'), False))
|
| 352 |
|
| 353 |
markdown_story.append("A Regressão Logística é um modelo de classificação que estima a probabilidade de um evento (neste caso, o churn do cliente) ocorrer. Ao contrário da regressão linear, que prevê um valor contínuo, a regressão logística utiliza a **função sigmoide** para mapear qualquer valor real para um valor entre 0 e 1, que pode ser interpretado como probabilidade.\n")
|
| 354 |
+
latex_story.append(NoEscape(r'A Regressão Logística é um modelo de classificação que estima a probabilidade de um evento (neste caso, o churn do cliente) ocorrer. Ao contrário da regressão linear, que prevê um valor contínuo, a regressão logística utiliza a \textbf{função sigmoide} para mapear qualquer valor real para um valor entre 0 e 1, que pode ser interpretado como probabilidade.\n\n'))
|
| 355 |
|
| 356 |
markdown_story.append("A equação básica de um modelo linear (`L`) é:\n`L = β₀ + β₁X₁ + β₂X₂ + ... + βₙXₙ`\nOnde `β` são os coeficientes (pesos) das features (`X`).\n")
|
| 357 |
latex_story.append(Math(data=[NoEscape(r'L = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \dots + \beta_n X_n')]))
|
| 358 |
+
latex_story.append(NoEscape(r'\nOnde $\beta$ são os coeficientes (pesos) das features ($X$).\n\n'))
|
| 359 |
|
| 360 |
markdown_story.append("A probabilidade (`P`) de churn é então calculada aplicando-se a função sigmoide (σ) a `L`:\n`P(Churn) = σ(L) = 1 / (1 + e⁻ᴸ)`\n")
|
| 361 |
latex_story.append(Math(data=[NoEscape(r'P(\text{Churn}) = \sigma(L) = \frac{1}{1 + e^{-L}}')]))
|
| 362 |
|
| 363 |
markdown_story.append("Esta função garante que a saída esteja sempre entre 0 e 1, representando a probabilidade de o cliente pertencer à classe 'Churn' (ou seja, `Exited = 1`). Se `P(Churn)` for maior que um determinado limiar (geralmente 0.5), o cliente é classificado como provável churn.\n")
|
| 364 |
+
latex_story.append(NoEscape(r'\nEsta função garante que a saída esteja sempre entre 0 e 1, representando a probabilidade de o cliente pertencer à classe `Churn` (ou seja, \texttt{Exited = 1}). Se $P(\text{Churn})$ for maior que um determinado limiar (geralmente 0.5), o cliente é classificado como provável churn.\n\n'))
|
| 365 |
|
| 366 |
# --- Subseção: Exemplo de Simulação Numérica (AGORA COM DADOS REAIS DA SIMULAÇÃO) ---
|
| 367 |
markdown_story.append("### Exemplo de Simulação Numérica com Cliente Simulado")
|
|
|
|
| 370 |
if sample_customer_df is not None:
|
| 371 |
# Reutilizamos os valores de logit_sample e prob_sample calculados anteriormente para o cliente simulado
|
| 372 |
markdown_story.append("Para ilustrar o cálculo, vamos usar as características do cliente simulado acima (ou o último cliente da Previsão Interativa) e os coeficientes do modelo treinado. Note que as características numéricas são **escalonadas** e as categóricas **one-hot encoded** antes de serem multiplicadas pelos coeficientes. \n")
|
| 373 |
+
latex_story.append(NoEscape(r'Para ilustrar o cálculo, vamos usar as características do cliente simulado acima (ou o último cliente da Previsão Interativa) e os coeficientes do modelo treinado. Note que as características numéricas são \textbf{escalonadas} e as categóricas \textbf{one-hot encoded} antes de serem multiplicadas pelos coeficientes. \n\n'))
|
| 374 |
|
| 375 |
markdown_story.append(f"**Características do Cliente 'Simulado':**\n" + sample_display_df.to_markdown(index=False) + "\n")
|
| 376 |
latex_story.append(NoEscape(sample_display_df.to_latex(index=False, caption='Características do Cliente Simulado', label='tab:sim_customer_example', longtable=False)))
|
|
|
|
| 384 |
markdown_story.append(f"1. **Calcular o Logit (L):** O Logit é a soma ponderada de todas as características do cliente (já processadas pelo pré-processador do modelo) multiplicadas por seus respectivos coeficientes, mais o intercepto do modelo. Para o cliente simulado, o modelo calculou um Logit de:\n`L = {logit_sample_formatted}`\n")
|
| 385 |
|
| 386 |
# LaTeX for Logit calculation
|
| 387 |
+
latex_story.append(NoEscape(r'\textbf{Passos do Cálculo para o Cliente "Simulado":}\n'))
|
| 388 |
+
latex_story.append(NoEscape(r'\begin{enumerate}'))
|
| 389 |
+
latex_story.append(NoEscape(fr'\item \textbf{{Calcular o Logit (L):}} O Logit é a soma ponderada de todas as características do cliente (já processadas pelo pré-processador do modelo) multiplicadas por seus respectivos coeficientes, mais o intercepto do modelo. Para o cliente simulado, o modelo calculou um Logit de:'))
|
| 390 |
latex_story.append(Math(data=[NoEscape(fr'L = {logit_sample_formatted}')]))
|
| 391 |
|
| 392 |
markdown_story.append(f"2. **Calcular a Probabilidade de Churn (P) usando a função Sigmoide:** A probabilidade é obtida aplicando-se a função sigmoide ao valor de `L`:\n`P(Churn) = 1 / (1 + e^(-L))`\n`P(Churn) = 1 / (1 + e^(-({logit_sample_formatted})))`\n`P(Churn) = 1 / (1 + e^{{-{logit_sample_formatted}}})`\n`P(Churn) ≈ {prob_sample_formatted}`\n")
|
| 393 |
|
| 394 |
# LaTeX for Probability calculation
|
| 395 |
+
latex_story.append(NoEscape(r'\item \textbf{Calcular a Probabilidade de Churn (P) usando a função Sigmoide:} A probabilidade é obtida aplicando-se a função sigmoide ao valor de $L$:'))
|
| 396 |
latex_story.append(Math(data=[NoEscape(r'P(\text{Churn}) = \frac{1}{1 + e^{-L}}')]))
|
| 397 |
+
latex_story.append(Math(data=[NoEscape(fr'P(\text{Churn}) = \frac{{1}}{{1 + e^{{-({logit_sample_formatted})}}}}')]))
|
| 398 |
+
latex_story.append(Math(data=[NoEscape(fr'P(\text{Churn}) = \frac{{1}}{{1 + e^{{-{logit_sample_formatted}}}}}}')]))
|
|
|
|
|
|
|
| 399 |
latex_story.append(Math(data=[NoEscape(fr'P(\text{Churn}) \approx {prob_sample_formatted}')]))
|
| 400 |
+
latex_story.append(NoEscape(r'\end{enumerate}\n'))
|
| 401 |
|
| 402 |
markdown_story.append(f"**Resultado da Simulação para o Cliente 'Simulado':**\n")
|
| 403 |
markdown_story.append(f"A probabilidade de Churn para este cliente específico é de **{prob_sample_formatted}**, ou seja, **{prob_sample_percent_formatted}**.\n")
|
| 404 |
markdown_story.append(f"Este resultado indica que o cliente possui uma probabilidade de churn de {prob_sample_percent_formatted}, guiando a interpretação do risco.\n")
|
| 405 |
|
| 406 |
+
latex_story.append(NoEscape(r'\textbf{Resultado da Simulação para o Cliente "Simulado":}\n'))
|
| 407 |
+
latex_story.append(NoEscape(fr'A probabilidade de Churn para este cliente específico é de \textbf{{{prob_sample_formatted}}}, ou seja, \textbf{{{prob_sample_percent_formatted}}}. '))
|
| 408 |
+
latex_story.append(NoEscape(fr'Este resultado indica que o cliente possui uma probabilidade de churn de {prob_sample_percent_formatted}, guiando a interpretação do risco.\n\n'))
|
| 409 |
else:
|
| 410 |
markdown_story.append("Não foi possível gerar o exemplo de simulação numérica, pois nenhum cliente simulado foi fornecido.\n")
|
| 411 |
+
latex_story.append(NoEscape(r'Não foi possível gerar o exemplo de simulação numérica, pois nenhum cliente simulado foi fornecido.\n\n'))
|
| 412 |
|
| 413 |
# --- Fim da Subseção de Exemplo ---
|
| 414 |
|
|
|
|
| 417 |
latex_story.append(Section(NoEscape(r'Importância das Variáveis (Coeficientes e Odds Ratio)'), False))
|
| 418 |
|
| 419 |
markdown_story.append("A análise dos coeficientes do modelo de Regressão Logística, transformados em Odds Ratios, nos permite entender a influência de cada característica na probabilidade de Churn. Um Odds Ratio maior que 1 indica que o aumento daquela feature (ou pertencer àquela categoria) aumenta as chances de Churn, enquanto um valor menor que 1 diminui.\n")
|
| 420 |
+
latex_story.append(NoEscape(r'A análise dos coeficientes do modelo de Regressão Logística, transformados em Odds Ratios, nos permite entender a influência de cada característica na probabilidade de Churn. Um Odds Ratio maior que 1 indica que o aumento daquela feature (ou pertencer àquela categoria) aumenta as chances de Churn, enquanto um valor menor que 1 diminui.\n\n'))
|
| 421 |
|
| 422 |
if not self.coefficients_df.empty:
|
| 423 |
markdown_story.append(self.coefficients_df.to_markdown(index=False) + "\n")
|
| 424 |
latex_story.append(NoEscape(self.coefficients_df.to_latex(index=False, caption='Coeficientes e Odds Ratios das Variáveis', label='tab:coefficients', longtable=False)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
|
| 426 |
+
# Início das correções para `SyntaxError: f-string: single '}' is not allowed`
|
| 427 |
+
# Linha original de Precisão (equivalente a ~458)
|
| 428 |
+
precision_value = self.metrics_dict.get('Precisão', 0)
|
| 429 |
+
precision_text_latex = (
|
| 430 |
+
f'\item \textbf{{Precisão ({precision_value:.2f}\%):}} ' # Usando f-string para formatar valor e \%
|
| 431 |
+
+ r'Das previsões de churn (\texttt{1}), quantos realmente foram churn. '
|
| 432 |
+
r'É importante para o banco não abordar clientes que não iriam dar churn (reduzir falsos positivos). '
|
| 433 |
+
f'Um valor de {precision_value:.2f}\% significa que das vezes que o modelo previu churn, essa porcentagem estava correta.'
|
| 434 |
+
)
|
| 435 |
+
latex_story.append(NoEscape(precision_text_latex + '\n'))
|
| 436 |
+
|
| 437 |
+
# Linha original de Recall (equivalente a ~464)
|
| 438 |
+
recall_value = self.metrics_dict.get('Recall (Sensibilidade)', 0)
|
| 439 |
+
recall_text_latex = (
|
| 440 |
+
f'\item \textbf{{Recall (Sensibilidade) ({recall_value:.2f}\%):}} ' # Usando f-string para formatar valor e \%
|
| 441 |
+
+ r'Dos clientes que realmente deram churn (\texttt{1}), quantos o modelo identificou. '
|
| 442 |
+
r'É crucial para o banco identificar o máximo de clientes em risco (reduzir falsos negativos). '
|
| 443 |
+
f'Um valor de {recall_value:.2f}\% significa que essa porcentagem de clientes que de fato deram churn foi corretamente identificada pelo modelo.'
|
| 444 |
+
)
|
| 445 |
+
latex_story.append(NoEscape(recall_text_latex + '\n'))
|
| 446 |
+
# Fim das correções
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
|
| 448 |
markdown_story.append(f"- **F1-Score ({self.metrics_dict.get('F1-Score', 0):.4f}):** É a média harmônica entre Precisão e Recall, útil quando há um desequilíbrio de classes e você precisa de um balanço entre identificar corretamente e não levantar falsos alarmes.\n")
|
| 449 |
+
latex_story.append(NoEscape(fr'\item \textbf{{F1-Score ({self.metrics_dict.get("F1-Score", 0):.4f})}}: É a média harmônica entre Precisão e Recall, útil quando há um desequilíbrio de classes e você precisa de um balanço entre identificar corretamente e não levantar falsos alarmes.' + '\n'))
|
| 450 |
|
| 451 |
+
latex_story.append(NoEscape(r'\end{itemize}' + '\n\n'))
|
| 452 |
else:
|
| 453 |
+
markdown_story.append("Nenhum coeficiente disponível. O modelo pode não ter sido treinado ou não possui coeficientes acessíveis.\n")
|
| 454 |
+
latex_story.append(NoEscape(r'Nenhum dado de avaliação disponível. O modelo pode não ter sido treinado ou avaliado.\n\n'))
|
| 455 |
|
| 456 |
# --- 7. Conclusão e Próximos Passos ---
|
| 457 |
markdown_story.append("## 7. Conclusão e Próximos Passos")
|
| 458 |
latex_story.append(Section(NoEscape(r'Conclusão e Próximos Passos'), False))
|
| 459 |
|
| 460 |
markdown_story.append("O modelo de Regressão Logística provê uma base sólida para a previsão de churn. As variáveis identificadas como mais influentes (pelos Odds Ratios) devem ser o foco para o planejamento estratégico de retenção. Por exemplo, campanhas de marketing direcionadas a grupos de maior risco ou ofertas personalizadas podem ser desenvolvidas com base nas características que aumentam a probabilidade de churn.\nPara aprimoramento contínuo, sugere-se a exploração de outros modelos, engenharia de novas features, e reavaliação periódica do modelo com dados mais recentes.")
|
| 461 |
+
latex_story.append(NoEscape(r'O modelo de Regressão Logística provê uma base sólida para a previsão de churn. As variáveis identificadas como mais influentes ( pelos Odds Ratios) devem ser o foco para o planejamento estratégico de retenção. Por exemplo, campanhas de marketing direcionadas a grupos de maior risco ou ofertas personalizadas podem ser desenvolvidas com base nas características que aumentam a probabilidade de churn.\n\nPara aprimoramento contínuo, sugere-se a exploração de outros modelos, engenharia de novas features, e reavaliação periódica do modelo com dados mais recentes.'))
|
| 462 |
|
| 463 |
return "\n".join(markdown_story), latex_story, self.plot_paths
|
| 464 |
|
|
|
|
| 500 |
shutil.copy2(logo_filename, logo_target_path) # Copia o logo para o diretório temporário do LaTeX
|
| 501 |
with doc.create(Figure(position='h!')) as logo_fig:
|
| 502 |
# Referencia pelo nome do arquivo, pois está no mesmo diretório do .tex
|
| 503 |
+
logo_fig.add_image(os.path.basename(logo_target_path), width='0.25\\textwidth')
|
| 504 |
logo_fig.add_caption(NoEscape(r'\vspace{-0.5cm}'))
|
| 505 |
else:
|
| 506 |
doc.append(Command('textbf', 'AVISO: Logo da UnB não encontrado! Certifique-se de que "MARCADOR.png" esteja na raiz do seu Hugging Face Space.'))
|