Пошаговая реализация на Python

Методы регуляризации используются для предотвращения переобучения в нейронных сетях. Переоснащение означает изучение шаблона, а не его обобщение. Мы подробнее рассмотрим переоснащение, когда увидим пакетное обучение.

В этом посте мы реализуем регуляризацию L1 и L2 в функции потерь. В этой технике мы добавляем штраф к проигрышу.

Штраф L1 означает, что мы добавляем абсолютное значение параметра к убытку, умноженному на скаляр.

И штраф L2 означает, что мы добавляем квадрат параметра к убытку, умноженному на скаляр.

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

Скачать Jupyter Notebook можно здесь.

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

Вернуться к предыдущему сообщению

Вернуться к первому сообщению

5.3 Регуляризация L1 и L2

Примечание. Архитектура нейронной сети такая же, как и в предыдущем посте, т. е. 4 слоя с 5, 3, 5 и 4 узлами.

Во-первых, функция активации для скрытых слоев — это функция ReLU
Во-вторых, функция активации для выходного слоя — это функция Softmax.
В-третьих, используемая функция потерь — категориальная кросс-энтропийная потеря, CE
Четвертое, мы будем использовать SGD с оптимизатором ускорения Нестерова со скоростью обучения = 0,01 и импульсом = 0,9

Теперь давайте посмотрим на шаги.

Step 1 - A forward feed like we did in the previous post but with
         penalties included in loss
Step 2 - Initializing SGD with Nesterov acceleration Optimizer
Step 3 - Entering the training loop
      Step 3.1 - A forward feed to see loss with penalties before
                 training
      Step 3.2 - Using Backpropagation to calculate gradients
      Step 3.3 - Using SGD with Nesterov acceleration Optimizer to
                 update weights and biases
Step 4 - A forward feed to verify that the loss has been reduced
         and to see how close predicted values are to true values

Шаг 1 — предварительная подача с добавлением штрафов к проигрышу

import numpy as np                          # importing NumPy
np.random.seed(42)
input_nodes = 5                             # nodes in each layer
hidden_1_nodes = 3
hidden_2_nodes = 5
output_nodes = 4

x = np.random.randint(1, 100, size = (input_nodes, 1)) / 100
x                                       # Inputs
y = np.array([[0], [1], [0], [0]])
y                                       # Outputs

def relu(x, leak = 0):                          # ReLU
    return np.where(x <= 0, leak * x, x)
def relu_dash(x, leak = 0):                     # ReLU derivative
    return np.where(x <= 0, leak, 1)
def softmax(x):                                 # Softmax
    return np.exp(x) / np.sum(np.exp(x))
def softmax_dash(x):                            # Softmax derivative
    
    I = np.eye(x.shape[0])
    
    return softmax(x) * (I - softmax(x).T)
def cross_E(y_true, y_pred):                    # CE
    return -np.sum(y_true * np.log(y_pred + 10**-100))
def cross_E_grad(y_true, y_pred):               # CE derivative
    return -y_true/(y_pred + 10**-100)

w1 = np.random.random(size = (hidden_1_nodes, input_nodes))    # w1
b1 = np.zeros(shape = (hidden_1_nodes, 1))                     # b1
w2 = np.random.random(size = (hidden_2_nodes, hidden_1_nodes)) # w2
b2 = np.zeros(shape = (hidden_2_nodes, 1))                     # b2
w3 = np.random.random(size = (output_nodes, hidden_2_nodes))   # w3
b3 = np.zeros(shape = (output_nodes, 1))                       # b3

Теперь перед подсчетом убытков добавим штрафы.

  1. Мы добавим штрафы L1 и L2 для весов w3 и смещений b3 со значением регуляризации L1 = 0,01 и L2 = 0,01.
  2. Мы добавим штраф L1 для весов w2 и смещений b2 со значением регуляризации L1 = 0,01.
  3. Мы добавим штраф L2 для весов w1 и смещений b1 со значением регуляризации L2 = 0,01.

Примечание. Во многих источниках вы обнаружите, что регуляризация L1 и L2 не используется для смещений, но чтобы показать вам, насколько легко ее реализовать, мы сделаем это здесь. При желании вы можете использовать разные значения регуляризации для разных параметров.

