ReLU (Rectified Linear Unit)
ReLU (выпрямленный линейный элемент) возвращает max(0, x): пропускает положительные входные значения и обнуляет отрицательные. Рассматриваются градиент, проблема затухающего ReLU, разновидности и примеры кода для PyTorch.
Выпрямленная линейная единица (ReLU) — это функция активации f(x) = max(0, x): она без изменений пропускает положительные входные значения, а для всех остальных выдает ноль. Это простое сравнение при практически нулевых вычислительных затратах дает нейронной сети (NN) нелинейность, необходимую для обучения распознаванию сложных закономерностей. ReLU стала стандартной функцией активации скрытых слоев в глубоком обучении после того, как Винод Наир и Джеффри Хинтон применили ее в 2010 году, а AlexNet — сверточная нейронная сеть (CNN) на основе ReLU — выиграла соревнование ImageNet в 2012 году. Она остается базовым вариантом, с которым сравнивают более новые функции активации, например SiLU и GELU.
Как работает ReLU#
Производная ReLU равна 1 для положительных входов и 0 для отрицательных. При нуле производная не определена, и такие фреймворки, как PyTorch, используют значение 0. Эти два значения объясняют основные свойства ReLU:
- Дешево вычислять: прямой проход сводится к сравнению с нулем, а обратный — к применению маски; в отличие от сигмоиды и tanh, не нужны экспоненты.
- Нет насыщения на положительных входах: градиент, равный ровно 1, проходит через каждый активный элемент без изменений, поэтому сигналы ошибки не ослабевают при обратном распространении через множество слоев. Это противодействует проблеме затухающего градиента, возникающей у насыщаемых функций.
- Разреженные активации: каждый отрицательный вход превращается в точный ноль, поэтому в каждый момент активна только часть слоя. При случайной инициализации весов для заданного входа около половины элементов слоя выдают ноль.
- Кусочно-линейная функция: сеть на основе ReLU вычисляет кусочно-линейную функцию своих входных данных; ее линейные области соединяются в точках, где элементы включаются или выключаются.
В примере ниже на PyTorch применяется nn.ReLU, а для вычисления градиента в тех же точках используется autograd:
import torch
from torch import nn
relu = nn.ReLU()
# Inputs on both sides of zero, tracked so PyTorch can compute gradients
x = torch.tensor([-2.0, -0.5, 0.0, 0.5, 2.0], requires_grad=True)
y = relu(x)
# Backpropagate a gradient of 1 from each output to its input
y.sum().backward()
print(f"Output: {y.tolist()}")
print(f"Gradient: {x.grad.tolist()}")Пример выводит значения [0.0, 0.0, 0.0, 0.5, 2.0] и градиенты [0.0, 0.0, 0.0, 1.0, 1.0]. Положительные входы проходят без изменений вместе со своими градиентами, а отрицательные входы и ноль не получают градиента вовсе. Это источник как разреженности ReLU, так и ее главного недостатка.
Проблема умирающих ReLU#
Элемент ReLU, вход которого отрицателен для всех примеров в обучающих данных, выдает ноль и получает нулевой градиент, поэтому его веса больше не меняются, а сам элемент фактически умирает. Обычно это происходит, когда большое обновление весов, часто вызванное слишком высоким коэффициентом обучения, сильно смещает его порог в отрицательную сторону. Несколько неактивных элементов не представляют большой проблемы, но при неудачной настройке обучения может отключиться значительная часть слоя. Инициализация Хе, при которой начальные веса масштабируются для слоев ReLU, вместе с умеренными коэффициентами обучения и пакетной нормализацией помогает сохранить большинство элементов активными, а варианты с наклоном устраняют эту проблему, не позволяя градиенту стать нулевым.
Варианты ReLU#
- Leaky ReLU: умножает отрицательные входы на небольшой постоянный коэффициент, обычно 0,01, чтобы неактивные элементы продолжали получать градиент.
- PReLU: обучается выбирать коэффициент наклона для отрицательной области. Каймин Хе и его коллеги представили ее в 2015 году в той же статье, где предложили инициализацию Хе.
- ReLU6: ограничивает выход значением 6: min(max(0, x), 6). Благодаря этому активации остаются в диапазоне, который хорошо представляется числами низкой точности. ReLU6 используется в MobileNetV1 и MobileNetV2.
- ELU: заменяет отрицательную область гладкой экспоненциальной кривой, которая насыщается на уровне −1, приближая средние активации к нулю.
- SiLU и GELU: гладкие функции x · σ(x) и x · Φ(x), которые ведут себя как ReLU при больших значениях, но пропускают небольшие отрицательные значения и не имеют излома в нуле. Они требуют больше вычислений для каждого элемента; в статье о GELU сообщалось о превосходстве над ReLU на всех проверенных задачах компьютерного зрения, обработки языка и речи.
ReLU, сигмоида и tanh#
До появления ReLU в скрытых слоях использовались сигмоида и tanh. Обе функции насыщаются: при больших положительных и отрицательных входах их кривые выравниваются, а производные стремятся к нулю. Даже в самой крутой точке производная сигмоиды не превышает 0,25. При перемножении таких производных по множеству слоев градиента не хватает для обучения ранних слоев. В статье об AlexNet четырехслойная CNN с ReLU достигла ошибки обучения 25% на CIFAR-10 в шесть раз быстрее, чем та же сеть с элементами tanh. Сигмоида и tanh по-прежнему используются там, где важен ограниченный диапазон значений: сигмоида — в выходных слоях для вероятностей и в вентилях рекуррентных ячеек, а tanh — в их скрытых состояниях.
ReLU в современных моделях#
В ResNet и большинстве CNN того времени ReLU используется повсюду, а в оригинальном трансформере 2017 года она применялась в полносвязных слоях. Многие современные модели перешли на более гладкие функции: GELU — в BERT и зрительных трансформерах, а SwiGLU — во многих современных больших языковых моделях. При этом ReLU по-прежнему широко используется там, где важны скорость и аппаратная поддержка. По умолчанию YOLO26 применяет SiLU в блоках Conv; строка activation: nn.ReLU() в YAML-файле модели заменяет ReLU все блоки Conv перед обучением. Такой вариант подходит для ускорителей, например AMD Zynq UltraScale+ DPU, которые выполняют ReLU напрямую, но не SiLU. В PyTorch nn.ReLU(inplace=True) перезаписывает входной тензор результатом, избавляя от необходимости выделять память под отдельный выходной тензор.










