Lasso, Ridge и кастомные регуляризаторы: основы
Сегодня разберем тему, которая хоть и звучит скромно — Lasso, Ridge и кастомные регуляризаторы, —, но на практике буквально спасает модели от переобучения. Если у вас бывало так, что модель на тренировочных данных показывает отличные результаты, а при проверке на валидации теряет весь блеск — поздравляю, вы столкнулись с тем самым переобучением! Регуляризация здесь как раз для того и нужна: помогает «усмирить» модель, добавляя ограничения, которые не дают ей запоминать лишние детали.
В этой статье кратко рассмотрим, как применить классические регуляризаторы Lasso и Ridge в Keras, а также создадим кастомные регуляризаторы, чтобы лучше контролировать поведение моделей.
Основы: Lasso и Ridge
Зачем нужны Lasso и Ridge?
Оба метода служат для уменьшения сложности модели, но работают через разные подходы к штрафам за большие веса. Эти штрафы «притягивают» модель к более простым решениям, что в итоге предотвращает переобучение.
Lasso (L1-регуляризация) добавляет штраф, пропорциональный абсолютной величине весов. Представьте, что есть веса, и хочется постепенно убрать из модели незначительные признаки — Lasso идеально подходит для этой задачи. Его фича в том, что при достаточно сильном регуляризационном параметре некоторые веса могут буквально обнуляться, благодаря чему модель отбрасывает лишние признаки. Это полезным для моментов, когда нужно сократить количество признаков или провести что-то вроде feature selection.
Ridge (L2-регуляризация) действует по-другому: он добавляет штраф, пропорциональный квадрату значений весов. Вместо того чтобы обнулять веса, Ridge уменьшает их, сглаживая большие значения. Этот подход хорош, когда нужно посчитать, что все признаки полезны, но некоторым из них стоит «снизить тон». Ridge удобен в задачах, где каждый признак несет определенный вклад в результат, и ни один из них нельзя совсем убрать.
В итоге:
Lasso имеет тенденцию к созданию разреженных моделей, где многие веса обнуляются.
Ridge снижает веса, не доводя их до нуля, поэтому он работает «мягче».
Реализация Lasso и Ridge в Keras
Можно добавить L1 или L2 регуляризацию, буквально указав параметр kernel_regularizer
при создании слоев.
Пример Lasso-регуляризации
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.regularizers import l1
# Создаем модель с L1-регуляризацией
model = Sequential([
Dense(64, input_dim=100, activation='relu', kernel_regularizer=l1(0.01)),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy')
Указали kernel_regularizer=l1(0.01)
. Этот коэффициент определяет, насколько сильно L1-регуляризация влияет на веса. Если значение штрафа слишком высокое, модель может стать «скупой» на зависимости, теряя сложные, но нужные признаки. Для большинства задач полезно начать с небольшого значения, например 0.001
или 0.01
, и постепенно его увеличивать, наблюдая за изменениями в качестве модели.
Чем больше значение, тем сильнее штраф и тем больше весов обнуляется.
Пример Ridge-регуляризации
from tensorflow.keras.regularizers import l2
# Создаем модель с L2-регуляризацией
model = Sequential([
Dense(64, input_dim=100, activation='relu', kernel_regularizer=l2(0.01)),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy')
Здесь указали kernel_regularizer=l2(0.01)
, что также контролирует величину штрафа. Этот параметр действует как «приглушение» для значений весов, уменьшая их, но сохраняя неравенство между ними. L2-регуляризация рекомендуется, если мы уверены, что все признаки значимы и не хотим их обнулять.
Комбинированные методы: L1 + L2
Для сложных задач часто используют комбинацию L1 и L2, которая называется Elastic Net. Keras поддерживает такую комбинацию через l1_l2
.
Пример Elastic Net
from tensorflow.keras.regularizers import l1_l2
model = Sequential([
Dense(64, input_dim=100, activation='relu', kernel_regularizer=l1_l2(l1=0.01, l2=0.01)),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy')
Этот метод позволяет одновременно сокращать лишние признаки (L1) и уменьшать большие значения весов (L2), предлагая баланс между двумя типами регуляризации.
Кастомные регуляризаторы: когда L1 и L2 недостаточно
L1 и L2 покрывают множество задач, но иногда хочется экспериментировать. Например, обнулять веса, превышающие определенный порог. Keras дает возможность создания собственных регуляризаторов через наследование класса Regularizer
.
К примеру, обнулим все веса, которые превышают пороговое значение 0.1
.
from tensorflow.keras.regularizers import Regularizer
import tensorflow as tf
class CustomThresholdRegularizer(Regularizer):
def __init__(self, threshold=0.1):
self.threshold = threshold
def __call__(self, x):
return tf.reduce_sum(tf.where(tf.abs(x) > self.threshold, tf.square(x), tf.zeros_like(x)))
def get_config(self):
return {'threshold': float(self.threshold)}
# Используем кастомный регуляризатор в модели
model = Sequential([
Dense(64, input_dim=100, activation='relu', kernel_regularizer=CustomThresholdRegularizer(0.1)),
Dense(1, activation='sigmoid')
])
Здесь tf.where
проверяет, превышает ли значение веса порог 0.1
, и если да, применяет штраф (в данном случае, квадрат веса).
Dropout как регуляризатор
Dropout — один из самых хороших методов регуляризации, хотя формально он не является L1 или L2. Суть Dropout в том, что на каждой итерации обучения случайно отключаются определенные нейроны, тем самым модель «привыкает» не полагаться на конкретные узлы. Dropout применяется в основном к Dense слоям, а также к свёрточным (для сверточных слоев применяется SpatialDropout2D
).
Пример использования Dropout
from tensorflow.keras.layers import Dropout
model = Sequential([
Dense(64, input_dim=100, activation='relu'),
Dropout(0.5),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy')
Dropout(0.5)
означает, что у каждого нейрона есть 50% вероятность отключения на каждой итерации.
Начните с Dropout вероятности от 0.2
до 0.5
и подберите оптимальное значение экспериментально.
Комбинированная модель с L2 и Dropout
Комбинация методов регуляризации может дать лучшие результаты, чем использование одного метода.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.regularizers import l2
model = Sequential([
Dense(64, input_dim=100, activation='relu', kernel_regularizer=l2(0.01)),
Dropout(0.5),
Dense(64, activation='relu', kernel_regularizer=l2(0.01)),
Dropout(0.5),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# Обучение модели
# model.fit(X_train, y_train, validation_data=(X_val, y_val), epochs=50)
Здесь на каждом слое применяется L2-регуляризация с коэффициентом 0.01
, а также Dropout с вероятностью 0.5
.
Заключение
Если остались вопросы или хотите обсудить конкретные кейсы — пишите в комментариях! Чтобы углубиться в тему, рекомендую изучить прочие техники регуляризации, напримеркак Batch Normalization, а также понять, как работают ранние остановки и их влияние на тренировку модели.
18 ноября в Otus пройдет открытый урок на тему «AutoML и подбор гипер-параметров». Участников занятия ждет практика на примере популярных библиотек pycaret и hyperopt. Если интересно, записывайтесь по ссылке.