Вы можете использовать PyTorch для вычисления градиентов тензора относительно другого тензора при некоторых ограничениях. Если вы внимательно следите за тем, чтобы оставаться в рамках тензорной структуры, чтобы гарантировать создание графа вычислений, то, многократно вызывая в обратном направлении каждый элемент выходного тензора и обнуляя член grad независимой переменной, вы можете итеративно запрашивать градиент каждой записи. Этот подход позволяет постепенно строить градиент векторнозначной функции.
К сожалению, этот подход требует многократного вызова backward
, что на практике может быть медленным и может привести к очень большим матрицам.
import torch
from copy import deepcopy
def get_gradient(f, x):
""" computes gradient of tensor f with respect to tensor x """
assert x.requires_grad
x_shape = x.shape
f_shape = f.shape
f = f.view(-1)
x_grads = []
for f_val in f:
if x.grad is not None:
x.grad.data.zero_()
f_val.backward(retain_graph=True)
if x.grad is not None:
x_grads.append(deepcopy(x.grad.data))
else:
# in case f isn't a function of x
x_grads.append(torch.zeros(x.shape).to(x))
output_shape = list(f_shape) + list(x_shape)
return torch.cat((x_grads)).view(output_shape)
Например, учитывая следующую функцию:
f(x0,x1,x2) = (x0*x1*x2, x1^2, x0+x2)
Якобиан в точке x0, x1, x2 = (1, 2, 3)
можно вычислить следующим образом
x = torch.tensor((1.0, 2.0, 3.0))
x.requires_grad_(True) # must be set before further computation
f = torch.stack((x[0]*x[1]*x[2], x[1]**2, x[0]+x[2]))
df_dx = get_gradient(f, x)
print(df_dx)
что приводит к
tensor([[6., 3., 2.],
[0., 4., 0.],
[1., 0., 1.]])
В вашем случае, если вы можете определить выходной тензор относительно входного тензора, вы можете использовать такую функцию для вычисления градиента.
Полезной функцией PyTorch является возможность вычислять векторно-якобиево произведение. В предыдущем примере потребовалось множество повторных применений цепного правила (также известного как обратное распространение) с помощью метода backward
для прямого вычисления якобиана. Но PyTorch позволяет вам вычислять матричное / векторное произведение якобиана с произвольным вектором, что намного эффективнее, чем фактическое построение якобиана. Это может быть больше в соответствии с тем, что вы ищете, поскольку вы можете использовать его для вычисления нескольких градиентов при различных значениях функции, аналогично тому, как, как я полагаю, работает numpy.gradient
.
Например, здесь мы вычисляем f(x) = x^2 + sqrt(x)
для x = 1, 1.1, ..., 1.8
и вычисляем производную (которая равна f'(x) = 2x + 0.5/sqrt(x)
) в каждой из этих точек.
dx = 0.1
x = torch.arange(1, 1.8, dx, requires_grad=True)
f = x**2 + torch.sqrt(x)
f.backward(torch.ones(f.shape))
x_grad = x.grad
print(x_grad)
что приводит к
tensor([2.5000, 2.6767, 2.8564, 3.0385, 3.2226, 3.4082, 3.5953, 3.7835])
Сравните это с numpy.gradient
dx = 0.1
x_np = np.arange(1, 1.8, dx)
f_np = x_np**2 + np.sqrt(x_np)
x_grad_np = np.gradient(f_np, dx)
print(x_grad_np)
что приводит к следующему приближению
[2.58808848 2.67722558 2.85683288 3.03885421 3.22284723 3.40847554 3.59547805 3.68929417]
person
jodag
schedule
20.08.2019