内容简介:鉴于机器学习(ML)对编程语言、编译器和生态系统的众多需求,现在已经有很多有趣的发展。不仅TensorFlow 和 PyTorch 等现有系统间的权衡得不到解决,而且这两个框架都包含不同的「静态图」和「eager execution」接口,但它们的形式已经比以前更加清晰。与此同时,机器学习模型基本上是可微分算法的思想(通常称为可微分编程)已经流行起来。当前的机器学习框架遇到了阻碍,很多已有的新项目都完全移除了计算图,从而使可微分编程成为主流。例如,由 Theano 团队开发的 Myia 可以求微分并编译
鉴于机器学习(ML)对编程语言、编译器和生态系统的众多需求,现在已经有很多有趣的发展。不仅TensorFlow 和 PyTorch 等现有系统间的权衡得不到解决,而且这两个框架都包含不同的「静态图」和「eager execution」接口,但它们的形式已经比以前更加清晰。与此同时,机器学习模型基本上是可微分算法的思想(通常称为可微分编程)已经流行起来。
当前的机器学习框架遇到了阻碍,很多已有的新项目都完全移除了计算图,从而使可微分编程成为主流。例如,由 Theano 团队开发的 Myia 可以求微分并编译 Python 的一个子集为高性能 GPU 代码。Swift forTensorFlow 作为 Swift 语言的扩展,它可以将兼容的函数编译为TensorFlow 计算图。最后,Flux 生态系统为Julia编译器提供了一些机器学习专用的工具,包括:first-class gradients、即时 CUDA 核编译、自动批处理(automatic batching)以及对新硬件(例如 TPU)的支持。
所有这些项目都有巨大的潜力,但目前看来Julia具有优势。
Flux 简介
我们需要一种语言来编写可微分算法,Flux 使Julia变成了这样的语言。Julia专为数学和数值计算而设计,非常适合表达机器学习算法。同时,它在编译器中融合了现代设计和新思想,可以更轻松地满足尖端 ML 的高性能需求。
典型的框架通常包含数十万行 C++代码,Flux 却只有千行Julia代码。只需要一个求梯度的包(Zygote.jl)、一个用于 GPU 支持的包(CuArrays.jl)、再加上一些轻量函数,我们就能得到一个功能齐全的机器学习堆栈。
与其他下一代机器学习系统一样,Flux 致力于提供直观(「eager」或「define-by-run」)的接口,并对任何类型的计算图构建或性能注释进行严格控制。从控制流、数据结构到宏,Flux 支持语言的所有特征。用户可以在Jupyter笔记本中交互式地写代码,并将高性能数值计算与方便的绘图、可视化相结合。但我们也希望获得传统上由「静态图」框架所带来的好处,例如零开销源到源 AD、OP 融合、多 GPU /分布式训练和二进制部署等。
我们怎么能做到这一切?实际上,我们需要直接从Julia语法中提取和分析「静态图」,这实际完全上是编译器的正常工作。通过适当的角度来看,大多数机器学习系统问题都是标准的且经过充分研究的编译器问题。使用编译语言足以解决许多问题,扩展该编译器是解决更多问题的最佳方法。本文仅介绍了我们目前在该领域的工作范例,即求梯度、为 GPU 和 TPU 提供代码编译,以及自动批处理。
求梯度
推动反向模式求微分的极限,我们将此视为语言层面的问题。求微分是一种符号转换,属于编译器的领域。现有框架通过追踪(实际上是一种部分评估或抽象解释)来实现这一目标。人们引入了一种新的张量类型,它记录了所执行的所有基本数学运算,生成一个计算图(或符号表达式),其中删除了宿主语言的控制流和数据结构。然而,这给出了一个艰难的权衡:我们要么接受解释器的开销(eager execution),要么固定用户的控制流并限制可以构建的模型种类(静态图)。
反之,如果「计算图」就是Julia自己的语法呢?通过将这个想法发挥到极致,我们构建了 Zygote,它直接在 SSA 形式的中间表征(IR)上工作,支持控制流、递归、数据结构和宏等语言功能。然后,我们可以通过 LLVM 之类的编译器生成 SSA 形式的伴随代码,并将传统编译器优化的所有优势应用于前向和后向传播。此外,这种方法还为扩展该编译器基础结构提供了可能,可以使用更高级和特定领域的优化,例如用于 TPU 等加速器的内核融合和编译。TensorFlow 的 Swift 和 Myia 开发人员在源到源 AD 技术的复兴中正在探索类似的方法。
Julia用于此任务的一个关键优势是它可用于实现基本数值计算库,如微分方程求解器或优化库;这巧妙地解决了机器学习社区不断增长的需求,研究人员通过高性能代码(如光线追踪和物理引擎)进行反向传播,但求梯度仍必须在 C++中手动实现。相比之下,由于Julia的实现是用Julia编写的,因此可以轻松对从 ODE 到金融定价模型等求微分。将这些强大的 工具 带入模型是深度学习真正成为可微分编程的关键。
编译Julia到 GPU 上
GPU 编程是现代机器学习的重要组成部分,但 GPU 通常被视为实现细节。因为框架在内部提供内核,但用户只能使用一组有限的数学运算,无法直接对 GPU 进行编程。相比之下,Julia中的 GPU 编程一直是一流的 CUDA 内核(可以很好地编写并从脚本或 notebook 中运行)。如下简单的向量加法内核看起来类似于 CUDA C:
function kernel_vadd(a, b, c) i = (blockIdx().x-1) * blockDim().x + threadIdx().x c[i] = a[i] + b[i] return end
但是,Julia的类型特化可以在 GPU 上实现一组强大的附加抽象。例如,上面的代码不限于浮点数的密集数组,而是可以给出复数的稀疏数组;Julia的常规特化机制将动态地生成一组新的 PTX 指令。我们甚至可以将此代码进一步抽象为可利用「+」函数的「高阶内核」,从而在四行代码内创建一整套函数 map(f,x,y)。
这可以实现一些强大的技巧,即使你自己从不编写 CUDA 代码。例如,我们可以透明地将大型广播(broadcast)表达式(例如 1 /(1 + exp(-x))及其向后传递融合到单个 GPU 内核中,从而获得显着加速。我们期望原生 GPU 代码生成能力和生态系统将为各种基于Julia的机器学习库提供支持。
编译Julia到 TPU 上
更进一步,谷歌最近开放了云 TPU 使用的 XLA IR,使得其他框架和用户都可以利用这个重量级硬件。XLA 功能强大但有限制:它无法运行 Python 解释器,当然也没有良好的性能。
而我们只需要从编写的Julia程序中提取「静态图」并将其直接编译为 XLA,从而允许Julia本身在 TPU 上运行。(事实上,这只是Julia一般编译过程的简单扩展,它在将程序发送到 LLVM 之前从程序中提取最大的「静态子图」。)这使我们可以充分利用Julia语言的表现力,包括控制流、递归、多调度、高阶函数、强大的数据结构和抽象、自定义数值类型,以及现有的包,如微分方程求解器和线性代数例程。所有这些都在获得高性能收缩阵列引擎的优势的同时,在 TPU 内运行。你今天就可以尝试,其中包括 ResNet 等大型机器学习模型和 TSVD 等线性代数例程。
项目地址:https://github.com/JuliaTPU/XLA.jl
自动批处理(AutomaticBatching)
为了从这些加速器中获得最大收益(每个内核启动可能会产生大量开销,但是在输入大小上可以很好地扩展),批处理程序通常会同时将前向和反向传播应用于多个训练样本。在简单的情况下,例如使用卷积网络,通过在额外的批量维度上拼接 10 张图像来处理这个问题会变得很简单。但是,当处理可变结构的输入(例如树或图形)时,此任务变得更加困难。
大多数研究人员通过人工完成批处理代码来解决这个问题,这样做的成本非常大。人们已经针对不同的框架提出了不同的解决方案(DyNet、TensorFlow Fold,它试图在可能的情况下将一些高级 OP 一起批处理,但是这些通常要么具有其自身的可用性问题,要么没有实现手写代码的性能。
我们认为这个问题与单程序多数据(SPMD)编程的问题完全相同,单程序多数据编程几十年来一直被语言和编译器社区充分研究。实际上,它与 GPU 内部使用的并行模型非常相似,并且已经实现 CPU 的 SIMD 单元的编译器变换。通过从这项工作中汲取灵感,我们在Julia中实现了相同的变换,为标量 SIMD 单元和模型级批处理提供 SPMD 编程。这使我们能够编写对单个样本进行操作的简单代码,同时仍然在现代硬件上获得最佳性能。
结论
我们相信机器学习的未来取决于编程语言和编译器技术,尤其是扩展新的或现有的语言以满足机器学习研究的高要求。这不仅适用于机器学习社区,也适用于一般的数值规划;能够支持微分、向量化和新型硬件的编程语言将足以推动科学的许多进步。
原文链接:https://julialang.org/blog/2018/12/ml-language-compiler
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 深度解析Python深度学习框架的对比
- 深度学习框架的前世今生
- 人工智能深度学习Caffe框架介绍,优秀的深度学习架构
- 三种 JavaScript 深度学习框架介绍
- 深度:从零编写一个微前端框架
- 深度:从零编写一个微前端框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
程序员修炼之道(影印版)
Andrew Hunt、David Thomas / 中国电力出版社 / 2003-8-1 / 39.00
本书直击编程陈地,穿过了软件开发中日益增长的规范和技术藩篱,对核心过程进行了审视——即根据需求,创建用户乐于接受的、可工作和易维护的代码。本书包含的内容从个人责任到职业发展,直至保持代码灵活和易于改编重用的架构技术。从本书中将学到防止软件变质、消除复制知识的陷阱、编写灵活、动态和易适应的代码、避免出现相同的设计、用契约、断言和异常对代码进行防护等内容。一起来看看 《程序员修炼之道(影印版)》 这本书的介绍吧!