作者 | Alexandr Honchar
来源 | Medium
编辑 | 代码医生团队
今天介绍的主题来自NIPS 2018最佳论文:神经常微分方程(神经ODE)。
在本文中,将简要介绍一下这篇论文的重要性、实际应用以及如何应用这种神经网络。
相关代码可以查看此GitHub存储库,建议您在Google Colab中启动它。
https://arxiv.org/abs/1806.07366
https://github.com/Rachnog/Neural-ODE-Experiments
什么是ODE?
首先,快速回顾一下常微分方程:它描述了某个过程的时间演变,这个过程取决于一个变量,并且这个时间的变化是通过一个衍生物来描述的。
简单的ODE示例
解微分方程,可以理解为有一些初始条件(此时过程开始),想看看过程将如何演变到某个最终状态。求解函数称为积分曲线(因为可以将方程积分得到解x(t))。相关代码如下:
from sympy import dsolve, Eq, symbols, Function
t = symbols('t')
x = symbols('x', cls=Function)
deqn1 = Eq(x(t).diff(t), 1 - x(t))
sol1 = dsolve(deqn1, x(t))
最终将返回解决方案
Eq(x(t), C1*exp(-t) + 1)
其中C1是常数,可以在给定一些初始条件时确定。如果以适当的形式给出,则可以分析地解析ODE,但通常它们以数字方式求解。最古老和最简单的算法之一是欧拉方法:核心思想是使用切线来逐步逼近求解函数:
http://tutorial.math.lamar.edu/Classes/DE/EulersMethod.aspx
请访问图片下方的链接以获取更详细的说明,最终会得出一个非常简单的公式
http://tutorial.math.lamar.edu/Classes/DE/EulersMethod.aspx
n个时间步的离散网格的解是
http://tutorial.math.lamar.edu/Classes/DE/EulersMethod.aspx
ResNets是ODE解决方案吗?
残差连接的公式: Y_ {N + 1} = y_n + F(t_n,y_n)。其中一些层的输出为所述层的输出的总和f()的本身和输入y_n此层。这基本上是神经ODE的主要思想:神经网络中的残差连接块链基本上是ODE与Euler方法的解决方案!在这种情况下,系统的初始条件是“时间” 0,它表示神经网络的第一层,并且x(0)将提供正常输入,可以是时间序列,图像,无论你想要什么!“时间” t的最终条件 将是神经网络的期望输出:标量值,表示类或其他任何东西的向量。
如果这些残差连接是欧拉方法的离散时间步长,这意味着可以调节神经网络的深度,只需选择离散方案,因此,使解决方案(又称神经网络)更多或不太准确,甚至使它无限层!
具有固定层数的ResNet与具有灵活层数的ODENet之间的差异
如果用一些抽象概念取代ResNet / EulerSolverNet 作为ODESolveNet,其中ODESolve将是一个函数,它提供ODE(低键:我们的神经网络本身)的解决方案,其精度比欧拉方法好得多。网络架构如下所示:
nn = Network(
Dense(...), # making some primary embedding
ODESolve(...), # "infinite-layer neural network"
Dense(...) # output layer
)
因为神经网络是一个可微分的函数,所以我们可以用基于梯度的优化程序来训练它。在实现过程中,可以通过ODESolve() 函数反向传播。
为ODESolve()方法制作“反向传播”
在反向传播过程中,系统通过反向链式传导的规则向后描述过程的每个点处的导数状态。这一个过程可以通过初始状态获得导数,并以类似的方式,通过建模动力学的函数的参数(一个“残差块”,或“旧的”欧拉方法中的离散化步骤) 。
神经ODE的应用
使用ODE代替“ResNets”的优点和动机:
当然它也有一定的缺点,具体如下:
对于缺点的描述,以及相关理论,请参考原始论文。同时,这里还有一些实际的例子,所有的实验代码如下:
https://github.com/Rachnog/Neural-ODE-Experiments
学习动力系统
微分方程可以被广泛用于描述复杂的连续过程。在现实生活中,可以将它们视为离散过程,因为在时间步骤t_i中的许多观察可能会有缺失。下面将介绍如何使用神经ODE处理它们。
具体步骤如下:
在实验中,使用一个简单的序列神经网络模型,具体代码如下:
self.net = nn.Sequential(
nn.Linear(2, 50),
nn.Tanh(),
nn.Linear(50, 2),
)
具体代码可以参考如下链接
https://nbviewer.jupyter.org/github/urtrial/neural_ode/
在可视化代码中,虚线代表拟合模型
true_A = torch.tensor([[-0.1, 2.0], [-2.0, -0.1]])
class Lambda(nn.Module):
def forward(self, t, y):
return torch.mm(y, true_A)
上边的相空间,下边的时空。直线代表真实的轨迹并且点缀一个 - 用于神经ODE系统学习的进化
随机矩阵函数
true_A = torch.randn(2, 2)/2.
上边的相空间,下边的时空。直线代表真实的轨迹并且点缀一个 - 用于神经ODE系统学习的进化
Volterra-Lotka系统
a, b, c, d = 1.5, 1.0, 3.0, 1.0
true_A = torch.tensor([[0., -b*c/d], [d*a/b, 0.]])
上边的相空间,下边的时空。直线代表真实的轨迹并且点缀一个 - 用于神经ODE系统学习的进化
非线性函数
true_A2 = torch.tensor([[-0.1, -0.5], [0.5, -0.1]])
true_B2 = torch.tensor([[0.2, 1.], [-1, 0.2]])
class Lambda2(nn.Module):
def __init__(self, A, B):
super(Lambda2, self).__init__()
self.A = nn.Linear(2, 2, bias=False)
self.A.weight = nn.Parameter(A)
self.B = nn.Linear(2, 2, bias=False)
self.B.weight = nn.Parameter(B)
def forward(self, t, y):
xTx0 = torch.sum(y * true_y0, dim=1)
dxdt = torch.sigmoid(xTx0) * self.A(y - true_y0) + torch.sigmoid(-xTx0) * self.B(y + true_y0)
return dxdt
上边的相空间,下边的时空。直线代表真实的轨迹并且点缀一个 - 用于神经ODE系统学习的训练。
可以看到,单个“残差块”无法很好地学习这个过程,因此要使用更加复杂的函数进行拟合。
神经网络功能
通过具有随机初始化权重的多层感知器对函数进行完全参数化:
true_y0 = torch.tensor([[1., 1.]])
t = torch.linspace(-15., 15., data_size)
class Lambda3(nn.Module):
def __init__(self):
super(Lambda3, self).__init__()
self.fc1 = nn.Linear(2, 25, bias = False)
self.fc2 = nn.Linear(25, 50, bias = False)
self.fc3 = nn.Linear(50, 10, bias = False)
self.fc4 = nn.Linear(10, 2, bias = False)
self.relu = nn.ELU(inplace=True)
def forward(self, t, y):
x = self.relu(self.fc1(y * t))
x = self.relu(self.fc2(x))
x = self.relu(self.fc3(x))
x = self.relu(self.fc4(x))
return x
上边的相空间,下边的时空。直线代表真实的轨迹并且点缀一个 - 用于神经ODE系统学习的进化
这里2-50-2网络失败可怕,因为它太简单了,增加它的深度:
self.net = nn.Sequential(
nn.Linear(2, 150),
nn.Tanh(),
nn.Linear(150, 50),
nn.Tanh(),
nn.Linear(50, 50),
nn.Tanh(),
nn.Linear(50, 2),
)
上边的相空间,下边的时空。直线代表真实的轨迹并且点缀一个 - 用于神经ODE系统学习的更新
神经ODEs作为生成模型
作者还声称可以通过VAE框架构建生成时间序列模型,使用神经ODE作为其中的一部分。它是如何工作的?
原始纸上的插图
作为一个概念证明,只是重新运行了这个存储库中的代码,它似乎在学习螺旋轨迹方面做得非常好:
https://nbviewer.jupyter.org/github/urtrial/neural_ode/blob/master/Neural%20ODEs%20%28Russian%29.ipynb
点是采样噪声轨迹,蓝线是真实轨迹,橙色线代表恢复和插值轨迹
其中x(t)为时空,x`(t)为衍生空间,并适应不同的VAE设置。这个用例可能对Mawi Band这样的可穿戴设备非常有用,因为必须恢复信号(必须在深度学习的帮助下进行,但实际上是通过深度学习来实现的,但是ECG是一个连续的信号,不是吗?)。不幸的是,它并没有很好地收敛,显示出过度拟合到单一形式节拍的所有迹象:
相空间。蓝线 - 真实轨迹,橙色线 - 采样和噪声轨迹,绿线 - 自动编码轨迹
时空。蓝线 - 实信号,橙线 - 采样和噪声信号,绿线 - 自动编码信号
还尝试了另一个实验:只在每个节拍的部分上学习这个自动编码器并从中恢复整个波形。不幸的是,没有提出任何有意义的信息,无论是向左还是向右外推这个信号 - 只要折叠到无穷大,无论对超参数和数据预处理做了什么。也许读者中的某些人可能会帮助理解出了什么问题
扩展
很明显神经ODE旨在学习相对简单的过程,因此需要一个能够为更丰富的函数族建模的模型。目前已经有两种增强的方法:
1.增强神经ODE:
https://github.com/EmilienDupont/augmented-neuralodes
2.神经跳随机DEs:
https://www.groundai.com/project/neural-jump-stochastic-differential-equations/1
结论
神经异构在技术还没有完善时,就尝试应用到实践中。这是个很好的想法。但是它不仅让人们想起Geoffrey Hinton的胶囊网络,现在它们在哪里......?它们可以在一些玩具任务上显示出良好的结果,但在接近实际应用或大规模数据集的任务上都失败了。
关于ODE现在能看到的,只有两个实际应用:
希望这个方向能有进一步的发展,产出更多的模型结构。