Шпаргалка формул нейросетей · Следующий уровень

GELU: вход, умноженный на вероятность

Активация BERT, GPT и ViT. Разбираем по косточкам, почему это не просто «сглаженная ReLU»: откуда берётся нормальная функция распределения Φ, как GELU получается усреднением случайной маски, где у неё минимум, чему равна производная Φ(x) + xφ(x) и насколько точны приближения через tanh и сигмоиду.

Javharbek Abdulatipov · 35 мин чтения
$$\operatorname{GELU}(x)=x\cdot\Phi(x)=\frac{x}{2}\Bigl(1+\operatorname{erf}\frac{x}{\sqrt2}\Bigr)$$

Зачем она нужна

Функция активации — это нелинейность, которая стоит после каждого линейного слоя. Без неё сколько угодно слоёв схлопываются в одно умножение на матрицу. Долгое время стандартом была ReLU: $\max(0,x)$ — пропустить положительное, обнулить отрицательное. GELU (Gaussian Error Linear Unit, Хендрикс и Гимпел, 2016) решает ту же задачу мягче: вход не пропускается или режется, а умножается на вероятность того, что его стоит пропустить.

Где она встречается:

  • BERT, RoBERTa, GPT, GPT-2, GPT-3 — в каждом блоке прямого распространения (feed-forward, FFN) трансформера: $\operatorname{FFN}(h)=W_2\,\operatorname{GELU}(W_1h+b_1)+b_2$;
  • Vision Transformer (ViT) и многие модели компьютерного зрения после него;
  • GeGLU — вентильный вариант $\operatorname{GELU}(xW)\odot(xV)$, например в Gemma; его родственник SwiGLU с SiLU стоит в LLaMA и Qwen;
  • везде, где важна гладкость: у GELU есть все производные, нет излома в нуле и нет «мёртвой» зоны с нулевым градиентом, как у ReLU.
Главное за минуту

$\operatorname{GELU}(x)=x\,\Phi(x)$, где $\Phi$ — функция распределения стандартного нормального закона. Большие положительные $x$ проходят почти целиком, большие отрицательные — почти обнуляются, около нуля работает плавный переход. У функции есть минимум $\approx-0{,}170$ в точке $x\approx-0{,}752$, производная $\Phi(x)+x\varphi(x)$ лежит в пределах от $-0{,}129$ до $1{,}129$, а на практике её часто считают через tanh-приближение с ошибкой не больше $4{,}7\cdot10^{-4}$.

Интуиция: вход, умноженный на вероятность

Представьте вентиль, который решает, какую долю сигнала $x$ пропустить дальше. У ReLU вентиль жёсткий: при $x>0$ он открыт полностью, при $x<0$ закрыт. У GELU вентиль приоткрыт на $\Phi(x)$ — на долю от 0 до 1, которая плавно растёт вместе с $x$. Большой положительный сигнал проходит почти целиком, большой отрицательный почти гасится, а около нуля сигнал проходит наполовину: $\Phi(0)=0{,}5$.

Потяните точку. Серая полоса — вход $x$ целиком, синяя — та его часть, которая прошла: её длина равна $x\cdot\Phi(x)$. Пунктир бирюзового цвета — сама доля $\Phi(x)$.

Рисунок 1. GELU, доля $\Phi(x)$ и производная. При $x=1$ пропускается доля $\Phi(1)\approx0{,}841$, поэтому $\operatorname{GELU}(1)\approx0{,}841$; при $x=-1$ пропускается только $0{,}159$, и выход равен $-0{,}159$. Кружок отмечает минимум функции.
Что заметить

Для отрицательных $x$ выход не равен нулю: он отрицательный и маленький. Сначала, при уменьшении $x$ от нуля, он убывает (вход растёт по модулю быстрее, чем падает доля), а примерно с $x=-0{,}75$ снова поднимается к нулю: доля $\Phi(x)$ гаснет быстрее, чем растёт $|x|$. Отсюда неглубокая «ямка» — главное отличие формы GELU от ReLU.

Формула по косточкам

Вся сложность GELU спрятана в $\Phi$. Разберём её на части.

