Сегодня разберем тему, которая хоть и звучит скромно — 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. Если интересно, записывайтесь по ссылке.
Комментарии (13)
CrazyElf
14.11.2024 05:51Мне кажется, лучше было бы начинать объяснять с простых моделей, например, с регрессии в
Scikit-Learn
. Можно было бы наглядно показать на примерах, какие коэффициенты получаются у моделей регрессии без регуляризации и с регуляризацией. Не знаю, может быть в приведённых вами моделях наKeras
тоже можно было показать коэффициенты, но вы этого в любом случае не сделали. А было бы наглядно. Ну и формулы показать можно было бы, словами не очень хорошо воспринимается описание формул.
Anyothernick
Вопрос. Кто-нибудь может подробно объяснить как регуляризация спасает от переобучения?
Сколько не читал статей на эту тему, сколько бы людей не спрашивал - ответа так и не смог найти. Может тут помогут.
Arastas
Если у вас очень гибкая модель без регуляризации, то она будет стремиться по максимуму описать вариации в исходных данных. Если эти вариации не несут важной информации, то их запоминание не поможет сделать хороший прогноз на новых данных- переобучение.
Если к этой же модели добавить регуляризацию и выкрутить ее на максимум, то модель проигнорирует вариации в данных и просто выучит среднее значение. Это среднее значение будет прогнозом для новых данных - нет переобучения.
Anyothernick
среднее значение будет прогнозом для новых данных - не говорит о том что нет переобучения. собственно вопрос-то только в этом.
Arastas
Тогда вопрос не в том, как регуляризация снижает переобучение, а в том, что такое отсутствие переобучения. Как вы для себя это определяете?
Anyothernick
Что такое отсутствия переобучения формально не скажу.
А вот критерии отсутствия переобучения - стандартны. Обычно это стабильность качества модели в терминах метрик при применении ее на новом потоке наблюдений.
Arastas
Ну тогда всё правильно - среднее значение более стабильно по сравнению с другими более гибкими линейными моделями (я рассматриваю случай линейной регрессии). Медиана была бы ещё стабильнее.
Arastas
.
Arastas
.
Ildus_Samigullin
На примере классической линейной регрессии. Если модель переобучена то мы увидим что это полином высокой степени (модель сложная) с большими весовыми коэффициентами (модель нестабильная). Чтобы это как то решить давайте введем в функционал ошибки штраф за большие и бесполезные коэффициенты - регуляризация.
Для полного понимания конечно то что я написал мало. Попробуйте посмотреть материал по этой теме на selfEdu + мне кажется Соколов достаточно понятно это разъясняет на своих лекциях.
Anyothernick
На примере той же линейной регрессии - мы получили некую линейную модель вида Y=Ax+B (обойдемся без крышек и эпсилонов). Предположим по ней мы видим падение метрик на тестовой выборке (емнип единственный стандартный критерий переобучения).
В результате применения регуляризации получили новую модель, которая отличается от исходной весами/факторами. Я понимаю что она формально в терминах bias-variance должна снижать variance, но почему это должно приводить к снижению переобучения - нет.
S_A
Смотрите, регуляризация - это наложение условий на веса модели через добавку в лосс. Почему в такой постановке работает - потому что это изменяет ландшафт лосс-функции, делая ее более оптимизируемой, и, следовательно с лучшим оптимумом. Об этом загляните в UDL book, https://udlbook.github.io/udlbook/
И еще чуть сложнее - регуляризация это внесение inductive bias в том числе, продвижение модели ближе к задаче (не очень точно выражено, но как-то так). Соответственно с ним перфоманс модели лучше, ее обобщающая способность.
makaedgar
Могу предложить следующее объяснение.
Пусть есть модель, которая параметризуется весами. В ходе обучения мы подбираем такие значения весов, которые наилучшим образом описывают входные данные. Таким образом, информация, содержащаяся во входных данных, перетекает в информацию, содержащуюся в весах. Если веса могут сохранить больше информации, чем содержится в исходных данных, то появляется вероятность переобучения. А именно: модель будет просто воспроизводить исходные данные вместо описания взаимосвязей. Простейший пример это фиттинг полинома N-й степени по N точкам, такую модель можно обучить всегда со стопроцентной точностью. Но на любой точке вне обучающей выборки она будет давать рандомный результат.
Как можно измерить информацию? Веса хранятся в памяти компьютера в виде байтов. Т.е., если байтов для весов выделенно много, возникает вероятность переобучения. Как решать эту проблему?
Во-первых, можно уменьшить количество весов. Меньше данных - меньше информации. Однако такая модель может недостаточно хорошо описывать сложные закономерности, например, кубическую функцию с 4 параметрами невозможно хорошо описать параболой из 3 параметров.
Во-вторых, можно уменьшить информацию в каждом отдельном весе. Чем больше число, тем больше байтов оно требует для хранения. И тут возникает идея регуляризации: давайте ограничим или введем штраф на размер весов, и тогда мы ограничим количество информации в них. При этом мы сохраняем возможность модели описывать сложные закономерности. Поэтому простейшие регуляризации L1 и L2 просто штрафуют за величину весов. Получается trade off в ходе обучения: оптимизатору выгодно частично запомнить данные, но не выгодно хранить слишком много информации. Итоговый результат с регуляризацией получается полезнее для объемной модели: она хорошо описывает сложные зависимости, но просто запомнить все не будет оптимальным.