Pytorch - Получение градиента для промежуточных переменных / тензоров - PullRequest
0 голосов
/ 09 ноября 2018

В качестве упражнения в рамках Pytorch (0.4.1) я пытаюсь отобразить градиент X (gX или dSdX) в простом линейном слое (Z = X.W + B). Чтобы упростить мой игрушечный пример, я возвращаю () из суммы Z (не потери).

Подводя итог, я хочу, чтобы gX (dSdX) S = sum (XW + B).

Проблема в том, что градиент Z (dSdZ) равен None . В результате gX, конечно, тоже не прав.

import torch
X = torch.tensor([[0.5, 0.3, 2.1], [0.2, 0.1, 1.1]], requires_grad=True)
W = torch.tensor([[2.1, 1.5], [-1.4, 0.5], [0.2, 1.1]])
B = torch.tensor([1.1, -0.3])
Z = torch.nn.functional.linear(X, weight=W.t(), bias=B)
S = torch.sum(Z)
S.backward()
print("Z:\n", Z)
print("gZ:\n", Z.grad)
print("gX:\n", X.grad)

Результат:

Z:
 tensor([[2.1500, 2.9100],
        [1.6000, 1.2600]], grad_fn=<ThAddmmBackward>)
gZ:
 None
gX:
 tensor([[ 3.6000, -0.9000,  1.3000],
        [ 3.6000, -0.9000,  1.3000]])

У меня точно такой же результат, если я использую nn.Module, как показано ниже:

class Net1Linear(torch.nn.Module):
    def __init__(self, wi, wo,W,B):
        super(Net1Linear, self).__init__()
        self.linear1 = torch.nn.Linear(wi, wo)
        self.linear1.weight = torch.nn.Parameter(W.t())
        self.linear1.bias = torch.nn.Parameter(B)
    def forward(self, x):
        return self.linear1(x)
net = Net1Linear(3,2,W,B)
Z = net(X)
S = torch.sum(Z)
S.backward()
print("Z:\n", Z)
print("gZ:\n", Z.grad)
print("gX:\n", X.grad)

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

blue-phoenox, спасибо за ваш ответ. Я очень рад слышать о register_hook ().

Что заставило меня подумать, что у меня неправильный gX, так это то, что он не зависит от значений X. Мне нужно будет сделать математику, чтобы понять это. Но использование CCE Loss вместо SUM делает вещи намного более чистыми. Поэтому я обновил пример для тех, кому это может быть интересно. Использование SUM было плохой идеей в этом случае.

T_dec = torch.tensor([0, 1])
X = torch.tensor([[0.5, 0.8, 2.1], [0.7, 0.1, 1.1]], requires_grad=True)
W = torch.tensor([[2.7, 0.5], [-1.4, 0.5], [0.2, 1.1]])
B = torch.tensor([1.1, -0.3])
Z = torch.nn.functional.linear(X, weight=W.t(), bias=B)
print("Z:\n", Z)
L = torch.nn.CrossEntropyLoss()(Z,T_dec)
Z.register_hook(lambda gZ: print("gZ:\n",gZ))
L.backward()
print("gX:\n", X.grad)

Результат:

Z:
 tensor([[1.7500, 2.6600],
        [3.0700, 1.3100]], grad_fn=<ThAddmmBackward>)
gZ:
 tensor([[-0.3565,  0.3565],
        [ 0.4266, -0.4266]])
gX:
 tensor([[-0.7843,  0.6774,  0.3209],
        [ 0.9385, -0.8105, -0.3839]])
0 голосов
/ 09 ноября 2018

Прежде всего, вы рассчитываете градиенты только для тензоров, где вы включаете градиент, устанавливая requires_grad в True.

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

PyTorch не сохраняет градиенты промежуточных результатов по соображениям производительности. Таким образом, вы просто получите градиент для тех тензоров, которые вы установили requires_grad на True.

Однако вы можете использовать register_hook, чтобы извлечь промежуточный градус во время расчета или сохранить его вручную. Здесь я просто сохраняю его в переменную grad тензора Z:

import torch

# function to extract grad
def set_grad(var):
    def hook(grad):
        var.grad = grad
    return hook

X = torch.tensor([[0.5, 0.3, 2.1], [0.2, 0.1, 1.1]], requires_grad=True)
W = torch.tensor([[2.1, 1.5], [-1.4, 0.5], [0.2, 1.1]])
B = torch.tensor([1.1, -0.3])
Z = torch.nn.functional.linear(X, weight=W.t(), bias=B)

# register_hook for Z
Z.register_hook(set_grad(Z))

S = torch.sum(Z)
S.backward()
print("Z:\n", Z)
print("gZ:\n", Z.grad)
print("gX:\n", X.grad)

Будет выведено:

Z:
 tensor([[2.1500, 2.9100],
        [1.6000, 1.2600]], grad_fn=<ThAddmmBackward>)
gZ:
 tensor([[1., 1.],
        [1., 1.]])
gX:
 tensor([[ 3.6000, -0.9000,  1.3000],
        [ 3.6000, -0.9000,  1.3000]])

Надеюсь, это поможет!

Кстати: обычно вы хотели бы, чтобы градиент был активирован для ваших параметров - ваших весов и смещений. Потому что то, что вы будете делать прямо сейчас при использовании оптимизатора, это изменение ваших входных данных X, а не ваших весовых коэффициентов W и смещения B. Поэтому обычно градиент активируется для W и B в таком случае.

...