Пошаговая реализация на 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
Теперь перед подсчетом убытков добавим штрафы.
- Мы добавим штрафы L1 и L2 для весов w3 и смещений b3 со значением регуляризации L1 = 0,01 и L2 = 0,01.
- Мы добавим штраф L1 для весов w2 и смещений b2 со значением регуляризации L1 = 0,01.
- Мы добавим штраф 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.
Большое спасибо за вашу поддержку и отзывы.
Если вам понравился этот курс, вы можете поддержать меня на
Это много значило бы для меня.