次のヤコビアン関数をpytorchに実装しました。私が間違えない限り、それは任意のテンソルw.r.tのヤコビアンを計算します。任意の次元入力:
import torch
import torch.autograd as ag
def nd_range(stop, dims = None):
if dims == None:
dims = len(stop)
if not dims:
yield ()
return
for outer in nd_range(stop, dims - 1):
for inner in range(stop[dims - 1]):
yield outer + (inner,)
def full_jacobian(f, wrt):
f_shape = list(f.size())
wrt_shape = list(wrt.size())
fs = []
f_range = nd_range(f_shape)
wrt_range = nd_range(wrt_shape)
for f_ind in f_range:
grad = ag.grad(f[Tuple(f_ind)], wrt, retain_graph=True, create_graph=True)[0]
for i in range(len(f_shape)):
grad = grad.unsqueeze(0)
fs.append(grad)
fj = torch.cat(fs, dim=0)
fj = fj.view(f_shape + wrt_shape)
return fj
これに加えて、n次導関数を計算するための再帰関数を実装しようとしました。
def nth_derivative(f, wrt, n):
if n == 1:
return full_jacobian(f, wrt)
else:
deriv = nth_derivative(f, wrt, n-1)
return full_jacobian(deriv, wrt)
簡単なテストを実行しました。
op = torch.ger(s, s)
deep_deriv = nth_derivative(op, s, 5)
残念ながら、これは私にヘッセ行列を取得することに成功します...しかし高階導関数はありません。多くの高階導関数は0でなければならないことは知っていますが、pytorchが分析的にそれを計算できるのであれば私は好みます。
1つの修正は、勾配計算を次のように変更することでした。
try:
grad = ag.grad(f[Tuple(f_ind)], wrt, retain_graph=True, create_graph=True)[0]
except:
grad = torch.zeros_like(wrt)
これは、これを処理するための受け入れられた正しい方法ですか?または、より良いオプションはありますか?それとも、最初から問題が完全に間違っている理由がありますか?
grad
関数 の呼び出しを繰り返すことができます:
import torch
from torch.autograd import grad
def nth_derivative(f, wrt, n):
for i in range(n):
grads = grad(f, wrt, create_graph=True)[0]
f = grads.sum()
return grads
x = torch.arange(4, requires_grad=True).reshape(2, 2)
loss = (x ** 4).sum()
print(nth_derivative(f=loss, wrt=x, n=3))
出力
tensor([[ 0., 24.],
[ 48., 72.]])