Imagine que você está analisando uma paisagem montanhosa. Se você olhar de muito perto, vê cada pedra e irregularidade. Se afastar um pouco, percebe os vales e colinas. De ainda mais longe, enxerga as cadeias de montanhas inteiras. O núcleo quadrático racional é como ter uma lente que consegue capturar todas essas escalas simultaneamente – ele modela tanto as variações locais quanto os padrões de longo alcance em uma única função elegante, sem precisar escolher entre focar nos detalhes ou no panorama geral.
Como isso funciona na prática?
O núcleo quadrático racional (Rational Quadratic) é essencialmente uma mistura infinita de kernels RBF com diferentes length_scales. Enquanto um kernel RBF tradicional assume uma única escala de variação, o núcleo quadrático racional combina múltiplas escalas através do parâmetro α (alpha), que controla a mistura entre variações locais e globais. Diferentemente do RBF que tem um “raio de influência” fixo, este kernel adapta-se naturalmente a dados que exibem comportamento em diferentes escalas, tornando-o particularmente útil para fenômenos complexos do mundo real onde padrões ocorrem simultaneamente em diferentes níveis de granularidade.
Mãos na massa: explorando o poder multiescala do núcleo quadrático racional
|
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 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
""" Explorando o núcleo quadrático racional e seu parâmetro alpha Demonstra como capturar padrões em múltiplas escalas simultaneamente """ import numpy as np import matplotlib.pyplot as plt from sklearn.gaussian_process.kernels import RationalQuadratic, RBF from sklearn.gaussian_process import GaussianProcessRegressor # Criando dados com variações em múltiplas escalas def criar_dados_multiescala(): """Cria dados com padrões em diferentes escalas temporais/espaciais""" np.random.seed(42) x = np.linspace(0, 20, 200).reshape(-1, 1) # Componentes em diferentes escalas componente_longo_prazo = 2 * np.sin(0.5 * x.ravel()) # Escala grande componente_medio_prazo = 1 * np.sin(2 * x.ravel()) # Escala média componente_curto_prazo = 0.5 * np.sin(8 * x.ravel()) # Escala pequena tendencia = 0.1 * x.ravel() # Tendência linear ruido = 0.1 * np.random.normal(size=len(x)) # Ruído y_multiescala = (componente_longo_prazo + componente_medio_prazo + componente_curto_prazo + tendencia + ruido) # Dados com apenas uma escala (para comparação) y_uma_escala = 2 * np.sin(2 * x.ravel()) + 0.1 * np.random.normal(size=len(x)) # Dados do mundo real simulados: temperatura ao longo do tempo dias = np.linspace(0, 365, 200) y_temperatura = (15 + 10 * np.sin(2 * np.pi * dias/365) + # Sazonalidade anual 2 * np.sin(2 * np.pi * dias/30) + # Variação mensal 0.5 * np.sin(2 * np.pi * dias/7) + # Variação semanal 0.5 * np.random.normal(size=len(dias))) # Ruído return x, y_multiescala, y_uma_escala, dias.reshape(-1, 1), y_temperatura x, y_multiescala, y_uma_escala, dias, y_temperatura = criar_dados_multiescala() print("=== EXPLORANDO O NÚCLEO QUADRÁTICO RACIONAL ===\n") print("O núcleo quadrático racional é definido por:") print("[latex]k(x_i, x_j) = \\left(1 + \\frac{d(x_i, x_j)^2}{2\\alpha l^2}\\right)^{-\\alpha}[/latex]") print("Onde α controla a mistura de escalas e l é o length_scale\n") # Explorando diferentes valores do parâmetro alpha valores_alpha = [0.1, 0.5, 1.0, 5.0, 50.0] print("Efeito do parâmetro alpha:") print("• α → 0: Comporta-se como RBF com length_scale único") print("• α → ∞: Mistura infinita de RBFs com diferentes length_scales") print("• α intermediário: Balance entre escalas locais e globais\n") # Testando diferentes alphas nos dados multiescala fig, axes = plt.subplots(2, 3, figsize=(18, 12)) axes = axes.ravel() for idx, alpha in enumerate(valores_alpha): kernel_rq = RationalQuadratic(length_scale=1.0, alpha=alpha) gpr = GaussianProcessRegressor(kernel=kernel_rq, alpha=0.0, random_state=42) gpr.fit(x, y_multiescala) # Previsões x_pred = np.linspace(0, 20, 300).reshape(-1, 1) y_pred, sigma = gpr.predict(x_pred, return_std=True) # Plot axes[idx].plot(x, y_multiescala, 'ro', alpha=0.4, markersize=2, label='Dados') axes[idx].plot(x_pred, y_pred, 'b-', linewidth=2, label='Previsão RQ') axes[idx].fill_between(x_pred.ravel(), y_pred - 1.96*sigma, y_pred + 1.96*sigma, alpha=0.2, label='Incerteza 95%') log_likelihood = gpr.log_marginal_likelihood() axes[idx].set_title(f'Rational Quadratic α = {alpha}\nLog-likelihood: {log_likelihood:.2f}', fontsize=11) axes[idx].grid(True, alpha=0.3) axes[idx].legend(fontsize=9) # Comparação com RBF no último subplot kernel_rbf = RBF(length_scale=1.0) gpr_rbf = GaussianProcessRegressor(kernel=kernel_rbf, alpha=0.0, random_state=42) gpr_rbf.fit(x, y_multiescala) y_pred_rbf, sigma_rbf = gpr_rbf.predict(x_pred, return_std=True) axes[5].plot(x, y_multiescala, 'ro', alpha=0.4, markersize=2, label='Dados') axes[5].plot(x_pred, y_pred_rbf, 'g-', linewidth=2, label='Previsão RBF') axes[5].fill_between(x_pred.ravel(), y_pred_rbf - 1.96*sigma_rbf, y_pred_rbf + 1.96*sigma_rbf, alpha=0.2, label='Incerteza 95%') axes[5].set_title(f'RBF (comparação)\nLog-likelihood: {gpr_rbf.log_marginal_likelihood():.2f}', fontsize=11) axes[5].grid(True, alpha=0.3) axes[5].legend(fontsize=9) plt.tight_layout() plt.show() # Comparação detalhada RQ vs RBF em diferentes cenários print("\n=== COMPARAÇÃO DETALHADA: RATIONAL QUADRATIC VS RBF ===\n") cenarios = { 'Dados Multiescala': (x, y_multiescala), 'Dados Uma Escala': (x, y_uma_escala), 'Dados Temperatura': (dias, y_temperatura) } resultados_comparacao = {} fig, axes = plt.subplots(len(cenarios), 2, figsize=(15, 12)) for i, (nome_cenario, (x_data, y_data)) in enumerate(cenarios.items()): # Rational Quadratic com alpha otimizado kernel_rq = RationalQuadratic(length_scale=1.0, alpha=1.0) gpr_rq = GaussianProcessRegressor(kernel=kernel_rq, random_state=42) gpr_rq.fit(x_data, y_data) # RBF para comparação kernel_rbf = RBF(length_scale=1.0) gpr_rbf = GaussianProcessRegressor(kernel=kernel_rbf, random_state=42) gpr_rbf.fit(x_data, y_data) # Previsões if nome_cenario == 'Dados Temperatura': x_pred = np.linspace(0, 365, 400).reshape(-1, 1) else: x_pred = np.linspace(x_data.min(), x_data.max(), 300).reshape(-1, 1) y_pred_rq, sigma_rq = gpr_rq.predict(x_pred, return_std=True) y_pred_rbf, sigma_rbf = gpr_rbf.predict(x_pred, return_std=True) # Plot Rational Quadratic axes[i, 0].plot(x_data, y_data, 'ro', alpha=0.5, markersize=2, label='Dados') axes[i, 0].plot(x_pred, y_pred_rq, 'b-', linewidth=2, label='Previsão RQ') axes[i, 0].fill_between(x_pred.ravel(), y_pred_rq - 1.96*sigma_rq, y_pred_rq + 1.96*sigma_rq, alpha=0.2, label='Incerteza') axes[i, 0].set_title(f'Rational Quadratic - {nome_cenario}\n' f'Kernel: {gpr_rq.kernel_}\n' f'LL: {gpr_rq.log_marginal_likelihood():.2f}', fontsize=10) axes[i, 0].grid(True, alpha=0.3) axes[i, 0].legend(fontsize=8) # Plot RBF axes[i, 1].plot(x_data, y_data, 'ro', alpha=0.5, markersize=2, label='Dados') axes[i, 1].plot(x_pred, y_pred_rbf, 'g-', linewidth=2, label='Previsão RBF') axes[i, 1].fill_between(x_pred.ravel(), y_pred_rbf - 1.96*sigma_rbf, y_pred_rbf + 1.96*sigma_rbf, alpha=0.2, label='Incerteza') axes[i, 1].set_title(f'RBF - {nome_cenario}\n' f'Kernel: {gpr_rbf.kernel_}\n' f'LL: {gpr_rbf.log_marginal_likelihood():.2f}', fontsize=10) axes[i, 1].grid(True, alpha=0.3) axes[i, 1].legend(fontsize=8) # Armazenar resultados resultados_comparacao[nome_cenario] = { 'RQ_LL': gpr_rq.log_marginal_likelihood(), 'RBF_LL': gpr_rbf.log_marginal_likelihood(), 'RQ_kernel': str(gpr_rq.kernel_), 'RBF_kernel': str(gpr_rbf.kernel_) } plt.tight_layout() plt.show() # Análise dos resultados print("=== ANÁLISE DOS RESULTADOS (Log-Likelihood) ===\n") for cenario, resultados in resultados_comparacao.items(): rq_ll = resultados['RQ_LL'] rbf_ll = resultados['RBF_LL'] diferenca = rq_ll - rbf_ll melhor = "Rational Quadratic" if diferenca > 0 else "RBF" print(f"{cenario}:") print(f" Rational Quadratic: {rq_ll:8.2f}") print(f" RBF: {rbf_ll:8.2f}") print(f" Diferença: {diferenca:8.2f} → {melhor} é melhor") print(f" Kernel RQ otimizado: {resultados['RQ_kernel']}") print() # Visualização das funções de covariância para diferentes alphas print("=== FUNÇÕES DE COVARIÂNCIA PARA DIFERENTES VALORES DE α ===\n") plt.figure(figsize=(15, 10)) # Plot das funções de covariância plt.subplot(2, 2, 1) distances = np.linspace(0, 5, 100) for alpha in [0.1, 0.5, 1.0, 5.0, 50.0]: kernel = RationalQuadratic(length_scale=1.0, alpha=alpha) K = kernel(np.array([[0]]), distances.reshape(-1, 1)) plt.plot(distances, K, label=f'α = {alpha}', linewidth=2) plt.title('Funções de Covariância - Rational Quadratic') plt.xlabel('Distância') plt.ylabel('Covariância') plt.legend() plt.grid(True, alpha=0.3) # Comparação com RBF plt.subplot(2, 2, 2) kernel_rbf = RBF(length_scale=1.0) K_rbf = kernel_rbf(np.array([[0]]), distances.reshape(-1, 1)) kernel_rq_small_alpha = RationalQuadratic(length_scale=1.0, alpha=0.1) K_rq_small = kernel_rq_small_alpha(np.array([[0]]), distances.reshape(-1, 1)) kernel_rq_large_alpha = RationalQuadratic(length_scale=1.0, alpha=50.0) K_rq_large = kernel_rq_large_alpha(np.array([[0]]), distances.reshape(-1, 1)) plt.plot(distances, K_rbf, 'k-', linewidth=3, label='RBF', alpha=0.8) plt.plot(distances, K_rq_small, 'r--', linewidth=2, label='RQ α=0.1 (≈ RBF)') plt.plot(distances, K_rq_large, 'b:', linewidth=2, label='RQ α=50.0 (multiescala)') plt.title('Comparação: RQ vs RBF') plt.xlabel('Distância') plt.ylabel('Covariância') plt.legend() plt.grid(True, alpha=0.3) # Guia de seleção de alpha plt.subplot(2, 2, 3) casos_uso_alpha = { 'α < 0.5': 'Comportamento próximo do RBF\nEscala única dominante', '0.5 ≤ α ≤ 2': 'Balanceado\nMúltiplas escalas moderadas', 'α > 2': 'Fortemente multiescala\nMuitas escalas diferentes', 'α → ∞': 'Mistura infinita de RBFs\nMáxima flexibilidade multiescala' } y_pos = range(len(casos_uso_alpha)) valores_representativos = [0.1, 1.0, 5.0, 50.0] plt.barh(y_pos, valores_representativos, color='lightcoral', alpha=0.7) plt.yticks(y_pos, casos_uso_alpha.keys()) plt.xlabel('Valor de α Recomendado') plt.title('Guia de Seleção do Parâmetro α') for i, (key, desc) in enumerate(casos_uso_alpha.items()): plt.text(1, i, desc, va='center', ha='left', fontsize=9) # Aplicações do mundo real plt.subplot(2, 2, 4) aplicacoes = [ ('Dados Climáticos', 'Variações diárias/sazonais/anuais', 'α = 1.0-5.0'), ('Dados Financeiros', 'Volatilidade em múltiplos horizontes', 'α = 0.5-2.0'), ('Processos Físicos', 'Dinâmica em diferentes escalas temporais', 'α = 1.0-3.0'), ('Dados Ambientais', 'Poluição, temperatura, pressão', 'α = 2.0-10.0'), ('Imagens/Sinais', 'Texturas e padrões em diferentes resoluções', 'α = 5.0-20.0') ] y_pos_app = range(len(aplicacoes)) app_names = [app[0] for app in aplicacoes] app_alpha = [float(app[2].split('=')[1].split('-')[0]) for app in aplicacoes] plt.barh(y_pos_app, app_alpha, color='lightseagreen', alpha=0.7) plt.yticks(y_pos_app, app_names) plt.xlabel('α Típico') plt.title('Aplicações do Mundo Real') for i, (nome, desc, alpha_range) in enumerate(aplicacoes): plt.text(1, i, f'{desc}\n{alpha_range}', va='center', ha='left', fontsize=8) plt.tight_layout() plt.show() # Resumo prático print("=== RESUMO PRÁTICO: QUANDO USAR RATIONAL QUADRATIC ===\n") vantagens_rq = [ "• Captura padrões em múltiplas escalas simultaneamente", "• Mais flexível que RBF para dados complexos do mundo real", "• Parâmetro α fornece controle intuitivo sobre a mistura de escalas", "• Aproxima-se do RBF quando α → 0 (comportamento conhecido)", "• Excelente para fenômenos com variações em diferentes horizontes temporais" ] casos_uso_especificos = [ "Dados climáticos e meteorológicos", "Séries temporais financeiras e econômicas", "Processos físicos com dinâmica multiescala", "Dados ambientais e ecológicos", "Qualquer dado onde você suspeita de padrões em diferentes níveis de zoom" ] print("VANTAGENS PRINCIPAIS:") for vantagem in vantagens_rq: print(vantagem) print("\nCASOS DE USO RECOMENDADOS:") for caso in casos_uso_especificos: print(f" • {caso}") print("\nCONFIGURAÇÃO INICIAL RECOMENDADA:") print(" • Comece com α = 1.0 como ponto de partida") print(" • Use os mesmos bounds para length_scale que usaria no RBF") print(" • Deixe a otimização ajustar α baseando-se nos dados") |
Os detalhes que fazem diferença
O parâmetro α no núcleo quadrático racional controla essencialmente quantas escalas diferentes o kernel consegue capturar simultaneamente. Valores pequenos de α (próximos de 0) fazem o kernel se comportar como um RBF tradicional com uma única escala dominante. Valores grandes de α criam uma mistura mais rica de escalas, permitindo que o modelo capture tanto variações locais quanto padrões de longo alcance. Contudo, α muito grande pode levar a overfitting se não houver dados suficientes para suportar todas as escalas. Uma propriedade matemática elegante é que o Rational Quadratic pode ser visto como uma mistura infinita de kernels RBF com distribuição Gamma nos length_scales.
- α → 0: Comporta-se como RBF com length_scale único
- α = 1.0: Ponto de partida balanceado recomendado
- α → ∞: Mistura infinita de RBFs com diferentes length_scales
- Length_scale: Controla a escala média, similar ao RBF
- Interpretação: α controla a “largura” da distribuição de length_scales na mistura
Perguntas que os iniciantes fazem
Você deve estar se perguntando: “Quando devo usar Rational Quadratic em vez de RBF?” Use Rational Quadratic quando suspeitar que seus dados têm padrões operando em diferentes escalas – por exemplo, variações diárias, sazonais e de longo prazo em dados climáticos. Uma confusão comum é pensar que Rational Quadratic é sempre melhor que RBF – na verdade, para dados com uma escala dominante clara, RBF pode ser mais eficiente e menos propenso a overfitting. Outra dúvida frequente: “Como escolher α inicial?” Comece com α = 1.0, que oferece um bom balance, e deixe a otimização ajustar a partir daí.
Para onde ir agora?
Experimente o núcleo quadrático racional em seus dados que exibam variações em múltiplas escalas temporais ou espaciais. Compare sistematicamente com RBF usando log-verossimilhança marginal. Preste atenção a como diferentes valores de α afetam a capacidade do modelo de capturar tanto detalhes locais quanto tendências globais. O momento “aha!” acontece quando você percebe que o Rational Quadratic consegue modelar naturalmente a complexidade multiescala presente em muitos fenômenos do mundo real, sem exigir que você escolha antecipadamente qual escala é mais importante.
Assuntos relacionados
Para aprofundar seu entendimento, estude:
- Misturas de kernels: fundamentos teóricos da combinação de múltiplos kernels
- Processos de escala: fenômenos que exibem comportamento similar em diferentes escalas
- Análise de Fourier: decomposição de sinais em componentes de frequência
- Wavelets: outra abordagem para análise multiescala
- Processos gaussianos não estacionários: generalizações para covariância variável