Otimização do Parâmetro de Temperatura Softmax: Teoria e Implementação com PyTorch 2.3

No desenvolvimento de modelos de deep learning, frequentemente focamos na arquitetura da rede ou na escolha da função de perda, negligencinado parâmetros sutis que controlam a distribuição das saídas. O parâmetro de Temperatura (T) no Softmax é um desses componentes críticos. Ele atua como um regulador da "confiança" do modelo, impactando diretamente áreas como destilação de conhecimento, calibração de incerteza e robustez adversarial.

1. Fundamentos Matemáticos e Intuição

A função Softmax tradicional converte um vetor de pontuações brutas (logits) em uma distribuição de probabilidade. A inclusão da temperatura modifica a fórmula da seguinte forma:

softmax(z_i, T) = exp(z_i / T) / Σ exp(z_j / T)

O impacto de T na distribuição é determinante:

  • T < 1 (Baixa temperatura): Aumenta o contraste entre as probabilidades. A maior pontuação torna-se muito próxima de 1, resultando em uma distribuição "aguda" (similar ao argmax).
  • T = 1: Softmax padrão.
  • T > 1 (Alta temperatura): Suaviza a distribuição. As probabilidades tornam-se mais uniformes, permitindo que o modelo "enxergue" melhor as relações entre classes secundárias.

Viusalização Prática com PyTorch 2.3

O código abaixo demonstra como diferentes valores de T alteram a percepção do modelo sobre os mesmos logits:

import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt

def analisar_temperaturas(logits_entrada, lista_t):
    resultados = {}
    for t in lista_t:
        probabilidades = F.softmax(logits_entrada / t, dim=-1)
        resultados[t] = probabilidades
    return resultados

# Exemplo com 3 classes
logits = torch.tensor([1.5, 3.2, 4.8])
temps = [0.2, 1.0, 5.0]

distribuicoes = analisar_temperaturas(logits, temps)

for t, prob in distribuicoes.items():
    print(f"Temperatura {t}: {prob.numpy()}")

2. Aplicação em Destilação de Conhecimento (Knowledge Distillation)

Na destilação de conhecimento, o objetivo é transferir o aprendizado de um modelo complexo (professor) para um modelo leve (estudante). Usar uma temperatura elevada (geralmente T entre 2 e 8) é essencial para revelar as "probabilidades suaves" que contêm informações sobre a similaridade entre classes.

def loss_destilacao(saida_estudante, saida_professor, alvos_reais, temp, alpha=0.3):
    # Perda suave (KL Divergence)
    p_suave_professor = F.softmax(saida_professor / temp, dim=-1)
    log_p_suave_estudante = F.log_softmax(saida_estudante / temp, dim=-1)
    
    perda_kd = F.kl_div(log_p_suave_estudante, p_suave_professor, reduction='batchmean') * (temp ** 2)
    
    # Perda padrão (Cross Entropy)
    perda_ce = F.cross_entropy(saida_estudante, alvos_reais)
    
    return alpha * perda_kd + (1 - alpha) * perda_ce

3. Calibração de Modelos via Temperature Scaling

Redes neurais modernas tendem a ser "superconfiantes", atribuindo probabilidades altas mesmo a predições incorretas. O Temperature Scaling é uma técnica de pós-processamento que ajusta T em um conjunto de validação para alinhar a confiança do modelo com a precisão real (reduzindo o Expected Calibration Error - ECE).

Implementação de um Wrapper de Calibração

class ModeloCalibrado(torch.nn.Module):
    def __init__(self, modelo_base):
        super().__init__()
        self.modelo = modelo_base
        # O parâmetro de temperatura é treinado separadamente
        self.temperatura = torch.nn.Parameter(torch.ones(1) * 1.3)

    def forward(self, x):
        logits = self.modelo(x)
        return self.ajustar_logits(logits)

    def ajustar_logits(self, logits):
        # Garante que a temperatura seja sempre positiva
        temp_ajustada = self.temperatura.clamp(min=1e-4)
        return logits / temp_ajustada

def otimizar_temperatura(modelo, val_loader):
    # Otimização apenas do parâmetro 'temperatura' usando LBFGS
    param_temp = [modelo.temperatura]
    otimizador = torch.optim.LBFGS(param_temp, lr=0.01, max_iter=50)
    
    def closure():
        otimizador.zero_grad()
        perda_total = 0
        for imagens, rotulos in val_loader:
            previsoes = modelo(imagens)
            perda = F.cross_entropy(previsoes, rotulos)
            perda.backward()
            perda_total += perda
        return perda_total

    otimizador.step(closure)

4. Robustez em Treinamento Adversarial

No contexto de ataques adversariais, a temperatura pode ser usada para suavizar as superfícies de decisão. Ao gerar exemplos adversariais (como via PGD - Projected Gradient Descent), o ajuste de T pode tornar o processo de otimização mais estável, dificultando que o ataque encontre gradientes exploráveis.

def pgd_com_temperatura(modelo, dados, alvo, epsilon=0.03, alpha=0.01, passos=15, temp=1.5):
    dados_adv = dados.clone().detach().requires_grad_(True)
    
    for _ in range(passos):
        saida = modelo(dados_adv)
        # Aplicar temperatura durante a geração do ataque
        perda = F.cross_entropy(saida / temp, alvo)
        perda.backward()
        
        with torch.no_grad():
            gradiente = dados_adv.grad.sign()
            dados_adv = dados_adv + alpha * gradiente
            eta = torch.clamp(dados_adv - dados, min=-epsilon, max=epsilon)
            dados_adv = torch.clamp(dados + eta, min=0, max=1).detach_()
            dados_adv.requires_grad_(True)
            
    return dados_adv

Estratégias de Ajuste

Para obter os melhores resultados, considere os seguintes pontos na escolha de T:

  • Incerteza de Dados: Em datasets com muito ruído, temperaturas mais altas (T > 1.5) costumam prevenir o overfitting às etiquetas ruidosas.
  • Complexidade da Classe: Se as classes forem muito parecidas visualmente, aumente a temperatura na destilação para capturar as nuances compartilhadas.
  • Estágio do Treinamento: É possível utilizar um scheduler de temperatura, começando com T alto para exploração e reduzindo para T=1 no final para refinamento.

Tags: Pytorch deep learning machine learning computer vision neural networks

Publicado em 7-18 22:08