在紧接着论文Neural Ordinary Differential Equations和使用blog的使用JAX库的方法中,了解了如何使用神经网络求解ODE之后,我尝试使用“普通” Pytorch做相同的事情,但发现了一点相当“晦涩”:如何正确地使用输入参数之一的函数(在本例中为模型)的偏导数。
要继续解决2中所示的问题,它打算在条件-2(<2)中以条件y(x = 0)= 1求解ODE y'= -2 * x * y。 = x <=2。不是使用有限差分,而是用具有10个节点的单层的NN(y(x)= NN(x))代替了解决方案。
我设法(或多或少)使用以下代码复制博客
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(),lr=0.1,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, self).__init__()
self.lin1 = nn.Linear(1,10)
self.lin2 = nn.Linear(10,1)
def forward(self, 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, x,
grad_outputs=y.data.new(y.shape).fill_(1),
create_graph=True, retain_graph=True)[0]
d2ydx2 = torch.autograd.grad(dydx, x,
grad_outputs=dydx.data.new(dydx.shape).fill_(1),
create_graph=True, 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, self).__init__()
self.lin1 = nn.Linear(1,1)
self.lin2 = nn.Linear(1,1)
def forward(self, x):
y = self.lin1(x)
z = self.lin2(y)
return z
在这种情况下,我希望优化通过两个线性函数的双遍查询,这将检索一个二阶函数...我得到错误:
RuntimeError:差异的张量之一似乎未在图中使用。如果这是所需的行为,请设置allow_unused = True。
在dydx的定义中添加选项并不能解决问题,而将其添加到d2ydx2会得到NoneType定义。
这些图层是否存在问题?
答案 0 :(得分:0)
快速解决方案:
将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, x, grad_outputs=dydx.data.new(
dydx.shape).fill_(1), create_graph=True, retain_graph=True)[0]
收件人
dydx = torch.autograd.grad(
y, x,
grad_outputs=y.data.new(y.shape).fill_(1),
create_graph=True, retain_graph=True, allow_unused=True)[0]
d2ydx2 = torch.autograd.grad(dydx, x, grad_outputs=dydx.data.new(
dydx.shape).fill_(1), create_graph=True, retain_graph=True, 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/dx
与x
时会引发错误。使用Sigmoid后,该错误就会消失,因为dy/dx
将是x
的函数。为避免该错误,请确保dy/dx
是x的函数或使用allow_unused=True