quando a resposta é uma probabilidade
Regressão logística é um algoritmo de classificação apesar do nome sugerir regressão. Ela estima a probabilidade de um exemplo pertencer a uma determinada categoria. Diferente da regressão linear, a saída está sempre entre 0 e 1. Por exemplo, podemos estimar a probabilidade de um e-mail ser spam. Primeiramente, o modelo usa uma função sigmoide que transforma qualquer valor em probabilidade. Além disso, essa função tem formato de “S”, comprimindo valores extremos. A decisão final usa um limiar (geralmente 0,5) para classificar o resultado.função sigmoide: o coração do modelo
A função sigmoide transforma combinações lineares de variáveis em probabilidades entre 0 e 1. Sua fórmula matemática é f(z) = 1 / (1 + e^{-z}), onde z é combinação linear. Primeiramente, valores de z muito negativos produzem probabilidades próximas de zero. Além disso, valores de z muito positivos produzem probabilidades próximas de um. Por exemplo, características que indicam spam produzem z positivo e probabilidade alta. O modelo aprende os coeficientes que melhor separam as duas classes. Essa curva em “S” é fundamental para o funcionamento do algoritmo.fronteiras de decisão
Regressão logística cria fronteiras lineares que separam as diferentes classes no espaço. Para duas dimensões, essa fronteira é uma linha reta no plano cartesiano. Primeiramente, pontos de um lado recebem classificação 0 e do outro lado 1. Além disso, podemos usar transformações polinomiais para criar fronteiras não lineares. Por exemplo, adicionar termos como x² e y² gera círculos como fronteira. A fronteira de decisão ocorre onde a probabilidade prevista é exatamente 0,5. Essa visualização ajuda a entender como o modelo faz classificações.avaliando classificadores binários
Avaliar regressão logística exige métricas específicas para classificação binária. Primeiramente, acurácia mede a proporção de acertos entre todas as previsões realizadas. Além disso, precisão indica quantos positivos previstos estavam corretos. Recall mostra quantos positivos reais conseguimos capturar corretamente. Curva ROC e AUC ajudam a avaliar o desempenho em diferentes limiares. Primeiramente, AUC próximo de 1 indica excelente capacidade de separação entre classes. Matriz de confusão organiza acertos e erros em quatro categorias distintas. Essas métricas oferecem visão completa do desempenho do modelo.aplicações no mundo real
Regressão logística é amplamente utilizada em problemas de classificação binária diversos. Primeiramente, bancos usam para prever inadimplência em empréstimos concedidos a clientes. Além disso, hospitais empregam para diagnosticar doenças com base em exames. Marketing utiliza para prever quais clientes responderão a campanhas promocionais. Recursos humanos aplicam para prever rotatividade de funcionários na empresa. Sistemas antifraude usam para identificar transações suspeitas em tempo real. Para iniciantes, regressão logística é a porta de entrada para classificação supervisionada. É simples, interpretável e eficaz para muitos problemas práticos do cotidiano.Contexto do Problema
Uma clínica médica deseja prever se um paciente tem diabetes com base em dois atributos: glicose (mg/dL) e IMC (kg/m²). Utilize a Regressão Logística para construir um modelo classificador.Características do Modelo
- Tipo: Modelo de classificação binária supervisionada
- Função de ativação: Sigmóide (Logística)
- Saída: Probabilidade entre 0 e 1
- Decisão: Classe 1 se P ≥ 0.5, senão Classe 0
- Limitação: Assume linearidade nos log-odds
Arquitetura do Modelo
A Regressão Logística é um modelo linear com uma única camada: \[ z = w_1 x_1 + w_2 x_2 + b \] \[ \hat{y} = \sigma(z) = \frac{1}{1 + e^{-z}} \] Onde:- \(x_1, x_2\): características de entrada (glicose, IMC)
- \(w_1, w_2\): pesos do modelo
- \(b\): viés (bias)
- \(\hat{y}\): probabilidade prevista da classe positiva
Hiperparâmetros
- C (regularização inversa): \(C = \frac{1}{\lambda}\) (padrão=1.0). Valores menores aumentam a regularização. Controle de overfitting através da regularização L2.
- penalty: Tipo de regularização (‘l1’, ‘l2’, ‘elasticnet’ ou None)
- max_iter: Número máximo de iterações para convergência (padrão=100)
- solver: Algoritmo de otimização (‘lbfgs’, ‘liblinear’, ‘newton-cg’, ‘sag’, ‘saga’)
- tol: Tolerância para critério de parada (padrão=1e-4)
Função de Custo (Log-Loss)
\[ J(\mathbf{w}) = -\frac{1}{m} \sum_{i=1}^{m} [y^{(i)} \log(\hat{y}^{(i)}) + (1-y^{(i)}) \log(1-\hat{y}^{(i)})] \]Tarefa
Implemente um modelo de Regressão Logística para classificar pacientes com diabetes. Utilize os dados sintéticos fornecidos e avalie o modelo com acurácia e matriz de confusão. Visualize a fronteira de decisão no espaço 2D.|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# Regressão Logística para Classificação de Diabetes # Execute este código no Google Colab import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import accuracy_score, confusion_matrix, classification_report import seaborn as sns # Configuração para gráficos bonitos plt.style.use('seaborn-v0_8-darkgrid') sns.set_palette("husl") # ==================== 1. GERAR DADOS SINTÉTICOS ==================== print("="*50) print("GERANDO DADOS SINTÉTICOS") print("="*50) np.random.seed(42) n_samples = 300 # Glicose (mg/dL) - normal: 70-100, diabético: >126 glicose_diabetico = np.random.normal(150, 25, n_samples//2) glicose_normal = np.random.normal(95, 15, n_samples//2) # IMC (kg/m²) - normal: 18.5-24.9, diabético: >25 imc_diabetico = np.random.normal(30, 4, n_samples//2) imc_normal = np.random.normal(22, 3, n_samples//2) # Combinar dados X = np.vstack([np.column_stack([glicose_diabetico, imc_diabetico]), np.column_stack([glicose_normal, imc_normal])]) y = np.array([1]* (n_samples//2) + [0]* (n_samples//2)) # Embaralhar os dados indices = np.random.permutation(n_samples) X, y = X[indices], y[indices] print(f"Total de amostras: {n_samples}") print(f"Pacientes diabéticos (classe 1): {sum(y)}") print(f"Pacientes não-diabéticos (classe 0): {len(y)-sum(y)}") print(f"\nEstatísticas:") print(f"Glicose - Média: {X[:,0].mean():.1f}, Desvio: {X[:,0].std():.1f}") print(f"IMC - Média: {X[:,1].mean():.1f}, Desvio: {X[:,1].std():.1f}") # ==================== 2. PRÉ-PROCESSAMENTO ==================== # Padronização dos dados (importante para Regressão Logística) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # Divisão treino/teste X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.3, random_state=42, stratify=y ) print(f"\nDivisão dos dados:") print(f"Treino: {len(X_train)} amostras") print(f"Teste: {len(X_test)} amostras") # ==================== 3. TREINAR REGRESSÃO LOGÍSTICA ==================== print("\n" + "="*50) print("TREINANDO REGRESSÃO LOGÍSTICA") print("="*50) # Hiperparâmetros configuráveis # C: inverso da regularização (menor C = mais regularização) # penalty: tipo de regularização # solver: algoritmo de otimização model = LogisticRegression( C=1.0, # Regularização padrão penalty='l2', # Regularização L2 solver='lbfgs', # Algoritmo de otimização max_iter=1000, # Máximo de iterações random_state=42 ) model.fit(X_train, y_train) print(f"Coeficientes (pesos) do modelo:") print(f"w1 (Glicose): {model.coef_[0][0]:.3f}") print(f"w2 (IMC): {model.coef_[0][1]:.3f}") print(f"bias (intercepto): {model.intercept_[0]:.3f}") # ==================== 4. AVALIAÇÃO DO MODELO ==================== y_pred = model.predict(X_test) y_prob = model.predict_proba(X_test)[:, 1] accuracy = accuracy_score(y_test, y_pred) print(f"\nAcurácia no teste: {accuracy:.3f} ({accuracy*100:.1f}%)") print("\nRelatório de Classificação:") print(classification_report(y_test, y_pred, target_names=['Não-diabético', 'Diabético'])) # Matriz de Confusão cm = confusion_matrix(y_test, y_pred) print("\nMatriz de Confusão:") print(cm) # ==================== 5. VISUALIZAÇÕES ==================== fig, axes = plt.subplots(2, 2, figsize=(14, 10)) # Gráfico 1: Dados originais ax1 = axes[0, 0] colors = ['blue' if y_i == 0 else 'red' for y_i in y] ax1.scatter(X[:, 0], X[:, 1], c=colors, alpha=0.6, edgecolors='k', s=50) ax1.set_xlabel('Glicose (mg/dL)', fontsize=12) ax1.set_ylabel('IMC (kg/m²)', fontsize=12) ax1.set_title('Dados Originais: Pacientes com e sem Diabetes', fontsize=14) from matplotlib.patches import Patch legend_elements = [Patch(facecolor='blue', label='Não-diabético (0)'), Patch(facecolor='red', label='Diabético (1)')] ax1.legend(handles=legend_elements, loc='upper left') # Gráfico 2: Fronteira de Decisão ax2 = axes[0, 1] # Criar grid para fronteira de decisão x_min, x_max = X_scaled[:, 0].min() - 0.5, X_scaled[:, 0].max() + 0.5 y_min, y_max = X_scaled[:, 1].min() - 0.5, X_scaled[:, 1].max() + 0.5 xx, yy = np.meshgrid(np.linspace(x_min, x_max, 200), np.linspace(y_min, y_max, 200)) # Prever probabilidades no grid Z = model.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1] Z = Z.reshape(xx.shape) # Plotar fronteira de decisão contour = ax2.contourf(xx, yy, Z, levels=20, alpha=0.7, cmap='RdBu_r') ax2.contour(xx, yy, Z, levels=[0.5], colors='black', linewidths=2, linestyles='-') ax2.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='RdBu_r', edgecolors='k', s=40, alpha=0.8) ax2.set_xlabel('Glicose (padronizada)', fontsize=12) ax2.set_ylabel('IMC (padronizado)', fontsize=12) ax2.set_title('Fronteira de Decisão do Modelo\n(Probabilidade = 0.5)', fontsize=14) plt.colorbar(contour, ax=ax2, label='Probabilidade de Diabetes') # Gráfico 3: Matriz de Confusão ax3 = axes[1, 0] sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=ax3, xticklabels=['Não-diabético', 'Diabético'], yticklabels=['Não-diabético', 'Diabético']) ax3.set_xlabel('Predito', fontsize=12) ax3.set_ylabel('Real', fontsize=12) ax3.set_title('Matriz de Confusão', fontsize=14) # Gráfico 4: Curva de Probabilidades ax4 = axes[1, 1] # Separar predições corretas e incorretas correct_idx = y_pred == y_test incorrect_idx = y_pred != y_test ax4.scatter(range(len(y_prob[correct_idx])), y_prob[correct_idx], c='green', alpha=0.6, s=50, label='Corretos', marker='o') ax4.scatter(range(len(y_prob[incorrect_idx])), y_prob[incorrect_idx], c='red', alpha=0.8, s=80, label='Incorretos', marker='X') ax4.axhline(y=0.5, color='black', linestyle='--', linewidth=2, label='Limite (0.5)') ax4.set_xlabel('Amostras de Teste', fontsize=12) ax4.set_ylabel('Probabilidade de Diabetes', fontsize=12) ax4.set_title('Probabilidades Previstas - Amostras de Teste', fontsize=14) ax4.legend() ax4.grid(True, alpha=0.3) plt.tight_layout() plt.show() # ==================== 6. ANÁLISE DE HIPERPARÂMETROS ==================== print("\n" + "="*50) print("ANÁLISE DE HIPERPARÂMETROS") print("="*50) # Testar diferentes valores de C (regularização) C_values = [0.001, 0.01, 0.1, 1, 10, 100] train_scores = [] test_scores = [] for C in C_values: model_c = LogisticRegression(C=C, max_iter=1000, random_state=42) model_c.fit(X_train, y_train) train_scores.append(accuracy_score(y_train, model_c.predict(X_train))) test_scores.append(accuracy_score(y_test, model_c.predict(X_test))) # Plotar efeito da regularização fig, ax = plt.subplots(figsize=(10, 6)) ax.semilogx(C_values, train_scores, 'o-', label='Treino', linewidth=2, markersize=8) ax.semilogx(C_values, test_scores, 's-', label='Teste', linewidth=2, markersize=8) ax.set_xlabel('C (Inverso da Regularização)', fontsize=12) ax.set_ylabel('Acurácia', fontsize=12) ax.set_title('Efeito da Regularização na Performance do Modelo', fontsize=14) ax.legend() ax.grid(True, alpha=0.3) ax.set_xticks(C_values) ax.set_xticklabels([f'{c}' for c in C_values]) plt.tight_layout() plt.show() print("\nMelhor C: {} (acurácia teste: {:.3f})".format( C_values[np.argmax(test_scores)], max(test_scores) )) # ==================== 7. EXEMPLO DE PREDIÇÃO ==================== print("\n" + "="*50) print("EXEMPLO DE PREDIÇÃO PARA UM NOVO PACIENTE") print("="*50) # Novo paciente: Glicose=130, IMC=28 novo_paciente = np.array([[130, 28]]) novo_paciente_scaled = scaler.transform(novo_paciente) probabilidade = model.predict_proba(novo_paciente_scaled)[0][1] predicao = model.predict(novo_paciente_scaled)[0] print(f"Novo paciente:") print(f"Glicose: 130 mg/dL") print(f"IMC: 28 kg/m²") print(f"\nProbabilidade de ter diabetes: {probabilidade:.3f} ({probabilidade*100:.1f}%)") print(f"Classificação: {'DIABÉTICO' if predicao == 1 else 'NÃO-DIABÉTICO'}") if probabilidade >= 0.7: print("Alto risco de diabetes - Recomendar consulta médica") elif probabilidade >= 0.5: print("Risco moderado - Recomendar exames adicionais") else: print("Baixo risco - Manter hábitos saudáveis") print("\n" + "="*50) print("FIM DA ANÁLISE") print("="*50) |