Continuando nossa jornada pelo guia do scikit-learn, chegamos a uma família de classificadores probabilísticos fundamentais: a Análise Discriminante Linear (LDA) e Quadrática (QDA). Primordialmente, estas técnicas representam abordagens elegantes para classificação que combinam princípios estatísticos com eficiência computacional.
Fundamentos Teóricos
Conforme observamos anteriormente com modelos lineares, frequentemente buscamos métodos que ofereçam bom desempenho com interpretabilidade. Analogamente, LDA e QDA surgem como alternativas baseadas em pressupostos sobre a distribuição dos dados.
Pressupostos Básicos
Ambos os métodos assumem que:
- Os dados seguem uma distribuição normal
- As features são contínuas
- As classes têm distribuições Gaussianas multivariadas
A diferença fundamental reside na suposição sobre as matrizes de covariância:
- LDA: Matriz de covariância comum para todas as classes
- QDA: Matriz de covariância distinta para cada classe
Análise Discriminante Linear (LDA)
Base Matemática
O LDA assume que todas as classes compartilham a mesma matriz de covariância Σ. A função discriminante para a classe k é dada por:
\(\delta_k(x) = x^T \Sigma^{-1} \mu_k – \frac{1}{2} \mu_k^T \Sigma^{-1} \mu_k + \log \pi_k\)Onde:
- μ_k é a média da classe k
- Σ é a matriz de covariância comum
- π_k é a probabilidade a priori da classe k
Características do LDA
- Produz fronteiras de decisão lineares
- Menos parâmetros para estimar
- Mais robusto com poucos dados
- Computacionalmente eficiente
Análise Discriminante Quadrática (QDA)
Base Matemática
O QDA permite que cada classe tenha sua própria matriz de covariância Σ_k. A função discriminante torna-se:
\(\delta_k(x) = -\frac{1}{2} \log |\Sigma_k| – \frac{1}{2} (x – \mu_k)^T \Sigma_k^{-1} (x – \mu_k) + \log \pi_k\)Características do QDA
- Produz fronteiras de decisão quadráticas
- Mais flexível que o LDA
- Requer mais dados para estimação confiável
- Pode capturar relações mais complexas entre features
Comparação entre LDA e QDA
A escolha entre LDA e QDA depende de vários fatores:
- Tamanho do conjunto de dados
- Número de features
- Similaridade das matrizes de covariância entre classes
- Complexidade das fronteiras de decisão necessárias
Inegavelmente, o LDA é preferível quando temos dados limitados, enquanto o QDA pode oferecer melhor performance quando as matrizes de covariância são realmente diferentes e temos dados suficientes para estimá-las adequadamente.
Implementação no scikit-learn
No scikit-learn, ambas as técnicas são implementadas através das classes:
LinearDiscriminantAnalysisQuadraticDiscriminantAnalysis
Estas classes oferecem funcionalidades adicionais como redução de dimensionalidade (no caso do LDA) e suporte a diferentes métodos de estimação de parâmetros.
Exemplo Prático em Python
Para ilustrar as diferenças entre LDA e QDA, vejamos um exemplo comparativo:
|
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 |
import numpy as np import matplotlib.pyplot as plt from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis from sklearn.datasets import make_classification, make_blobs from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.preprocessing import StandardScaler ''' Exemplo comparativo entre LDA e QDA em diferentes cenários de dados para ilustrar suas características distintas ''' # Cenário 1: Dados com matrizes de covariância similares print("=== CENÁRIO 1: Matrizes de Covariância Similares ===") X1, y1 = make_classification( n_samples=300, n_features=2, n_informative=2, n_redundant=0, n_clusters_per_class=1, class_sep=2.0, random_state=42 ) # Cenário 2: Dados com matrizes de covariância diferentes print("=== CENÁRIO 2: Matrizes de Covariância Diferentes ===") X2, y2 = make_blobs( n_samples=300, centers=2, cluster_std=[1.0, 2.5], random_state=42, n_features=2 ) # Transformando para criar covariâncias diferentes transformation = [[0.6, -0.6], [-0.4, 0.8]] X2 = np.dot(X2, transformation) cenarios = [ ('Covariância Similar', X1, y1), ('Covariância Diferente', X2, y2) ] ''' Configurando os modelos e avaliando em cada cenário ''' models = { 'LDA': LinearDiscriminantAnalysis(), 'QDA': QuadraticDiscriminantAnalysis() } plt.figure(figsize=(15, 10)) for cenario_idx, (cenario_nome, X, y) in enumerate(cenarios, 1): # Dividindo os dados X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 ) # Padronizando os dados scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # Criando mesh para plotar fronteiras de decisão h = 0.02 x_min, x_max = X_train_scaled[:, 0].min() - 1, X_train_scaled[:, 0].max() + 1 y_min, y_max = X_train_scaled[:, 1].min() - 1, X_train_scaled[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) for model_idx, (model_name, model) in enumerate(models.items(), 1): plt.subplot(2, 2, (cenario_idx - 1) * 2 + model_idx) # Treinando o modelo model.fit(X_train_scaled, y_train) # Fazendo previsões y_pred = model.predict(X_test_scaled) accuracy = accuracy_score(y_test, y_pred) # Plotando fronteira de decisão Z = model.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.Paired) # Plotando dados de treino - CORREÇÃO: removendo parâmetro cmap do scatter colors = ['red', 'blue', 'green'] for class_idx, class_label in enumerate(np.unique(y_train)): class_mask = y_train == class_label plt.scatter(X_train_scaled[class_mask, 0], X_train_scaled[class_mask, 1], alpha=0.8, label=f'Classe {class_label}', color=colors[class_idx % len(colors)]) plt.title(f'{cenario_nome}\n{model_name} - Acurácia: {accuracy:.3f}') plt.xlabel('Feature 1 (padronizada)') plt.ylabel('Feature 2 (padronizada)') plt.legend() plt.tight_layout() plt.show() ''' Análise detalhada dos parâmetros dos modelos - VERSÃO CORRIGIDA ''' print("\n=== ANÁLISE DOS PARÂMETROS ===") for (cenario_nome, X, y) in cenarios: print(f"\n{cenario_nome}:") X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 ) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) for model_name, model in models.items(): model.fit(X_train_scaled, y_train) if model_name == 'LDA': # LDA - usando atributos corretos disponíveis print(f" {model_name}:") print(f" Forma do scaling: {model.scalings_.shape}") print(f" Número de coeficientes: {len(model.coef_.flatten())}") print(f" Número de classes: {len(model.classes_)}") print(f" Médias por classe: {[f'Classe {i}: {mean}' for i, mean in enumerate(model.means_)]}") else: # QDA - usando atributos corretos disponíveis print(f" {model_name}:") print(f" Número de classes: {len(model.classes_)}") print(f" Médias por classe: {[f'Classe {i}: {mean}' for i, mean in enumerate(model.means_)]}") # Para QDA, as covariâncias são armazenadas de forma diferente if hasattr(model, 'covariance_'): for i, covariance in enumerate(model.covariance_): print(f" Classe {i}: forma da matriz {covariance.shape}") else: print(" Info: Matrizes de covariância disponíveis internamente para cálculo") ''' Exemplo com dados reais: Iris dataset - VERSÃO CORRIGIDA ''' from sklearn.datasets import load_iris print("\n=== EXEMPLO COM DATASET IRIS ===") iris = load_iris() X_iris, y_iris = iris.data, iris.target # Usando apenas duas features para visualização X_iris_2d = X_iris[:, :2] X_train_iris, X_test_iris, y_train_iris, y_test_iris = train_test_split( X_iris_2d, y_iris, test_size=0.3, random_state=42 ) scaler_iris = StandardScaler() X_train_iris_scaled = scaler_iris.fit_transform(X_train_iris) X_test_iris_scaled = scaler_iris.transform(X_test_iris) for model_name, model in models.items(): model.fit(X_train_iris_scaled, y_train_iris) y_pred_iris = model.predict(X_test_iris_scaled) accuracy_iris = accuracy_score(y_test_iris, y_pred_iris) print(f"{model_name} no dataset Iris: Acurácia = {accuracy_iris:.3f}") # Informações adicionais específicas de cada modelo - CORRIGIDO if model_name == 'LDA': print(f" Número de componentes: {model.n_components}") if hasattr(model, 'explained_variance_ratio_'): print(f" Variância explicada: {model.explained_variance_ratio_}") else: print(f" Número de classes: {len(model.classes_)}") print(f" Prior probabilities: {model.priors_}") ''' Demonstração adicional: acesso seguro aos atributos ''' print("\n=== ATRIBUTOS DISPONÍVEIS POR MODELO ===") for model_name, model in models.items(): print(f"\n{model_name} - Atributos disponíveis:") atributos = [attr for attr in dir(model) if not attr.startswith('_')] # Mostrando apenas os principais atributos principais = [attr for attr in atributos if not attr.startswith('_') and any(keyword in attr for keyword in ['coef', 'mean', 'class', 'score', 'predict'])] for attr in principais[:10]: # Mostrando apenas os 10 primeiros print(f" - {attr}") |
Interpretação dos Resultados
Analisando o exemplo, podemos observar que:
- No cenário com matrizes de covariância similares, LDA e QDA performam similarmente
- No cenário com matrizes de covariância diferentes, QDA geralmente supera LDA
- As fronteiras de decisão do LDA são sempre lineares, enquanto as do QDA são quadráticas
- O LDA estima menos parâmetros, tornando-o mais eficiente com dados limitados
Considerações Práticas
Ao aplicar LDA ou QDA em problemas reais, é importante considerar:
- Verificar os pressupostos de normalidade quando possível
- Considerar o tamanho do dataset para escolha entre LDA e QDA
- Utilizar validação cruzada para avaliação robusta do desempenho
- Explorar a redução de dimensionalidade oferecida pelo LDA
Vantagens e Limitações
Embora poderosos, ambos os métodos têm suas particularidades:
- LDA: Mais robusto mas menos flexível
- QDA: Mais flexível mas requer mais dados
- Ambos: Sensíveis a violações dos pressupostos de normalidade
- Excelente desempenho quando os pressupostos são atendidos
Portanto, a escolha entre LDA e QDA deve considerar as características específicas dos dados e os objetivos do projeto. Inclusive, em muitos casos práticos, testar ambas as abordagens com validação adequada pode revelar qual método é mais apropriado para o problema em questão.