$$\operatorname{GELU}(x)=x\cdot\Phi(x),\qquad \Phi(x)=\int_{-\infty}^{x}\varphi(t)\,dt,\qquad \varphi(t)=\frac{1}{\sqrt{2\pi}}\,e^{-t^{2}/2}$$
$\varphi(t)$
плотность стандартного нормального распределения $N(0,1)$ — колокол Гаусса с максимумом $1/\sqrt{2\pi}\approx0{,}399$ в нуле;
$\Phi(x)$
функция распределения: площадь под колоколом левее точки $x$, то есть вероятность $P(\varepsilon\le x)$ для $\varepsilon\sim N(0,1)$; растёт от 0 до 1, $\Phi(0)=\tfrac12$;
$x\cdot\Phi(x)$
вход, умноженный на эту вероятность.

Интеграл от гауссианы не выражается через элементарные функции, поэтому для него есть специальное имя — функция ошибок (error function), отсюда «Gaussian Error Linear Unit»:

$$\operatorname{erf}(z)=\frac{2}{\sqrt\pi}\int_0^{z}e^{-t^2}\,dt,\qquad \Phi(x)=\frac12\Bigl(1+\operatorname{erf}\frac{x}{\sqrt2}\Bigr)\quad\Longrightarrow\quad\operatorname{GELU}(x)=\frac{x}{2}\Bigl(1+\operatorname{erf}\frac{x}{\sqrt2}\Bigr)$$

Множитель $1/\sqrt2$ переводит стандартную нормальную величину с плотностью $e^{-t^2/2}$ к ядру $e^{-t^2}$, под которое определена erf. Сама erf нечётная, меняется от $-1$ до $1$, поэтому $\tfrac12(1+\operatorname{erf})$ меняется от 0 до 1.

$x$$\Phi(x)$$\operatorname{GELU}(x)$$\operatorname{GELU}'(x)$$\operatorname{ReLU}(x)$
−30,0013−0,0040−0,01190
−20,0228−0,0455−0,08520
−10,1587−0,1587−0,08330
−0,50,3085−0,15430,13250
00,50000,00000,50000
0,50,69150,34570,86750,5
10,84130,84131,08331
20,97721,95451,08522
30,99872,99601,01193

Таблица показывает три вещи. Справа GELU быстро догоняет ReLU: при $x=3$ разница всего $0{,}004$. Слева выход отрицательный, но маленький: самое глубокое значение в таблице — $-0{,}159$ при $x=-1$. Производная в нуле равна $0{,}5$, а при $x>0{,}752$ (в таблице — начиная с $x=1$) она больше единицы: функция растёт чуть быстрее прямой $y=x$, догоняя её.

Откуда она взялась: шумная маска в среднем

Авторы GELU объединили две идеи. ReLU умножает вход на детерминированную маску: $x\cdot\mathbb 1[x>0]$. Dropout умножает вход на случайную маску, которая от входа не зависит. А что если маска случайна, но зависит от входа: чем больше $x$, тем вероятнее, что его пропустят?

$$m\sim\operatorname{Bernoulli}\bigl(\Phi(x)\bigr),\qquad \mathbb E\,[\,x\cdot m\,]=x\cdot P(m=1)=x\,\Phi(x)=\operatorname{GELU}(x)$$

Такую маску легко получить: возьмите шум $\varepsilon\sim N(0,1)$ и положите $m=1$, если $\varepsilon\le x$. Тогда $P(m=1)=\Phi(x)$ по определению функции распределения. Почему нормальный закон? Авторы объясняют выбор так: входы нейронов, особенно после нормализации (BatchNorm, LayerNorm), распределены примерно нормально, и $\Phi(x)$ отвечает на естественный вопрос: какая доля типичных входов меньше данного $x$.

Рисунок 2. Слева оранжевые точки — среднее $x\cdot m$ по $n$ случайным маскам в 25 точках от $-3$ до $3$, справа — шум $\varepsilon$ и доля $\Phi(x_0)$, при которой маска открыта. Нажмите «Пуск»: при $n=20$ наибольшее отклонение от GELU равно $0{,}146$, при $n=2000$ — уже $0{,}0125$; ошибка среднего убывает как $1/\sqrt n$. В точке $x_0=0{,}5$ доля открытых масок приближается к $\Phi(0{,}5)\approx0{,}691$.
GELU сама не случайна

Шумная маска — только объяснение формы. В сети стоит детерминированное среднее $x\Phi(x)$: случайности при обучении и выводе нет (если нужен шум, отдельно добавляют dropout).

