一个深度学习突破的方向:神经常微分方程ODE

https://www.toutiao.com/a6703302712311677452/

 

 2019-06-17 09:18:47

可视化的神经ODE学习动力系统

本文正在追赶AI世界中最近取得的进展。本文的背景论文来自NIPS 2018《Neural Ordinary Differential Equations》(神经常微分方程)。本文在简单介绍论文的基础上,强调实际应用,我们如何在应用程序中使用神经网络。

我们为什么关心ODE?

首先,让我们快速回顾一下常微分方程是什么。它描述了某个过程的时间演变,这个过程取决于一个变量(这就是为什么普通),并且这个时间的变化是通过一个衍生物来描述的:

简单的ODE示例

通常,我们可以谈论解决这个微分方程,如果我们有一些初始条件,我们想看看过程将如何演变到某个最终状态。求解函数也称为积分曲线(因为我们可以将方程积分得到解x(t))。让我们尝试使用SymPy包解决上图中的等式:

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,但通常它们以数字方式求解。最古老和最简单的算法之一是欧拉方法核心思想是使用切线来逐步逼近求解函数:

 

最后,我们最终会得出一个非常简单的公式:

 

n个时间步的离散网格的解是:

 

还有许多具有时间演变的化学,物理和工业领域的过程示例,用ODE描述。

ResNets是ODE解决方案吗?

y_{n+1} = y_n + f(t_n, y_n),在ResNet,叫残余连接,其中一些层的输出为所述层的f()总和和输入y_n。

这基本上是神经ODE的主要思想:神经网络中的残余块链基本上是ODE与Euler方法的解决方案!在这种情况下,系统的初始条件是"时间" 0,它表示神经网络的第一层,并且x(0)将提供正常输入,可以是时间序列、图像,无论你想要什么!"时间" t的最终条件将是神经网络的期望输出:标量值,表示类或其他任何东西的向量。

如果我们记得,这些残余连接是欧拉方法的离散时间步长,这意味着我们可以调节神经网络的深度,只需选择离散方案,因此,使解决方案(如神经网络)更多或不太准确,甚至使它无限层!

具有固定层数的ResNet与具有灵活层数的ODENet之间的差异

欧拉是太原始的ODE解决方法吗?确实如此,让我们用一些抽象概念取代ResNet / EulerSolverNet 作为ODESolveNet,其中ODESolve将是一个函数,它提供ODE(我们的神经网络本身)的解决方案,其精度比欧拉方法好得多。网络架构现在可能如下所示:

nn = Network(
 Dense(...), # making some primary embedding
 ODESolve(...), # "infinite-layer neural network"
 Dense(...) # output layer
)

神经网络是一个可微分的函数,所以我们可以用基于梯度的优化程序来训练它。我们应该如何通过ODESolve()函数反向传播,在我们的例子中它实际上也是一个黑盒子。特别是,我们需要输入和动力学参数的衰减函数梯度。

数学技巧称为伴随灵敏度方法。实质内容如下图所示(L代表我们要优化的主要损失函数):

ODESolve()方法制作"反向传播"梯度

简而言之,伴随着描述过程的原始动力系统,伴随系统通过链规则(这是众所周知的反向传播的根源)向后描述过程每个点处的导数状态。正是根据它,我们可以通过初始状态获得导数,并以类似的方式,通过建模动力学的函数的参数(一个"残差块",或"旧的"欧拉方法中的离散化步骤) 。

神经ODE的可能应用

首先,使用它们而不是"普通ResNets"的优点和动机:

  • 内存效率:我们不需要在反向传播时存储所有参数和渐变
  • 自适应计算:我们可以通过离散化方案平衡速度和准确性,而且在训练和推理时使其不同
  • 参数效率:附近"层"的参数自动捆绑在一起(见论文)
  • 归一化流动新型可逆密度模型
  • 连续时间序列模型:连续定义的动态可以自然地合并在任意时间到达的数据。

除了用计算机视觉替代ResNet和ODENet之外,我认为现在看起来有点不切实际:

  • 将复杂的ODE压缩成单个动态建模神经网络
  • 将其应用于缺少时间步的时间序列
  • 可逆的规范化流程

学习动力系统

