Přeskočit na obsah
_CORE
AI & Agentic Systems Core Informační Systémy Cloud & Platform Engineering Data Platforma & Integrace Security & Compliance QA, Testing & Observability IoT, Automatizace & Robotika Mobile & Digital Banky & Finance Pojišťovnictví Veřejná správa Obrana & Bezpečnost Zdravotnictví Energetika & Utility Telco & Média Průmysl & Výroba Logistika & E-commerce Retail & Loyalty
Reference Technologie Blog Knowledge Base O nás Spolupráce Kariéra
Pojďme to probrat

Regularizace — Dropout, L1/L2 a boj s overfittingem

01. 01. 2024 4 min čtení intermediate

Overfitting je jeden z nejčastějších problémů při trénování neuronových sítí. Když se model naučí trénovací data příliš dokonale, ztrácí schopnost generalizace na nová data. Regularizační techniky jako Dropout, L1 a L2 regularizace jsou osvědčené nástroje pro řešení tohoto problému.

Co je to overfitting a proč škodí

Overfitting je jedním z nejčastějších problémů v machine learningu. Model se naučí tréninková data příliš dobře – zapamatuje si každý detail včetně náhodného šumu, ale pak selhává na nových datech. Představte si studenta, který se nazpaměť naučí učebnici, ale nerozumí principům a u zkoušky propadne.

Regularizace je soubor technik, které tomuto problému předcházejí. Přidávají do tréninku “tření”, které brání modelu v přílišné specializaci na tréninková data. Podívejme se na tři nejdůležitější techniky.

Dropout – náhodné “vypínání” neuronů

Dropout je elegantní technika, která během tréninku náhodně deaktivuje část neuronů. Je to jako kdyby jste při učení náhodně zavírali oči nebo si zacpávali uši – nutíte mozek spoléhat na různé kombinace vstupů.

Jak dropout funguje

Během forward pass náhodně nastavíme vybrané neurony na nulu s pravděpodobností p (typicky 0,2-0,5). Zbývající neurony vynásobíme faktorem 1/(1-p), aby se zachovala celková síla signálu.

import torch
import torch.nn as nn

class SimpleNet(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, dropout_rate=0.3):
        super().__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.fc3 = nn.Linear(hidden_size, output_size)
        self.dropout = nn.Dropout(dropout_rate)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.relu(self.fc1(x))
        x = self.dropout(x)  # Dropout po aktivaci
        x = self.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)
        return x

# Použití
model = SimpleNet(784, 256, 10, dropout_rate=0.4)
model.train()  # Důležité: dropout funguje jen v train módu

Klíčové je pamatovat, že dropout používáme pouze během tréninku. Při inferencí voláme model.eval(), čímž dropout deaktivujeme.

L1 a L2 regularizace – trestání velkých vah

L1 a L2 regularizace přidávají do loss funkce penalty za velikost vah modelu. Princip je jednoduchý: velké váhy často vedou k overfittingu, takže je “potrestáme”.

L2 regularizace (Weight Decay)

L2 regularizace přidává do loss funkce člen λ∑w², kde λ je regularizační koeficient. Trestá kvadraticky velké váhy, ale nezahání je úplně k nule.

import torch.optim as optim

# L2 regularizace přes weight_decay v optimizeru
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)

# Nebo manuálně v loss funkci
def l2_regularization(model, lambda_reg=1e-4):
    l2_reg = 0
    for param in model.parameters():
        l2_reg += torch.norm(param, p=2) ** 2
    return lambda_reg * l2_reg

# Použití v tréninku
criterion = nn.CrossEntropyLoss()
output = model(inputs)
loss = criterion(output, targets) + l2_regularization(model)

L1 regularizace – vytváření sparse modelů

L1 regularizace používá λ∑|w| a má zajímavou vlastnost – dokáže vynulovat méně důležité váhy, čímž vytváří sparse (řídké) modely.

def l1_regularization(model, lambda_reg=1e-4):
    l1_reg = 0
    for param in model.parameters():
        l1_reg += torch.norm(param, p=1)
    return lambda_reg * l1_reg

# Kombinovaná L1 + L2 regularizace (Elastic Net)
def elastic_net_regularization(model, l1_lambda=1e-4, l2_lambda=1e-4):
    l1_reg = sum(torch.norm(p, p=1) for p in model.parameters())
    l2_reg = sum(torch.norm(p, p=2) ** 2 for p in model.parameters())
    return l1_lambda * l1_reg + l2_lambda * l2_reg

Praktické tipy pro použití regularizace

Ladění hyperparametrů

Síla regularizace se musí pečlivě vyladit. Příliš slabá nepomůže, příliš silná model “udusí” a nebude se učit.

  • Dropout rate: Začněte s 0,2-0,3 pro skryté vrstvy, 0,1-0,2 pro vstupní vrstvu
  • Weight decay: Typicky 1e-4 až 1e-6, závisí na velikosti datasetu
  • L1 regularizace: Obvykle slabší než L2, začněte s 1e-5

Kombinování technik

class RegularizedNet(nn.Module):
    def __init__(self, input_size, hidden_sizes, output_size, dropout_rates):
        super().__init__()
        self.layers = nn.ModuleList()
        self.dropouts = nn.ModuleList()

        # Vstupní vrstva
        prev_size = input_size
        for i, (hidden_size, dropout_rate) in enumerate(zip(hidden_sizes, dropout_rates)):
            self.layers.append(nn.Linear(prev_size, hidden_size))
            self.dropouts.append(nn.Dropout(dropout_rate))
            prev_size = hidden_size

        # Výstupní vrstva (bez dropout)
        self.output_layer = nn.Linear(prev_size, output_size)
        self.relu = nn.ReLU()

    def forward(self, x):
        for layer, dropout in zip(self.layers, self.dropouts):
            x = self.relu(layer(x))
            x = dropout(x)
        return self.output_layer(x)

# Model s postupně se snižujícím dropout
model = RegularizedNet(
    input_size=784,
    hidden_sizes=[512, 256, 128],
    output_size=10,
    dropout_rates=[0.2, 0.3, 0.4]  # Vyšší dropout v hlubších vrstvách
)

# Optimizer s weight decay
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)

Monitoring účinnosti

Sledujte rozdíl mezi training a validation loss. Regularizace funguje, když se tento rozdíl zmenšuje, aniž by se validation loss výrazně zhoršovala.

# Sledování během tréninku
train_losses = []
val_losses = []

for epoch in range(num_epochs):
    # Trénink
    model.train()
    train_loss = 0
    for batch in train_loader:
        # ... standardní tréninkový cyklus
        pass

    # Validace
    model.eval()
    val_loss = 0
    with torch.no_grad():
        for batch in val_loader:
            # ... validace bez gradientů
            pass

    train_losses.append(train_loss)
    val_losses.append(val_loss)

    print(f"Epoch {epoch}: Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}")

Shrnutí

Regularizace je nezbytná pro trénink robustních modelů. Dropout náhodně deaktivuje neurony a nutí model učit se redundantní reprezentace. L1/L2 regularizace trestají velké váhy a podporují jednodušší modely. Klíčem k úspěchu je správné vyladění hyperparametrů a kombinování technik. Pamatujte: lepší je mírně underfit model, který generalizuje, než perfektně overfit model, který selhává v praxi.

dropoutregularizaceoverfitting