Свойства и форма

  • Область значений — $[-0{,}170;\,+\infty)$: снизу функция ограничена, сверху нет.
  • Гладкость: GELU бесконечно дифференцируема; $\operatorname{GELU}(0)=0$, $\operatorname{GELU}'(0)=\tfrac12$.
  • Немонотонность: функция убывает на $(-\infty;\,-0{,}752)$ и возрастает правее; минимум $\approx-0{,}170$.
  • Асимптоты: $\operatorname{GELU}(x)-x\to0$ при $x\to+\infty$ и $\operatorname{GELU}(x)\to0$ при $x\to-\infty$, причём слева хвост гаснет как гауссиана: $x\Phi(x)\approx-\varphi(x)$, уже $\operatorname{GELU}(-4)\approx-1{,}3\cdot10^{-4}$.
  • Тождество: $\operatorname{GELU}(x)-\operatorname{GELU}(-x)=x$, потому что $\Phi(x)+\Phi(-x)=1$.
  • Выпуклость: только на интервале $(-\sqrt2;\,\sqrt2)$; за его пределами функция вогнута (знак второй производной — в следующем разделе).

GELU = ReLU минус чётная «ямка»

Из тождества следует удобная запись. Для $x>0$: $x\Phi(x)=x-x\Phi(-x)$; для $x<0$: $x\Phi(x)=-|x|\Phi(-|x|)$. В обоих случаях

$$\operatorname{GELU}(x)=\operatorname{ReLU}(x)-|x|\,\Phi(-|x|)$$

Поправка $|x|\Phi(-|x|)$ чётная: она одинакова слева и справа от нуля, достигает максимума $0{,}170$ при $|x|=0{,}752$ и быстро гаснет. Тем же свойством обладает SiLU $x\,\sigma(x)$, только её «ямка» шире и глубже: $0{,}278$ при $|x|=1{,}278$.

Рисунок 3. Сверху — GELU, SiLU и ReLU, снизу — разности $\operatorname{ReLU}-f$. Наведите курсор на любой из графиков. При $x=-1$: $\operatorname{GELU}=-0{,}159$, $\operatorname{SiLU}=-0{,}269$. Точки на нижнем графике — наибольшие отличия от ReLU: $0{,}170$ у GELU и $0{,}278$ у SiLU; это и глубины их минимумов.
Связь с сигмоидой

Функции распределения нормального и логистического законов очень похожи: $\Phi(x)\approx\sigma(1{,}702\,x)$ с ошибкой не больше $0{,}0095$. Поэтому GELU почти совпадает с $x\,\sigma(1{,}702x)$ — это SiLU с более крутой сигмоидой. Подробнее о самой $\sigma$ — на странице сигмоиды.

Производная: Φ(x) + x·φ(x)

Производная нужна при обратном распространении ошибки. Выведем её и найдём минимум функции.

Вывод первой и второй производной

Главный факт: производная функции распределения — плотность, $\Phi'(x)=\varphi(x)$ (основная теорема анализа для интеграла с переменным верхним пределом). По правилу производной произведения:

$$\operatorname{GELU}'(x)=(x)'\cdot\Phi(x)+x\cdot\Phi'(x)=\Phi(x)+x\,\varphi(x)$$

У гауссианы $\varphi'(x)=-x\,\varphi(x)$. Дифференцируем ещё раз:

$$\operatorname{GELU}''(x)=\varphi(x)+\varphi(x)+x\cdot\bigl(-x\,\varphi(x)\bigr)=\varphi(x)\,\bigl(2-x^{2}\bigr)$$

Знак $\operatorname{GELU}''$ совпадает со знаком $2-x^2$: функция выпукла на $(-\sqrt2;\,\sqrt2)$ и вогнута вне этого отрезка, а производная $\operatorname{GELU}'$ имеет экстремумы в точках $x=\pm\sqrt2$.

$$\operatorname{GELU}'(x)=\underbrace{\Phi(x)}_{\text{доля}}+\underbrace{x\,\varphi(x)}_{\text{изменение доли}}$$

У производной два слагаемых. Первое — сама доля $\Phi(x)$: так вела бы себя производная, если бы вентиль не зависел от $x$. Второе — поправка от того, что вентиль открывается при росте $x$; она положительна справа от нуля и отрицательна слева. Сравните с ReLU, у которой производная — ступенька 0/1.

