Python、OCaml 和机器学习(2020)

2021-08-08 06:35:50

欢迎来到 Signals and Threads,深入探讨 Jane Street 技术堆栈的每一层。我是罗恩·明斯基。今天,我们将在 Jane Street 与 Laurent Mazare 就 Python 和 OCaml 的使用进行对话。 Jane Street 因我们使用 OCaml 而广为人知,这是一种静态类型的函数式编程语言,类似于 Scala 和 F# 以及 Swift 和 Haskell 等语言,但故事比这更复杂。我们不仅使用 OCaml,我们还使用其他一些语言,我们使用的一种非常重要的语言是 Python,我们主要使用 Python 进行数据分析和机器学习任务,但它也有点超出了这些领域。所以,今天早上谈话的主题是关于 Python 如何与 OCaml 相适应,以及它如何与 Jane Street 更广泛的基础设施相适应。我很高兴与 Laurent 进行这次对话的原因之一是,他在 Jane Street 内部做了很多工作,既使用 Python 工作,又开发 Python 工具本身,更好,而且,他他拥有在不同环境中使用不同语言的更广泛经验,因此,他不仅在 Jane Street 度过了一段时间,还对此有一些看法。所以,劳伦特,首先,你能告诉我们更多关于你在简街以外的经历以及它是如何促使你在这里工作的吗?是的。所以,我在学术界之外的第一份工作经历是在一家名为 LexiFi 的小公司,我们在那里使用 OCaml 来建模金融合同。因此,在这种情况下,OCaml 主要用作领域特定语言来表示复杂的财务收益。我花了几年时间研究我们出售给银行和资产管理公司的整体基础设施。在那之后,我于 2010 年开始在伦敦的高盛工作,担任股票策略师,在那里我使用了一种名为 Slang 的内部编程语言,人们可以将其视为 Python 的一种变体。它主要是一种脚本语言,实际上快速查看您正在执行的操作的输出是相当不错的,但是当然,效率有点令人担忧。我在高盛工作了几年,然后在 2013 年,我加入了 Jane Street,担任软件开发人员。我在 Jane Street 主要从事交易系统的工作,所以回到在几乎所有事情上都使用 OCaml,在设计生产关键的东西时,我非常喜欢函数式编程方面和强类型系统。在 Jane Street 工作了四年之后,我实际上离开了去 DeepMind 工作,主要是因为那时我对机器学习充满热情,我想在该领域的前沿工作。在 DeepMind,我主要使用 Python 进行机器学习,当然还有 TensorFlow,而且我还需要使用 Swift 作为 Python 的替代品。我在那里工作了一年,然后我回到简街工作,现在我更专注于研究领域。所以,我会说我有一半的时间在 OCaml 上工作,一半的时间在 Python 上工作,但是这两半是非常不同的。你背景的另一部分是你在开源方面也做了一些有趣的工作,你能说一下吗。因此,我尝试活跃于开源社区,并借此机会开发了几个软件包。最著名的可能是 ocaml-torch,它为 PyTorch 框架提供 OCaml 绑定。 PyTorch 是 Facebook 开发的这个令人惊奇的东西,它可以让你用 Python 编写一些深度学习算法,并利用你的 GPU 和自动微分的力量。通过这些 OCaml 绑定,您也可以从 OCaml 中做到这一点——因此,您可以在混合中添加类型安全。我还为它研究了 Rust 绑定,所以,它是一样的;在这种情况下,您在 Rust 中有绑定,因此您最终编写了 Rust,而不是编写 Python 代码。我还在 Clippy 上做了一些工作,它是一个 Rust 静态分析器,在那里,您尝试分析 Rust 代码并找到合法的错误。我查看了许多其他 OCaml 包——TensorFlow 的绑定、Apache Arrow 的绑定、我尝试为 OCaml 编写数据框库……以及其他一些东西。所有这些都可以在 GitHub 上找到。所以,这一切都反映了你花了很多时间在不同的地方使用不同的语言和生态系统工作。一路上你提到,你大约一半的时间花在 Python 上,一半的时间花在 OCaml 上,这两半是非常不同的。也许你可以多说一点,这两个有什么不同?是的,我的前半部分时间都花在了使用 Python 上,这主要是为了研究目的。在这种情况下,我将在笔记本中使用 Python。所以,对于不知道笔记本是什么的人来说,它是一种网络 UI,在那里你有一些单元格,你可以在其中输入一些你的编程语言,大部分时间是 Python,你可以按顺序评估每个单元格.在这种情况下,开发模式非常具有交互性。因此,您查看实际输出,稍微调整您的代码,然后再次评估它,然后再次调整您的代码,因为您注意到自己犯了一些错误,然后一遍又一遍地重新开始。所以,这是某种交互式开发。相当不错的是,您在编辑代码和查看实际结果之间有一个非常快速的反馈循环。当然,它与非常庞大的绘图库的 Python 生态系统非常相配。因此,您实际上可以在一些数据上运行您的算法,然后插入输出,检查它是否符合您的直觉,如果不符合,请尝试调试、调整您的代码,然后重新开始。所以,这将主要是我使用 Python 的目的,另一方面,有 OCaml,在那里,它用于更多 [一般] 开发工作和生产关键开发,我会说,你倾向于构建系统相当有弹性。我经常感到惊讶的是,你写了一些 OCaml 工作,你花了一个星期的时间,部署它,然后几年后你又回来了,甚至你可能已经离开并沿着方式,并且您注意到您的工作实际上仍然存在并且仍在工作,这非常了不起。当然,在 Python 方面,事情会在很长一段时间内继续工作的情况更为罕见。多说为什么会这样。您认为 OCaml 是什么,它使编写这些更健壮的任务、捕获错误等所有这些变得更容易?

