Зачем она нужна
Функция активации — это нелинейность, которая стоит после каждого линейного слоя. Без неё сколько угодно слоёв схлопываются в одно умножение на матрицу. Долгое время стандартом была 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)$.
Для отрицательных $x$ выход не равен нулю: он отрицательный и маленький. Сначала, при уменьшении $x$ от нуля, он убывает (вход растёт по модулю быстрее, чем падает доля), а примерно с $x=-0{,}75$ снова поднимается к нулю: доля $\Phi(x)$ гаснет быстрее, чем растёт $|x|$. Отсюда неглубокая «ямка» — главное отличие формы GELU от ReLU.
Формула по косточкам
Вся сложность GELU спрятана в $\Phi$. Разберём её на части.
- $\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»:
Множитель $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)$ |
|---|---|---|---|---|
| −3 | 0,0013 | −0,0040 | −0,0119 | 0 |
| −2 | 0,0228 | −0,0455 | −0,0852 | 0 |
| −1 | 0,1587 | −0,1587 | −0,0833 | 0 |
| −0,5 | 0,3085 | −0,1543 | 0,1325 | 0 |
| 0 | 0,5000 | 0,0000 | 0,5000 | 0 |
| 0,5 | 0,6915 | 0,3457 | 0,8675 | 0,5 |
| 1 | 0,8413 | 0,8413 | 1,0833 | 1 |
| 2 | 0,9772 | 1,9545 | 1,0852 | 2 |
| 3 | 0,9987 | 2,9960 | 1,0119 | 3 |
Таблица показывает три вещи. Справа 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$, тем вероятнее, что его пропустят?
Такую маску легко получить: возьмите шум $\varepsilon\sim N(0,1)$ и положите $m=1$, если $\varepsilon\le x$. Тогда $P(m=1)=\Phi(x)$ по определению функции распределения. Почему нормальный закон? Авторы объясняют выбор так: входы нейронов, особенно после нормализации (BatchNorm, LayerNorm), распределены примерно нормально, и $\Phi(x)$ отвечает на естественный вопрос: какая доля типичных входов меньше данного $x$.
Шумная маска — только объяснение формы. В сети стоит детерминированное среднее $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|)$. В обоих случаях
Поправка $|x|\Phi(-|x|)$ чётная: она одинакова слева и справа от нуля, достигает максимума $0{,}170$ при $|x|=0{,}752$ и быстро гаснет. Тем же свойством обладает SiLU $x\,\sigma(x)$, только её «ямка» шире и глубже: $0{,}278$ при $|x|=1{,}278$.
Функции распределения нормального и логистического законов очень похожи: $\Phi(x)\approx\sigma(1{,}702\,x)$ с ошибкой не больше $0{,}0095$. Поэтому GELU почти совпадает с $x\,\sigma(1{,}702x)$ — это SiLU с более крутой сигмоидой. Подробнее о самой $\sigma$ — на странице сигмоиды.
Производная: Φ(x) + x·φ(x)
Производная нужна при обратном распространении ошибки. Выведем её и найдём минимум функции.
Вывод первой и второй производной
Главный факт: производная функции распределения — плотность, $\Phi'(x)=\varphi(x)$ (основная теорема анализа для интеграла с переменным верхним пределом). По правилу производной произведения:
У гауссианы $\varphi'(x)=-x\,\varphi(x)$. Дифференцируем ещё раз:
Знак $\operatorname{GELU}''$ совпадает со знаком $2-x^2$: функция выпукла на $(-\sqrt2;\,\sqrt2)$ и вогнута вне этого отрезка, а производная $\operatorname{GELU}'$ имеет экстремумы в точках $x=\pm\sqrt2$.
У производной два слагаемых. Первое — сама доля $\Phi(x)$: так вела бы себя производная, если бы вентиль не зависел от $x$. Второе — поправка от того, что вентиль открывается при росте $x$; она положительна справа от нуля и отрицательна слева. Сравните с ReLU, у которой производная — ступенька 0/1.
Где минимум
Минимум — там, где $\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$ — каждая итерация примерно удваивает число верных знаков.
Второе равенство следует из условия минимума $\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:
Именно эта формула записана в исходном коде BERT и GPT-2 (в библиотеке Hugging Face она называется gelu_new, в PyTorch — approximate="tanh"). Наибольшая ошибка — $4{,}7\cdot10^{-4}$ при $|x|\approx2{,}70$. Второе приближение — через сигмоиду:
Оно грубее: ошибка до $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=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$.
Модель, обученная с tanh-приближением, в выводе должна использовать его же. Разница не больше $4{,}7\cdot10^{-4}$ на нейрон, но в сотнях слоёв и тысячах нейронов она даёт заметное расхождение логитов при конвертации весов и ломает побитовые тесты.
Семейство x·Φ(x/s) в 3D: от ReLU к прямой
Шум в маске не обязан быть единичным. Если $\varepsilon\sim N(0,s^2)$, то $P(\varepsilon\le x)=\Phi(x/s)$, и получается целое семейство функций с «температурой» $s$:
- $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$. На поверхности это прямая бирюзовая линия на дне «оврага».
В сети 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 кирпичики гладкие, поэтому гладкую зависимость можно описать меньшим их числом.
Обучение использует ту же производную: градиент, приходящий к нейрону, умножается на $\operatorname{GELU}'(z)=\Phi(z)+z\varphi(z)$. В отличие от ReLU, нейрон с отрицательным $z$ продолжает получать сигнал, поэтому GELU-сети реже страдают от «мёртвых» нейронов.
Блок прямого распространения трансформера
В BERT и GPT после каждого слоя внимания (attention) стоит двухслойный перцептрон, который обрабатывает каждый токен отдельно:
В BERT-base $d=768$, так что на каждый токен в каждом из 12 слоёв приходится $4d=3072$ вычисления GELU — для последовательности из 512 токенов это почти 19 миллионов вычислений за один проход. Поэтому скорость самой активации важна, и вместо точной erf часто берут tanh-приближение. Перед FFN стоит LayerNorm, поэтому входы $W_1h+b_1$ в начале обучения порядка единицы — ровно та область, где у GELU есть и «ямка», и плавный переход, то есть где она заметно отличается от ReLU.
Хендрикс и Гимпел предложили 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-вариант бывает быстрее на старом железе.
Частые ошибки
- Считать, что выход GELU неотрицателен, как у ReLU. Он может быть до $-0{,}170$; если дальше идёт логарифм, корень или деление, это нужно учитывать.
- Смешивать точную GELU и tanh-приближение между обучением и выводом или при конвертации модели (ONNX, TensorRT, другой фреймворк). Ошибка мала, но накапливается по слоям.
- Путать $x\,\sigma(1{,}702x)$ с GELU. Это быстрая аппроксимация с ошибкой до $0{,}02$, по сути SiLU с параметром $\beta=1{,}702$.
- Думать, что GELU полностью избавляет от затухающих градиентов. При $x\to-\infty$ производная гаснет как гауссиана: уже $\operatorname{GELU}'(-4)\approx-0{,}0005$. Нормализация входов по-прежнему нужна.
- Писать $\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.