Какие проблемы возникают, если batch normalization обновляет статистику во время валидации?
Что проверяют
Понимаете ли вы, что batch normalization ведёт себя по-разному при обучении и на инференсе, и знаете ли, как это переключается во фреймворке. Типичное продолжение: что будет, если забыть model.eval(), и как BN ведёт себя на батче из одного примера.
Как отвечать
Сначала механика. В режиме обучения слой нормализует активации по статистике текущего батча и заодно обновляет скользящие running_mean и running_var (в PyTorch с momentum=0.1 по умолчанию). В режиме инференса он берёт накопленные значения, и выход для примера не зависит от соседей по батчу.
Если на валидации слой остаётся в режиме обучения, проблем сразу несколько:
- утечка валидации в модель: скользящие статистики сдвигаются к распределению валидационных данных. Веса не меняются, но состояние модели меняется, и сохранённый чекпойнт уже «видел» валидацию. На тесте модель ведёт себя иначе, чем показала метрика;
- зависимость от батча: предсказание для примера меняется в зависимости от того, с кем он попал в батч. Метрика плывёт при смене
batch_size, последний неполный батч даёт шумную статистику, а если в канале всего одно значение, PyTorch в режиме обучения падает с ошибкой; - невоспроизводимость: две валидации подряд дают разные цифры, потому что после первой статистики успели сдвинуться.
Как правильно: в PyTorch model.eval() перед валидацией и model.train() после, плюс torch.no_grad() или torch.inference_mode(), чтобы не строить граф градиентов. В Keras режим задаёт аргумент training при вызове слоя, а evaluate() и predict() передают training=False сами. При fine-tuning в Keras слой BN с trainable = False работает в режиме инференса.
Где спотыкаются
Путают torch.no_grad() с model.eval(). Первый отключает градиенты, но BN и dropout остаются в режиме обучения, нужны оба вызова.
Говорят, что модель «переобучается на валидации». Градиентного шага нет, сдвигаются только буферы статистик, и точность формулировки здесь важна.
Не знают, где пересчёт статистик полезен. При сдвиге домена его делают осознанно (AdaBN, test-time adaptation), а для маленьких батчей берут GroupNorm или LayerNorm, которые от батча не зависят.