是的。所以,我认为实际上有两个方面。一个是关于 OCaml,另一个是关于我们围绕 OCaml 的一般工程实践。所以,事实证明,当你编写 OCaml 代码时,你通常会为你的事情编写适当的测试,你会考虑很多错误情况。再次,部分原因,因为类型系统会强制执行,每个函数都会告诉你,哦,实际上,我可能会返回一个结果,但我也可能会返回一个错误,你必须考虑错误是什么以及你想如何处理它。你的目标是,构建一些有弹性的东西,所以你也处于一种心态,你会考虑很多极端情况,而在 Python 世界中,更多的是关于运行时异常。所以,你不会花太多时间去考虑函数实际输出什么,而且大多数时候你使用一个你并不真正知道函数输出什么的库,结果证明你有点猜,试试它,大多数情况下它都有效,然后您继续这样做,您只需以交互方式检查它是否符合您的期望。我真的被你在这里的描述震惊了。两种语言之间存在多种差异,其中一些是语言的基本特征。其中一些是关于围绕语言的生态系统的事情,其中​​一些是关于 Jane Street 作为一个组织围绕不同语言的实践的问题。所以,也许我们可以稍微分开研究一下。让我们先谈谈生态系统。 Python 生态系统和 OCaml 生态系统有何不同之处?我认为,如今,Python 是有关机器学习和数据分析的一切事物的事实上的标准。因此,如果您希望能够再次在笔记本中绘制结果,您有很多库可以做到这一点,大量在线教程将帮助您找到正确的方法,如果您遇到任何类型的问题,同样,你可以谷歌一下,你会很容易得到一些结果。还有各种各样的机器学习库可用,所有主要的现代深度学习框架都有 Python 前端,并且都是以 Python 优先的方式发布的,因此开发人员将关注的 API 是 Python 的。我不认为 Python 在那个领域比另一种有点相似 [如 Ruby] 的语言更成功,但事实证明,你拥有的生态系统越多,它就越成功。它吸引了人们,并在某个时候滚雪球。另一方面,当涉及到 OCaml 时,有很多库。我认为,总的来说,图书馆的质量往往更好一些,但它们的数量要少得多。解决这个问题的一种方法是,您可以绑定到其他语言,这就是我们为 TensorFlow 和 PyTorch 所做的,但您甚至可以绑定到 Python。所以,当你想在 OCaml 中绘制一些东西时,我们实际上有一些 Matplotlib 绑定​​,这将调用 Python 本身,将使用这个非常著名的 Matplotlib 库来运行这些图。而且我认为非常值得注意的是,当您谈到 Python 的生态系统优势时,首先谈到的是可视化,我认为可视化部分非常重要,而且我认为笔记本是一种如此有价值的方式的根本原因的工作。做研究的基本模式是探索性的,你开始尝试一些东西,看看结果,再尝试别的东西,然后再看看结果,有一个快速的工作流程,让你这样做,并让你嵌入以一种直接的方式在您的工作流程中进行可视化……我认为这就是为什么 notebook 是一种如此引人注目的工作方式的原因之一,它与软件工程师工作的传统工作流程大不相同,后者涉及纯文本和文本编辑器,并且只是没有相同的可视化组件。不过,确实,我会说这两种模式在某种程度上有点相似,双方都希望进行快速迭代。在一种情况下,您将在笔记本中进行这种快速迭代。因此,您将快速迭代数据,绘制出一些图,并实际可视化算法的输出,检查一些极端情况并再次迭代。在 OCaml 世界中,你倾向于对快速迭代做同样的事情,但它是关于类型安全的。所以,你保存你的文件,你的构建系统会告诉你,哦,这个类型不匹配,这个类型不匹配,这个类型不匹配。最后,一旦您设法让东西进行编译,您就会期望 OCaml 代码几乎可以工作。而在 Python 方面,您没有这种类型方面。所以,你有点依赖实验来告诉你事情是否正常。我想知道 OCaml 世界上是否有更多这种来回实验的内容,而不是您认为的那样。我经常遇到这种情况的地方是我们在构建所谓的期望测试方面所做的工作。所以,期待测试,这是一个在 Jane Street 环境之外鲜为人知的事情,尽管实际上它最初是从外部想法中窃取的,但基本想法实际上来自 Mercurial 版本控制系统使用的测试工具——但无论如何,expect test 的基本思想是你有这种编写代码和测试的统一方式,在某些方面感觉很像笔记本。你写了一段代码,然后你写了一个特殊的注释,这意味着捕获到这里的代码输出并将其插入到代码的文本中,然后你再写一些代码并在另一个地方捕获输出。然后当测试运行时,它会重新生成所有输出,如果有任何更改,它会说,“哦,输出已更改,您要接受新版本吗?”然后你可以在你的编辑器中按下一个键并加载这些新的变化,我们经常将它用于各种不同类型的测试,但也用于各种探索性编程。因此,例如,我使用它完成的一件事是进行网络抓取:您想要抓取、分析和转换一些网络数据,并从中提取一些信息。这是非常具有探索性的。您通常不会一直尝试以一种通用的方式使其正确,而是尝试处理某种特定类型的文档,因此,您编写了一些转换,然后应用它们并查看输出看看它是否有意义,构建系统实际上为您提供了一个相对良好且快速的循环。你去哪里,你做一个改变,构建完成,你会看到新的输出。最大的区别之一,没有图形组件,一切都是纯文本。您从笔记本风格的工作流程中获得的一大优势是能够在网页中实际显示图形内容。这是正确的。我有点同意期望测试为您提供某种交互式实验。在那里,我认为您更多地关注极端情况,以及我们算法的难点,而大部分时间在 Python 笔记本实验中,您并没有编写一些代码,这些代码已经存在很长时间了。因此,您实际上希望它处理您的数据而不是任何潜在数据。所以,即使期望测试有点互动,我认为它仍然是一个不同的实验。