l1 = 0.01                                  # L1 regularization value
l2 = 0.01                                  # L2 regularization value

Давайте посмотрим, как добавить штрафы к потере.

Когда мы говорим, что добавляем штрафы, мы имеем в виду это

Или, в сокращенной форме для Python, мы можем сделать это.

Прямая подача будет выглядеть так,

in_hidden_1 = w1.dot(x) + b1                   # forward feed
out_hidden_1 = relu(in_hidden_1)
in_hidden_2 = w2.dot(out_hidden_1) + b2
out_hidden_2 = relu(in_hidden_2)
in_output_layer = w3.dot(out_hidden_2) + b3
y_hat = softmax(in_output_layer)
y_hat                                          # y_hat
y                                              # y
(                      cross_E(y, y_hat)       # loss with penalties
                               + 
            l1 * np.sum(abs(w3)) + l1 * np.sum(abs(b3))
            + l2 * np.sum((w3)**2) + l2 * np.sum((b3)**2) 
                               +
            l1 * np.sum(abs(w2)) + l1 * np.sum(abs(b2)) 
                               + 
            l2 * np.sum((w1)**2) + l2 * np.sum((b1)**2)            )                                        

Шаг 2 — Инициализация SGD с помощью Оптимизатора ускорения Nesterov

learning_rate = 0.01                   # learning rate
momentum = 0.9                         # momentum
update_w1 = np.zeros(w1.shape)         # Initializing updates with 0
update_b1 = np.zeros(b1.shape)
update_w2 = np.zeros(w2.shape
)update_b2 = np.zeros(b2.shape)
update_w3 = np.zeros(w3.shape)
update_b3 = np.zeros(b3.shape)

Шаг 3. Вход в цикл обучения

epochs = 1000

Шаг 3.1 — Предварительная подача, чтобы увидеть потери перед тренировкой

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

for epoch in range(epochs):
#---------------------Forward Propagation---------------------------
    
    in_hidden_1 = w1.dot(x) + b1
    out_hidden_1 = relu(in_hidden_1)
    in_hidden_2 = w2.dot(out_hidden_1) + b2
    out_hidden_2 = relu(in_hidden_2)
    in_output_layer = w3.dot(out_hidden_2) + b3
    y_hat = softmax(in_output_layer)
    
    loss = (               cross_E(y, y_hat) 
                                  + 
                  l1 * np.sum(abs(w3)) + l1 * np.sum(abs(b3)) 
                 + l2 * np.sum((w3)**2) + l2 * np.sum((b3)**2) 
                                  +
                  l1 * np.sum(abs(w2)) + l1 * np.sum(abs(b2)) 
                                   + 
                  l2 * np.sum((w1)**2) + l2 * np.sum((b1)**2)      )                                                 
    
    print(f'loss before training is {loss} -- epoch number {epoch +
                                                                1}')
    print('\n')

Шаг 3.2. Расчет градиентов с помощью обратного распространения

Вот ответ на ваш вопрос. Как на градиенты влияют штрафы при проигрыше?

Если вы помните из последнего поста, нам нужно вычислить grad_w3, что равно

Возьмем первый член, т. е.

Из термина потерь мы видим, что

Таким образом, для каждого термина в grad_w3 мы можем написать это

or,

Мы уже знаем, как вычислить первую матрицу.

А вторую и третью матрицы можно свести к

Таким образом, мы вычисляем градиенты для b3, w2, b2, w1 и b1. Все, что нам нужно сделать, это добавить приведенную форму штрафных производных в градиенты.

Итак, теперь давайте посмотрим на градиенты в Python.

