Несколько непревзойденных матриц в обратном распространении во времени - PullRequest
1 голос
/ 19 апреля 2020

Я собираюсь реализовать двоичное добавление с помощью Recurrent Neural Network (RNN) в качестве образца. Я решил проблему с ее реализацией Python, поэтому я решил поделиться с ней своей проблемой, чтобы предложить идеи по ее устранению.

Как видно из моего кода ноутбука (раздел обратного распространения по времени (BPTT)). Для обновления матрицы входного веса, как показано ниже, существует цепное правило, как показано ниже:

chain rule

Моя проблема эта часть:

w_input

Я пытался реализовать эту часть в моем Python коде или блокноте код (class input_layer, backward метод), но несоответствующие размеры вызывают ошибку.

В моем примере кода W_hidden равно 16*16, тогда как результат delta pre_hidden равен 1*2. Это делает ошибку. Если вы запустите код, вы увидите ошибку.

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

Как я знаю, множественные несопоставимые матрицы с точки зрения размерности невозможны. Если мое правило цепочки верное, как оно может быть реализовано с помощью Python? Есть идеи?

Заранее спасибо.

1 Ответ

0 голосов
/ 26 апреля 2020

Вам необходимо применить балансировку размеров к градиентам. Взятый из курса Стэнфорда cs231n , он сводится к двум простым модификациям:

Учитывая и , у нас будет:

,

Вот код, который я использовал для проверки правильности расчета градиента. Вы должны быть в состоянии обновить свой код соответственно.

import torch

torch.random.manual_seed(0)

x_1, x_2 = torch.zeros(size=(1, 8)).normal_(0, 0.01), torch.zeros(size=(1, 8)).normal_(0, 0.01)
y = torch.zeros(size=(1, 8)).normal_(0, 0.01)

h_0 = torch.zeros(size=(1, 16)).normal_(0, 0.01)
weight_ih = torch.zeros(size=(8, 16)).normal_(mean=0, std=0.01).requires_grad_(True)
weight_hh = torch.zeros(size=(16, 16)).normal_(mean=0, std=0.01).requires_grad_(True)
weight_ho = torch.zeros(size=(16, 8)).normal_(mean=0, std=0.01).requires_grad_(True)

h_1 = x_1.mm(weight_ih) + h_0.mm(weight_hh)
h_2 = x_2.mm(weight_ih) + h_1.mm(weight_hh)
g_2 = h_2.sigmoid()
j_2 = g_2.mm(weight_ho)
y_predicted = j_2.sigmoid()

loss = 0.5 * (y - y_predicted).pow(2).sum()

loss.backward()


delta_1 = -1 * (y - y_predicted) * y_predicted * (1 - y_predicted)
delta_2 = delta_1.mm(weight_ho.t()) * (g_2 * (1 - g_2))
delta_3 = delta_2.mm(weight_hh.t())

# 16 x 8
weight_ho_grad = g_2.t() * delta_1

# 16 x 16
weight_hh_grad = h_1.t() * delta_2 + (h_0.t() * delta_3)

# 8 x 16
weight_ih_grad = x_2.t() * delta_2 + x_1.t() * delta_3

atol = 1e-10
assert torch.allclose(weight_ho.grad, weight_ho_grad, atol=atol)
assert torch.allclose(weight_hh.grad, weight_hh_grad, atol=atol)
assert torch.allclose(weight_ih.grad, weight_ih_grad, atol=atol)
...