Сегодня разберем тему, которая хоть и звучит скромно — 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)


  1. Anyothernick
    14.11.2024 05:51

    Вопрос. Кто-нибудь может подробно объяснить как регуляризация спасает от переобучения?

    Сколько не читал статей на эту тему, сколько бы людей не спрашивал - ответа так и не смог найти. Может тут помогут.


    1. Arastas
      14.11.2024 05:51

      Если у вас очень гибкая модель без регуляризации, то она будет стремиться по максимуму описать вариации в исходных данных. Если эти вариации не несут важной информации, то их запоминание не поможет сделать хороший прогноз на новых данных- переобучение.
      Если к этой же модели добавить регуляризацию и выкрутить ее на максимум, то модель проигнорирует вариации в данных и просто выучит среднее значение. Это среднее значение будет прогнозом для новых данных - нет переобучения.


      1. Anyothernick
        14.11.2024 05:51

        среднее значение будет прогнозом для новых данных - не говорит о том что нет переобучения. собственно вопрос-то только в этом.


        1. Arastas
          14.11.2024 05:51

          Тогда вопрос не в том, как регуляризация снижает переобучение, а в том, что такое отсутствие переобучения. Как вы для себя это определяете?


          1. Anyothernick
            14.11.2024 05:51

            Что такое отсутствия переобучения формально не скажу.

            А вот критерии отсутствия переобучения - стандартны. Обычно это стабильность качества модели в терминах метрик при применении ее на новом потоке наблюдений.


            1. Arastas
              14.11.2024 05:51

              Ну тогда всё правильно - среднее значение более стабильно по сравнению с другими более гибкими линейными моделями (я рассматриваю случай линейной регрессии). Медиана была бы ещё стабильнее.


    1. Arastas
      14.11.2024 05:51

      .


    1. Arastas
      14.11.2024 05:51

      .


    1. Ildus_Samigullin
      14.11.2024 05:51

      На примере классической линейной регрессии. Если модель переобучена то мы увидим что это полином высокой степени (модель сложная) с большими весовыми коэффициентами (модель нестабильная). Чтобы это как то решить давайте введем в функционал ошибки штраф за большие и бесполезные коэффициенты - регуляризация.
      Для полного понимания конечно то что я написал мало. Попробуйте посмотреть материал по этой теме на selfEdu + мне кажется Соколов достаточно понятно это разъясняет на своих лекциях.


      1. Anyothernick
        14.11.2024 05:51

        На примере той же линейной регрессии - мы получили некую линейную модель вида Y=Ax+B (обойдемся без крышек и эпсилонов). Предположим по ней мы видим падение метрик на тестовой выборке (емнип единственный стандартный критерий переобучения).

        В результате применения регуляризации получили новую модель, которая отличается от исходной весами/факторами. Я понимаю что она формально в терминах bias-variance должна снижать variance, но почему это должно приводить к снижению переобучения - нет.


    1. S_A
      14.11.2024 05:51

      Смотрите, регуляризация - это наложение условий на веса модели через добавку в лосс. Почему в такой постановке работает - потому что это изменяет ландшафт лосс-функции, делая ее более оптимизируемой, и, следовательно с лучшим оптимумом. Об этом загляните в UDL book, https://udlbook.github.io/udlbook/

      И еще чуть сложнее - регуляризация это внесение inductive bias в том числе, продвижение модели ближе к задаче (не очень точно выражено, но как-то так). Соответственно с ним перфоманс модели лучше, ее обобщающая способность.


    1. makaedgar
      14.11.2024 05:51

      Могу предложить следующее объяснение.

      Пусть есть модель, которая параметризуется весами. В ходе обучения мы подбираем такие значения весов, которые наилучшим образом описывают входные данные. Таким образом, информация, содержащаяся во входных данных, перетекает в информацию, содержащуюся в весах. Если веса могут сохранить больше информации, чем содержится в исходных данных, то появляется вероятность переобучения. А именно: модель будет просто воспроизводить исходные данные вместо описания взаимосвязей. Простейший пример это фиттинг полинома N-й степени по N точкам, такую модель можно обучить всегда со стопроцентной точностью. Но на любой точке вне обучающей выборки она будет давать рандомный результат.

      Как можно измерить информацию? Веса хранятся в памяти компьютера в виде байтов. Т.е., если байтов для весов выделенно много, возникает вероятность переобучения. Как решать эту проблему?

      Во-первых, можно уменьшить количество весов. Меньше данных - меньше информации. Однако такая модель может недостаточно хорошо описывать сложные закономерности, например, кубическую функцию с 4 параметрами невозможно хорошо описать параболой из 3 параметров.

      Во-вторых, можно уменьшить информацию в каждом отдельном весе. Чем больше число, тем больше байтов оно требует для хранения. И тут возникает идея регуляризации: давайте ограничим или введем штраф на размер весов, и тогда мы ограничим количество информации в них. При этом мы сохраняем возможность модели описывать сложные закономерности. Поэтому простейшие регуляризации L1 и L2 просто штрафуют за величину весов. Получается trade off в ходе обучения: оптимизатору выгодно частично запомнить данные, но не выгодно хранить слишком много информации. Итоговый результат с регуляризацией получается полезнее для объемной модели: она хорошо описывает сложные зависимости, но просто запомнить все не будет оптимальным.


  1. CrazyElf
    14.11.2024 05:51

    Мне кажется, лучше было бы начинать объяснять с простых моделей, например, с регрессии в Scikit-Learn. Можно было бы наглядно показать на примерах, какие коэффициенты получаются у моделей регрессии без регуляризации и с регуляризацией. Не знаю, может быть в приведённых вами моделях на Keras тоже можно было показать коэффициенты, но вы этого в любом случае не сделали. А было бы наглядно. Ну и формулы показать можно было бы, словами не очень хорошо воспринимается описание формул.