正如我们之前所见,微分方程被广泛用于描述复杂的连续过程。当然,在现实生活中,我们将它们视为离散过程,最重要的是,在时间步骤t_i中的许多观察可能只是缺失。假设你想用神经网络建模这样一个系统。在经典的序列建模范例中,我们将如何处理这种情况?不管怎样,把它扔到递归的神经网络上。在这一部分中,我们将看看神经ODE如何处理它们。

我们的设置如下:

1. 定义ODE本身我们将模型化为PyTorch nn.Module()

2. 定义一个简单的(或不是真正的)神经网络,它将模拟从h_t到h_ {t + 1}的两个后续动态步骤之间的动态,或者在动态系统的情况下,x_t和x_ {t + 1}。

3. 运行通过ODE求解器反向传播的优化过程,并最小化实际和建模动态之间的差异。

在以下所有实验中,神经网络只是一个跟随(据说足以用两个变量建模简单函数):

self.net = nn.Sequential(
 nn.Linear(2, 50),
 nn.Tanh(),
 nn.Linear(50, 2),
 )

所有进一步的例子都受到高度启发和惊人的解释。在接下来的小节中,我将展示我们建模的动力系统如何在代码中展示,以及ODENet如何适应系统随时间的演变和相位画像

简单的螺旋函数

在此以及所有未来的可视化中,虚线代表拟合模型。

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)

 

 

上边相空间,下边时间空间。实线代表真实的轨迹,虚线代表神经Neural ODE系统学习的进化过程。

随机矩阵函数

true_A = torch.randn(2,2)/ 2。

 

 

上边相空间,下边时间空间。实线代表真实的轨迹,虚线代表神经Neural 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.]])

 

 

上边相空间,下边时间空间。实线代表真实的轨迹,虚线代表神经Neural 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) 
returndxdt

 

 

上边相空间,下边时间空间。实线代表真实的轨迹,虚线代表神经Neural 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

 

 

上边相空间,下边时间空间。实线代表真实的轨迹,虚线代表神经Neural 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),
 )

 

 

上边相空间,下边时间空间。实线代表真实的轨迹,虚线代表神经Neural ODE 系统学习的进化过程。

神经ODEs作为生成模型

作者还声称他们可以通过VAE框架构建生成时间序列模型,使用神经ODE作为其中的一部分。它是如何工作的?

 

  • 首先,我们使用一些"标准"时间序列算法对输入序列进行编码,假设RNN用于获取进程的主要嵌入
  • 通过神经ODE运行嵌入以获得"连续"嵌入
  • 以VAE方式从"连续"嵌入中恢复初始序列

作为一个概念证明,本文只是重新运行了的代码,它似乎在学习螺旋轨迹方面做得非常好:

 

点是采样噪声轨迹,蓝线是真实轨迹,橙色线代表恢复和插值轨迹

然后,我决定将心跳从心电图(ECG)转换为相位肖像,其中x(t)为时空,x`(t)为衍生空间(如本作品中所示),并尝试适应不同的VAE设置。这个用例可能对这样的可穿戴设备非常有用,因为我们必须恢复信号(我们必须在,但实际上我们是通过,但是ECG是一个连续的信号,不是吗?)。不幸的是,它并没有很好地收敛,显示出过度拟合到单一形式节拍的所有迹象:

 

 

相空间。蓝线 - 真实轨迹,橙色线 - 采样和噪声轨迹,绿线 - 自动编码轨迹

 

 

时间空间。蓝线 - 实信号,橙线 - 采样和噪声信号,绿线 - 自动编码信号

我还尝试了另一个实验:只在每个节拍的部分上学习这个自动编码器并从中恢复整个波形(即让我们推断一个信号)。不幸的是,我没有提出任何有意义的信息,无论是向左还是向右外推这个信号 - 只要折叠到无穷大,无论我对超参数和数据预处理做了什么。也许,读者中的某些人可能会帮助理解出了什么问题:(

结论

神经异构体尚未准备好在实践中使用。这个想法本身很棒,而且通过创新的水平让我想起Geoffrey Hinton的胶囊网络,但现在它们在哪里......?除了神经ODE,它们在小任务上显示出良好的结果,但在接近实际应用或大规模数据集的任何事情上都失败了。

我现在只能看到两个实际应用:

  • 使用ODESolve()层来平衡经典神经网络中的速度/准确度权衡
  • 将常规ODE"挤压"到神经架构中,将它们嵌入到标准数据科学管道中

你可能感兴趣的:(人工智能,神经常微分方程ODE,一个深度学习突破的方向)