Можно ли настраивать только Batch Normalization, не трогая архитектуру нейронной сети?

Ответ

Да, это распространенная практика тонкой настройки (fine-tuning) или стабилизации обучения. Параметры Batch Normalization (BN) слоев можно и часто нужно настраивать независимо от весов сверточных или полносвязных слоев.

Какие параметры BN можно настраивать и зачем:

Параметр (PyTorch) Назначение Типичные значения / Настройка
momentum Определяет, как быстро обновляются скользящие средние (running mean/var). По умолчанию ~0.1. Увеличение (например, до 0.3) делает статистики более устойчивыми к мини-батчам, уменьшение — более адаптивными.
eps (epsilon) Малое число для численной стабильности при делении на дисперсию. По умолчанию 1e-5. Увеличение (например, до 1e-4) может помочь при очень малых дисперсиях.
affine Включает/выключает обучаемые параметры масштаба (weight - γ) и сдвига (bias - β). affine=True (по умолчанию). При affine=False слой только нормализует, не сдвигая и не масштабируя.
track_running_stats Включает/выключает расчет скользящих статистик. Обычно True. При False использует статистики текущего батча даже в режиме оценки (eval()).

Практический пример настройки в PyTorch:

import torch.nn as nn

# Создание сверточной сети с настроенным BatchNorm
model = nn.Sequential(
    nn.Conv2d(3, 64, kernel_size=3, padding=1),
    # Настраиваемый BN: меньший momentum для быстрой адаптации, отключен affine
    nn.BatchNorm2d(64, momentum=0.01, eps=1e-4, affine=False),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(64, 128, kernel_size=3, padding=1),
    # Другой BN: стандартный momentum, но увеличенный eps
    nn.BatchNorm2d(128, eps=1e-3),
    nn.ReLU(),
    nn.AdaptiveAvgPool2d(1),
    nn.Flatten(),
    nn.Linear(128, 10)
)

# При fine-tuning часто "замораживают" все слои, кроме BN
for name, param in model.named_parameters():
    if 'bn' not in name and 'bias' not in name:  # Замораживаем веса conv/fc
        param.requires_grad = False
    else:
        param.requires_grad = True  # Оставляем обучаемыми BN и bias

Критически важный нюанс: Эффективность BN сильно зависит от размера мини-батча. При очень маленьких батчах (например, < 16) статистики становятся шумными, и BN может работать плохо. В таких случаях стоит рассмотреть альтернативы: Layer Normalization (для RNN/Transformers), Group Normalization или Instance Normalization (для стилей).

Ответ 18+ 🔞

А, ну это ж классика, ёпта! Ты про то, как эти ваши батчнормы настраивать, да? Ну слушай, история древняя, как мир, но до сих пор у всех мозги ебёт.

Вот представь: ты собрал свою нейронку, красавицу, а она учится как мартышлюшка — то в одну сторону дёрнется, то в другую. И ты такой: «Какого хуя?» А всё потому, что эти чёртовы BatchNorm слои — они, блядь, особенные. С ними нельзя как со всеми.

Так какие же у них кнопки есть, чтобы крутить?

Смотри, табличку для наглядности, а то слов много:

Параметр (в PyTorch) Зачем он нужен, если по-простому Где обычно тыкают
momentum Это как инерция. Чем больше, тем упрямее статистика (среднее и дисперсия) и меньше дёргается от каждого нового батча. По дефолту 0.1. Если данные — пиздец какой шум, ставь повыше, типа 0.3. Если хочешь, чтобы быстрее адаптировался — понижай.
eps Маленькая циферка, чтобы не делить на ноль, если дисперсия в батче совсем хуйня. Обычно 1e-5. Если модель не сходится и ты подозреваешь, что всё накрылось медным тазом из-за численной нестабильности — пробуй 1e-4.
affine Включает два обучаемых параметра: масштаб (γ) и сдвиг (β). Без них слой просто выравнивает данные, а с ними — ещё и умножает/прибавляет, что нужно. По умолчанию True. Если выключить (False), то слой становится тупым нормализатором без своих «хотелок».
track_running_stats Самая хитрая жопа. Если True — в режиме оценки (eval()) использует накопленные за обучение статистики. Если False — даже в eval() считает по текущему батчу, что иногда нужно, но редко. Почти всегда оставляют True. Меняют только в каких-то экзотических случаях, когда каждый батч — это отдельная вселенная.

Ну и как это в коде выглядит, спросишь ты? Держи, чувак:

import torch.nn as nn

# Делаем модель, где BN настроены по-разному
model = nn.Sequential(
    nn.Conv2d(3, 64, kernel_size=3, padding=1),
    # Вот этот BN — быстрый и дерзкий. momentum маленький, affine выключен.
    nn.BatchNorm2d(64, momentum=0.01, eps=1e-4, affine=False),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(64, 128, kernel_size=3, padding=1),
    # А этот — консервативный увалень. eps увеличен для спокойствия.
    nn.BatchNorm2d(128, eps=1e-3),
    nn.ReLU(),
    nn.AdaptiveAvgPool2d(1),
    nn.Flatten(),
    nn.Linear(128, 10)
)

# Классический финт ушами при дообучении (fine-tuning):
# Веса свёрток и линейных слоёв замораживаем, а BN — нет!
for name, param in model.named_parameters():
    if 'bn' not in name and 'bias' not in name:  # Заморозка!
        param.requires_grad = False
    else:  # BN и bias оставляем свободными!
        param.requires_grad = True

И вот тут, блядь, главный подвох, о котором все забывают, а потом охуевают:
Вся магия BatchNorm напрямую зависит от размера батча. Если ты его сделаешь маленьким (меньше 16, а то и 8), то статистики (то самое среднее и дисперсия) будут считаться по трём с половиной примерам. Получится не нормализация, а пиздец какой шум, доверия ебать ноль. Модель будет скакать как угорелая.

Что делать, если батч маленький?
Выкидывай нахуй BatchNorm и смотри в сторону других нормализаций:

  • LayerNorm — для последовательностей (RNN, Transformers) — огонь.
  • GroupNorm или InstanceNorm — для задач со стилями или когда батч совсем микроскопический.

Короче, BatchNorm — инструмент мощный, но с характером. Подходи с умом, не тыкай параметры как попало, и будет тебе счастье. А не подойдёшь — будет тебе хиросима, а не обучение.