所以,我很好奇,您在多大程度上认为这是编程语言的问题或围绕该语言的工具和生态系统的问题。如果,有那么一分钟,我们想象一个世界,其中在笔记本中使用 OCaml 的工具都非常完善,您将拥有所有习惯的好东西,从我们使用类似语言的编辑器集成体验OCaml,(例如,您可以导航到一段代码并查找给定表达式的类型或导航到给定值的定义,所有这些,所有这些类似 IDE 的功能都非常有效),同时,在交互性和可视化事物的能力方面,您拥有在笔记本中使用 Python 的所有好处,并且您可以使用所有机器学习和数据分析库……那时,您还有理由想要使用 Python 吗?语言本身是否有什么东西使它成为某些此类探索性研究工作的更好工具?是的。这是一个非常有趣的问题。当然,要明确回答有点困难,因为你不知道如果你在 OCaml 上添加所有可能的东西会是什么样子。尽管如此,我还是觉得 Python 的动态方面有一些你在 OCaml 方面没有的优势。你会在 Python 中做的一个典型的事情是你编写你的小算法,它实际上适用于整数,你对它很满意,但后来发现,你实际上想要使用浮点数。因此,在 OCaml 中,为了使类型系统满意,您必须修改所有操作这一事实对您来说有点烦人。而在 Python 中,您会期望大部分时间都能正常工作。您仍然会遇到这个问题,如果有一些错误,您可能无法检测到它们,但是在大多数合理的输出上,您所做的检查应该让您确信它运行得相当好。所有这些动态方面都相当不错。它让您可以在任何数据上尝试几乎任何类型的算法,并依靠鸭子类型,只要您的对象实现了正确的方法,事情就会解决,而您实际上并不知道幕后发生了什么。我想用静态类型系统的语言工作的一大好处是类型提供了很多保证,保证你不能从程序的任何一次运行中获得,你不能获得任何数量的测试,因为类型系统的保证本质上是通用的。它们适用于您的程序的每一个可能的执行。所以,这是一个非常强大的东西,但它是有代价的。获得这些保证需要您将程序有效地适应类型系统所需的结构。类型系统擅长捕获某些类型的规则,而不擅长捕获其他类型的规则。因此,本质上有一种协商步骤,您可以在其中找出如何与类型系统完美配合。在像 OCaml 这样的语言中,这实际上非常有效。类型系统非常轻量级,不需要很多类型注释,而且很灵活。因此,您不必将程序弯曲得太厉害以使其适合并获得类型的好处,但是动态类型语言的好处是您可以完全跳过该协商步骤。如果您的程序在您碰巧运行的数据的特定上下文中工作,那么您不必考虑它在更一般的上下文中如何工作,并且当您进行探索性研究时,编写一点点您从中获取信息然后可能再也不会运行的代码。我想权衡是非常不同的。在这一点上,能够在没有类型的情况下进行操作具有很大的吸引力。是的。因此,OCaml 中的类型系统确实可以证明您的代码是正确的,并且该代码对于所有可能的输入都是正确的,但是在 Python 中,默认情况下您没有得到任何代码证明会正确运行。您只会在运行时收到异常。事实证明,您关心的输入片段可能非常小。因此,您在 OCaml 方面寻找极端情况并修复它们的时间实际上在 Python 方面是不必要的,因为您自己知道您不会有那种输入。然而,当然,如果你有一个打字机和一个编译器来确保类型安全,编译器就不能假设任何事情。所以,你必须教它,“哦,实际上我并不真正关心这个变体,也不关心这个变体”等等。我觉得我们应该不那么宏伟,因为类型系统并不是为您提供程序整体正确性的证明,而是它确实捕获了某些正确性元素,并且很好地确定了它们。并且在某些类型的程序中,这会让您走得非常远,在类型系统的审查下,大量错误会完全消失。还有一点值得一提的是,像 OCaml 这样的类型系统实际上在捕获数字代码中的错误方面做得相对较少。数值程序的难点之一是当你弄错算法时,事情看起来会更糟,以更模糊的方式。如果你没有正确地进行线性代数分解,那么并不是你得到了一个完全疯狂的答案,你得到的答案不是最优的,或者没有那么快收敛,或者根本没有收敛。并分离出这些错误,找到这些错误实际上非常困难,我怀疑如果 OCaml 有一个非常擅长发现那种错误的类型系统,人们会非常渴望在数据分析上下文中使用它,但在实践中,数值分析中最难的错误通常只是在没有类型系统帮助你这么多的情况下溜走。印地……