Dropout в нейронных сетях.
Сегодня поговорим об одном методе борьбы с переобучением нейронных сетей. Статья [1], где этот метод был впервые описан вышла в 2012 году, и рассказывала о Dropout в применении к обычным нейронным сетям (т.е. состоящим из полносвязных слоёв). Рецепт выглядел достаточно просто: давайте на каждом шаге тренировки случайным образом выключать часть нейронов, однако, результаты получались очень не плохие.
В дальнейшем, методика развивалась, как в сторону уточнений вида: “какой процент нейронов отключать на каждом этапе тренировки?” или “какие именно нейроны выключать с большей вероятностью?”. Так и в сторону применения данной методики не только к полносвязным слоям, но и к свёрточным или рекуррентным сетям.
Dropout в обычных нейронных сетях
Начнем с начала, т.е. со статьи [1], где Dropout слой был предложен.
Исходный вариант
Рассмотрим сеть, состоящую из полносвязных слоёв. Формально каждый такой слой из $M$ нейронов представляет собой преобразование:
\[y = \sigma(W \cdot x)\]здесь $x \in \mathbb R^{N}$ - вектор поступающий на вход слоя, $y \in \mathbb R^{M}$ - вектор, который передаётся на вход следующего слоя, $\sigma$ - нелинейная функция, а $W \in \mathbb R^{M \times N}$ матрица весов слоя (которые мы тренируем).
Мы тут “потеряли” bias. Будем считать, что в $x$ добавили координату с $1$ и bias “уехал” в матрицу $W$. Это ни на что особо не влияет, но позволяет сделать формулы покомпактнее
В работе [1], в которой собственно и был впервые описан dropout, предлагалось между двумя слоями (X) и (Y) вставить еще один Dropout слой, который бы, в процессе тренировки, с вероятностью $1 - p$ выключал нейроны слоя (X).
Формально это можно записать следующим образом: рассмотрим вектор $d \in \{0, 1\}^{M}$, каждая координата которого подчинена распределению Бернулли с параметром $p$, т. е. для каждого $i = 1, 2, …, M$ координата $d_i = 1$ с вероятностью $p$, или $d_i = 0$ с вероятностью $(1 - p)$. Т. е. i-ый нейрон с вероятностью $p$ остаётся или с вероятностью $(1 - p)$ отключается.
Тогда добавление Dropout в тренировку выглядит как:
\[y = d \circ \sigma(W \cdot x)\]$\circ$ - поэлементное умножение векторов.
Когда же мы используем сеть для вывода в процессе тестирования, вместо выключения нейронов, Dropout слой домножает выход всех нейронов на $p$, получается:
\[y = p\,\sigma\left(W \cdot x \right)\]У авторов статьи [1] обоснование умножения на $p$ довольно простое (там на скрытом слое $p = 0.5$): “уменьшаем вдвое веса, чтобы компенсировать тот факт, что при тестировании включаем вдвое больше нейронов, чем на тренировке”. Можно предложить более развернутое объяснение.
Возьмем для простоты сеть с одним скрытым слоем размерности $M$ на котором применяется dropout и рассмотрим эту сеть как набор из $2^M$ сетей, каждая реализуется при своём векторе $d$, со своей вероятностью: $p^{\sum d_i}(1 - p)^{M - \sum d_i}$ (а вариантов таких M-мерных, бинарных векторов, очевидно, ровно $2^M$). И если при выводе/тестировании сети брать в качестве выхода математическое ожидание ответа по всем подсетям, получаем как раз формулу с домножением на $p$.
Если говорить об интуиции, лежащей в основе dropout, то в статье [1] предлагается два способа смотреть на эту методику. Первое рассуждение выглядит так: постоянно отключая отдельные нейроны во время тренировки, мы не даём сети перетренироваться на какие-то комбинации признаков элементов тренировочного датасета. Это, в том числе, приводит к формированию более сильных признаков верхнего уровня, потому что каждый такой признак сам по себе должен вносить вклад в конечное решение принимаемое сетью об объекте.
Dropout и Bagging
Второй вариант - это смотреть на dropout как на способ тренировать сразу ансамбль сетей, и для получения ответа использовать взвешенное голосование этого ансамбля, т. е. имеем что-то похожее на bagging.
Bagging - это когда мы берем тренировочный датасет и делаем из него несколько. Каждый новый датасет мы наполняем просто выбирая случайным образом сэмплы из исходного с возвращением (т.е. один и тот же сэмпл может попасть в дочерний датасет несколько раз). После чего на каждом из дочерних датасетов мы тренируем свою модель. А в процессе вывода прогоняем наш семпл, через все модели и усредняем их ответы.
Однако, надо отметить и важные отличия dropout от bagging-а.
Во-первых, в dropout все “подсети” используют общие веса, только включая или исключая нейроны, в отличи от bagging-а где каждая модель тренируется строго независимо от всех остальных.
Во-вторых, часть (бо́льшая) подсетей скорее всего не получит и одного шага backpropаgation, т.е. конфигурация отключенных нейронов, соответствующая большинству подсетей, никогда не реализуется при тренировке.
И, в-третьих, если мы берем сеть с одним скрытым слоем и softmax выходом выдающим вероятности отдельных классов, то dropout можно рассмотреть как апроксимацию геометрического среднего вероятностей (в отличии от bagging), получаемых от набора сетей (с выключенными отдельными нейронами), или арифметического среднего логарифмов верояностей.
Реализация
С практической точки зрения, dropout обычно реализуют как отдельный слой, у которого имеется флажок, определяющий тренируем ли мы сеть или уже тестируем. В случае тренировки, слой, просто передаёт через себе отклик от нейрона с вероятностью $p$, или, с вероятностью $(1 - p)$ этот отклик теряет, а в случае тестирования, отклик передаёт всегда, но домножает его на $p$.
Можно (и часто именно так и делают) реализовать и другой вариант. Во время тестирования, dropout слой не делает совсем ничего, т.е. просто прозорачно прокидывает сквозь себя данные, А во время тренировки, такой слой с вероятностью $p$, передаёт через себя усиленный умножением на $\frac 1 p$ отклик от нейрона и с вероятностью $(1 - p)$ этот отклик теряет. Результат тот же самый.
В [1] проверяют dropout в том числе на MNIST. MNIST датасет, который состоит из черно-белых картинок размера $28 \times 28$ с рукописными цифрами. Чтобы классифицировать их полносвязной сетью, каждую картинку разворачивают в вектор размерности $764$ и прогоняют этот вектор через скрытый слой (или несколько), на выходе используется слой с softmax для разделения картинок на 10 классов. Мы такое проделывали разбираясь с разницой полносвязной и сверточной сетей. Dropout-ом авторы выключают входные нейроны с вероятностью $0.2$ и нейроны скрытого слоя с вероятностью $0.5$. Сеть с dropout действительно делает меньше ошибок при классификации (в статье число ошибок уменьшается со 160 до 130, но там применяется не только dropout).
Интересно, что полученная таким образом сеть, как и прогнозировалось в теоретическом обосновании, лучше отбирает признаки, не завязываясь на связи между ними. Для сравнения две картинки из статьи.
Признаки с первого скрытого слоя отобранные без dropout:
Признаки с первого скрытого слоя отобранные с использованием dropout:
Dropconnect
В статье [2] обобщают Dropout - предлагают выключать при тренировке не нейроны, а связи между ними:
\[y = \sigma\left((D \circ W) \cdot x\right)\]здесь $D \in \mathbb \{0, 1\}^{M \times N}$ - матрица, каждый элемент которой равен единице с вероятностью $p$ и нулю с вероятностью $1 - p$.
Почему это обобщение dropout? Авторы предлагают следующее объяснение: большинство функций активации в нуле равны нулю (и действительно, можно рассмотреть ReLU, гиперболический тангес и пр.) поэтому в формуле для классического dropout можно внести случайный вектор $d$ под функцию активации:
\[y = \sigma\left(d \circ (W \cdot x)\right)\]и теперь заменяя вектор на матрицу - мы обобщаем классическую формулу.
Всё это очень элегантно выглядит и хорошо работает во время тренировки. Однако, возникает резонный вопрос: а что делать на этапе вывода/тестирования сети? Очевидно, что просто умножение на коэффициент, как это было в dropout, здесь не сработает. Предлагается следующая схема:
Рассмотрим $z = (D \circ W) \cdot x$ - вектор, который мы получаем перед функцией активации. Можно расписать его координаты:
\[z_i = \sum_j D_{ij} W_{ij} x_j = \sum_j (W_{ij} x_j) D_{ij}\]Получается, что $z_i$ это взвешенная сумма распределений Бернулли, и значит можно аппроксимировать её распределением Гаусса с соответствующим математическим ожиданием: $\mathbb E(z) = pWx$ и дисперсией $\mathbb D(z) = p(1 - p) (W \circ W) (v \circ v)$. Поэтому при выводе сети, мы генерируем некоторое наперёд заданное число семплов из нормального распределения: $\{z^{(k)} \in \mathcal N(\mathbb E(z), \mathbb D(z)) | k = 1,…, Q\}$ и в качестве выхода считаем среднее:
\[y = \frac 1 Q \sum_k \sigma(z^{(k)})\]Если в dropout реализация была достаточно простой - дополнительный слой либо выключающий нейроны, либо домножающий на коэффициент, с dropconnect все сильно сложнее, нужно генерировать несколько семплов, из нормального распределения и потом их усреднять, кроме того выход сети при тестировании в некотором смысле будет стохастическим, поскольку мы вынуждены будем генерировать случайные сэмплы.
Standout
В статье [3] было предложено, не просто выключать случайные нейроны, а сделать такое отключение адаптивным, подбирая во время тренировки параметры распределения на основе которого отключаются нейроны.
\[y = d' \circ \sigma(W \cdot x)\]$d’$ вектор каждая координата которого $d’_i$ получается согласно распределению Бернулли с параметром $p_i$, а вектор $p = (p_1, …, p_M) \in [0, 1]^M$ в свою очередь, получается как $p = g(\mathcal{P} \cdot x)$, при этом матрица $\mathcal{P} \in \mathbb R^{M \times N}$ подбирается в процессе тренировки, как и остальные веса сети, а функция $g(\cdot)$ - сигмоида (можно взять и какую-то другую функцию $g: \mathbb R \rightarrow [0, 1]$).
Концепция достаточно красивая, только слишком сложная в исполнении и затратная по времени тренировки (честно говоря, не вполне понятно сколько надо добавить данных в датасет, чтобы всё это сработало). Поэтому авторы статьи предложили упрощение: считать, что матрица $\mathcal{P}$ получается просто афинным преобразованием матрицы $W$:
\[\mathcal{P} = \alpha W + \beta\]а $\alpha$ и $\beta$ - гиперпараметры модели, которые регулируют “силу Dropout” и задаются до начала тренировки.
Annealed и curriculum dropout-ы
Естественным обобщением dropout кажется изменение вероятности $p$ во время тренировки. И здесь есть две строго противоположные концепции:
-
Вероятность включения нейронов надо увеличивать от итерации к итерации [4] - annealed dropout
\[p = \begin{cases} p_0 + \frac t N (1 - p_0), & t < N\\ 1, & t \ge N \end{cases}\]$N$ здесь какое-то наперёд заданное число итераций пока работает dropout, $p_0$ - начальное значение вероятности.
Интуиция такая. Мы, применяя dropout, как бы тренируем много сетей, и потом усредняем их ответы и получаем результат. Если вероятность $p$, или, что тоже самое, число включенных нейронов мало, то мы на каждой итерации тренируем совсем небольшую сеть. И вначале это хорошо. Но по мере тренировки “маленькие” подсети уже достаточно хороши, но не достаточно приёмисты, поэтому мы увеличиваем $p$ и объеденяем эти “маленькие” подсети в “большие”.
-
Вероятность включения нейронов надо уменьшать от итерации к итерации [5] - curriculum dropout
\[p = p_{\infty} + e^{-\gamma\,t}(1 - p_{\infty})\]$p_{\infty}$ - минимальная вероятность включения нейронов, которая реализуется на больших итерациях, $\gamma$ - регулирует скорость уменьшения вероятности.
Интуиция такая. Выключая нейроны в сети, мы как бы добавляем в тренировку шум. Чем больше мы выключили нейронов (т.е. чем меньше $p$), тем больше шума добавили. Поэтому начинать надо с малого зашумления и большого $p$, чтобы сети было проще, а постепенно, в процессе тренировки, снижать $p$ и увеличивать шум.
Dropout в свёрточных сетях
Свёрточные нейронные сети кардинально отличаются от полносвязных. Поэтому применение вариантов dropout, которые были описаны выше пользы не приносит. Действительно, свёрточное ядро действует сразу на пачку близких нейронов, которые сильно коррелированы между собой, отключение одного нейрона не поможет - информация будет восстановлена из его соседей.
SpatialDropout
Поэтому в [6] предлагается применять dropout к каналам целиком, выключая, либо включая все нейроны выбранного канала с вероятностью $p$. Такой способ называется SpatialDropout и если задуматься он вполне логичен. Для обычных полносвязных сетей мы либо включаем, либо выключаем какой-то признак у объекта, в случае, свёрточных слоёв мы включаем или выключаем какой-то определённый атрибут у всех пикселей.
Max-Drop layer и Stochastic Dropout
В статье [7] предлагают два варианта dropout, которые можно эфективно применять вместе:
Max-Drop layer выборочно отключает максимальные отклики. Обоснование выглядит следующим образом. Две картинки двух объектов одного класса не всегда имеют сразу все характерные признаки объектов этого класса. Например, глаза - сильный признак лица, но на каких-то изображениях, глаза могут быть чем-то закрыты, на других - лицо снято с такого ракурса, что видно только один глаз. Таким образом, выключая какую-то часть наиболее сильных откликов мы помогаем сети тренироваться на более широкий набор признаков объекта этого класса.
Чтобы выбрать какой нейрон отключить, можно действовать двумя разными способами, либо выбрать случайный канал с вероятностью $q$ и затем отключить на этом канале нейрон с максимальным откликом, либо выбрать случайный пиксель с вероятностью $q$ и занулить у него атрибут с максимальным значением. Интересно, что при одном и том же значении $q$ эти два способа отключают разный процент нейронов. Причем, для первых слоёв (близких к исходному изображению), которые обычно ещё достаточно большого геометрического размера, но имеют мало каналов, при фиксированном $q$, первый способ будет отключать меньше нейронов чем второй, а для более глубоких слоёв, с меньшими размерами, но с большим числом каналов - ситуация будет строго обратная.
Stochastic Dropout. Это расширение annealed и curriculum dropout подходов. Авторы предлагают не фиксировать вероятность выключения нейронов (в сверточных сетях - канала), а каждый раз выбирать эту вероятность из некоторого случайного распределения, например, нормального или равномерного (т.е. сначала выбираем вероятность из случайного распределения, а потом используем эту вероятность, чтобы выбирать величины из распределения Бернулли).
Сutout
Еще один вариант обобщения dropout для работы с картинками, это cutout, описанный в статье [8]. Авторы предлагают занулять на входных картинках случайно расположенные прямоугольные блоки, это похоже на dropout: в исходной статье [1] предлагалось в том числе занулять случайным образом часть атрибутов входных сэмплов. Поскольку теперь в качестве сэмплов у нас выступают изображения - зануляем у них прямоугольные куски. Обоснование похоже на Max-Drop layer: часто на изображениях часть объекта закрыта, и поэтому если во время тренировки мы не показываем сетке часть картинки это позволяет выучить более широкий набор признаков у объекта.
Надо отметить, что cutout можно рассматривать и как вариацию dropout для свёрточных сетей и как способ аугментации данных.
DropBlock
В статье [9] предлагается нечто среднее между классическим dropout, выключающем отдельные нейроны и spatial dropout, который выключает канал целиком. Будем как и в cutout выключать целые прямоугольники, но не только на входном изображении, но после любого свёрточного слоя. Определяем два параметра $S_b$ - размер блока и $\gamma$ - вероятность включения нейрона. Заполняем трёхмерный тензор $M$ каждый элемент которого получается из распределения Бернулли с параметром $\gamma$. Размер тензора $M$ совпадает с размером тензора на выходе соответствующего свёрточного слоя. Теперь для каждого нулевого элемента $M_{ijk}$ зануляем всех соседей на канале $k$ в квадрате с центром в $(i, j)$ и сторонами $S_b$. Собрав таким образом маску применяем её к выходу свёрточного слоя, дополнительно нормализуем полученный результат домножая на коэффициент: ${\rm Size}(M_{ijk}) / \sum_{ijk} M_{ijk}$.
При выводе/тестировании запускаемся уже не включая нейроны, а нормализация заменяет необходимость домножать выходы нейронов на коэфициент, как это делается в обычном dropout.
Max-pooling dropout
Обычно в свёрточных сетях применяется два типа слоёв: собственно свёрточные слои и слои pool-инга. Последнии обрабатывают не пересекающиеся блоки нейронов покрывающие каждый канал, обычно из блока отдаётся одно значение, иногда среднее (average pooling), иногда максимальное (max pooling). В статье [10] авторы предлагают добавить dropout в max pooling слои. Этот вариант dropout-а они назвали Max-pooling dropout.
Во время тренировки действуют по классической схеме, на каждом блоке pool-инга размера $S \times S$ генерируется матрица $D \in \mathbb \{0, 1\}^{S \times S}$ с элементами полученными независимо в соответствии с распределением Бернулли с параметром $p$. Значения в блоке, соответствующие нулевым элементам матрицы “выключаются”, среди оставшихся выбирается максимальный, который и передаётся на выход слоя.
Интересно здесь рассмотреть, как авторы предлагают действовать на этапе вывода/тестирования сети. Они рассматривают два варианта. Первый вариант: просто оставляем все значения в блоке, выбираем максимальный и умножаем его, как и в случае классического dropout на параметр распределения Бернулли: $p$. Они назвали такую схему scaled max-pooling. Однако, в своей работе используют второй вариант. Рассмотрим все значения попавшие в блок: $\{a_1, …, a_n\},\, n = S^2$ и упорядочим их по возрастанию. Переобозначая получим:
\[a'_1 \le a'_2 \le ... \le a'_n\]На этапе тренировки $a’_i$ попадёт на выход блока с вероятностью $p_i = p \cdot (1 - p)^{n-i}$. Действительно, чтобы на выходе оказался $i$-ый по порядку элемент, необходимо, чтобы он остался включенным и при этом все $n-i$ элементов, идущие по порядку за ним, отключились. Вероятность этого события как раз $p_i$, поэтому на этапе вывода из max pooling блока c dropout сетка отдаёт, математическое ожидание ответа, т.е. взвешенную сумму элементов с учетом вероятностей:
\[\sum_i p_i a'_i\]ResNet dropout
С появлением ResNet появились и варианты dropout для такого типа сетей. Напомним, что в ResNet свёрточные слои объеденины в блоки, и кроме того добавляются связи, которые соединяют уровни напрямую в обход этих блоков. Т.е. вход уровня $(l+1)$ можно формально записать как:
\[x^{(l+1)} = \sigma(F^{(l)}(x^{(l)}) + x^{(l)})\]здесь $F^{(l)}$ - это функция, которая описывает $l$-ый свёрточный блок.
Stochastic depth
В статье [11] авторы предлагают dropout, который они назвали Stochastic depth. Принцип простой - отключаем случайным образом ResNet блоки, оставляя только identical связи. Таким образом, мы как бы регулируем глубину сети (действительно, отключая ResNet блок мы просто “удаляем” один уровень сети). Формально, мы снова берем вектор $d \in \{0, 1\}^{L}$, только размерность его соответствует количеству слоёв в нашей нейронной сети, а ноль или единица выбирается из распределения Бернулли, независимо, с вероятностями $p_l,\,l=1,…,L$, после чего используем формулу:
\[x^{(l+1)} = {\rm ReLU}(d_l F^{(l)}(x^{(l)}) + x^{(l)})\]для $l$-го слоя. В качестве нелинейности тут используется ${\rm ReLU}$ важно отметить, что поэтому для случая $d_l = 0$ мы получим фактически:
\[x^{(l+1)} = x^{(l)}\]посколько $x^{(l)}$ сам является выходом ${\rm ReLU}$ и значит положительный.
Все это происходит во время тренировки. При тестирование/выводе сети, мы включаем все блоки, но, аналогично как в классическом dropout, домножаем на вероятности:
\[x^{(l+1)} = {\rm ReLU}(p_l F^{(l)}(x^{(l)}) + x^{(l)})\]Вероятности, здесь разные для разных уровней сети. При это для более глубоких уровней вероятность отключения увеличивается (т.е. $p_l$ уменьшается с ростом $l$):
\[p_l = 1 − \frac l L(1 - p_L),\,l=0,..., L\]Для входного слоя вероятность $p_0 = 1$, т.е. его мы не отключаем никогда, а для последнего уровня: $p_L$ - это гиперпараметр, который регулирует кол-во отключаемых слоёв. Авторы [11] предлагают использовать $p_L = 0.5$.
Swapout
В [12] авторы обобщили stochastic depth, решив, что выключать целыми блоками это слишком просто. Поэтому на каждом уровне надо формировать два трехмерных тензора из нулей и единиц (нули и единицы берутся независимо из распределений Бернулли) $\Theta_1,\,\Theta_2$ и формулу для ResNet переписать в виде:
\[x^{(l+1)} = \sigma(\Theta_1 \circ F^{(l)}(x^{(l)}) + \Theta_2 \circ x^{(l)})\]Т.е. есть четыре варианта того, что попадёт на вход нейрона следующего блока: $0$, $x^{(l)}$, $F^{(l)}(x^{(l)})$, $F^{(l)}(x^{(l)}) + x^{(l)}$.
Всё это происходит на этапе тренировки. На этапе вывода, к сожалению, каким-то простым образом аппроксимировать математическое ожидания выхода нейронов не получается, поэтому авторы предлагают стохастический вывод - т.е. прогонять исходную картинку, через сеть несколько раз, каждый раз генерируя свои dropout маски для слоёв.
Spectral Dropout
Еще один вариант dropout для свёрточных сетей описан в [13]. Авторы предлагают при помощи своего метода отключать высокочастотный шум у признаков. Для этого вначале к тензору признаков применяется дискретное косинус преобразование (DCT), Перейдя в частотное пространство, и получив новый терхмерный тензор, авторы задаются некотором порогом $\tau$, и случайным образом (снова используя распределение Бернулли) зануляют часть частот, которые больше $\tau$, при этом частоты меньше этого порога оставляют без изменений. Получившийся после зануления части элементов тензор обратным DCT преобразованием возвращают в пространство признаков и передают на следующий CNN слой.
Авторы предлагают три варианта, как применять DCT к трехмерному тензору признаков.
Первый - применять двумерное DCT к каждому каналу признаков по отдельности.
Второй - брать в каждой пространственной точке вектор признаков, и применять к нему одномерное DCT преобразование.
Наконец, третий вариант, похожий на второй - снова берем вектор в каждой точке, его размерность равна числу каналов (обозначим её $C$), далее преобразовываем вектор в матрицу размерности $\sqrt{C}\times\sqrt{C}$ и применяем двумерное DCT к этой матрице. Очевидно, что если использовать последний вариант, надо чтобы число каналов было полным квадратом.
Прямое и обратное DCT преобразование авторы предлагают оформить в виде свёрточного слоя и встроить в сеть.
Weighted channel dropout
В [14] авторы придумывают weighted channel dropout, опираясь на следующую идею. Известно, что на высокоуровневых (т.е. максимально удаленных от входного слоя с изображением) слоях свёрточной сети каждый канал отвечает за какой-то сложный элемент изображения - этот канал выделяет на картинке глаза, этот рот, этот нос, а вот этот уши. Соответственно, если нейрон в слое с “носами” активирован и на его выходе большое значение, то мы можем вернуться по слоям назад и в соответствующем месте изображения отыскать нос. И вот авторы предлагают: использовать слой WCD на последних уровнях свёрточной сети, и отключать с помощью него те каналы у которых слабоактивированные нейроны.
Более формально предлагается следующее. Для каждого канала ($k$) подсчитываем арифметическое среднее по всем нейронам (т.е. применяем Global Average Pooling):
\[score_k = \frac 1 {WH} \sum_{i=1}^W\sum_{j=1}^H x_{ijk},\, k=1,...,K\]и далее оставляем слой с вероятность тем больше, чем больше средняя оценка нейронов этого слоя:
\[p_k = \frac {score_k} {\sum_{t=1}^K score_t}\]Т.е. мы снова генерируем вектор $d \in \{0, 1\}^K$ каждая координата которого выбирается из множеcтва $\{0, 1\}$ независимо со своей вероятностью $p_k$ и отвечает за то остаётся ли включенным целый канал признаков.
При тренировке, чтобы скомпенсировать то, что часть каналов отключается, такой dropout слой передавая данные оставшихся каналов, усиливает их домножением на $\alpha$:
\[\alpha = \frac {\sum_{k=1}^{K} score_k} {\sum_{k=1}^{K} d_k\cdot score_k}\]При тестировании/выводе такой слой просто прозрачно передаёт все данные с выхода предыдущего слоя на вход следующего.
Интересно сравнить этот подход с max-drop layer, рассмотренным выше, там авторы предлагали отключать отдельные нейроны с максимальной активацией, а здесь наоборот, предлагается отключать каналами те признаки, которые плохо активируются.
CorrDropout
Следующий вариант dropout описан в статье [15] и использует информацию о корреляции (пространнственной или межканальной) между признаками для выбора тех нейронов, которые надо отключить.
Рассмотрим $N$ векторов признаков размерности $K$ (про то как это ляжет на свёрточную сеть мы уточним чуть позже):
\[X_i = (x_{i1},..., x_{iK}),\, i=1,...,N\]Нормализуем эти вектора:
\[\tilde X_i = \frac {X_i} {\|X_i\|}\,,\]посчитаем попарно скалярные произведения нормализованных векторов:
\[a_{ij} = \left(\tilde X_i, \tilde X_j\right) = \sum_{k=1}^{K} \tilde x_{ik} \tilde x_{jk},\,i=1,...,N;\,j=1,...,N\]и запишем матрицу “корреляции”:
\[B = (b_{ij}),\; b_{ij} = \begin{cases} 0, & i=j\\ a_{ij}, & иначе \end{cases}\]Посчитаем среднее арифметическое по строкам матрицы $B$:
\[\hat p_i = \frac 1 N \sum_{j=1}^N b_{ij},\]которое будет характеризовать силу “коррелированности” данного признака по отношению к остальным. Авторы предлагают оставлять с большей вероятностью те признаки, которые более коррелированы, т.е. снова используем распределение Бернулли, чтобы получить ответ оставлять признак или нет. Параметр распределения Бернулли вычислим взяв softmax от набора $\hat p$:
\[p_i = e^{\hat p_i} \big/ \sum_{j=1}^N e^{\hat p_j}\]Таким образом мы как и обычно получаем вектор маски $\hat d \in \{0, 1\}^N$. Неприятность в том, что мы не можем регулировать, какая часть признаков при таком подходе будет отброшена. Чтобы иметь контролируемый процесс авторы предлагают, как и в обычном dropout, задаться некоторой постоянной вероятностью $p$ и сгенерировать вектор $d \in \{0, 1\}^N$ из распределения Бернулли, с вероятностью:
\[p' = \max\left\{0, 1 - \frac {(1 - p) \cdot N} {N - \sum \hat d_i}\right\}\]Теперь мы отключаем только те признаки для которых $d_i = \hat d_i = 0$.
Уточним как это работает. Пока в $\hat d$ нулей меньше $(1 - p) N$, мы имеем $p’= 0$, а значит вектор $d$ будет полностью состоять из нулей, и маскировать будем в точности по $\hat d$. Как только нулей в $\hat d$ становится слишком много в дело вступает вектор $d$ и часть из них превращает в единицы.
При работе с изображениями, на каждом уровне свёрточной нейронной сети мы имеем трехмерный тензор: $F \in \mathbb R^{W\times H \times C}$, в зависимости от того, как превращать $F$ в набор векторов признаков имеется два варианта CorrDropout.
Пространственный CorrDropout
Рассмотрим $F$ как набор из $N = W\cdot H$ векторов размерности $C$. Можно было бы применить CorrDropout непосредственно к ним, но обычно, пространственно близкие вектора существенно более коррелированы, и в целом неэффективно отбрасывать какой-то один “пиксел”. Авторы используют тот же подход, что и в DropBlock, маскируя блоки пространственно близких пикселей.
Для начала они разбивают прямоугольник $W\times H$ на непересекающиеся квадраты $k\times k$ и усредняют вектора по этим квадратам (фактически это average pooling слой с размером ядра $k\times k$ и шагом (stride-ом) $k$). Получается набор векторов признаков размера $N’ = \frac N {k^2} = \frac W k \cdot \frac H k$, с которым уже проделываются все операции CorrDropout.
В конечном итоге в исходном тензоре признаков максируются блоки размера $k\times k \times C$, соответствующие векторам признаков маскированным в CorrDropout.
Поканальный CorrDropout
Теперь рассмотрим $F$ как $C$ векторов признаков размерности $N = W\cdot H$, тогда при помощи CorrDropout схемы, мы получим маску размерности $C$ для каналов. Так же как в SpatialDropout будем отключать целые каналы, для которых в маске содержится ноль.
LocalDrop
Еще одна познавательная статья [16], которая правда только краем задевает именно dropout технику. Авторы анализируют локальную сложность по Радемахеру и предлагают добавлять дополнительный член в штрафную функцию для регуляризации процесса тренировки, который работает и для полносвязных и для свёрточных слоёв. При этом если дополнительно используется dropout, то вероятности отключения нейронов так же попадают в штрафную функцию. Для свёрточных слоёв при этом используется DropBlock подход, описанный выше, когда из канала выключается набор соседних нейронов, образующих прямоугольный блок.
Статья интересная в части рассмотрения математической подложки процессов и привлечения такого понятия как сложность класса функций по Радемахеру, но с практической точки зрения, все эти переусложнения, на мой взгляд будет тяжело использовать в реальных задачах.
AutoDropout
В [17] предлагается подбирать нейронны, которые отключаться при dropout-е используя reinforce алгоритм. Т.е. вначале авторы берут прямоугольный набор пикселей, потом проделывают с ним геометрические преобразования (повороты, искажения по осям) и используют в качестве маски для dropout. Все параметры (размер и положение прямоугольника, угол поворота, параметры искажения и т.п.) при этом подбираются оптимизируя некоторую функцию качества, пользуясь reinforcement алгоритмом.
Кажется, что такой подход даже демонстрирует прирост качества в некоторых ситуациях, но в пределах 0.5%.
Dropout и BatchNormalization
Dropout и BatchNormalization в каком-то смысле решают сходные задачи - пытаются защитить нейронные сети от переобучения. BatchNormalization появилась на три года позже, и поначалу выступила заменой в том числе и dropout, попытки использовать при тренировке обе методики приводили скорее к ухудшению результирующего качества. Однако, в 2019 вышла статья [18] в которой авторы показали как можно эффективно совмещать dropout и batch normalization в свёрточных нейронных сетях.
Главное, что предложили авторы статьи: заменить схему
на
т.е. вместо того, чтобы применять dropout после свёрточного слоя, применить его до. В статье есть как теоретическое обоснование - использование статистик подсчитанных для batch notmalization во время тренировок, когда часть нейронов отключается, плохо работает на этапе вывода, так и практические результаты, которые демонстрируют, что изменение порядка слоёв приводит к улучшению качества сети.
Так же в этой работе авторы предлагают заменить batch normalization на group normalization (нормализации мы недавно достаточно подробно разбирали).
Dropout в рекурентных сетях
Мы уже ранее обсуждали рекуррентные сети, теперь посмотрим как использовать в них dropout.
Первый пример использованиия dropout в рекуррентных сетях можно найти в [19] и [20], и там и там авторы предлагают не применять dropout к рекуррентым связям, а использовать его либо на переходах между слоями многослойной рекуррентной сети, либо перед отправкой откликов рекуррентной сети в полносвязные слои для дальнейшей обработки. В [19] этот подход применяется (с успехом) при решении задачи распознавания рукописных текстов, а в [20] он тестируется на нескольких примерах из различных областей.
Однако, применять dropout только к полносвязным слоям не очень интересно, поэтому в [21], [22] и [23] авторы предлагают варианты как прикрутить dropout к рекуррентной части сети.
Рассмотрим LSTM. Формально её можно описать как:
\[\begin{align*} g_t &= \phi\left(W_{g} [x_t|h_{t-1}] + b_g\right)\\ i_t &= \sigma\left(W_{i} [x_t|h_{t-1}] + b_i\right)\\ o_t &= \sigma\left(W_{o} [x_t|h_{t-1}] + b_o\right)\\ f_t &= \sigma\left(W_{f} [x_t|h_{t-1}] + b_f\right)\\ c_t &= g_t \circ i_t + c_{t-1} \circ f_t\\ h_t &= \phi(c_t) \circ o_t \end{align*}\]В [21] авторы предлагают применять dropout к $c_t$, т.е. поменять формулы перехода состояния ячейки на:
\[c_t = d(g_t \circ i_t + c_{t-1} \circ f_t)\]здесь $d(\cdot)$ как обычно определяется маской dropout-а, в случае если нейрон не отключается (в маске единица), то во время тренировки мы усиливаем отклик, который через него проходит умножением на $1/p$.
В [22] авторы идут дальше и применяют dropout к внутреннему состоянию $h_{t}$, заменяя первые 4 формулы на:
\[\begin{align*} g_t &= \phi\left(W_{g} [x_t|d(h_{t-1})] + b_g\right)\\ i_t &= \sigma\left(W_{i} [x_t|d(h_{t-1})] + b_i\right)\\ o_t &= \sigma\left(W_{o} [x_t|d(h_{t-1})] + b_o\right)\\ f_t &= \sigma\left(W_{f} [x_t|d(h_{t-1})] + b_f\right)\\ \end{align*}\]Важно отметить, что в обоих случаях, маска для dropout-а генерируется один раз для каждой входной последовательности, и не зависит от $t$. Логика авторов в том, что если на каждом шаге выключать разные нейронны внутреннего состояния, то за время прохождения последовательности через рекуррентную сеть каждый нейрон будет отключен хотя бы однажды и таким образом будет сломано основное свойство рекуррентой нейронной сети - сохранение знания из начала последовательности и совмещение этого знания с более поздними данными.
В [23] авторы замечают, что если мы на каждом шаге, прогоняя через неотключенный нейрон данные, будем умножать отклик на $1/p$ как это требуется в dropout слое (ну или при другой реализации на каждом шаге использования/тестирования умножать все отклики нейронов на p), то при длинных последовательностях может получиться, что величина отклика будет с каждым шагом экспоненциально расти, а это приведет к серьёзным проблемам при тренировке. Так же в [23] пишут, что по их наблюдениям dropout, на рекуррентных слоях по рецепту из [21] улучшает сеть, если не использовать с ним в паре dropout на полносвязных слоях. Если же включить рекуррентный dropout в дополнение к полносвязному, то улучшения качества не будет.
В итоге в [23] предлагают свой вариант - зашумлять вектор $g_t$, т.е. применять dropout только к одному слагаемому в формуле перехода состояния ячейки:
\[c_t = d(g_t) \circ i_t + c_{t-1} \circ f_t\]При этом их способ во-первых, позволяет не фиксировать маску для последовательности, а выбирать случайную на каждом шаге $t$ и поскольку dropout применяется не непосредственно к нейронам внутреннего состояния сети, это не приведет к потери памяти, с чем боролись, используя общую маску для всей последовательности и в то же время исключит проблемы с экспоненциальным ростом отклика у нейронов.
Во-вторых, в отличии от [21], dropout из [23] легко переносится и на другие варианты рекуррентных сетей. Рассмотрим, например, GRU:
\[\begin{align*} z_t &= \sigma\left(W_{z}[x_t|h_{t-1}] + b_z\right)\\ r_t &= \sigma\left(W_{r}[x_t|h_{t-1}] + b_r\right)\\ g_t &= \phi\left(W_{g}[x_t|r_t \circ h_{t-1}] + b_g\right)\\ h_t &= (1 - z_t) \circ h_{t-1} + z_t \circ g_t \end{align*}\]По аналогии с LSTM заменяем в последней формуле
\[h_t = (1 - z_t) \circ h_{t-1} + z_t \circ d(g_t)\]Для наглядности три картинки, красным подсвечены связи, где применяется dropout в каждой из методик:
[21]
[22]
[23]
Заключение
Это точно не все вариации dropout, которые были придуманы за 12 лет с момента выхода статьи. Мне больше интересно про картинки, поэтому выше наблюдается явный перекос в сторону свёрточных сетей, хотя рекуррентные тоже затронуты.
Аккуратное использование dropout обычно приносит пользу при решении задач, но как и большинство технологий, связанных с нейронными сетями, не всегда удаётся эту пользу теоретически обосновать (ну или теоретических обоснований больше одного), хотя и получается практически пронаблюдать.
Литература
-
G. Hinton, N. Srivastava, A. Krizhevsky, I. Sutskever, and R. Salakhutdinov, “Improving neural networks by preventing co-adaptation of feature detectors”, arXiv:1207.0580, 2012.
-
L. Wan, M. Zeiler, S. Zhang, Y. LeCun, and R. Fergus, “Regularization of neural networks ussing dropconnect”, PMLR 28(3):1058-1066, 2013.
-
Lei Jimmy Ba, Brendan Frey, “Adaptive dropout for training deep neural networks”, (NIPS 2013), 2013.
-
S. J. Rennie, V. Goel, and S. Thomas, “Annealed dropout training of deep networks”, 2014.
-
P. Morerio, J. Cavazza, R. Volpi, R. Vidal, and V. Murino, “Curriculum dropout”, arXiv:1703.06229, 2017.
-
J. Tompson, R. Goroshin, A. Jain, Y. LeCun, and C. Bregler, “Efficient object localization using convolutional networks”, arXiv:1411.4280, 2014.
-
S. Park and N. Kwak, “Analysis on the dropout effect in convolutional neural networks”, (ACCV 2016), 2016.
-
T. DeVries and G. W. Taylor, “Improved regularization of convolutional neural networks with cutout”, arXiv:1708.04552, 2017.
-
G. Ghiasi, T.-Y. Lin, and Q. V. Le, “Dropblock: a regularization method for convolutional networks”, arXiv:1810.12890, 2018.
-
H. Wu and X. Gu, “Towards dropout training for convolutional neural networks”, arXiv:1512.00242, 2016.
-
G. Huang, Y. Sun, Z. Liu, D. Sedra, and K. Weinberger, “Deep networks with stochastic depth”, arXiv:1603.09382, 2016.
-
S. Singh, D. Hoiem, and D. Forsyth, “Swapout: Learning an ensemble of deep architectures”, arXiv:1605.06465, 2016.
-
S. Khan, M. Hayat, and F. Porikli, “Regularization of deep neural networks with spectral dropout”, arXiv:1711.08591, 2017.
-
S. Hou and Z. Wang, “Weighted channel dropout for regularization of deep convolutional neural network”, AAAI, 2019
-
Y. Zeng, T. Dai, B. Chen, S.-T. Xia, and J. Lu, “Correlation-based structural dropout for convolutional neural networks”, 2021.
-
Z. Lu, C. Xu, B. Du, T. Ishida, L. Zhang, and M. Sugiyama, “Localdrop: A hybrid regularization for deep neural networks”, arXiv:2103.007192021.
-
H. Pham and Q. Le, “Autodropout: Learning dropout patterns to regularize deep networks”, arXiv:2101.01761 2021.
-
S. Cai, J. Gao, M. Zhang, W. Wang, G. Chen, and B. C. Ooi, “Effective and efficient dropout for deep convolutional neural networks”, arXiv:1904.03392, 2019.
-
V. Pham, T. Bluche, C. Kermorvant, and J. Louradour, “Dropout improves recurrent neural networks for handwriting recognition”, arXiv:1312.4569, 2013.
-
W. Zaremba, I. Sutskever, and O. Vinyals, “Recurrent neural network regularization”, arXiv:1409.2329, 2014.
-
T. Moon, H. Choi, H. Lee, and I. Song, “Rnndrop: A novel dropout for RNNs in ASR”, 2015
-
Y. Gal and Z. Ghahramani, “A theoretically grounded application of dropout in recurrent neural networks”, arXiv:1512.05287, 2015.
-
S. Semeniuta, A. Severyn, and E. Barth, “Recurrent dropout without memory loss”, arXiv:1603.05118, 2016.