#--------------Gradient Calculations via Back Propagation-----------
    error_upto_softmax = np.sum(cross_E_grad(y, y_hat) *
           softmax_dash(in_output_layer), axis = 0).reshape((-1, 1))
    
    grad_w3 = error_upto_softmax .dot( out_hidden_2.T ) + l1 * (w3 /
                                 (abs(w3) + 10**-100)) + l2 * 2 * w3
    
    grad_b3 = error_upto_softmax + l1 * (b3 / (abs(b3) + 10**-100))
                                                       + l2 * 2 * b3
    
    #-----------------------------------------
    
    error_grad_H2 = np.sum(error_upto_softmax * w3, axis = 0)
                                                   .reshape((-1, 1))
    
    grad_w2 = error_grad_H2 * relu_dash(in_hidden_2) .dot(
                 out_hidden_1.T ) + l1 * (w2 / (abs(w2) + 10**-100))
    
    grad_b2 = error_grad_H2 * relu_dash(in_hidden_2) + l1 * (b2 /
                                               (abs(b2) +10**-100))
    
    #-----------------------------------------
    
    error_grad_H1 = np.sum(error_grad_H2 * relu_dash(in_hidden_2) *
                                     w2, axis = 0) .reshape((-1, 1))
    
    grad_w1 = error_grad_H1 * relu_dash(in_hidden_1) .dot( x.T ) +
                                                         l2 * 2 * w1
    
    grad_b1 = error_grad_H1 * relu_dash(in_hidden_1) + l2 * 2 * b1

Шаг 3.3. Использование SGD с оптимизатором ускорения Nesterov для обновления весов и смещений

#--------Updating weights and biases with SGD Momentum Nesterov-----
    update_w1 = - learning_rate * grad_w1 + momentum * update_w1
    update_w1_ = - learning_rate * grad_w1 + momentum * update_w1
    w1 += update_w1_                                  # w1
    
    update_b1 = - learning_rate * grad_b1 + momentum * update_b1
    update_b1_ = - learning_rate * grad_b1 + momentum * update_b1
    b1 += update_b1_                                  # b1
    
    update_w2 = - learning_rate * grad_w2 + momentum * update_w2
    update_w2_ = - learning_rate * grad_w2 + momentum * update_w2
    w2 += update_w2_                                  # w2
    
    update_b2 = - learning_rate * grad_b2 + momentum * update_b2
    update_b2_ = - learning_rate * grad_b2 + momentum * update_b2
    b2 += update_b2_                                  # b2
    
    update_w3 = - learning_rate * grad_w3 + momentum * update_w3
    update_w3_ = - learning_rate * grad_w3 + momentum * update_w3
    w3 += update_w3_                                  # w3
    
    update_b3 = - learning_rate * grad_b3 + momentum * update_b3
    update_b3_ = - learning_rate * grad_b3 + momentum * update_b3
    b3 += update_b3_                                  # b3

Цикл обучения будет выполняться 1000 раз.

Это небольшой скриншот после тренировки.

Шаг 4. Предварительная подача, чтобы убедиться, что потери уменьшились, и посмотреть, насколько близки прогнозируемые значения к истинным значениям

in_hidden_1 = w1.dot(x) + b1                      # forward feed
out_hidden_1 = relu(in_hidden_1)
in_hidden_2 = w2.dot(out_hidden_1) + b2
out_hidden_2 = relu(in_hidden_2)
in_output_layer = w3.dot(out_hidden_2) + b3
y_hat = softmax(in_output_layer)
y_hat                                             # predicted values
y                                                 # true values
(                      cross_E(y, y_hat)       # loss with penalties
                               + 
            l1 * np.sum(abs(w3)) + l1 * np.sum(abs(b3))
            + l2 * np.sum((w3)**2) + l2 * np.sum((b3)**2) 
                               +
            l1 * np.sum(abs(w2)) + l1 * np.sum(abs(b2)) 
                               + 
            l2 * np.sum((w1)**2) + l2 * np.sum((b1)**2)            )

Надеюсь, теперь вы понимаете, как реализовать регуляризацию L1 и L2 в нейронных сетях.

Если вам понравился этот пост, подпишитесь на мой канал на YouTube neuralthreads и присоединяйтесь ко мне на Reddit.

В ближайшее время я буду загружать новые интерактивные видео на канал YouTube. И я буду рад помочь вам с любыми сомнениями на Reddit.

Большое спасибо за вашу поддержку и отзывы.

Если вам понравился этот курс, вы можете поддержать меня на

Это много значило бы для меня.

Перейти к следующему сообщению — 5.4 Dropout.