Transcription
[音乐] 大家好,欢迎来到生成的程序化解决方案教程,我们非常兴奋地来谈论一些程序化音频和代码生成方面的内容。我叫 Shaw Hassan,是国家塔楼大学的教员,两位荣誉组织者是 Leppy,他是奥拉大学的教员,以及 Shing,他是谷歌 DeepMind 的一名研究科学家。这是我们今天的日程安排。Lei 和我将从 32 个不同的角度来谈论程序化音频,很快我们还将谈论代码生成。所以我们将在每场讲座的最后安排问答环节。我们曾多次讨论是否要在中间安排休息,但后来意识到我们有很多想讲的内容,所以可能不会安排正式的休息,但你们可以根据需要自行休息。好的,我将开始我的部分,我将谈论学习将程序合成可解释和可泛化的策略。所以,让我们先来谈谈深度强化学习。深度强化学习在许多领域都非常强大,比如机器人代码、机器人启动和停止。如果你想一下机器人到底是什么,它就是关于一个在环境中行动的代理和策略,环境有一个状态,就像这种迷宫导航之类的任务。你有一个代理在这个迷宫里,然后它想导航。代理可以观察环境以获得观察结果,并且根据这个观察结果,我们希望能够查看我们可以采取的行动类型,并采取行动。因为这个代理在这个环境中,所以代理反过来会影响环境的状态,所以环境会过渡到下一个状态,环境还会提供一个信号奖励,告诉策略你做得怎么样。强化学习的目标是最大化总奖励,也称为回报。这种表述非常强大,它可以解决许多顺序决策问题。但在这次演讲中,我想谈论的是深度强化学习表述中的问题。深度强化学习一个非常重要的问题是泛化。如果你想一下,它实际上是关于拥有一个深度神经网络来表示策略。你可以训练这个神经网络来导航不同大小的迷宫、不同布局的迷宫、不同的起点和终点位置。一旦你训练好了,你就会得到一个策略。这个策略知道如何在不同类型的迷宫中导航。所以你必须知道迷宫导航的基本规则,也就是右手规则。有人知道迷宫导航中的右手规则吗?知道右手规则的请举手。哦,太好了,有很多。好的,对于那些不知道的人来说,右手规则是这样的:如果你在迷宫中卡住了,希望不要发生,你可以把右手放在墙上,然后一直把右手贴着墙走,穿过整个迷宫,你就可以到达出口。这就是右手规则。所以不要在迷宫中卡住,但如果你卡住了,请记住这个右手规则。现在你有了这个策略,在这个不同 AI 的迷宫中,你可以导航。所以它必须学会右手规则。但是,当我们把这个训练好的策略应用到更大的迷宫中时,它就不起作用了。这就是为什么我们尝试了,它就是不起作用。这个深度学习只是以一种非常奇怪的方式找到了记忆如何解决导航的方法,但它并没有超越任务范围进行泛化。所以这是一个问题,你不想让一个不泛化的机器人策略在你家里惨败,对吧?所以泛化是一个问题,另一个重要问题是可解释性。当你有一个神经网络策略时,很难理解这个策略的想法。它只是将状态映射到动作,但你不知道这个网络在想什么。你不知道它接下来会做什么,它之后会做什么,以及这个策略的决策逻辑是什么,你就是不知道,因为这是一个黑箱。我们不想要这种黑色的策略,尤其是在机器人学习或这种安全关键的任务中,你想要一个你理解的策略,你想要理解决策逻辑,所以这是深度神经网络作为策略的问题。所以,相反,有些人从更广阔的视角来看待强化学习,他们研究了一个我们称之为程序化强化学习的课题。其动机是:我们能否拥有一个更具泛化性和可解释性的策略?这就是整个想法。所以这类工作他们会做:嘿,我们选择一个更具结构化或在可解释性或泛化性方面更好的表示方法。然后说:嘿,我们还开发了可以从奖励中学习这种策略的方法。所以一些例子是决策树。决策树是神经网络的先驱,是一种更传统的机器学习模型,对人类来说更易于理解。或者我们可以做这种语言描述式的策略,描述先做什么,再做什么,以及如果条件满足或不满足时该做什么。还有一些人正在做这种符号程序,你学习一个参数化程序,然后你可以运行这些程序,或者状态机策略,你有很多模式,可以在不同的模式之间转换,不同的模式代表不同的宏观动作。所以这些是一些程序化强化学习的例子,其整体目标是:我们能否不使用深度神经网络作为策略,因为这只是对人类来说不太容易阅读,也不容易泛化,我们能否做得更好?我们能否拥有更具结构化的表示?这就是我定义的程序化强化学习。所以,这些工作,他们在 NeurIPS 2018 的论文中,他们做了决策树策略,整个想法是:我们能否拥有一个更具可解释性和可验证性的策略?他们所做的是:嘿,给定任务,我们首先训练一个神经网络策略,我们将这个神经网络标记为 Oracle,然后我们试图提炼出这个神经网络在不同情况下的行为。这更像是模仿学习方法,我们有一个决策树来提炼神经网络所学到的东西。这是我们的工作,整个要点是:我们能否拥有一个我可以验证的策略,也就是说,我知道我添加到状态中的扰动有多大,我将获得相同的动作,这给了你保证,你知道了边际,因为现在我们有了决策树策略。但有一点需要注意,就是给定这个决策树,你实际上需要手工制作特征状态特征,这样你才能学习这个决策树,但它比深度神经网络策略更具可解释性。还有另一项工作,Propel,是关于符号程序策略的。这项工作的好处是:当你想要提炼一个神经网络 Oracle 时,你可能不应该在最后一步做。就像 Viper 一样,你训练了一个神经网络,它获得了最大的回报,然后你提炼了这个网络,但你没有在训练神经网络策略时考虑这个提炼。这可能效果不好。所以这篇论文提出的建议是,在训练这个神经网络策略的同时,你也应该考虑你想将它的策略投射到哪里。当你想要将其投射到符号表示时,那么你可能应该以某种方式正则化这个神经网络策略,这样投射就不会丢失太多信息。这就是 Propel 论文的想法,他们展示了这种赛道环境,其中符号程序可以更好地泛化到不同的赛道。我想要提到的另一种策略是状态机策略,你拥有这种有限状态机策略。你可以有一组宏观动作模式,然后你学习提取一组宏观动作后,你学习在不同模式之间进行转换。这篇论文展示了有趣的事情,比如平行停车。如果你知道如何停车,你就会注意到平行停车的问题。他们表明,通过学习状态机策略,你可以泛化到比你在训练样本中看到的任何技术都更难的问题。他们在不同的领域展示了这一点,非常有趣,他们称之为归纳泛化。到目前为止,我们已经讨论了所有这些不同的策略表示。我想在这次演讲中重点关注的是 DSL 策略,你想用编程语言来描述程序。这样做的好处是,首先,它比决策树更具可解释性。你可以阅读决策树,但现在如果你有一个用编程语言表示的策略,只要你了解一些基本的编程技能,只要你了解像 if-else 这样的控制流,或者像 while 循环,你就可以阅读程序策略。所以这是人类可解释的,而且它是可执行的。一旦你有了这个 DSL(领域特定语言)定义,你也可以有一个编译器来运行这些程序。希望通过这种良好的结构,它能更具泛化性。想象一下,当你学习编程入门课程时,当你编写加法程序时,你可能只能做三到五位数的加法程序,但一旦你的程序没有 bug,它就可以泛化到十万位数的加法问题。我们也希望这些程序具有这种良好的泛化性,因为结构搜索空间可以更好地表示策略。在接下来的内容中,我将讨论一系列工作,这些工作试图用语言程序来表示策略,并看看它们在泛化性和不同任务方面的表现如何。我首先想谈论的是从演示视频中学习神经程序。如果你考虑我们问题的一个子集,模仿学习,给定演示,你想学习一个能够模仿专家行为的策略。人们通常会做的是:我能否 just 学习一个神经网络策略,它可以模仿在不同情况下的专家行为。在这项工作中,我们想做的是:能否 instead,给定演示,我们想编写一个程序,明确地告诉你这个专家的决策逻辑。一旦我们有了这个程序,因为这个程序是可执行的,所以在不同的情况下,我就可以 just 执行这个程序,然后模仿专家的行为。这就是我们想在这个项目中做的。所以,我们在这两个领域创建了大量的不同程序,然后我们执行程序来获得演示,我们可以将它们分成训练、验证和测试集,我们可以使用训练集来学习这种演示到程序的映射。如果你回到使用神经网络策略的模仿学习,他们所做的是:给定演示,你学习神经网络策略,它们可以学习状态到动作的映射。给定一些验证的初始状态,我们就可以 just 推出一个策略来获得预测的执行,然后我们可以将其与真实执行进行比较,然后这就是我们如何评估策略的学习情况。在我们的情况下,我们实际上想编写一个程序。给定一个训练程序,我们可以让网络根据演示预测一系列 token,我们可以计算真实程序和预测程序之间的损失。然后当我们评估时,我们想知道这个策略在模仿专家方面做得有多好。然后我们可以运行程序并获得预测的执行。然后我们可以计算一个损失,计算准确率或损失,与真实执行的某种损失。这就是我们如何改变电池。现在预测的目标不再是状态到动作,而是演示到程序。我们比较了这两种表示策略的方式:程序和神经网络。我们发现,在不同的领域,实际上使用程序来表示策略可以获得更好的性能。当我们查看定性结果时,为什么程序更好?我们发现的是:给定一个演示,它有一个底层的程序,有一个 if-else 分支。当你有一个演示,比如这个,有很多程序都走向 if 分支,只有一个演示走向 else 分支。那么当你有一个神经网络作为策略时,它就会学习这种非常普遍的移动趋势,因为很多演示都只是移动。一旦你 just 移动,你就会得到一个非常低的损失。它就会错过次要分支。但是当你让你的策略成为一个程序时,当你进行这种学习时,你迫使策略真正意识到发生了什么,专家在做什么。因为如果它错过了 else 分支,它就会受到很大的惩罚。通过让这个网络做一项更困难的工作,而不仅仅是状态到动作的映射,你实际上迫使网络真正学习专家的想法。这就是这项工作的有趣发现。所以在这项工作中,我们讨论了如何从演示到程序,这个程序是策略的一个很好的表示。但在许多情况下,在强化学习中,你没有演示,你只有奖励。所以我们能否 just 给出奖励,我们就能获得程序策略,而无需任何演示?这就是我们接下来要做的事情。与深度强化学习不同,我们有神经网络,我们可以 just 执行一个神经网络来执行这一系列状态动作,然后收集它们,收集奖励,然后进行学习。我们想做的是,我们想再次将策略表示为程序。当我们想进行强化学习时,我们只是通过编译器执行程序。现在的问题是:我们能否有一个可以编写程序的模型?一旦你编写了一个程序,并且作为程序,你获得了奖励,那么你能否改进这个程序?这就是我们想在这个项目中做的。所以,给定只有奖励信号,我们想有一个可以执行并最大化回报的程序。我们提出了一个我们称之为 Lips 的框架。要获得一个可以编写程序并最大化回报的模型非常困难。你需要理解程序语法,编程语言语法。你需要理解环境动态,这个程序将如何执行,这个程序的执行轨迹是什么。你还需要理解任务,这个任务是什么,我如何最大化回报?这非常困难。所以我们试图将这个困难的问题分成两个步骤。在第一步,我们甚至不考虑任务,我们只考虑如何学习语法,如何学习环境动态。所以我们所做的是,我们只是随机生成大量的程序,它们与任务完全无关,它们只是随机程序。给定一个语法,我们可以采样大量的随机程序。所以我们采样了许多与任务无关的程序,然后我们学习一个自编码器,它可以将程序编码到潜在空间,然后将程序解码回来,我们可以 just 最大化这个重建损失。我们还有一些正则化项,试图使嵌入在行为方面平滑,这样更接近的程序表现相似,而更远的程序表现不同。所以你可以得到一个嵌入空间,当你从这个嵌入空间采样向量时,它代表了不同的行为。所以这就是我们首先要做的事情。它考虑了程序语法。现在解码器可以解码语法正确的程序。我们也考虑了行为,以及程序如何在环境中执行。但现在我们还没有谈论任务,我们甚至不了解这个任务。我们只是基于语法和环境动态学习了整个嵌入空间。然后,给定我们真正想解决的任务。我们所做的是非常非常简单的。我们只是在嵌入空间中搜索。我们可以采样大量的程序候选者在这个嵌入空间中,这个嵌入向量可以被解码成程序,因为我们已经学习了这个解码器。一旦你将其解码成程序,你就可以执行程序,然后你就可以获得这个程序的奖励,这就是你的业务分数。所以我们使用这种交叉熵方法来不断地在这个嵌入空间中搜索,直到我们找到一个好的程序,它可以被解码并最大化回报。我们说:好了,我们完成了。这就是我们的程序策略。这就是我们为整个框架所做的。我们学习了嵌入空间,我们在嵌入空间中搜索。因为我们能够解码程序。所以现在你有一个可行的程序策略。我们在许多不同的算术任务中评估了这个框架,我们表明,与深度强化学习相比,我们可以解决具有稀疏奖励的任务,并且我们专门化了搜索,在嵌入空间中,整个搜索种群越来越接近具有控制行为的程序。我们可以解决许多任务,与一些其他算法相比。但这只是一个很酷的例子。所以我们只是,你可以解决一些任务,就这样。真正让我们兴奋的是泛化性和可解释性。所以我们尝试了,就像我们谈到的那个例子一样。给定一个 N x N 的迷宫环境,我们使用 Lips 框架生成了程序策略。然后我们只是把这个 Lips 生成的程序放到一个更大的环境中,但它仍然是关于导航的,而且它确实有效。如果你仔细看,这个程序实际上学会了右手规则。所以这是一个导航程序。它可以在任何导航问题中运行。它可以解决导航问题。所以这个程序实际上就是解决方案。不仅仅是取决于你试图记住你见过的任何训练样本。just 基于这个 N x N 的迷宫例子,现在你有一个策略,可以在任何环境中进行导航。这种泛化性正是我们想要的,也是我们希望在开发策略的程序化空间时所期望的。所以我们也展示了量化的结果,我们的泛化能力比强化学习和决策树策略好得多。所以这是关于泛化性。关于可解释性,有趣的事情是,我们给了我们方法生成的一些次优程序,我们称一些本科生来修复这些程序。我们只是雇佣他们说:嘿,你能做一些三到五行的程序 token 编辑吗?你能改进这个策略吗?结果是,有编程技能的人可以改进这个策略。而当你有一个神经网络策略时,几乎不可能雇佣某人说:嘿,你能调试这个神经网络策略吗?你能改变这个神经网络的方式并提高性能吗?但现在你有一个程序策略。它是可解释的,人们可以理解,可以改进它们。所以这是非常令人兴奋的。现在你可以,有了这种可解释性,你不仅可以理解,还可以修复程序。所以这是这个框架的发现。另一项我想谈论的工作是 Lips 项目的后续。我们知道,纯粹从奖励中学习程序作为策略是可能的,但它仍然存在一些问题。一个问题是有限的分布问题。所以在 Lips 中,我们通过随机生成大量程序来扩展程序嵌入空间。而那个训练数据集实际上限制了程序的分布。想象一下,现在你拥有所有少于 100 行程序的整个数据集。给定一个你想让模型运行的测试任务,比如需要 1000 行代码。这个解码器永远不会解码 1000 行代码。所以它通过在数据集上训练来限制分布。另一个问题是归因问题。当我们进行搜索时,我们执行整个程序并获得一个单一的分数。但这个环境告诉你更多,你沿途获得奖励。但在评估搜索方法时,你只是将所有奖励加起来,这并不那么详细。所以我们想知道,程序的哪个部分做得好,哪个部分做得不好,你想改进做得不好的部分。所以 Lips 没有做到这一点,我们只考虑了整体分数。在这个项目中,我们称之为分层程序化强化学习。我们想解决这两个问题。我们仍然学习嵌入空间,但我们不再在这个嵌入空间中进行搜索。我们所做的是,我们学习一个策略,给定状态并尝试将这个程序嵌入空间视为动作空间,然后我们进行强化学习。我们可以采取嵌入空间中的动作,然后这个动作程序潜在向量可以被解码成一个程序片段,一个子程序。然后我们可以执行程序并获得下一个状态,以及执行这个小片段程序时获得的奖励。策略会选择另一个动作,就像强化学习一样,你现在采取第二个动作,这个动作仍然是一个程序潜在向量,然后你可以解码它。然后一旦你解码了它,你就可以执行第二个程序片段,你也获得奖励。你可以一直这样做,就像你运行强化学习一样,你顺序地采取一系列动作,然后你获得下一个状态和奖励。就像强化学习一样,我们想做的是最大化总回报。所以现在我们又将问题变成了一个强化学习问题。但现在我们的问题是找到程序策略,然后组合起来的程序就是我们的程序策略。通过这样做,我们表明在所有任务中,HPR 都做得更好。我们尝试创建另一组更难的任务。我们也表明,我们的方法在这种更难的任务中比先前的方法做得更好。但这只是任务性能。我们真的想验证这种性能提升是否来自这种有限的分布问题以及归因问题。所以我们尝试运行测试任务越来越长。我们表明 Lips 实际上无法生成更长的程序,但 HPR 可以。我们也尝试看看奖励整个程序在最后,或者通过这种步进动作奖励的制定来给予更密集的奖励是否会使性能更好,比如你可以更快地收敛,你可以获得更好的策略。所以这就是 HPR 框架。我想快速谈论的一项工作是我们将在 NeurIPS 上发表的论文,它解决了 HPR 的问题。HPR 的问题是,我们将程序嵌入空间视为动作空间,然后你进行强化学习,但这个动作嵌入空间非常非常大。它是 256 维的。有了这么大的动作空间,训练策略非常困难。在一些困难的任务上,训练这个框架需要近一周的时间。所以我们能否使其效率更高?Hippo 正是这样做的。我们尝试首先将其分成两个阶段。在第一阶段,我们首先检索一组程序片段,这些片段非常多样化,并且在从任务的某些部分获得奖励方面非常有效,而且它们可以组合在一起。然后我们可以学习一个高级策略来 just 使用这个有限的选项集。这可以学得更快。我们表明,在许多其他任务中,这可以做得更快。这将在周五的 5 号海报会议上。如果你对学习程序化强化学习感兴趣,可以与 Jhan 和 Guanter 会话交流。最后我想提到的关于程序化策略的工作是,我们如何使用大型语言模型来加速整个过程。现在我们知道,许多工作都进行了程序化强化学习,比如通过搜索或强化学习等不同的优化方法来生成程序策略。这类方法很好,因为它们直接优化目标,但它们也不那么好,因为它们是无假设的,它们不对任务做任何假设。但在这种算法任务中,我们实际上知道得更多。我们知道人类对什么样的问题感兴趣,我们对随机生成任务不感兴趣,有一些目标导向的行为是我们关心的。但这些方法,它们不知道,它们只是进行任何搜索优化来获得策略。另一方面,你有大型语言模型。你可以想象大型语言模型是知识库,是人类文明在互联网上的克隆。它们对人类感兴趣的东西有很多了解。但直接优化它们也很困难,因为像 GPT 这样的网络,你甚至无法微调模型。所以它们拥有知识和编程技能,但直接优化它们非常困难。另一方面,你有一些搜索方法或优化方法或强化学习,你可以直接优化某个目标,但它们对这个世界一无所知。所以在这个项目中,我们试图将它们结合起来。我们提出了一个名为 LM-guided search 的方法,你首先通过提示大型语言模型来生成初始程序搜索分布,然后从那里开始。你进行搜索来改进程序,这实际上可以让你更有效地采样,你可以进行更少的人工执行程序执行,并且你可以获得非常好的性能。我们还展示了如何将不同的搜索方法与大型语言模型结合使用,以及在不同的设置下,它都能很好地工作。我们还想强调一个例子,大型语言模型是否可以在没有任何搜索方法的情况下持续改进程序。所以我们尝试了不同的方法让大型语言模型知道程序是如何生成的。我们尝试提供奖励,尝试提供执行轨迹,并尝试让大型语言模型改进程序。结论是,大型语言模型可以很快饱和。它不了解环境,不知道发生了什么。所以它无法持续改进程序,但搜索方法可以。这就是我们从这个项目得出的结论。所以我们快速地回顾了所有这些试图从奖励、从演示中获得程序策略的工作。我只是想让你们记住:结合程序合成和强化学习可以获得更具可解释性和更具泛化性的策略。这只是一个维度,我们现在使用程序来表示策略。还有一些有趣的工作,它们使用代码作为策略来执行一些机器人学习任务,也非常有趣。还有一些其他维度我们可以使用程序。一个令人兴奋的方向是使用代码来生成奖励函数,然后训练模型来执行不同类型的任务,比如这个记录,以及语言到奖励。还有另一种使用程序进行智能体学习的方法是,你可以生成代码来程序化地生成不同类型的任务,然后你训练模型。还有另一种使用程序的方法是,你使用程序作为模型。所以有一个代码世界模型,你试图做基于模型的强化学习。你试图让一个模型为你编写代码,然后这个代码会执行,你就可以进行这种想象中的执行,而无需与环境交互。这也是另一个令人兴奋的方向。所以有很多方向可以利用程序来改进智能体学习,改进强化学习,它们非常令人兴奋,你可以去看看。所以,这就是我想分享的内容。抱歉,由于一些技术问题,有点仓促。我把所有参考文献都放在这里了。我也会把我的幻灯片放在我的个人网页上,你也可以扫描二维码。所以我们可以进行两到三分钟的提问,然后我将把舞台交给 Leppy。有什么问题吗?是的,请。我一直在想嵌入空间的质量。似乎对于这类方法来说,嵌入空间应该是平滑的。我的意思是,基本上奖励函数在嵌入空间中应该是平滑的。你如何构建嵌入空间使其具有这些属性?是的,在 Lips 论文中,这是非常重要的。所以,如果只是重建,程序只是在这个嵌入空间中分散开来,它们没有意义。所以我们有这个正则化项,有两个级别的正则化。一个是,我们有另一个策略,它解码嵌入,并试图遵循程序的行为。我们可以最大化真实程序的动作序列,以及神经网络解码策略的动作序列,使它们彼此靠近。另一个方法是,给定解码的程序,你可以执行解码的程序,并获得预测的动作序列。你也可以尝试使它们更接近。因为这种执行是不可微的,你可以进行强化学习来正则化嵌入空间。所以我们有一些正则化,试图使行为相同的两个程序在嵌入空间中更接近。这非常重要。我有一个问题。所以这个想法很酷。我想知道模型生成的程序有多复杂。是的。所以这个阶段的程序一点也不复杂。它们最多不到 100 行 token。所以它们在这个阶段非常初级。好的,谢谢。谢谢。是的,请。我一直对你让模型生成 LLM 提示的想法很着迷,但感觉创建文本提示并通过分词器运行它有点浪费。你不能直接创建 token 而不是文本提示吗?所以我们只是,比如写关于领域和任务的描述,然后我们只是提示 GPT。所以希望它能理解这个领域是如何工作的,以及这个任务是什么。是的,这就是我们所做的。好的,谢谢。谢谢。我还有一个问题。好的。只有一个。是的。所以你学习的潜在空间中的流形必须足够宽,才能包含最优轨迹。你怎么能保证你训练了足够多的包含好的轨迹的轨迹?还有,你怎么优化流形上的行走以产生最优值?是的,非常感谢。所以,是的,没有保证。我们所做的是,我们创建了一个随机程序数据集,试图包含许多许多程序,但这些程序中的许多实际上并没有那么有用。所以,整个想法是,你仍然需要在这个大型空间中进行一些搜索或强化学习,并试图找到对你的任务有用的程序。但没有保证。好的,我的名字是 Leppy。谢谢 Shaw。这对于程序化强化学习来说是一个非常好的介绍。也许在我们再次深入这个话题之前,我想先退一步,试着消化一下 Shaw 今天展示的一些非常酷的想法。所以我想从这个声明开始,深度强化学习就是程序化强化学习。之所以如此简单,是因为:所以,Shaw 向我们展示了你有一个强化学习问题。你想找到一个策略来解决这个问题。我们将定义我们将要搜索的语言,并尝试找到一个解决方案。所以,如果你看看深度强化学习,也许我们可以有一个卡通式的视角,我们正在寻找所有可能的策略,你可以用神经网络获得。所以你将坐下来,写下神经网络的架构,然后你将用梯度下降来训练神经网络。你得到的输出是你的策略。但是,如果你退一步,考虑到你有选择语言的能力,那么我们将得到类似这样的东西。所以,与其总是选择,比如用神经网络来表示策略,我可以选择别的东西。所以,例如,我可以选择 Shaw 今天向我们展示的一些示例中的 Carell 语言。我认为这非常重要,因为它允许我们回顾并选择我们需要的正确偏见,以便解决我们感兴趣的强化学习问题。比如,如果我们以蜜蜂为例,蜜蜂实际上可以相对较快地学会区分不同艺术家的绘画风格,以及蜜蜂大脑中的偏见是什么。所以这是我们可以回顾并试图理解我们需要的偏见类型,以便选择正确的语言,然后一旦我们有了正确的语言,我们就可以尝试解决我们感兴趣的问题。所以,Shaw 主要关注泛化性和理解能力。我想增加一个新维度,这个新维度将贯穿我今天教程的这部分:组合这些程序的能力。所以一旦我们学会了如何解决一个问题,我们就可以得到这个解决方案的不同部分,并将它们组合成更大的解决方案,从而发现新的东西。所以,这将是共同的主题,我们将记住,从我现在的观点来看,深度强化学习将是程序化强化学习的一个子集。所以,搜索程序化策略。那么,鉴于我们有一个强化学习问题,并且我们有兴趣解决这个问题,我们需要什么?所以,首先,我们需要定义搜索空间。一旦我们定义了搜索空间,我们就说,这是我们将要搜索的策略家族,并将尝试找到一个策略来最大化回报。我还必须定义搜索过程。所以,一旦我定义了搜索空间,我将如何遍历搜索空间并找到我想要的策略?如果你看看深度强化学习的方法,我们所拥有的是,搜索空间是神经网络的权重。因为我们在实践中所做的是,我们将坐下来,写下我们将要使用的深度强化学习代理的架构。一旦我们这样做了,现在我们就必须找到我们的空间,我们将搜索这个空间,以便找到正确的权重集,这样代理就可以产生最大化回报的行为。搜索过程是什么?嗯,搜索过程将是基于梯度的。所以我们将进行梯度上升,以便最大化回报。这就是我们在深度强化学习中所做的。所以,退一步,试图找到这个问题的更通用的解决方案,我们将同时改变搜索空间和搜索过程。所以,让我举一个非常具体的例子。这是一个教程。我将给出一个非常简单的东西,以便你能理解发生了什么,也许你就能坐下来自己实现这个更通用的解决强化学习问题的方法。所以,我们将从定义搜索空间开始。为了定义搜索空间,我们将定义一种编程语言。所以编程语言将定义我们可以搜索的内容,我们将用上下文无关文法来做到这一点。所以,这里我们有第一个符号,大写 S,在第一行,我们可以用右侧的某个产生式规则来转换这个大写 S。比如,我们可以将大写 S 转换成这个 if-then-else 语句,这是我们在右侧的最后一个产生式规则。我们还有这个大写 B 字母,它也是我们称之为非终结符的符号,我们可以用底部的某个产生式规则来替换它。所以,这个语言本质上允许你进行加法,并且你有一些 if-then-else 检查来编写这些程序。所以,让我确保每个人都还在关注。我将问一个小测验。那么这个程序呢?如果 x 小于等于 y,则为 0,否则为 1。我们能用上面的语言写出这个程序吗?答案应该是肯定的,我们可以做到。我将展示你需要应用的产生式规则,以便从初始符号 S 开始,一直到这里显示的程序。所以,我们将从符号 S 开始,然后用 if-then-else 替换它,然后我们可以替换这些非终结符符号中的每一个,直到我们到达底部的这个程序。所以,这是一个程序,我们说它被这个语言接受,并且我们在这个语言中可以编写的每个程序都将定义我们可以搜索的策略空间。所以,这就是我们定义问题的方式。正如你所能猜到的,如果你熟悉深度强化学习,我们对深度强化学习所做的最大假设是,搜索空间将是完全可微的,这非常好,现在我们可以端到端地训练,并拥有我们的策略。但现在我有了可能不连续的空间,我不能 just 即插即用梯度下降来解决这个问题。所以我需要一些不同的东西,我将告诉你一个如何实现这一点的例子。在我们这样做之前,在我们进入搜索部分之前,我们如何遍历空间并找到解决方案,让我告诉你我们如何在内存中表示它。我们如何在内存中表示程序?这几乎就像给你实现。如果你想坐下来实现它,你需要实现这个我们称之为抽象语法树的东西,它本质上代表了整个程序。你可以看到树中的产生式规则。你看树的根部有初始符号大写 S。然后在第一层,它应用了第一个产生式规则,我们将大写 S 替换为 if-then-else。然后我们将继续这样做。所以在树的叶节点,我们有我们称之为非终结符或终结符的符号。请原谅我。它由我们上面蓝色显示的程序给出。所以,如果你 just 遍历叶节点,你就会完全恢复程序。所以,这就是我们如何在内存中表示它。如果你想实现一个解释器,以便你可以遍历这棵树并在内存中运行这个程序,这也是一个非常方便的方式。好的,这就是我们在内存中的表示方式。我们如何定义这个空间是通过这个语言。那么问题是,我们如何在空间中搜索?为了在这个空间中搜索,我们将定义一个我们称之为邻域函数,因为我们将使用某种局部搜索算法。所以,如果你熟悉人工智能的入门课程,你可能会记得爬山这种局部搜索,它也与 Shawul 谈到的我们进行局部搜索时非常相似。所以,让我们定义一个邻域函数。邻域函数将接受一个我们称之为候选解的东西,它本质上是一个程序,是我们要找的策略的候选者,以便我们可以最大化回报。我们将要做的是,我们将对这棵树进行一点变异,以便我们可以得到一个邻居。我们将随机选择一个子树。比如,我们选择一个子树。我们将用别的东西替换它。这个别的东西,我们可以回到语言,让它为我们生成一些东西。所以,我们可以生成整个树,整个子树,并得到一些不同的东西。所以,我们在这里做的是,我们将用别的东西替换那个子树。这里有一些重要的属性。所以,右边的程序是左边程序的邻居的想法是,右边的程序与左边的程序相似,但它做的事情不同。所以,这将在我们讨论如何学习对搜索算法友好的空间时变得重要。好的。所以,如果我这样做,比如 k 次,我将有 k 个邻居,然后我可以评估所有这 k 个邻居,然后搜索算法将定义我们接下来要做什么。所以,我们如何在这个空间中搜索?我想让它尽可能简单。所以我想有一个像梯度下降一样简单的算法,但现在是针对这个更通用的问题,我们不能假设一切都是完全可微的。所以这将是爬山。我们将如何进行爬山是首先生成一个随机的候选解,这可以通过应用语言中的产生式规则来完成。所以,让我们说,我之前在我的小测验中展示的程序实际上是我们随机生成的程序。所以,我们只是
应用所有这些不同的生成规则,直到我们达到这样一个点:我们程序中的所有符号实际上都是终端符号。然后,我将只使用您在底部看到的这个小圆圈。它将代表该程序。这个椭圆形只是代表我们将要搜索的整个状态空间或整个程序空间。所以,我们将把这个小符号一直移动到椭圆形空间,我们将使用我们的邻域函数。所以,让我们说我们查询邻域函数。我们得到了一堆邻居,现在我们将评估这些邻居中的每一个。如果我们正在解决一个强化学习问题,那么这些邻居中的每一个都将代表一个策略,我们将展开该策略并获得一些反馈。所以,我们查看所有这些邻居,然后最好的一个将闪烁。我相信它在某个地方闪烁。好了。所以,那就是最好的一个,我们将跳到那里。所以,我们将超级贪婪。这就是爬山搜索的想法。然后我们再次生成邻居,然后我们跳到最好的一个,然后我们生成它的邻居。我们将继续这样做,直到我们达到这样一个点:我们当前的候选解决方案实际上比所有邻居都好。所以,这就是爬山方法,我们将在那里停止。我们刚刚达到了这个局部最优值,这就是爬山将返回给我们的解决方案,这与我们使用梯度上升的方式非常相似,只是现在我们没有梯度信息。我们正处于这个更通用的空间中,其中程序实际上是不连续的。好的,但我们该怎么做呢?所以,在人工智能入门课程中,我们应该教人们我们应该进行重启、重启和重启。所以,如果您正在进行局部搜索,那么这些局部搜索往往非常快,但我们可以运行很多次。所以,如果我再次运行搜索,我将从一个不同的初始候选解决方案开始。我可能会处于空间的不同部分,然后我将再次运行搜索,我一遍又一遍地这样做,一旦我没有时间了,我就可以查看我遇到的所有解决方案,然后返回最好的一个,这在某种意义上也是一个很棒的解决方案,因为它非常容易并行化。我可以在每个核心上运行一次搜索,并且我可以同时进行多次搜索,这非常强大。好了。所以,我给您提供了一个非常具体的例子,说明如何使用这个更通用的框架来解决强化学习问题。所以,我们将退一步说,我将不仅仅使用神经网络作为我的语言。我将选择一种语言,以便我可以选择正确的偏置,这将有助于我解决我想要解决的任何问题。好的。所以,让我们回到我之前的那张幻灯片,我们需要定义搜索空间和搜索过程。您可能还记得,对于深度强化学习,我们有神经网络的权重作为空间,梯度上升作为搜索算法。但现在我仍然想保持简单,我将使用局部搜索,如爬山搜索,或者类似的东西,比如 Shiaua 在他的演讲部分也解释过的交叉熵方法。所以,我们将使用那种搜索。然后,我认为有趣的问题在于如何定义搜索空间,以便我们可以使其对这种搜索算法友好,以便我们可以利用我们能够访问的所有核心,并且我们可以并行运行多次搜索并成功找到这些问题的解决方案。所以,我将再次从一个稍微不同的角度来回顾 Shahu 解释的 leaps 方法。所以我意识到我的观点与他的观点略有不同。所以,他解释的论文,他是合著者。我没有参与那篇论文,但我绝对喜欢那项工作,并且我研究那篇论文的程度如此之深,以至于我觉得它就像我自己的论文一样。所以,我可以像我写了那篇论文一样向您解释。所以,我将再次回顾它,因为它对我们接下来的步骤很重要。接下来的步骤将与学习语义空间有关。这就是我们将如何使搜索空间更友好,以便我们可以更快地找到我们正在寻找的策略。最后,如果您正在查看所有这些并说嘿,我不在乎所有这些离散搜索,我喜欢我的端到端方法。那么请坐好,我将向您展示我们如何实际做到这一点,并且从编程的角度看待这些策略,就像将神经网络视为程序一样,实际上非常有帮助。我将向您展示我们如何通过采用这种编程方法在强化学习中学习选项。好的。所以,这或多或少是我演示的最后一部分的路线图。我将回顾 leaps,然后是语义空间,最后是如何分解神经网络来学习选项。好了,我们继续。这就是 leaps 方法,我们在这里真正要做的是尝试学习一个空间,在这种情况下是一个对搜索算法友好的潜在空间,这绝对是我的梦想,我不必处理编程语言给出的离散且棘手的空间。我将提前学习一个潜在空间,这将使我的生活变得更加轻松。他们做得非常巧妙。所以,他们将学习这个自动编码器,其中您在这里看到的中间的这个向量 z 将是定义我们将要搜索策略的空间。所以,有一个通常的重建损失。所以,这里有一个程序将为我们提供输入。我将学习如何重建这个程序,这很好。它正在训练自动编码器,但这还不够。所以,他们想出了另外两个损失。所以,第一个损失是试图将空间中的向量聚集在一起,这些向量将解码成行为相同的程序。它们应该在嵌入空间中彼此靠近。所以,这实际上是在尝试在嵌入空间中创建这种局部性,以便稍后的搜索会更容易。在他演讲之后,有一个与此相关的问题,是关于 Shiaua 的,这实际上是在回答您的问题。所以,这实际上是关于获得行为的想法。例如,我在左侧有这个程序 roll。我将在环境中运行它,然后我将运行重建的程序在环境中运行。我将查看采取的行动。如果这两个程序采取相同的行动。我不在乎语法是否不同。只要它们的行为相同,那么我想确保这两个向量在这个空间中彼此靠近,因为这将在以后有助于搜索。他们还有一个额外的损失,它也试图通过直接从向量 z 获取动作并将动作与程序 roll 在环境中执行的任何动作进行比较来实现完全相同的事情。所以,它实际上是在尝试创建一个对搜索友好的搜索空间。然后,一旦我们学习了空间,我们就可以进行局部搜索,这与我之前描述的爬山局部搜索非常相似,只是它被称为交叉熵方法。所以,我们从一个候选解决方案开始。我们有这个向量。我们将应用一些噪声。我们得到了一堆邻居。我们在环境中评估它们,然后在环境中获得的奖励将反馈到搜索中,以便我们可以决定使用哪个向量来继续我们的搜索。所以,这与爬山方法非常非常相似,只是现在在这个我们希望能够搜索的更美好的空间中。所以,这绝对是一个非常巧妙的想法。我喜欢它。问题是当我们回到并应用我之前描述的非常简单的方法时。我们有语法,我们从语法中采样,我们在语言空间中进行爬山搜索。我将称之为语法空间,因为我们正在改变程序的语法以便获得邻居。这样我们实际上可以做得比在潜在空间中进行搜索要好得多。所以,这里只是这个是我将展示的唯一经验结果。在 y 轴上是回报。在 x 轴上是剧集数量。然后我将展示三种方法。所以,这个 hc 实际上是爬山。我们正在语法空间中进行爬山,就像我之前展示的那样。SS 和 sem 将是我们将在潜在空间中进行的搜索。所以,您可以看到在语法空间中搜索实际上比在潜在空间中搜索更容易。发生这种情况有很多原因,我很乐意与您谈谈。我还没有放弃这个想法。我仍然喜欢 leaps 的想法,但仍然有工作要做,以便我们能够实际实现它。最近有一篇由 Makfolene 和 Bonet 撰写的论文,Matthew 在这里吗?也许 Matthew 不在这里。所以,Matthew 将参加会议。您应该完全与他谈谈他的工作。这是应用类似于 leaps 的东西,但用于监督学习问题,在 arc 挑战中,他有一些非常有趣的结果,我相信在监督环境中应用学习潜在空间以在该空间内进行编程搜索会容易得多,而这正是我们应该首先探索的地方,然后再转向强化学习。好的,这就是我关于这些结果要说的全部内容,但我不想放弃 LEAPS 背后的核心思想,即学习一个潜在空间,使我们更容易找到策略。如果我们无法在语法空间中击败爬山,为什么不尝试结合这两个想法呢?这正是我们正在做的。我们称之为语义空间。所以,比较两个程序的行为,以确保两个向量在空间中彼此靠近的想法。在强化学习的背景下,这具有语义感,因为强化学习中程序的语义实际上是代理在环境中的行为方式。所以,现在我想确保我能够强制执行以下想法。如果我在这个邻域空间中进行搜索,那么我想确保行为相似的程序将具有语义上的不同。所以,每当我生成一个邻居时,我希望这个邻居在行为上与我当前的程序不同。所以,如果您从搜索的角度来看,这也适用于深度强化学习,我不太确定如何在深度强化学习中使用这个想法,但每当您获得一个新的策略,它是当前策略的邻居策略时,您都希望它表现不同,否则您正在评估与您之前拥有的策略完全相同的策略。所以,您只是在浪费您的样本。您正在使用更多的样本来一遍又一遍地评估相同的策略。这在语法空间中经常发生,我之前向您描述过的。您更改了树中的一个子树,然后得到了一个新的,最后,您将拥有完全相同的行为。所以,代理的行为将完全相同。所以,如果我必须画一个这个空间的卡通版本,它看起来会像这样。所以,它充满了平台,我们知道这对局部搜索来说非常糟糕,因为您环顾四周,每个人看起来都一样,猜猜怎么着,他们看起来一样,因为他们的行为与当前候选解决方案完全相同。所以,从搜索的角度来看,这根本没有帮助。为了给您非常具体的数字,在我们研究的一些语法空间问题中,五分之一的邻居会与当前候选解决方案的行为完全相同。所以,我们想做得更好。我们想创建一个对局部搜索更友好的搜索空间。这就是我们试图用语义空间实现的。所以,我们希望它看起来更像这样。所以,它非常平滑,我们确切地知道下一步该去哪里。为了给您非常具体的信息,我们可以从五分之一的邻居减少到五十分之一的邻居与当前候选解决方案相同。所以,现在我知道,每当我评估一个程序时,我知道它的行为不同。所以,另一种看待它的方式是,我们将行为作为回报的代理。它不会完全相同,但它是一个很好的代理。就像您查看一个邻居,它将与当前邻居相同,您知道回报不会改变。但如果您能确保邻居在行为上发生变化,那么回报也有可能发生变化。所以我认为这里有一些更深层的东西,我们可以借鉴并用于深度强化学习。不完全清楚,但也许这里有一个研究机会。好的。那么,我们如何实现这一点呢?我们如何获得一个看起来像我们底部那个的景观?我们通过使用程序库来做到这一点。所以,这个想法非常简单。我们将获得一个 MDP。所以,这是我们要解决的问题。我们可以将其视为一个强化学习问题。以及一个领域特定语言。所以,这是我们将要搜索的语言,我们将尝试近似该语言的底层语义空间。所以,我们将通过从问题的某个解决方案开始来做到这一点。所以,您看到的策略,它是一个程序,它是解决我们感兴趣的任何问题的程序。而且因为它具有这样的良好结构,您实际上可以生成一个大型语言模型并获得任何您试图解决问题的初始解决方案。一旦我们分解了这个程序,我们将得到一堆子程序,就像我们那里一样。子程序为我们提供了代理的行为。所以,如果您熟悉强化学习中的选项框架,那么我们获得的这些子程序,它们本质上是强化学习中的选项。所以,我们得到了一组选项,我们将使用一组选项,这些选项构成了我们称之为不同行为库的内容,以诱导这个语义空间。但我们该怎么做呢?所以,让我向您展示。所以,这里右侧有一个小框,那就是我们的语义空间。所以,我们仍然会使用与我们之前所做的类似的想法,即我们得到一个表示我们程序的树,然后我们将更改该树的一个子树,以便我们可以得到一个新的树,并且我们将通过这种方式获得邻居。但是,我们不会随机生成子树,而是将使用我们在这里左侧构建的库。所以,假设我们有这个,也许这太小了,但那里的小圆圈写着 n。那是我们的程序 n,那是我们当前的候选解决方案。我们想生成 n 的邻居。n 由您在屏幕上看到的程序表示。也许它也太小了,无法告诉您发生了什么。所以,在程序 N 的末尾有一行写着 C4。它只是调用一个函数。我们将要做的是生成一个邻居 N',即底部的那个,我们将用库中的一个程序替换 C4,这个程序的子树。一旦我们这样做,我们就知道我们正在进入库并获得一些定义明确的东西。所以,我们保留在库中的所有程序都有明确定义的行为。所以,我们得到整个程序,然后将其插入到另一个程序中。所以,我们通过我们以前的程序组合了一个新程序。所以,这样一来,这两个程序很可能具有完全不同的行为。这正是我们在这里试图近似的。好的。给定这个新的邻域函数,现在我们可以搜索由这个邻域函数给出的空间,该函数近似于语义空间,我们心中有一个关键思想,即邻居程序应该表现不同。但是,我们该怎么做呢?所以,我们不能完全依赖语义空间。原因是我们最初的库可能为空,然后根本没有语义空间。所以,我们需要将旧语法空间中的搜索与语义空间中的搜索混合起来,以确保我们可以混合这两种想法,比如语法空间中的搜索。它正在探索程序空间,以便我们可以学习新程序并将它们输入我们的库,然后一旦我们有了体面的库,我们就可以使用它来在空间中获得捷径,并且我们正在模仿这个底层的语义空间。所以,搜索将与我们以前的非常相似。所以,在这个下面的黄色框中就是爬山。我们将从某个初始候选解决方案分布中采样。假设是 n。一旦我们得到 n,我们将应用贪婪方法,即查看邻居并选择最好的邻居。所以,最好的邻居是由这个 argmax 函数给出的,在这种情况下,我们的邻域函数是语义空间的邻域函数,这将以 1-epsilon 的概率发生。但以 epsilon 的概率,我们仍然会回到语法空间,我们将使用那里的邻域函数。所以,想法是,每当我们走底部通道时,我们都在探索空间并学习新程序。当我们走上部通道时,我们正在学习组合我们库中已有的现有程序。然后其他一切都是爬山。我们将得到 argmax 运算符的结果。然后我们将检查这是否是局部最优值。如果不是局部最优值,我们就从那里继续搜索。这正是爬山所做的。如果它是局部最优值,那么我们将把它作为这个搜索的结果返回。但我们不会就此止步。一旦我们找到 n*,我们将使用它来反馈并扩展我们的不同行为库。所以,我们搜索得越多,库就越大,语义空间的近似就越好。这真的很酷,因为代理正在通过使用这个不同行为库来学习如何搜索。好的。我们有非常强的经验证据表明语义空间非常有利于搜索,并且它确实借鉴了 leaps 的一些想法,比如 leaps 试图将这些行为相似的向量组合在一起,而我们在这里试图做一些类似的事情。我们试图找到一个非常平滑且漂亮的、能够与爬山很好地配合的空间。我们在这里所做的基本上是给我下一个行为,给我下一个行为,依此类推。好的。而且,当我们拥有这种表示时,这也很好,因为您不能要求大型语言模型提供神经网络的权重集,因为我想开始学习解决这个问题。但是您可以要求提供一个解决这个问题的程序,它实际上做得相当不错,我们可以使搜索快得多。这就像通过提供这个行为库以及大型语言模型生成的内容来赋予您的代理与生俱来的能力。还有很多工作要做,顺便说一句,我正在寻找对这些主题感兴趣的博士生。所以,想法是学习如何学习,一旦我们有了这个不同行为库,如何处理新问题,鉴于我知道我了解所有这些类型的行为。那么,我该如何搜索语义空间呢?好了。所以,这是第一部分。让我看看第二部分,只是在这里做数学,看看我还有多少时间。沙,如果您能留意一下并告诉我,好的,太棒了。所以,现在是第二部分,也许您真的不喜欢在离散空间中进行搜索的想法,并且您根本不想这样做,那没关系。我实际上有时完全支持。所以,我有这两个研究方向,我们同时推进。而这里的第二条线是,如果我们尝试从神经网络中学习所有需要的程序,以便我们可以端到端地学习呢?这是我们尝试将从软件工程中学到的技巧应用于我们的代理。所以,这里有一个非常简单的程序,chat GPT 为我生成的,其中我们有两家商店的销售额,如果销售额超过某个值,我们将给予 10% 的折扣,然后您可以通过执行两次逻辑来解决这个问题,您将获得这个销售向量,然后您将执行折扣逻辑,您将获得第二个销售向量,您将再次执行逻辑,但我们知道我们可以做得更好,我们可以重用代码,对吧?我可以得到,例如,这个框,我可以创建一个函数,然后我不必重复相同的代码。我只需要调用一个函数,我就完成了。这是一种重用我们已生成知识的方式。但是我们如何用神经网络做到这一点呢?所以,我将向您展示我们如何分解神经网络。一旦我们将神经网络视为程序,我们就可以将其分解成更小的程序,这些程序在不同任务之间具有更好的泛化能力。我们将使用一种非常特定的架构来做到这一点。我们正在研究更通用的架构或其他架构,但我们将使用具有分段线性函数的前馈神经网络。所以,我们将使用 relu 激活函数,在这个简化的神经网络中,我只是去掉了所有的偏置,以便让它更容易一些。但是,以及我们在这里的这两个隐藏节点 A1 和 A2,我们将使用左侧的 relu。所以,对于这些神经元中的每一个,它们要么不活跃,如果我们位于图的左侧,要么它们将是活跃的,如果我们位于图的右侧。所以,对于 A1,它要么是输入 X1 和 X2 的线性函数,要么是零。对于 A2 也是如此,它将是输入的线性函数,要么是零。所以,现在我可以查看这两种神经元的所有四种可能组合。所以,例如,如果我查看 A1 是线性函数且 A2 是线性函数的组合。现在我可以将此神经网络的输出 H 重写为 X1 和 X2 的线性函数,因为线性函数的线性函数也是线性函数。这里是推导,以确保每个人都在同一页面上。我只是将 A1 和 A2 替换为两个线性函数。然后在底部我们得到 X1 θ' + X2 θ''。所以,这只是重写这两种情况的一种方式。我可以涵盖所有这些。有四种情况。我可以涵盖所有这些。然后上面的神经网络等同于一个带有 if-else 程序的程序。一旦我们这样看待神经网络,我们就可以进入神经网络内部,挑选这个程序的 little pieces,也许整个神经网络不会泛化到不同的任务,但神经网络的更小的 pieces 会泛化到不同的任务。所以,这正是我们将要做的。所以,让我展示一个稍微不同的视角。我认为理解神经网络程序中有多少可能的子程序更容易。但在我这样做之前,您应该真正将神经网络视为编码 if-else 程序的非常有效的数据结构。好的,我们称这种结构为神经树,它允许我们推断出我们拥有的可能子程序的数量。这是完全相同的情况,我们现在只有颜色,因为我想向您展示这个 if-else 程序作为一棵树。这本质上是一个斜决策树。例如,我可以这样说,A2 神经元,它是粉色的,将是树的根。然后从树的根部,我查看这两种可能性。它要么是零,然后我将沿着左分支,要么是线性函数,然后我将沿着右分支。然后我们将根据这个决定(无论是零还是线性函数)来重写神经网络的输出。但我也可以让蓝色神经元成为树的根。一旦我这样做,我实际上会得到一个完全不同的神经树。例如,我可以只突出这个程序。这个程序是一个子策略,我们可以用它来解决我们有兴趣解决的强化学习问题。这与子树不同,因为我改变了树的根。那么问题是我们有多少个?所以,有一个非常直观的方法可以发现我们有多少个。如果我们得到叶节点,那也是一个我们想关注的有效策略,我们想看看这是否真的是一个我们想给代理的子程序,以便代理可以尝试解决这个问题。所以,做到这一点的方法是考虑每个神经元的所有三种可能性。所以,一个神经元可以是不活跃的,也可以是活跃的。我们说神经元不活跃,当我们沿着树的左分支时。假设是树的根。所以,如果您得到这个顶部的,您实际上看不到它。好的,现在您可以看到了。所以,如果您得到这个顶部的,然后我们沿着左分支,那么我们将说这个神经元是不活跃的,因为左侧的所有程序都是在该神经元不活跃的情况下获得的。如果我们沿着右分支,那么右侧的所有程序都将在该粉色神经元活跃的情况下获得。最后,还有第三种可能性,即神经元将成为程序的一部分。例如,如果我们得到整个子树,这里突出显示了,那么蓝色神经元就是程序的一部分。所以,对于任何输入 X1 和 X2,我都会问那个神经元,您活跃还是不活跃?所以,鉴于此,如果我们有 n 个隐藏神经元,我们将有 3 的 n 次方个可能的子程序。所以,现在我们有了一个不同的搜索空间,我们仍然可以用梯度下降进行端到端搜索。这是正在进行的研究,但我们可以搜索 3 的 n 次方空间,并用梯度下降找到我们正在寻找的子程序,以便我们可以将其传递给代理以解决下游任务。好的。所以,让我们讨论一下当您扩展您的不同行为库时所产生的影响。所以,假设我们要学习如何解决一个问题。我们分解神经网络,现在我们有了 3 的 n 次方个不同的程序。我们应该选择哪一个?如果我们选择太多,那么问题就会变得更难。它会变得更难,因为现在有更多的函数需要我们学习如何使用。这非常像您和我。每当我们学习如何使用 API 时,如果 API 非常庞大,那么就有太多函数需要我们学习。这将使我们更难。但它也会使事情更容易,因为您将拥有这些非常强大的函数,一旦您学会了如何使用它们,您就可以更快地解决问题。所以,问题是如何平衡这两者,我们将将其视为一个子集选择问题,我们将使用一个称为 Lving 损失的损失。所以,Leving 损失是我们过去用来指导树搜索算法(如 A*)的算法。它被称为 Leving 树搜索。但在这里,我们将使用它来选择一个不同行为的库。所以,目前我们评估所有程序。但正如我告诉您的,有一种方法可以做到端到端。我将跳过 Leving 损失幻灯片,因为时间关系。但让我给您一个直观的理解。我们有大量的不同子程序。我们应该选择哪一个?我们评估所有程序的 Leving 损失。我们选择最能最小化 Leving 损失的那个。然后一旦我们选择了那个,我们就进入下一个,下一个,依此类推。最终,随着我们扩展不同行为的库,Leving 损失将上升,那时我们将停止。我们将把这些程序集传递给代理。所以,代理可以使用它来解决下游问题。它基本上是给代理一个选项集,您可以在强化学习中使用。所以,希望这能让您更容易解决这些问题。所以,我们将在它下降时停止。我们称之为 deck options,因为我们正在分解神经网络,并从这些分解的神经网络中提取选项。所以,总的来说,我们的想法是学习如何解决一些问题,比如持续学习设置,一旦我们这样做,我们将分解神经网络。我们将将其组织成更小的、非常有用的、可跨不同任务重用的部分,然后我们将选择其中的一个子集,然后我们将这些选项用于下游任务。所以,这很有帮助,比如如果您要解决的问题是相关的,那么您实际上可以从中提取结构。让我给您举个例子。所以,这就是我们称之为 combo grid 的。所以,它是一个网格世界,就像我们在强化学习中经常做的那样,但它不是任何网格世界。它是一个网格世界,如果您想上下左右移动,您实际上必须应用一系列动作。所以,例如,如果您应用动作 0 2 1,那么代理在网格中间的效果是向下移动一个单位。然后如果您应用所有其他动作,那么您将获得以下行为。代理将向上、向上,然后向下、向左移动。所以,而不是简单地上、下、左、右移动,它必须应用一系列动作才能实现。然后问题是,我们能从数据中学习到这一点吗?我们能从数据中学习环境的动态吗?让我给您标签,这样更容易可视化。所以,前四个动作将使代理向下移动。接下来的四个动作将使代理向上移动,依此类推。那么,我们实际上通过分解神经网络学到了什么?所以,例如,我们将学习选项三在这种情况下,它完全匹配向下。我们将与其他选项类似,例如选项二将根据状态向上移动,或者根据其他状态向右移动。还有选项四并不完全是向下移动,但它完成了大部分向下移动。所以,这将使代理更容易解决这个问题。好的。所以,我没时间了。所以,让我试着总结一下。所以,如果您从这个编程角度来看待强化学习,并且真正退一步,将策略视为程序,那么我们可以进行知识保留。所以,创建这个不同行为库的想法,您不会忘记它,它一直都在那里,并且您可以随着学习如何更好地解决这些问题而改变库,但您将拥有一个您将学会如何使用的行为库。您可以重用它们,因为您可以简单地调用这些函数并使用它们来解决新问题,或者您可以通过组合它们来发现新知识。记住这个想法,我所说的一切的共同主线是,我们可以组合所有这些策略,以便通过组合它们获得新的东西。即使您不喜欢这种拥有不可微分空间的想法,我们无法端到端学习,就像您考虑解决这些 MDP 的问题一样,解决强化学习问题,从这个编程角度来看,它非常有帮助,因为您可以将神经网络视为一个您可以分解的程序,并且您可以对它做各种很酷的事情。所以,思考程序可能就足够了。如果您不喜欢看到 if-else 等等,或者有限状态机,那么以这种方式看待策略非常有启发性。好的,我正在寻找博士生,如果您对其中一些主题感兴趣,请与我交谈,但我将在这里停止,也许我可以问一个问题,应该没问题,一个问题。明白了。所以,问题如下。您是否研究过将潜在空间中的搜索与实际语法空间中的搜索结合起来?我还没有自己做过。我们发现,在语法空间中搜索通常足够好。我们需要混合的是语义空间和语法空间中的搜索,因为这才是真正带来区别的。这就像探索-利用困境。它发生在程序空间中。但这是一个很好的观点。也许这里有一些东西,因为我们可以永远学习如何,我们从不停止学习,对吧?您学习一些潜在空间,但当您在语法空间中进行搜索时,您可以尝试利用这些信息进行反馈。我喜欢这个想法。欢迎来到教程的最后一部分。我是来自 Google Dine 的 Sing Chan,我将讨论神经代码生成,特别是,我将讨论那些真正触发了从为每个单独的编码应用程序构建专业系统(使用深度神经网络)到构建通用的基于大型语言模型的代码生成系统的范式转变的基本技术。那么,让我们开始吧。如今,我们已经看到了许多以强大的大型语言模型为后盾的编码产品。所以,基本上,有了这些基于 ARM 的编码模型,我们可以看到它们可以在软件开发过程的各个方面帮助我们。在幻灯片中,我展示了来自 Cursor 的演示。所以,基本上,根据用户的提问,这个 Cursor 系统将首先解释它将如何解决问题,然后它将建议一些新的代码片段,最后对现有代码库进行一些编辑。随着这些模型在解决复杂的编码和推理问题方面变得越来越好,我们也期望这些模型为我们做更多的事情。我们希望它们成为能够端到端处理软件工程任务的可靠开发人员。所以,例如,从 Zoric 的最新商业使用演示中,我们可以看到这个云模型能够基本上首先编写代码来创建一个用户要求的网站,然后该系统能够控制计算机来调试代码并展示网站的外观,最后保存它所做的所有更改。最近,人们还在开发 ARM 代理来完成一些数据科学和机器学习任务。所以,在幻灯片中,我展示了 MLE bench 数据集,由 OpenAI 创建,其中他们展示了 OM 预览模型能够在该基准测试的 17% 的 Cargo 竞赛中获胜。当然,目前的代码生成模型距离完美还有很长的路要走。但至少对我来说,我认为最新 D 模型的性能已经很神奇了。如果我们看到了在 ARM 出现之前就已经存在的工作。所以我一直在神经代码生成领域工作,大约从 2016 年到 17 年开始,我真的认为这个地牢模型是代码生成的游戏规则改变者,它确实触发了构建通用和记者编码系统的范式。那么,让我们回顾一下 2017 年,当时出现了第一批关于具有某些执行要求的代码生成的论文。所以,在幻灯片中,我展示了这篇发表在 IO 2017 上的神经符号程序合成论文。在这项工作中,他们处理的是 flash view 基准测试。所以,对于这个基准测试,它基本上是试图在领域特定语言中生成字符串转换的程序。所以,当时从他们的结果来看,他们表明他们可以生成最多 13 行代码。所以,当时如果你想保证代码的正确性,生成超过 10 到 20 行的代码已经非常具有挑战性了。所以,后来我们将真正看到大型语言模型能够显著提高代码的复杂性,以及这些神经模型能够完成的编码应用的广泛性。在我教程的其余部分,我将讨论三个部分。首先,我将讨论 ARM 时代之前神经代码生成的基本技术和背景。然后,我将讨论大型模型如何改变代码生成范式。基本上是从构建专业系统到通用系统的范式转变。最后,我将讨论开发 ARM 代码代理的开放性问题。那么,让我们从第一部分开始。所以,代码生成一直是一个长期存在的挑战,其历史可以追溯到 1950 年代。所以,在统计数据中,我展示了我们之前提到的 flashfield 任务。所以,实际上,这个任务是在 2011 年为 Microsoft Excel 的 splashy 自动完成功能引入的,但当时我们使用的是符号搜索而不是深度神经网络来生成相应的代码。类似地,在 2016 年之前,从自然语言生成代码的领域方法也不使用深度学习。相反,当时我们主要开发学习自然语言和编程语言语法之间显式对齐的方法。这类技术很难扩展到处理更复杂的语言。然后,从大约 2016 年到 17 年开始,我们展示了一些神经网络架构,用于在 flash field 领域生成代码,如前所述。此外,还有一些关于在更复杂的编程语言中生成程序以及处理更复杂的自然语言描述(如文本到 SQL 生成)的其他工作,如幻灯片所示。在 2021 年,我的作品 special coder 表明,我们可以从 Google Sheets 中模糊的表格上下文中进一步生成这些特殊公式。这是如今 Google Sheets 中支持公式预测功能的系统。所以,从这个过程图来看,基于学习的技术能够支持不同格式的更复杂和模糊的程序。这很难用纯符号方法来实现,因为处理程序表示中的每种不同类型的噪声和模糊性都需要许多不同的启发式方法,并且手动调整所有这些方法需要大量的人力。所以,在本教程的背景下,我将专注于程序合成设置。所以,这里对于程序合成,我们的意思是,我们希望在给定用户指定的要求的情况下生成独立的代码。所以,这里我将不关注那些自动完成技术,如果关于生成的代码应该如何行为没有清晰的描述,或者生成的代码本身在语法正确性方面不完整。所以,我之前的教程的其他认知者也谈到了一些其他的程序合成应用。所以,在我的部分,我将专注于软件工程中最常用的两种规范格式。第一种是从输入输出样本生成程序,其中给定指定的输入,程序需要具有与指定输出相同的执行输出。另一种格式是从自然语言生成程序。所以,在幻灯片中,我展示了从自然语言命令生成 ift 程序的应用程序。如今,这也是一个非常重要的应用程序,与 API 调用和工具使用有关。在 ARM 之前,程序合成的标准神经架构遵循这种编码器-解码器框架,具有规范编码器和程序解码器。所以,编码器架构可以是 RSTM 卷积神经网络图神经网络等,具体取决于不同的输入格式。然后对于程序解码器,我们也可以让它以不同的形式生成程序。例如,我们可以让它生成 token 序列,这也是目前 D junction 模型常用的做法。如果我们有一个树解码器,那么我们可以以抽象语法树格式解码程序,或者如果我们使用图解码器,我们甚至可以生成语义图格式的代码。为了讨论一些关于模型架构设计的动机和原则,让我们进入这个教程的第一个案例研究:文本到 SQL 生成。所以,在文本到 SQL 生成应用程序中,输入包括一个自然语言问题和数据库内容。然后相应的输出应该是 SQL 查询,如果我们对数据库执行 SQL 查询,它应该返回自然语言问题所需的信息。所以,在 ARM 之前,通常对于文本到 SQL 生成模型,我们将设计一些专门的编码器来处理此应用程序中的一些独特挑战。第一个挑战是模式链接,我们需要理解问题与模式项不同部分之间的对应关系。所以,让我们来看一个简单的例子:谁注册了学士学位课程,列出第一个。
姓名 中间名 姓氏。因此,从这个问题中,我们可以已经看到数据库内容各部分的重要性的一些例子。具体来说,模型需要理解名字、中间名、姓氏对应不同的表列,而学士学位项目对应于“学位项目”表中的数据库值“学士”。另一个挑战是理解不同表之间的关系,特别是那些通过外键连接的表。因此,这对于在 SQL 查询中生成连接子句很重要。处理这些挑战的一个代表性架构称为 Red SQL。因此,在它们的编码器中,它们设计了一种称为关系感知自注意力的注意力机制,基本上它们为每种预定义的边类型学习一些额外的嵌入,如幻灯片所示。这样,它们的自注意力层就偏向于这种具有关系的 token,而不是这里的模式链接,并且其他类型的边的构建也主要通过文本匹配完成。这种设计对于拼写错误和同义词不是非常健壮。然后对于解码器,它们还设计了一个基于 SQL 语法和表模式的树状解码器。这种设计的优点是可以保证生成的 SQL 查询是可执行的。这里幻灯片上我展示的是这个 Spider 文本到 SQL 生成基准的最新排行榜的截图。我们可以看到,这个 Red SQL 架构是 2020 年到 21 年间多个参赛作品的骨干。总的来说,这个树状解码器是为 SQL 以外的多种编程语言设计的。因此,幻灯片中我展示的是序列到树架构中的这个通用分层树解码方案。基本上,模型每次需要生成一个子树时,它都会首先生成非终结符 token n,然后子树将根据这个非终结符 token 的上下文嵌入生成,用于输入和输出都是程序的应用程序,例如代码翻译。在我们的工作中,对于树到树神经网络,我们展示了我们可以设计一个具有树状编码器和解码器的模型,从而更好地表示输入和输出程序之间的结构对应关系。最后,我们还可以通过添加一些可以表示程序节点之间语法和语义关系的边来将代码表示为图。现在让我们来谈谈训练和推理技术。因此,对于训练,我将讨论带有合成数据的监督学习以及来自同步和执行反馈的强化学习。然后对于推理时间技术,我将讨论执行引导的合成、代码重新排序和合成循环中的调试。显然,这不是一个详尽的想法列表,但这些是代表性的方向,展示了提高代码生成性能的通用原则,即利用代码语法和执行作为强大的信号。因此,让我们从程序样本应用程序的训练部分开始。设计合成数据实际上一直是一种常见的做法。因此,回想一下,在“举例说明程序”任务中,模型输入包括输入输出对,模型输出是程序。然后对于合成数据生成,基本上我们切换了程序每个部分生成的方式。因此,基本上我们首先有一个随机输入生成器来从预定义空间中的可能输入进行采样,例如在新鲜字段域中。这将是一些字符串。然后我们将有一个随机程序生成器来从程序员空间中采样程序。然后,给定这些随机生成的输入和程序,我们将执行程序以获得输出。这个过程保证了生成的输入输出对和程序是可验证正确的。最后,我们可以有另一个过滤过程来进一步控制数据质量。例如,进行一些去重和删除琐碎程序。由于自然语言语法的复杂性,为文本到代码生成合成数据要困难得多。因此,在 2015 年,有一篇题为“一夜之间构建语义解析器”的论文,它介绍了在某种特定领域的语言中生成文本到代码配对数据的过程。其思想是,我们首先基于自然语言和代码之间预定义的翻译语法生成合成文本和代码数据。然后有一个众包阶段来释义合成问题,使它们看起来更自然和多样化。从这个生成过程中,我们可以看到一个明显的限制,因为这种过程依赖于自然语言代码之间手动设计的对齐规则。这本质上限制了可以处理的语法的复杂性。现在让我们来谈谈来自语法和执行反馈的强化学习。因此,使用强化学习的动机在教程的前一部分已经被广泛涵盖。因此,我将在这里更多地讨论这个软件工程应用程序。因此,在许多情况下,解决问题的正确代码可能不是唯一的,因为语法的多样性,并且在许多情况下,如果我们只关心最终执行,算法实现中可能存在不同的细节。因此,如果我们只进行监督学习,它可能只会惩罚那些也可以准确但具有 RL 的替代程序,它将鼓励模型探索那些替代路径。因此,由于算法部分已被之前的教程演讲者涵盖,我将仅列出一些我们可以用于此软件工程应用程序的示例奖励。因此,例如,在幻灯片上我展示了一个示例设计,如果我们有单元测试作为反馈,我们可以根据代码是否通过了所有单元测试,或者只通过了部分单元测试,或者有任何运行时错误来设计不同的正负奖励。现在我们将从执行引导合成开始讨论推理时间技术。在教程的前一部分,其他演讲者已经谈到了这个 Carol 领域。因此,我在这里也将讨论生成 Carol 程序的情况二,但在这里我们将看到这些不同的测试时间技术如何能够协同提高性能。因此,回想一下,在 Carol 领域,我们正在尝试生成一个程序来控制 2D 网格世界中的机器人。因此,它基本上可以将输入状态转换为输出状态。因此,在输入输出程序合成的标准编码器解码器架构中,在每个生成步骤中,模型的输入将是这些输入输出对的单独嵌入向量,然后解码器将依次生成一个程序。因此,如果我们深入研究这个过程,编码器设计的一个限制是,我们在每个解码步骤中始终将初始输入对馈送到模型。因此,我们并没有真正利用代码执行的这些属性。那么我在这里的意思是什么呢?因此,基本上,如果我们把程序看作一系列程序语句,那么实际上在每个语句之后,它会尝试将执行状态从上一步转换为下一步。而且,当我们人类编写代码时,我们隐式或显式地思考程序将如何逐行或逐块执行。我们通常不会在不考虑这种语义意义的情况下编写整个程序。因此,基本上基于这个直觉,我们在工作中设计了这个执行引导的神经程序合成,我将首先使用一个生成直线程序的示例来说明这个想法。这意味着这里的程序非常简单。它没有复杂的控制流结构,如循环和条件。因此,在开始时,模型的输入包括初始输入对作为当前状态和目标状态。但是,通过执行引导的合成,每次模型预测一个程序语句,例如在此处放置标记,该语句将立即执行并产生一个新状态。因此,在下一个时间步,我们可以看到当前状态已更改为这个中间执行状态。这个新状态将获得后续程序的合成。通过这种方式,模型能够真正利用这些中间执行结果,这些结果明确地告诉模型当前状态与目标状态的接近程度。这将非常有益于模型规划后续程序的生成。我们可以进一步将此框架扩展到生成带有分支的程序。因此,每次模型预测一个 if 语句时,我们都知道这个 if 语句将有两个分支,一个用于 true,一个用于 false。因此,在标准框架中,对于这两个分支,我们将所有输入示例馈送到模型以生成程序。但直观地说,true 分支仅取决于满足条件的那些输入,而 false 分支取决于其余输入。因此,在执行引导的合成中,每次模型预测一个 if 条件时,我们将首先评估所有输入在该条件下的情况,并将其分成两个子集,一个用于 true 分支,一个用于 false 分支。然后我们使用输入示例的子集生成相应的分支,最后我们将两个分支组合在一起。通过这种方式,我们可以更好地利用这个程序语义意义来指导合成过程。但是,也有一些编程语言或者一些场景,这个程序解释器不可用,无法明确地给我们这些中间执行状态。因此,在我们后续的工作中,我们提出了这个潜在执行器框架,我们训练了一个神经网络来近似这些中间执行状态。因此,基本上对于这个潜在执行器,给定上一个时间步的执行状态和当前生成的程序,潜在执行器将预测下一个执行状态。训练这个数据执行器的一个主要挑战是,对这些中间执行状态没有直接的监督,而这正是我们首先想要训练这个数据执行器的原因。然而,至少我们总是有最后一个时间步的训练目标,那就是完整的程序输出。因此,训练这个模块的想法是基本上构建一个包含短程序和长程序的语料库。我们希望模型能够通过学习短而完整的程序的执行结果来学习长程序中的中间执行状态。而且,由于这些程序执行语料库可以大规模合成生成,正如我们之前提到的。因此,基本上这允许我们灵活地控制语料库的长度分布。在我们的关系中,我们展示了通过对程序执行进行建模,我们可以看到在 Carol 领域(我们可以有解释器提供中间执行状态)以及在另一个生成 C 程序进行列表处理的领域(没有可用的解释器)的性能改进。之前我们已经展示了我们可以在生成程序时使用这种执行,事实上,一旦我们生成了完整的程序,我们也可以利用执行,特别是如果我们允许一次生成多个程序,并从不止一个程序中选择最终候选程序。因此,基本上在执行引导的合成工作中,我们也设计了这个执行引导的集成。因此,基本上,假设我们训练了多个模型,它们生成不同的程序,我们将首先在给定的输入样本上执行所有这些程序,然后我们将看到它们是否至少通过了给定的输入。对于那些已经失败的,它们将不会在第二阶段的程序选择中被考虑。但是,在典型的“举例说明程序”任务中,通常也会有一些隐藏的测试。这意味着即使模型通过了给定的输入输出对,它仍然可能在最终测试中失败。对于那些通过了这些输入示例的程序,在程序描述中,我们在这项工作中使用了非常简单的集成原则,包括选择最短的预测程序或选择多数投票,即基于精确匹配的最频繁预测代码。因此,对于 Carol 领域是可行的,因为在那里的语法相对简化,但稍后在第二部分讨论 RMS 时,我们将讨论一些更高级的技术来处理更复杂的通用语言,如 Python 和 C++。如果我们把所有东西放在一起,我们可以看到,在我们使用的所有不同类型的合成器上,这些集成技术总是会提高性能,特别是如果我们将其与不使用执行的合成器进行比较。最后,我将讨论这个在合成循环中调试的过程。这可能看起来更像是与当前的 a aent 工作流程相关。因此,基本上这是我们在 2020 年完成的一项工作。其思想是,在我们拥有合成器之后,我们将训练另一个神经网络作为调试器,以在合成器输出的基础上提出更多的编辑。因此,例如,这里我展示了一个非常简单的示例,即编辑跟踪,在每个 token 的基础上,这个调试器将决定我们是否要保留当前 token,或者插入更多的程序语句,或者延迟当前的 token。当时我们分两个阶段训练另一个独立的调试器。在第一阶段,我们将使用合成数据训练一个模型,其中包含程序损坏的代码及其对应的正确代码。通过这种方式,我们可以大规模生成这些数据,在第二阶段,我们将使用合成器模型生成的低真实预测代码和对应的正确代码来训练一个模型。通过这种方式,我们还可以为评估调整合成器和调试器。我们可以看到,在不同的合成器之上,调试器也能带来改进,实际上我们展示了我们可以训练一个单一的调试器,它可以帮助多个不同的合成器。因此,总结一下,第一部分我将展示,与基于规则的系统相比,神经代码生成技术支持更模糊的程序和更多样化的格式。然而,在 RMS 之前,这些神经代码生成模型仍然存在一些弱点。首先,它需要为不同的编程语言进行单独的训练和架构设计,并且生成的代码的复杂性仍然有限。另一方面,稍后我们将看到,神经代码生成的基本技术在 RMS 之前和之后仍然很重要,特别是利用代码语法和执行来设计丰富反馈信号的通用原则。现在让我们继续讨论第二部分,关于 RMS 如何改变代码生成范式。因此,RMS 之后最显著的趋势是我们不再过多地关注为不同的编码应用程序设计不同的架构。相反,我们正在为不同的模态训练一个统一的生成模型,特别是文本和代码之间存在许多共享的训练配方。因此,基本上,这些最新的 RMS 将编程语言视为另一种自然语言,并且这些工作表明,如果我们拥有大量数据和大规模的现成模型架构(现在主要基于 Transformer),那么它将为文本和代码提供高性能的生成模型。说明这种图式范式的第一个成功是 OpenAI 的 Codex 模型。因此,如果我们再次查看这个演示,现在可能看起来不那么令人惊讶了,但当时演示首次出现时,它非常令人印象深刻,因为如果我们看幻灯片中的自然语言指令,它非常复杂,包含很多实现细节。但是一旦模型接收到这个自然语言指令,模型就能够立即生成代码,没有任何错误。并且在我们执行代码后,它就给了我们想要的东西。因此,这绝对是一项更艰巨的任务,特别是在输入规范部分的复杂性方面,如果我们与之前的文本到代码学术基准进行比较。同样,与文本领域一样,我们也看到了这些编码性能与模型大小的缩放曲线。因此,这里我展示了 OpenAI Codex 模型和 Google PaLM 模型的缩放曲线。我们可以看到,在不同的模型家族中,如果我们扩展模型架构,通常我们会看到在不同基准上的准确性提高。因此,在 RMS 部分,我将更侧重于训练和推理技术。因此,对于训练,我将再次讨论带有合成数据的监督学习。但这次的合成数据不再是程序生成的。它们是由 RMS 生成的,我将讨论带有执行反馈的强化学习。但这次我们将看到,除了那些标量奖励之外,我们还将把这些自然语言信息作为反馈循环的一部分,以指导模型在后续项中生成代码。然后对于推理时间技术,我将讨论代码重新排序、合成循环中的调试,我还将讨论一个将所有组件整合在一起的通用流程。因此,如果我们考虑这些技术,它们仍然利用了我们在第一部分讨论的代码语法和语义信息。但通过 RMS,我们进一步表明,我们可以利用这些最新的基础模型的自然语言理解和指令遵循能力来进一步提高代码生成性能。因此,让我们从第一部分开始。因此,对于合成数据生成,我将再次讨论两个典型用例。第一个用例是在机器可验证合成问题设置中生成代码。因此,幻灯片上我展示了来自论文“语言模型可以教会自己更好地编程”的图。在这项工作中,他们考虑了这个编程谜题问题。因此,在每个示例中,f 函数是谜题,它实际上显示了复杂性。因此,如果我们看这些示例,特别是底部第二个示例,它实际上与我们之前讨论的输入示例有一些相似之处,它非常明确地显示了预期的执行行为。然后这个树函数是预期的相应解决方案。这个过程与之前为输入输出程序合成生成合成数据相比的主要区别在于,这次规范函数和相应的程序解决方案都是由 RMS 生成的。例如,这些特定的函数可以通过 fual 提示生成,并且由于这种设置,我们可以完全验证每个生成问题的正确性。RMS 合成数据生成的重大突破确实发生在文本到代码生成领域。因此,幻灯片上我展示了 Magic Coder 论文的图,该论文用于构建 OSS Instruct 数据。因此,基本上,这项工作表明我们可以提示 RM 生成自然语言编码问题及其相应的解决方案。并且在这个领域,有几种方法可以改进 RMS 的数据质量。例如,在这个 OSS Instruct 数据生成中,它们以一些外部语料库的数据生成为条件。通过这种方式,它们可以控制当前提示在编码应用程序方面要解决的问题,以及要生成的代码作为目标。在其他一些工作中,例如在 Llama 3 论文中,我们也可以提示 arm 生成相应的单元测试以进行数据过滤,显然,由于这些生成的单元测试仍然不完美。因此,与之前的机器可验证程序合成设置相比,在此过程中生成的数据仍然会存在一些噪声。因此,质量真正取决于 RMS 的能力。但至少与之前的 RMS 之前使用语法采样的方法相比,这个过程允许我们获得更多样化的数据,并且数据本身的复杂性也可以大大提高。然后让我们来谈谈自然语言中的执行反馈强化学习。因此,幻灯片上我展示了来自 Meta 的 RF 论文的示例。因此,这里的想法是,当模型尝试生成程序时,除了告诉模型它是否通过了单元测试的最终奖励之外,由于我们已经在循环中使用了执行器,我们还将把这些自然语言的执行反馈纳入这个生成过程中。例如,当代码未能通过单元测试时,我们可以存储这些信息,总结哪些单元测试失败了。然后基本上这个执行反馈将成为下一轮代码生成提示的一部分。如果我们以这种方式训练模型,它不仅可以改进模型在程序生成过程中的表现,还可以帮助模型更好地进行调试。因此,在他们的评估中,他们基本上在不同规模的 Llama 3 模型上对不同的基准测试尝试了这种方法,他们表明它始终能提高性能,并且这种改进在多轮设置中比单轮设置更显著,因为模型可以充分利用其进行多轮调试的能力。现在让我们来谈谈推理时间技术。从代码重新排序开始,我们将进入本教程的第三个案例研究,即使用 AlphaCode 系统生成竞争性编程解决方案。因此,基本上在 DeepMind 的 AlphaCode 工作中,我们考虑了这个竞争性编程设置,其中问题中有一个非常长且复杂的测试描述,旨在欺骗最优秀的人类程序员,并且我们也有像往常一样的输入示例。因此,使用最新的 AlphaCode 2 技术报告,该报告基于 Gemini 1.0 Zero Pro 模型,该系统能够在在线 Codeforces 编码竞赛中排名前 15%。因此,与最新的模型相比,这个模型本身不再是最好的,但仍然可以通过非常先进的推理技术(如基于模型构建)实现相当不错的性能。现在我展示了这个 AlphaCode 框架。这是我们发布 AlphaCode 论文第一个版本的框架,该论文于 2021 年发表在《科学》杂志的封面上。当时在训练过程中,我们仍然训练了一个单独的较小模型,使用了专门的 GitHub 数据预训练,例如,这现在可以用更强大的大型语言模型取代。但对于推理时间技术,它对于性能仍然非常重要。因此,基本上在教程中,我将重点关注这个过滤和聚类的推理时间技术,它基本上表明我们可以利用代码执行来进行重新排序。第一阶段的过滤与我们之前讨论的执行引导集成的第一阶段非常相似。因此,我们基本上想在给定的问题描述的输入样本上执行所有生成的程序,然后过滤掉那些失败的程序,因为对于竞争性编程问题,许多情况下生成的程序甚至可能在给定的输入示例上失败。因此,这个过滤过程非常重要。主要的新东西是如何从剩余的解决方案中选择能够通过可见输入输出示例的程序。回想一下,这里的挑战在于,对于测试问题,我们不知道评估系统背后的最终测试套件。因此,在其他代码工作中,我们从不同的角度考虑这个问题。因此,既然没有测试,为什么我们不自己生成测试呢?因此,基本上在 AlphaCode 工作中,我们训练了一个单独的模型来根据问题描述生成新的测试输入。并且由于我们有一个训练问题语料库,我们可以在此基础上训练另一个模型。然后我们将所有这些生成的程序在这些生成的输入上执行。然后我们将具有相同输出的程序聚类在一起。这是假设如果这个测试生成模型是好的,它们将普遍代表示例以覆盖可能的执行空间。因此,对于具有相同输出的程序,它们应该是语义等价的。然后我们将从 10 个最大的集群中采样一个程序。因此,在我们的最终评估中,我们将把这 10 个顶级程序发送进行完整测试。然后幻灯片上我展示了消融研究,以显示过滤和聚类的效果。如果我们比较这条红线与其他曲线,我们可以看到使用执行进行过滤至关重要,因为正如我们之前提到的,在许多情况下,这些程序甚至无法通过给定的输入样本。然后如果我们比较其他曲线,特别是如果我们比较底部表格,它显示了将模型从 90 亿扩展到 410 亿的改进,与 410 亿模型相比,我们添加了一个聚类过程。我们可以看到,实际上这里的聚类过程可以提供比简单地扩展模型规模更多的性能提升。因此,这还提供了另一个证据,即扩展推理时间技术,它在许多情况下可以帮助扩展模型本身。但如果我们与 Oracle 样本选择进行比较,蓝色曲线显然仍然存在差距。因此,在 AlphaCode 工作中,我们正在尝试训练另一个模型来进行测试生成,但我们可以尝试让单个大型模型同时生成代码和生成单元测试。我们只需要设计不同的提示方案来做到这一点。因此,基本上幻灯片上我展示了 Code T 论文的框架,他们试图通过提示生成单元测试。然后他们将测量生成的代码和单元测试之间的执行一致性来进行排名。因此,显然这里的重新排序性能将严重依赖于生成的单元测试的质量,而这又取决于任务的复杂性以及底层模型有多强大。在本教程的最后一个案例研究中,我将讨论这个在合成循环中的调试。我将介绍我们的工作 SelfDebugging。因此,这次我们不再需要训练另一个调试器模型。相反,我们将通过与单个 ARM 的提示来实现这个调试过程。因此,基本上在我们的 for 循环中,在每一步,我们首先生成代码,然后执行代码,执行器将提供关于单元测试或是否存在运行时错误的反馈。然后,通过这些执行和代码,我们还将提示 ARM 生成自然语言的解释,说明代码试图做什么,以及执行结果告诉我们什么。然后,有了所有这些反馈消息,模型本身还需要决定当前生成的代码是否准确。如果是,则调试过程将终止。否则,所有这些 fe 消息将被放入下一轮以生成新代码。因此,在这个 SelfDebugging 工作中,我们研究了不同类型的反馈格式,取决于我们从问题中获得什么,以及在许多情况下基于人类直觉,如何提高我们的调试效率。因此,在最简单的反馈中,我们基本上拥有这些简短的通用信息,用于所有错误的代码,说代码是错误的,请修复它。然后对于单元测试反馈,我们将执行结果作为后续轮次模型输入的一部分。然后对于代码解释,我们将提示模型生成当前实现的逐行解释。这受到人类机器人陪聊调试过程的启发,通过向自己解释代码,我们可以更好地了解代码的作用,并帮助我们找到那些微妙的实现错误。最后,我们设计了跟踪反馈。其思想是,我们还可以提示 ARM 模拟逐行执行并自行生成执行跟踪。因此,由于这个跟踪是由模型生成的,它可能会有一些错误,特别是在模拟这些中间执行状态时。因此,基本上这里的想法与第一部分中的潜在执行器有关。但再次,这种设计的优点是,这种跟踪反馈可以跨不同的编程语言通用,并且它还可以利用模型的自然语言可理解性,以便模型能够更好地理解以自然语言格式总结的执行信息。因此,对于 eration,我们基本上在不同的基准测试上,以不同的模型为骨干,进行了这个调试过程。因此,我们可以看到,这个 SelfDebugging 过程在 Spider 文本到 SQL 生成任务、TransCoder 任务的 Python 和 C++ 翻译以及 MPPP(一个早期的文本到 Python 生成基准)上,在不同的 ARM 上持续提高了性能,并且我们可以看到更具信息量的反馈可以进一步提高调试性能。因此,到目前为止,我们已经涵盖了构建通用 ARM 编码代理的基本组件。因此,幻灯片上我展示了 Codex 论文中的一个示例框架,并且还有其他论文中讨论的其他框架,以及目前在不同编码产品中。因此,总的来说,构建一个有效的编码代理需要几个核心组件。首先,我们需要一个能够进行多轮生成并带有执行反馈的模型,这在教程的前一部分已经讨论过,并且我们需要一个能够进行推理和规划以执行每个动作的模型。因此,这也将严重依赖于基础模型的能力,最后,还有一些可以用来改进这个代理环境接口的工作。默认原则是,如果你能给模型更多信息,它总是会有帮助的,但也有一些其他有趣的设计选择,由于时间限制,我不会在这里详细介绍,但我列出了一些参考文献,如果你有兴趣,可以看看。另一个想法是设计这个多代理代码生成框架,它也更多地受到人类编码实践中不同团队成员之间协作的启发。因此,基本上我们可以有多个 ARM 代理扮演不同的角色。幻灯片上我展示了 Agent Coder 论文中的这个框架。基本上有不同的组件,如程序员、测试设计者、代码审查者等。因此,早些时候,在调试器合成器循环中,我们展示了我们需要训练不同的模型来扮演不同的角色。但实际上,通过 RMS,我们可以只在同一个 ARM 后端上使用不同的提示方案来进行角色扮演。因此,总结一下,第二部分我展示了 RMS 之后的趋势是为不同的模态构建统一的生成模型。因此,基本上,此时代码格式通常与其他模态(如文本和图像)混合,并且我们通常希望有一个模型用于多个应用程序,例如一个模型可以进行代码生成、编辑、调试、问答等。因此,这种基于 ARM 的代码生成范式的一个优点是,它可以通过提示快速适应新任务。因此,我们不需要为我们想要的每个功能重新调整一个模型。并且对多轮自由形式自然语言对话的支持也提供了更具交互性的用户体验,这现在基本上是我们用来构建编码代理的基础。最后,我将讨论一些在开发基于 ARM 的编码代理方面存在的开放性问题。首先,当然是干净评估与实际应用之间的性能差距。这也是为什么现在人们仍然对当前编码模型是否真的有效存在一些矛盾的看法。因此,例如,在左侧的图中,我展示了来自最新 OpenBlock 的结果。他们向我们展示了,使用 O 模型,它能够在 Codeforces 竞赛中击败 90% 的人类程序员。具体的数字在这里并不重要,但关键是这个 SOTA 模型在竞争性编程设置中表现相当好,而这对于大多数人类来说是一项非常艰巨的任务。另一方面,如果你看它们在解决常见软件工程任务(如在 SW Bench 中解决 GitHub 问题)的其他基准测试上的表现,目前最好的准确率仍然低于 60%。而对于大多数人类程序员来说,如果我们足够耐心并花费足够的时间,我们可能会解决几乎所有这些问题。因此,模型可以处理的算法难度与模型执行其软件工程全流程中的多轮动作的能力之间仍然存在差距,这适用于不同的应用程序,但即使我们考虑相同的应用程序,我们仍然可以看到不同设置之间的巨大差距。因此,这里幻灯片上我展示了 Spider 基准的第二个版本。因此,在我之前的演讲部分,我主要讨论了第一个版本的 Spider 数据集。因此,如果你还记得之前的部分,你会看到实际设置要干净得多。问题和数据库模式之间似乎存在一个非常明确的语法对齐。但基本上在这个排名的第二个版本中,他们表明,如果你让它更像企业案例,你需要一些外部知识或文档来查看数据库和问题之间的对齐,并且这个过程将是一个更少自包含在单个提示中。你真的需要一个编码代理能够进行这种多轮生成。现在实际上所有 SOTA 模型在这个基准的第二个版本上表现都不好,特别是例如这个 O 模型只能解决这个基准中 17% 的任务。另一个令人担忧的因素是安全性。因此,幻灯片上我展示了一个名为 Red Code 的基准测试。其思想是,如果我们要求这些编码代理执行有风险的代码,而这会是它们任务的一部分,那么当前的编码代理的拒绝率会非常低。因此,基本上这里有两个因素。一个是模型可能仍然对代码的语义理解有限。所以它不知道当前代码是否有风险。第二个因素可能是模型在遵循用户指令时仍然忽略了一些它应该考虑的安全因素。因此,总的来说,对于神经代码生成,特别是现在在这个 ARM 时代,我们需要考虑几个开放性问题。第一个是关于长上下文理解。这对于代码生成至关重要,因为最终我们希望这个代理能够真正处理我们正在处理的整个存储库。这与标准地解决一些标准的 STEM 问题非常不同,因为问题本身可以非常自包含。另一个挑战是检索增强生成。因此,由于语料库太大,在许多情况下我们无法将所有内容都作为模型输入。那么我们如何设计一个系统,让模型能够理解它应该查看代码的哪个部分呢?因此,这对于实现多文件编辑和长期推理和规划至关重要,因为正如我们之前看到的,要真正端到端地解决软件工程任务,模型不能一次性生成代码。它包括一个非常完整的循环,例如,它需要知道如何调试,它需要知道如何设计单元测试。它需要从各种角度审查代码,考虑安全性因素,或者有时还需要检索相关文档以进行后续生成。所有这些都真正需要这种长期连贯的规划,而这仍然是当前模型应该改进的地方,以及关于代码语义理解。因此,我们看到的许多安全问题是模型可能仍然对代码执行行为有一些有限的理解。因此,这就是为什么在许多情况下,生成的代码仍然会错过一些边界情况。这也是为什么有些人担心人们会过度依赖当前的 ARM,因为在许多情况下,ARM 生成的代码在很多情况下看起来都很好,但它仍然严重错过了那些边界情况。然后是代码验证。因此,在 ARM 之前的旧时代,我们会看到在许多代码生成设计中,他们试图确保代码一旦生成就是有效的,因为在架构和解码算法中有许多控制因素,但在 ARM 空间中,如果 ARM 本身产生了一些东西,那么验证这些特性会更难。因此,基本上对于未来的工作,另一个需要考虑的因素是如何通过在代码生成算法中加入更多的验证过程来提高用户信任度,特别是那些由 ARM 驱动的代码生成算法。最后,安全绝对是一个问题,因为在许多情况下,如果模型错误地解决了你的数学问题,你只会感到非常失望。但如果模型意外地生成了一些坏代码并注入了病毒到你的系统中,那么这就不是你想要的了。因此,这就是为什么现在即使具有计算能力,你可能仍然希望在模型完成任务时观察它。你可能不希望它只是自由地处理你的计算机并做一些你不想看到的事情。因此,教程到此结束。感谢大家的到来和聆听。现在我们可以开始提问了。我有一个问题,你谈到了代码生成的演变,如果我理解正确的话,转向 LLM 编码器的范式转变彻底改变了整个领域,问题是我们能从你所谈到的所有过去的经验中学习到什么,比如在 2022 年或 2023 年之前,或者这一切都过时了。是的,谢谢你的问题。因此,基本上我认为这里的主要变化是如何基本上将这些代码特定功能嵌入到模型中。因此,过去人们主要考虑的是架构变化,这基本上会更具体地针对每个单独的编程语言。但在 ARM 时代,人们会更多地考虑这种规模化、数据和训练技术。因此,例如,在第二部分我一直在谈论如何将这些不同的反馈以自然语言格式或其他格式纳入训练过程中,以便模型能够执行这种多轮代码生成和编辑等。因此,我认为利用更多基于编码的功能是仍然非常重要的。它更多的是关于我们应该在哪里添加这种启发式方法,以及哪些技术可以真正扩展并适用于多种模态。因此,这是在当前基础模型时代向前发展的重要因素。嗨,我叫约翰。我有一个关于日常编码问题和竞争级别编码问题之间差异的问题。你可以看到他们的表现存在差距,SOTA 模型在竞争级别编码问题上表现得非常好。但如果你看看 Street Bench,SOTA 模型在这个时候仍然很普通。是的,谢谢你的问题。因此,这里有几个因素。首先是关于这种有向任务与更多单次自包含问题,因为竞争性编程是一个非常干净的设置。你的问题描述需要是自包含的,因为在在线竞赛环境中你无法获得进一步的反馈,并且还有很多单元测试,这些测试可能非常清晰地表明我们想要什么,但在解决真实 GitHub 问题的情况下,这是一个多轮过程,即使是最简单的任务,如修复一些错误,通常也需要代理下载存储库,它需要安装相关的包,并在出现意外问题时进行调试,基本上这是一个来回的过程。因此,对于现有的编码模型,我想说它们越来越好,在许多情况下,如果需要执行这种多轮操作,仍然存在一些一致性问题。因此,这就是原因,特别是对于竞争性编程,包括 AlphaCode 和其他系统,我们通常会在之前的竞争性编程问题上对模型进行微调,如果你因为我个人年轻时也参加过竞争性编程,那么想法是,如果你在往年的问题上练习得足够多,那么它们之间就存在一些高级的相似性。因此,如果你的模型很好,它就会知道这些算法的组合。但对于现实世界的软件工程任务,它可能更加多样化,尽管在推理路径方面可能没有那么复杂。