如果有Python库象征着差异化程序的简单性,灵活性和实用性,那么它必须是Autograd。
Autograd:缺少的机器学习库
等等,人们使用TensorFlow和PyTorch之外的库吗?
向一群深度学习从业人员询问他们选择的编程语言,无疑您会听到很多关于Python的知识。另一方面,询问他们的入门机器学习库,您很可能会获得混合使用TensorFlow和PyTorch的两个库系统的图片。虽然有很多人可能都熟悉这两种方法,但是在机器学习(ML)的一般商业应用中,往往倾向于使用TensorFlow进行控制,而在人工智能/ ML中的研究项目则 大多使用PyTorch。尽管去年发布的TensorFlow 2.0中 默认引入了急于执行的功能,并且这两个库之间存在着显着的融合 ,并且可以使用以下方法构建静态可执行模型 Torchscript,大多数似乎大部分都坚持使用。
虽然普遍的共识似乎是,如果您想加入一家公司,应该选择TensorFlow以获得更好的部署和边缘支持,如果您想从事学术研究,则应该选择PyTorch以获得灵活性和可读性,但是AI / ML库不仅仅是PyTorch和TensorFlow。就像AI / ML不仅限于深度学习。实际上,支持深度学习的梯度和张量计算有望在从物理学到生物学的各个领域产生广泛影响。虽然我们敢打赌,所谓的ML / AI研究人员短缺是过分夸张的,我们希望在可预见的将来,差异化编程的工具对于各种专业人士将越来越有价值。
可区分的计算比深度学习更大
深度学习是基于对哺乳动物大脑中的计算思想非常松散地使用多层人工神经网络而闻名的,它对计算机视觉和自然语言处理等领域产生了影响。我们还发现,在过去的十年中,与深度学习一起开发的许多硬件和软件课程(梯度下降,函数逼近和加速张量计算)在没有神经网络的情况下也得到了有趣的应用。
量子电路参数的自动微分和 梯度下降在 嘈杂的中级量子(NISQ)计算设备(
即
现在可用的量子计算设备)时代为量子计算提供了有意义的实用工具。DeepMind在CASP13 蛋白质折叠预测会议上令人印象深刻的倒数第二 倒一步,比赛使用的是直接在预测的氨基酸位置上应用的梯度下降,而不是深深的神经网络,因为Google Alphabet子公司是众所周知的。这些只是人工神经元范式不受限制的可区分编程功能的几个示例。
深度学习可以归类为更一般的可区分编程的子空间。深度神经进化是指通过选择对神经网络进行优化,而没有明确的区分或梯度下降。
除无梯度优化方法(例如神经进化/进化算法)外,差异化编程是涵盖大多数深度学习的更广泛的编程范例。Facebook首席AI科学家Yann LeCun在Facebook帖子中吹捧了差异化编程的可能性 (内容 反映在Github要点中)。听到LeCun讲的话,可区分的编程只不过是对现代深度学习的重新命名,它结合了带有循环和条件的神经网络的动态定义。
我认为,广泛采用差异化编程的后果更接近于安德烈·卡尔帕西(Andrej Karpathy)所描述的“软件2.0”,尽管他还将讨论主要限于 神经网络。有理由争辩说,与LeCun或Karpathy所描述的相比,软件2.0 /可区分编程在整体上是一个更广泛的范例。可微编程代表了超越神经网络作为函数逼近器的约束的一般化,以促进针对广泛系统的基于梯度的优化算法。如果有Python库象征着差异化程序的简单性,灵活性和实用性,那么它必须是Autograd。
将深度学习与差异化编程相结合
关于任意物理模拟和数学原语的区分为深度神经网络效率低下或无效的解决方案提供了机会。这并不是说您应该抛弃所有深度学习的直觉和经验。相反,最令人印象深刻的解决方案将结合深度学习的要素和可区分编程的更广泛功能,例如Degrave等人的工作 。2018年,其作者将微分物理引擎与神经网络控制器相结合来解决机器人控制任务。
从本质上讲,他们将环境的不同部分扩展到了神经网络之外,以包括模拟的机器人运动学。然后,他们可以将机器人环境的参数反向传播到神经网络策略中,从而使优化过程的采样效率提高约6到8倍。他们选择使用 Theano 作为自动区分库,这阻止了他们通过条件语句进行区分,从而限制了他们可以实现的联系约束的类型。使用Autograd或什至是最新版本的PyTorch或Tensorflow 2.0构建的可区分物理模拟器,通过动态分支支持区分,将有更多的可能性来优化神经网络机器人控制器,
例如
提供更真实的碰撞检测。
深层神经网络的 通用逼近能力(通用近似性定理)使它们成为解决科学,控制和数据科学问题的不可思议的工具,但是有时,这种灵活性比实用性更具责任感,任何为过度拟合而苦苦挣扎的人都可以证明。正如约翰·冯·诺伊曼(John von Neumann)的一句名言所说:“有了四个参数,我就可以适应一头大象,而有了五个参数,我就可以使他扭动他的树干。”
在现代机器学习实践中,这意味着要小心,不要使模型与数据集不匹配,这对于小型数据集来说非常容易被发现。换句话说,对于只有几百到几千个样本的许多定制数据集,大型转换网络可能会显得过分杀伤力。例如,在许多物理问题中,最好以数学方式描述您的问题,并对自由参数进行梯度下降。Autograd是一个非常适合此方法的Python软件包,特别适合那些对Python感兴趣的数学家,物理学家以及其他使用Python矩阵和数组计算软件包NumPy在低层次上描述问题有良好实践的人。
Autograd:您可以通过NumPy进行的任何操作,都可以与众不同
这是Autograd可以做什么的简单示例:
import autograd.numpy as np
from autograd import elementwise_grad as egrad
import matplotlib.pyplot as plt
x = np.linspace(-31.4,31.4, 256)
sinc = lambda x: np.sin(x) / x
plt.figure(figsize=(12,7))
plt.title(“sinc function and derivatives”, fontsize=24)
my_fn = sinc
for ii in range(9):
plt.plot(x, my_fn(x), lw=3, label=”d{} sinc(x)/dx{}”.format(ii,ii))
plt.legend(fontsize=18)
plt.axis([-32, 32, -0.50, 1.2])
plt.savefig(“./sinc_grad{}.png”.format(ii))
my_fn = egrad(my_fn)
与Autograd的差异化。在这种情况下,Autograd能够微分到7阶导数,然后再遇到x = 0周围的一些数值稳定性问题(请注意图中心的橄榄绿色尖峰)。
Autograd是一个功能强大的自动区分库,可以区分原生Python和NumPy代码。可以将导数计算为任意顺序(可以采用导数的导数等),只要最终输出为标量(例如,损失函数),就可以将其分配给多个参数数组。产生的代码是 Pythonic,也就是可读且可维护的,并且不需要学习新的语法或样式。这意味着我们不必担心存储复杂的API,例如torch.nn或tf.keras.layers的内容,而我们可以集中精力解决问题的细节,
例如
将数学转化为代码。
Autograd + NumPy是一个成熟的库
,可以维护但不再开发,因此,将来的更新不会破坏您的项目。
您
可以
使用Autograd轻松实现神经网络,因为稠密神经层(矩阵乘法)和卷积的数学原语(您可以轻松地使用Fourier变换,或者使用来自scipy的convolve2d)在NumPy中具有相对较快的实现。
如果您复制要点并在本地虚拟环境中运行,则需要同时安装autograd和scikit-learn,后者用于其数字数据集。完成所有设置后,运行代码应生成如下的进度报告:
epoch 10, training loss 2.89e+02, train acc: 5.64e-01, val loss 3.94e+02, val accuracy 4.75e-01
total time: 4.26, epoch time 0.38
epoch 20, training loss 8.79e+01, train acc: 8.09e-01, val loss 9.96e+01, val accuracy 7.99e-01
total time: 7.73, epoch time 0.33
epoch 30, training loss 4.54e+01, train acc: 9.20e-01, val loss 4.55e+01, val accuracy 9.39e-01
total time: 11.49, epoch time 0.35
…
epoch 280, training loss 1.77e+01, train acc: 9.99e-01, val loss 1.39e+01, val accuracy 9.83e-01
total time: 110.70, epoch time 0.49
epoch 290, training loss 1.76e+01, train acc: 9.99e-01, val loss 1.39e+01, val accuracy 9.83e-01
total time: 115.41, epoch time 0.43
经过不到两分钟的培训,这是98.3%验证准确度的相当不错的结果。稍微调整一下超参数,您就可以将性能提高到100%精度或非常接近。Autograd轻松高效地处理了这个小型数据集(虽然Autograd和NumPy操作不在GPU上运行,但矩阵乘法之类的基元确实利用了多个内核)。但是,如果您要做的只是构建一个浅层MLP,则可以使用更加主流和现代的机器学习库,在开发和计算时间方面更快地做到这一点。
当然,在这样的低级构建简单模型中有一些实用性,当然,控制是优先考虑的事情或作为学习练习,但是,如果最终目标是一个小的密集神经网络,我们建议您坚持使用PyTorch或TensorFlow与诸如GPU之类的硬件加速器的简洁性和兼容性。相反,让我们深入研究一些有趣的事情:模拟光学神经网络。以下教程确实涉及了一些物理知识和相当多的代码:如果不是,那么您可以随时跳到下一部分,在此我们将讨论Autograd的一些限制。
用Autograd模拟光学神经网络
光学神经网络(ONNs)是一个古老的想法,科学期刊《应用光学》(Applied Optics)于1987年 和1993 年再次 发表关于该主题的专题文章 。
由于用于描述神经网络和光学传播的数学上的相似性,光是实现神经网络的一种有吸引力的物理现象。得益于透镜的 傅立叶变换特性 和傅立叶变换的 卷积特性 ,卷积层可以通过在2个焦距和一个透镜远离输入平面(这称为4f相关器)之后放置一个摄动元素来实现。 矩阵乘法可以通过将元素设置为2个焦距和1个镜头来实现。但这不是光学课程,它是编码教程,所以让我们看一些代码!
要安装必要的依赖关系,请使用所选的环境管理器激活所需的虚拟环境,并使用pip安装Autograd和scikit-image(如果尚未安装)。
pip install autograd
pip install scikit-image
我们将模拟一个本质上充当单输出发生器的光学系统,通过使它通过一系列均匀间隔的相位图像来处理平坦的输入波前。为了使本教程相对简单并减少行数,我们将尝试仅匹配单个目标图像,如下所示(如果需要,可以将图像下载到工作目录中)。完成本简单教程之后,您可能会倾向于尝试构建光学分类器,自动编码器或其他一些图像转换。
现在对于一些Python,从导入我们需要的包开始。
import autograd.numpy as np
from autograd import grad
import matplotlib.pyplot as plt
import time
import skimage
import skimage.io as sio
我们将使用角度光谱方法来模拟光学传播。这是近场条件的好方法,在这种情况下,镜头或光束的孔径大小与传播距离相似。以下函数在给定起始波阵面及其尺寸,光的波长和传播距离的情况下执行角度光谱方法的传播。
def asm_prop(wavefront, length=32.e-3, \
wavelength=550.e-9, distance=10.e-3):
if len(wavefront.shape) == 2:
dim_x, dim_y = wavefront.shape
elif len(wavefront.shape) == 3:
number_samples, dim_x, dim_y = wavefront.shape
else:
print(“only 2D wavefronts or array of 2D wavefronts supported”)
assert dim_x == dim_y, “wavefront should be square”
px = length / dim_x
l2 = (1/wavelength)**2
fx = np.linspace(-1/(2*px), 1/(2*px) – 1/(dim_x*px), dim_x)
fxx, fyy = np.meshgrid(fx,fx)
q = l2 – fxx**2 – fyy**2
q[q<0] = 0.0
h = np.fft.fftshift(np.exp(1.j * 2 * np.pi * distance * np.sqrt(q)))
fd_wavefront = np.fft.fft2(np.fft.fftshift(wavefront))
if len(wavefront.shape) == 3:
fd_new_wavefront = h[np.newaxis,:,:] * fd_wavefront
New_wavefront = np.fft.ifftshift(np.fft.ifft2(\
fd_new_wavefront))[:,:dim_x,:dim_x]
else:
fd_new_wavefront = h * fd_wavefront
new_wavefront = np.fft.ifftshift(np.fft.ifft2(\
fd_new_wavefront))[:dim_x,:dim_x]
return new_wavefront
而不是将我们的ONN限制为卷积或矩阵乘法运算,我们将通过一系列均匀间隔的相位对象图像传播光束。从物理上讲,这类似于通过一系列薄的波浪形玻璃板照射相干的光束,仅在这种情况下,我们将使用Autograd反向传播通过系统来设计它们,以便它们引导来自输入波前的光匹配最后给出给定的目标模式。通过相位元素后,我们将在相当于图像传感器的位置上收集光。这给了我们从复杂场到实值强度转换的良好非线性,我们可以通过将其中一些叠加在一起来构建更复杂的光学神经网络。
通过穿过一系列短距离分隔的相位图像来定义每一层。这在计算上被描述为在短距离内传播,然后是薄相位板(实现为乘法):
def onn_layer(wavefront, phase_objects, d=100.e-3):
for ii in range(len(phase_objects)):
wavefront = asm_prop(wavefront * phase_objects[ii], distance=d)
return wavefront
在Autograd中训练模型的关键在于定义一个返回标量损失的函数。然后可以将此损失函数包装在Autograd的grad函数中以计算梯度。您可以指定哪个参数包含用于计算grad的argnum参数的梯度的参数,并且请记住,损失函数必须返回单个标量值,而不是数组。
def get_loss(wavefront, y_tgt, phase_objects, d=100.e-3):
img = np.abs(onn_layer(wavefront, phase_objects, d=d))**2
mse_loss = np.mean( (img – y_tgt)**2 + np.abs(img-y_tgt) )
return mse_loss
get_grad = grad(get_loss, argnum=2)
首先,让我们读入目标图像并设置输入波前。随意使用您选择的64 x 64图像,或者从本文前面的位置下载灰度笑脸图像。
# target image
tgt_img = sio.imread(“./smiley.png”)[:, :, 0]
y_tgt = 1.0 * tgt_img / np.max(tgt_img)
# set up input wavefront (a flat plane wave with an 16mm aperture)
dim = 128
side_length = 32.e-3
aperture = 8.e-3
wavelength = 550.e-9
k0 = 2*np.pi / wavelength
px = side_length / dim
x = np.linspace(-side_length/2, side_length/2-px, dim)
xx, yy = np.meshgrid(x,x)
rr = np.sqrt(xx**2 + yy**2)
wavefront = np.zeros((dim,dim)) * np.exp(1.j*k0*0.0)
wavefront[rr <= aperture] = 1.0
接下来,定义学习速率,传播距离和模型参数。
lr = 1e-3
dist = 50.e-3
phase_objects = [np.exp(1.j * np.zeros((128,128))) \
for aa in range(32)]
losses = []
如果您熟悉使用PyTorch或类似库来训练神经网络,则训练循环应该看起来很熟悉。我们称我们先前定义的梯度函数(这是我们编写的用于计算损失的函数的函数转换),并将得到的梯度应用于模型的参数。我发现该模型通过仅通过梯度的相位而不是原始复数梯度本身来更新参数(phase_objects),可以获得更好的结果。使用NumPy的np.angle可以访问渐变的实值相位分量,并通过np.exp(1.j * value)将其转换回复数值。
for step in range(128):
my_grad = get_grad(wavefront, y_tgt, phase_objects, d=dist)
for params, grads in zip(phase_objects, my_grad):
params -= lr * np.exp( -1.j * np.angle(grads))
loss = get_loss(wavefront, y_tgt, phase_objects,d=dist)
losses.append(loss)
img = np.abs(onn_layer(wavefront, phase_objects))**2
print(“loss at step {} = {:.2e}, lr={:.3e}”.format(step, loss, lr))
fig = plt.figure(figsize=(12,7))
plt.imshow(img / 2.0, cmap=”jet”)
plt.savefig(“./smiley_img{}.png”.format(step))
plt.close(fig)
fig = plt.figure(figsize=(7,4))
plt.plot(losses, lw=3)
plt.savefig(“./smiley_losses.png”)
如果一切顺利,您将看到均方误差损失单调减少,并且该代码将保存一系列描述光网络输出的数字,因为它越来越接近与目标图像匹配。
尝试匹配目标图像的光学系统的优化。每个带有蓝色背景的编号图像都是在不同训练步骤中输出的模型。毫不奇怪,对于单个样本的训练,损失在训练过程中会平稳地减少。
Autograd的用途和局限性
Autograd是一个灵活的自动差异化软件包,它在许多方面影响了主流机器学习库。确定像机器学习这样的迅速发展的空间中不同思想如何相互影响的祖先并不总是那么容易。但是,强制性,按运行定义方法在Chainer,PyTorch以及2.0之后的TensorFlow版本(主要是急于执行)中很突出(
动态图
)。根据libraries.io的说法,另外 十个Python软件包依赖于Autograd,包括用于解决逆运动学, 灵敏度分析和 高斯过程的软件包 。我个人最喜欢的是量子机器学习软件包 PennyLane。
Autograd可能没有PyTorch或TensorFlow强大,并且没有所有最新的深度学习技巧的实现,但是在某些方面,这可能是开发某些阶段的优势。不需要记住很多专门的API,对于熟悉Python和/或NumPy的人来说,学习曲线特别温和。它没有部署或扩展的任何条件,但是对于控制和自定义很重要的项目而言,它简单有效。它特别适合需要将抽象思想从数学转换为代码,以在较低的实现水平上构建任意机器学习或优化解决方案的数学家和物理学家。