Рисунок 4. Производная GELU (оранжевая) и два её слагаемых. Наибольшее значение $\Phi(\sqrt2)+\sqrt2\,\varphi(\sqrt2)\approx1{,}129$, наименьшее $\approx-0{,}129$ при $x=-\sqrt2$ — сумма этих чисел равна 1, ведь $\operatorname{GELU}'(x)+\operatorname{GELU}'(-x)=1$. Производная обращается в ноль при $x\approx-0{,}752$; в начальной точке $x=-0{,}75$ она уже почти нулевая. Включите вторую производную: её нули — в $\pm\sqrt2$.

Где минимум

Минимум — там, где $\operatorname{GELU}'(x)=0$, то есть $\Phi(x)=-x\,\varphi(x)$. Это уравнение не решается в элементарных функциях; метод Ньютона $x_{k+1}=x_k-\operatorname{GELU}'(x_k)/\operatorname{GELU}''(x_k)$ от $x_0=-1$ даёт $-0{,}6557$, $-0{,}7448$, $-0{,}75175$, $-0{,}751792$ — каждая итерация примерно удваивает число верных знаков.

$$x^{*}\approx-0{,}7518,\qquad \operatorname{GELU}(x^{*})=x^{*}\Phi(x^{*})=-(x^{*})^{2}\varphi(x^{*})\approx-0{,}1700$$

Второе равенство следует из условия минимума $\Phi(x^*)=-x^*\varphi(x^*)$. В минимуме пропускается доля $\Phi(x^*)\approx0{,}226$.

Отрицательный градиент слева

При $x<-0{,}752$ производная отрицательна: если увеличить вход, выход уменьшится. ReLU в этой зоне даёт ровно ноль, а GELU — маленький, но ненулевой сигнал (не меньше $-0{,}129$). Он затухает как гауссиана: $\operatorname{GELU}'(-3)\approx-0{,}012$, $\operatorname{GELU}'(-4)\approx-0{,}0005$.

Быстрые приближения: tanh и сигмоида

Вычислять erf дороже, чем элементарные функции, поэтому авторы предложили два приближения на случай, когда скорость важнее точности. Первое использует tanh:

$$\operatorname{GELU}(x)\approx\frac{x}{2}\Bigl(1+\tanh\Bigl[\sqrt{2/\pi}\,\bigl(x+0{,}044715\,x^{3}\bigr)\Bigr]\Bigr)$$

Именно эта формула записана в исходном коде BERT и GPT-2 (в библиотеке Hugging Face она называется gelu_new, в PyTorch — approximate="tanh"). Наибольшая ошибка — $4{,}7\cdot10^{-4}$ при $|x|\approx2{,}70$. Второе приближение — через сигмоиду:

$$\operatorname{GELU}(x)\approx x\,\sigma(1{,}702\,x)$$

Оно грубее: ошибка до $0{,}020$ при $|x|\approx2{,}27$. Обе ошибки — чётные функции, потому что все три формулы удовлетворяют тождеству $f(x)-f(-x)=x$.

Откуда берутся √(2/π) и 0,044715

Нужно приблизить $\operatorname{erf}(z)$ функцией $\tanh(u)$. Разложим обе около нуля: $\operatorname{erf}(z)=\frac{2}{\sqrt\pi}\bigl(z-\frac{z^3}{3}+\dots\bigr)$ и $\tanh u=u-\frac{u^3}{3}+\dots$. Возьмём $u=\frac{2}{\sqrt\pi}(z+cz^3)$ и приравняем коэффициенты.

$$z^{1}:\ \ \frac{2}{\sqrt\pi}=\frac{2}{\sqrt\pi};\qquad z^{3}:\ \ \frac{2c}{\sqrt\pi}-\frac{8}{3\pi\sqrt\pi}=-\frac{2}{3\sqrt\pi}\ \Longrightarrow\ c=\frac{4}{3\pi}-\frac13$$

