Как устроена реализация дропаута в PyTorch
Что проверяют
Знаете ли вы, что делает слой на обучении и на инференсе, и в частности про масштабирование, из-за которого после eval() ничего не нужно пересчитывать. Следом часто просят написать dropout руками или спрашивают, почему модель выдаёт разные ответы на одном входе.
Как отвечать
В PyTorch реализован так называемый inverted dropout:
- на обучении: для каждого элемента тензора независимо сэмплируется маска Бернулли, элемент обнуляется с вероятностью
p, а выжившие умножаются на1 / (1 - p). Математическое ожидание активаций сохраняется; - на инференсе: после
model.eval()слой возвращает вход как есть. Масштабирование уже сделано на обучении, веса не трогают; - обратный проход: градиент идёт только через выжившие элементы с тем же множителем, маска сохраняется с прямого прохода;
- параметры:
nn.Dropout(p=0.5, inplace=False), гдеpэто вероятность обнулить элемент, а не точная доля. Обнулится примерноpэлементов, но не ровно.
Ручная реализация: mask = (torch.rand_like(x) > p).float(), затем x * mask / (1 - p), и только при self.training.
Варианты: nn.Dropout1d и nn.Dropout2d обнуляют каналы целиком, что осмысленнее для свёрток с сильно коррелированными соседями, nn.AlphaDropout сохраняет статистики в сетях на SELU. В трансформерах dropout стоит после attention и в FFN, у F.scaled_dot_product_attention есть dropout_p, который на инференсе нужно самому передавать нулём. Типичные p: 0.1 в трансформерах, от 0.2 до 0.5 в полносвязных слоях.
Monte Carlo dropout: слой оставляют включённым на инференсе и по разбросу нескольких прогонов оценивают неопределённость.
Где спотыкаются
Вызывают F.dropout(x, 0.5) в forward без training=self.training. У функциональной версии по умолчанию training=True, и dropout работает даже после eval().
Думают, что model.eval() отключает градиенты. Он переключает только режим слоёв вроде dropout и BatchNorm, для инференса без графа нужен torch.no_grad() или torch.inference_mode().
Говорят, что на инференсе веса умножают на 1 - p. Так было в исходной статье, в PyTorch масштабирование перенесено на обучение.