如何在pytorch中定义依赖于输入模型的偏导数的损失函数?

在紧接着论文Neural Ordinary Differential Equations和使用blog的使用JAX库的方法中,了解了如何使用神经网络求解ODE之后,我尝试使用“普通” Pytorch做相同的事情,但发现了一点相当“晦涩”:如何正确地使用输入参数之一的函数(在本例中为模型)的偏导数。

要继续解决2中所示的问题,它打算在条件-2(

我设法(或多或少)使用以下代码复制博客

import torch
import torch.nn as nn
from torch import optim
import matplotlib.pyplot as plt
import numpy as np 

# Define the NN model to solve the problem
class Model(nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        self.lin1 = nn.Linear(1,10)
        self.lin2 = nn.Linear(10,1)

    def forward(self,x):
        x = torch.sigmoid(self.lin1(x))
        x = torch.sigmoid(self.lin2(x))
        return x

model = Model()

# Define loss_function from the Ordinary differential equation to solve
def ODE(x,y):
    dydx,= torch.autograd.grad(y,x,grad_outputs=y.data.new(y.shape).fill_(1),create_graph=True,retain_graph=True)

    eq = dydx + 2.* x * y # y' = - 2x*y
    ic = model(torch.tensor([0.])) - 1.    # y(x=0) = 1
    return torch.mean(eq**2) + ic**2

loss_func = ODE

# Define the optimization
# opt = optim.SGD(model.parameters(),lr=0.1,momentum=0.99,nesterov=True) # Equivalent to blog
opt = optim.Adam(model.parameters(),amsgrad=True) # Got faster convergence with Adam using amsgrad

# Define reference grid 
x_data = torch.linspace(-2.0,2.0,401,requires_grad=True)
x_data = x_data.view(401,1) # reshaping the tensor

# Iterative learning
epochs = 1000
for epoch in range(epochs):
    opt.zero_grad()
    y_trial = model(x_data)
    loss = loss_func(x_data,y_trial)

    loss.backward()
    opt.step()

    if epoch % 100 == 0:
        print('epoch {},loss {}'.format(epoch,loss.item()))

# Plot Results
plt.plot(x_data.data.numpy(),np.exp(-x_data.data.numpy()**2),label='exact')
plt.plot(x_data.data.numpy(),y_data.data.numpy(),label='approx')
plt.legend()
plt.show()

从这里我设法得到如图所示的结果。 enter image description here

问题在于,在ODE函数的定义上,我宁愿传递类似(x,fun)(其中的乐趣是我的模型)之类的东西,而不传递(x,y),以便偏导数和特定可以通过电话进行模型评估。因此,类似

def ODE(x,fun):
    dydx,= "grad of fun w.r.t x as a function"

    eq = dydx(x) + 2.* x * fun(x)        # y' = - 2x*y
    ic = fun( torch.tensor([0.]) ) - 1.  # y(x=0) = 1
    return torch.mean(eq**2) + ic**2

有什么想法吗?预先感谢

编辑

经过一些试验,我找到了一种将模型作为输入传递的方法,但发现了另一种奇怪的行为...新问题是用BC y(x = -2)=求解ODE y''= -2 -1和y(x = 2)= 1,其解析解为y(x)= -x ^ 2 + x / 2 + 4

让我们将之前的代码修改为:

import torch
import torch.nn as nn
from torch import optim
import matplotlib.pyplot as plt
import numpy as np 

# Define the NN model to solve the equation
class Model(nn.Module):
    def __init__(self):
        super(Model,x):
        y = torch.sigmoid(self.lin1(x))
        z = torch.sigmoid(self.lin2(y))
        return z

model = Model()

# Define loss_function from the Ordinary differential equation to solve
def ODE(x,fun):
    y = fun(x)

    dydx = torch.autograd.grad(y,retain_graph=True)[0]

    d2ydx2 = torch.autograd.grad(dydx,grad_outputs=dydx.data.new(dydx.shape).fill_(1),retain_graph=True)[0]

    eq  = d2ydx2 + torch.tensor([ 2.])             # y'' = - 2
    bc1 =  fun(torch.tensor([-2.])) - torch.tensor([-1.]) # y(x=-2) = -1
    bc2 =  fun(torch.tensor([ 2.])) - torch.tensor([ 1.]) # y(x= 2) =  1
    return torch.mean(eq**2) + bc1**2 + bc2**2

loss_func = ODE

因此,在这里,我将模型作为参数传递,并成功导出了两次……到目前为止一切都很好。但是,在这种情况下使用S形函数不仅是不必要的,而且所得到的结果与分析结果相去甚远。

如果我将NN更改为:

class Model(nn.Module):
    def __init__(self):
        super(Model,1)
        self.lin2 = nn.Linear(1,x):
        y = self.lin1(x)
        z = self.lin2(y)
        return z

在这种情况下,我希望优化通过两个线性函数的双遍查询,这将检索一个二阶函数...我得到错误:

  

RuntimeError:差异的张量之一似乎未在图中使用。如果这是所需的行为,请设置allow_unused = True。

在dydx的定义中添加选项并不能解决问题,而将其添加到d2ydx2会得到NoneType定义。

这些图层是否存在问题?

lhgfliuyang 回答:如何在pytorch中定义依赖于输入模型的偏导数的损失函数?

快速解决方案:

allow_unused=True添加到.grad函数中。因此,更改

dydx = torch.autograd.grad(
    y,x,grad_outputs=y.data.new(y.shape).fill_(1),create_graph=True,retain_graph=True)[0]

d2ydx2 = torch.autograd.grad(dydx,grad_outputs=dydx.data.new(
    dydx.shape).fill_(1),retain_graph=True)[0]

收件人

dydx = torch.autograd.grad(
    y,retain_graph=True,allow_unused=True)[0]

d2ydx2 = torch.autograd.grad(dydx,allow_unused=True)[0]

更多说明:

查看allow_unused的操作:

allow_unused (bool,optional): If ``False``,specifying inputs that were not
        used when computing outputs (and therefore their grad is always zero)
        is an error. Defaults to ``False``.

因此,如果您尝试将w.r.t区分为不用于计算值的变量,则会产生错误。另外,请注意,只有在使用线性图层时才会发生错误。

这是因为使用线性图层时,您拥有y=W1*W2*x + b = Wx+b,而dy/dx不是x的函数,而仅仅是W。因此,当您尝试区分dy/dxx时会引发错误。使用Sigmoid后,该错误就会消失,因为dy/dx将是x的函数。为避免该错误,请确保dy/dx是x的函数或使用allow_unused=True

本文链接:https://www.f2er.com/3109099.html

大家都在问