Подставляя $z=x/\sqrt2$, получаем $\sqrt{2/\pi}\,(x+c'x^3)$ с $c'=c/2=\frac{2}{3\pi}-\frac16\approx0{,}0455$. Совпадение рядов даёт точность только около нуля, поэтому в формуле стоит коэффициент, подобранный численно по всей оси: $0{,}044715$. Проверка показывает, что это минимаксный коэффициент для самой $\Phi$: при нём наибольшая ошибка приближения $\Phi(x)\approx\tfrac12\bigl(1+\tanh[\sqrt{2/\pi}(x+c'x^3)]\bigr)$ минимальна и равна $1{,}8\cdot10^{-4}$. Для самой GELU оптимум чуть другой ($c'\approx0{,}04438$, ошибка $3{,}9\cdot10^{-4}$), но разница невелика. С тейлоровским $0{,}0455$ максимальная ошибка $7{,}5\cdot10^{-4}$, с $0{,}044715$ — $4{,}7\cdot10^{-4}$, а без кубического члена ($c'=0$) — $0{,}034$.

Рисунок 5. Сверху — точная GELU и два приближения, снизу — модуль ошибки в логарифмической шкале (провалы — точки, где ошибка меняет знак). Двигайте коэффициент $c$: при $c=0{,}044715$ максимальная ошибка $4{,}7\cdot10^{-4}$, при $c=0$ — $0{,}034$, а при $c=0{,}04438$ — всего $3{,}9\cdot10^{-4}$. При $x=1$: точное значение $0{,}8413$, tanh-приближение $0{,}8412$, сигмоидное $0{,}8458$.
Это разные функции

Модель, обученная с tanh-приближением, в выводе должна использовать его же. Разница не больше $4{,}7\cdot10^{-4}$ на нейрон, но в сотнях слоёв и тысячах нейронов она даёт заметное расхождение логитов при конвертации весов и ломает побитовые тесты.

Семейство x·Φ(x/s) в 3D: от ReLU к прямой

Шум в маске не обязан быть единичным. Если $\varepsilon\sim N(0,s^2)$, то $P(\varepsilon\le x)=\Phi(x/s)$, и получается целое семейство функций с «температурой» $s$:

$$f(x,s)=x\,\Phi\Bigl(\frac{x}{s}\Bigr)=s\cdot\operatorname{GELU}\Bigl(\frac{x}{s}\Bigr)$$
  • $s\to0$: шума нет, $\Phi(x/s)$ превращается в ступеньку, и $f\to\operatorname{ReLU}(x)$;
  • $s=1$: обычная GELU;
  • $s\to\infty$: шум заглушает вход, $\Phi(x/s)\to\tfrac12$, и $f\to x/2$ — прямая, нелинейность исчезает.

Второе равенство показывает, что все члены семейства подобны: минимум находится в точке $x=-0{,}752\,s$ и равен $-0{,}170\,s$. На поверхности это прямая бирюзовая линия на дне «оврага».

Рисунок 6. Поверхность $f(x,s)=x\,\Phi(x/s)$ (её можно вращать) и срез при текущем $s$. Оранжевая линия — срез, бирюзовая — линия минимумов. При $s=1$ минимум в $x=-0{,}752$ равен $-0{,}170$. Переключитесь на производную $\operatorname{GELU}'(x/s)$: при малых $s$ она становится ступенькой ReLU, а с ростом $s$ растягивается вдоль оси $x$ и становится всё более пологой; в пределе $s\to\infty$ это константа $\tfrac12$.

В сети GELU применяется к вектору поэлементно: $h=\operatorname{GELU}(W_1x+b_1)$. Каждая координата — своя «складка» вдоль гиперплоскости $w_j^\top x+b_j=0$, но, в отличие от ReLU, складка гладкая, а с той стороны, где ReLU даёт ноль, у неё небольшой отрицательный провал.

GELU в обучении и в трансформере

Сеть с одним скрытым слоем строит ответ как сумму «кирпичиков» $w_{2j}\,f(w_{1j}x+b_{1j})$. У ReLU каждый кирпичик — ломаная с одним изломом, и сумма — тоже ломаная. У GELU кирпичики гладкие, поэтому гладкую зависимость можно описать меньшим их числом.

Рисунок 7. Одна и та же сеть из 4 нейронов с одинаковой инициализацией, обучение Adam с шагом $0{,}03$ на 40 точках зашумлённой кривой $\sin1{,}5x+0{,}3x$. Справа — среднеквадратичная ошибка (MSE) в логарифмической шкале. За 2000 шагов GELU-сеть снижает MSE с $7{,}06$ до $0{,}0042$ — это даже чуть ниже уровня шума в данных (средний квадрат добавленного шума $0{,}0051$), а ReLU-сеть застревает на $0{,}0083$: четырёх изломов не хватает. Переключатель «Нейроны» показывает отдельные кирпичики одной из сетей.

Обучение использует ту же производную: градиент, приходящий к нейрону, умножается на $\operatorname{GELU}'(z)=\Phi(z)+z\varphi(z)$. В отличие от ReLU, нейрон с отрицательным $z$ продолжает получать сигнал, поэтому GELU-сети реже страдают от «мёртвых» нейронов.

Блок прямого распространения трансформера

В BERT и GPT после каждого слоя внимания (attention) стоит двухслойный перцептрон, который обрабатывает каждый токен отдельно:

$$\operatorname{FFN}(h)=W_2\,\operatorname{GELU}(W_1h+b_1)+b_2,\qquad W_1\in\mathbb R^{4d\times d},\ \ W_2\in\mathbb R^{d\times4d}$$

В BERT-base $d=768$, так что на каждый токен в каждом из 12 слоёв приходится $4d=3072$ вычисления GELU — для последовательности из 512 токенов это почти 19 миллионов вычислений за один проход. Поэтому скорость самой активации важна, и вместо точной erf часто берут tanh-приближение. Перед FFN стоит LayerNorm, поэтому входы $W_1h+b_1$ в начале обучения порядка единицы — ровно та область, где у GELU есть и «ямка», и плавный переход, то есть где она заметно отличается от ReLU.

Как GELU стала стандартом

Хендрикс и Гимпел предложили GELU в 2016 году и показали, что на задачах зрения, речи и текста она не хуже ReLU и ELU. Широкую известность ей принесли GPT (2018) и BERT (2018), затем GPT-2, GPT-3 и Vision Transformer (2020). В 2020 году Шазир показал, что вентильные варианты GeGLU и SwiGLU ещё лучше, и новые LLM чаще используют их.

В коде: точно, быстро и устойчиво

В NumPy нет функции erf, поэтому её берут из SciPy. Чтобы не терять точность при больших отрицательных $x$, функцию распределения удобно считать через дополнительную функцию ошибок: $\Phi(x)=\tfrac12\operatorname{erfc}(-x/\sqrt2)$. Формула $\tfrac12(1+\operatorname{erf})$ при $x=-10$ вычитает почти равные числа и даёт ровно 0, хотя истинное значение $\operatorname{GELU}(-10)\approx-7{,}6\cdot10^{-23}$; для самой активации это неважно, но та же $\Phi$ часто нужна и в других формулах.

pythonimport numpy as np
from scipy.special import erfc        # в самой NumPy функции erf нет

SQRT2 = np.sqrt(2.0)

def gelu(x):
    """Точная GELU: x·Φ(x), где Φ(x) = ½·erfc(−x/√2) — без вычитания близких чисел."""
    x = np.asarray(x, dtype=float)
    return x * 0.5 * erfc(-x / SQRT2)

def gelu_grad(x):
    """Производная: Φ(x) + x·φ(x)."""
    x = np.asarray(x, dtype=float)
    cdf = 0.5 * erfc(-x / SQRT2)
    pdf = np.exp(-0.5 * x * x) / np.sqrt(2.0 * np.pi)
    return cdf + x * pdf

def gelu_tanh(x):
    # приближение из исходного кода BERT и GPT-2
    return 0.5 * x * (1.0 + np.tanh(np.sqrt(2.0 / np.pi) * (x + 0.044715 * x**3)))

np.set_printoptions(precision=5, suppress=True)
x = np.array([-3.0, -1.0, 0.0, 1.0, 3.0])
print(gelu(x))        # [-0.00405 -0.15866  0.       0.84134  2.99595]
print(gelu_grad(x))   # [-0.01195 -0.08332  0.5      1.08332  1.01195]
print(np.abs(gelu_tanh(x) - gelu(x)).max())   # ≈ 4.1e-04 (в точках x = ±3)
pythonimport torch
import torch.nn as nn
import torch.nn.functional as F

x = torch.tensor([-1.0, 0.0, 1.0], requires_grad=True)
y = F.gelu(x)                          # −0.1587, 0.0000, 0.8413
y.sum().backward()
print(x.grad)                          # −0.0833, 0.5000, 1.0833 = Φ(x) + x·φ(x)
print(F.gelu(x, approximate="tanh"))   # −0.1588, 0.0000, 0.8412

# Блок FFN трансформера (как в BERT и GPT): d → 4d → d
class FeedForward(nn.Module):
    def __init__(self, d_model=768, mult=4, p_drop=0.1):
        super().__init__()
        self.fc1 = nn.Linear(d_model, mult * d_model)
        self.act = nn.GELU()           # для весов GPT-2: nn.GELU(approximate="tanh")
        self.fc2 = nn.Linear(mult * d_model, d_model)
        self.drop = nn.Dropout(p_drop)

    def forward(self, h):
        return self.drop(self.fc2(self.act(self.fc1(h))))
Правило

Выбирайте вариант GELU так же, как его выбрали при обучении весов: nn.GELU() — точная, nn.GELU(approximate="tanh") — приближённая. Для новой модели разницы в качестве почти нет; tanh-вариант бывает быстрее на старом железе.

Частые ошибки

  1. Считать, что выход GELU неотрицателен, как у ReLU. Он может быть до $-0{,}170$; если дальше идёт логарифм, корень или деление, это нужно учитывать.
  2. Смешивать точную GELU и tanh-приближение между обучением и выводом или при конвертации модели (ONNX, TensorRT, другой фреймворк). Ошибка мала, но накапливается по слоям.
  3. Путать $x\,\sigma(1{,}702x)$ с GELU. Это быстрая аппроксимация с ошибкой до $0{,}02$, по сути SiLU с параметром $\beta=1{,}702$.
  4. Думать, что GELU полностью избавляет от затухающих градиентов. При $x\to-\infty$ производная гаснет как гауссиана: уже $\operatorname{GELU}'(-4)\approx-0{,}0005$. Нормализация входов по-прежнему нужна.
  5. Писать $\Phi$ как $\tfrac12(1+\operatorname{erf}(x))$, забыв деление на $\sqrt2$. Получится функция распределения $N(0,\tfrac12)$, то есть $x\Phi(\sqrt2x)$ — другая кривая.
  • ReLU: предел $x\,\Phi(x/s)$ при $s\to0$ и «скелет» GELU: $\operatorname{GELU}=\operatorname{ReLU}-|x|\Phi(-|x|)$.
  • SiLU / Swish: та же схема «вход × мягкий вентиль», но с сигмоидой вместо $\Phi$; $x\,\sigma(1{,}702x)\approx\operatorname{GELU}(x)$.
  • Сигмоида: $\sigma(1{,}702x)$ приближает $\Phi(x)$ с ошибкой не больше $0{,}0095$ — связь логит- и пробит-моделей.
  • tanh: основа самого популярного приближения GELU.
  • Leaky ReLU: другой способ пропустить сигнал при $x<0$ — постоянным маленьким наклоном вместо гладкой «ямки».
  • Adam и MSE: оптимизатор и потеря из симуляции на рисунке 7.

Итоги

1) $\operatorname{GELU}(x)=x\,\Phi(x)$: вход, умноженный на вероятность $P(\varepsilon\le x)$ для $\varepsilon\sim N(0,1)$; это среднее от случайной маски, зависящей от входа. 2) $\operatorname{GELU}=\operatorname{ReLU}-|x|\Phi(-|x|)$: гладкая ReLU с минимумом $-0{,}170$ при $x=-0{,}752$. 3) $\operatorname{GELU}'=\Phi+x\varphi\in[-0{,}129;\,1{,}129]$, $\operatorname{GELU}''=\varphi\,(2-x^2)$. 4) tanh-приближение с коэффициентом $0{,}044715$ ошибается не больше чем на $4{,}7\cdot10^{-4}$, но это другая функция. 5) $x\,\Phi(x/s)$ соединяет ReLU ($s\to0$) и $x/2$ ($s\to\infty$). 6) Стоит в FFN-блоках BERT, GPT и ViT.

Проверь себя

1. Чему примерно равна $\operatorname{GELU}(-1)$?
2. Чему равна производная GELU в нуле?
3. Где находится минимум GELU?
4. Что означает $\Phi(x)$ в формуле GELU?
5. Чему равна $\operatorname{GELU}(3)-\operatorname{GELU}(-3)$?