Использование библиотек автоматического дифференцирования для вычисления частных производных произвольного тензора

(Примечание: это не вопрос обратного распространения.) Я пытаюсь решить на графическом процессоре нелинейную PDE, используя тензоры PyTorch вместо массивов Numpy. Я хочу вычислить частные производные произвольного тензора, аналогичные действию центра конечных разностей numpy.gradient. У меня есть другие способы обойти эту проблему, но, поскольку я уже использую PyTorch, мне интересно, можно ли использовать модуль autograd (или вообще любой другой модуль автодифференциации) для выполнения этого действия.

Я создал тензорно-совместимую версию функции numpy.gradient, которая работает намного быстрее. Но, возможно, есть более элегантный способ сделать это. Я не могу найти никаких других источников, посвященных этому вопросу, чтобы показать, что это возможно или невозможно; возможно, это отражает мое незнание алгоритмов автодифференциации.


person BenjaminDSmith    schedule 29.07.2019    source источник


Ответы (2)


У меня сам был тот же вопрос: при численном решении PDE нам нужен постоянный доступ к пространственным градиентам (которые может дать нам функция numpy.gradients) - можно ли использовать автоматическое дифференцирование для вычисления градиентов вместо использования конечных -различие или какая-то изюминка?

«Мне интересно, можно ли использовать модуль автоградации (или вообще любой другой модуль автодифференциации) для выполнения этого действия».

Ответ отрицательный: как только вы дискретизируете свою проблему в пространстве или времени, тогда время и пространство становятся дискретными переменными с сетчатой ​​структурой, а не явными переменными, которые вы вводите в какую-либо функцию для вычислить решение PDE.

Например, если бы я хотел вычислить поле скорости некоторого потока жидкости u (x, t), я бы дискретизировал в пространстве и времени, и у меня было бы u[:,:], где индексы представляют положения в пространстве и времени.

Автоматическое дифференцирование позволяет вычислить производную функции u (x, t). Так почему же здесь нельзя вычислить пространственную или временную производную? Потому что вы дискретизировали свою проблему. Это означает, что у вас нет функции для u для произвольного x, а есть функция от u в некоторых точках сетки. Вы не можете автоматически различать расстояние между точками сетки.

Насколько я могу судить, написанная вами тензорно-совместимая функция, вероятно, является вашим лучшим выбором. Вы можете видеть, что аналогичный вопрос задавался на форумах PyTorch здесь и здесь < / а>. Или вы могли бы сделать что-то вроде

dx = x[:,:,1:]-x[:,:,:-1]

если вас не беспокоят конечные точки.

person Nick McGreivy    schedule 29.01.2020

Вы можете использовать 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
comment
Я не думаю, что этот ответ очень уместен для вопроса, поскольку я пытаюсь вычислить градиент произвольного тензора чисел, который не обязательно имеет хорошее аналитическое выражение. - person BenjaminDSmith; 25.02.2020
comment
Я неправильно понял исходный вопрос. Я предположил, что вы получили свой произвольный тензор с помощью некоторой последовательности дифференцируемых операций и хотите получить градиент w.r.t. независимые переменные. Поскольку вы этого не сделали, ответ @NickMcGreivy правильный. Градиент функции определяется как предел по постепенно уменьшающимся областям вокруг образца, как только вы дискретизируете свой домен, вы отбрасываете информацию, необходимую для его вычисления. На самом деле существует бесконечно много дифференцируемых функций, которые могли бы сгенерировать ваш тензор, а истинный градиент может быть любым. - person jodag; 29.02.2020