2017年1月27日,星期五

强化学习和语言支持

指定从经验中学习的程序的正确方法是什么?现有的通用编程语言旨在简化任何软件的规范。所以我们可以只使用这些编程语言进行强化学习,对吗?有点。

抽象很重要

用高性能服务进行类比可能会有所帮助。有关高性能投放的早期影响力页面(Dan Kegel的C10K问题)概述了几种I / O策略。我已经尝试了很多。一种策略是事件驱动的编程,其中核心事件循环监视事件的文件描述符,然后分派处理程序。这种样式可产生高性能的服务器,但难以编程且对程序员错误敏感。除了故障隔离问题(如果所有事件都在相同的地址空间中运行)之外,这种风格还很容易受到任何事件处理程序执行时间过长(例如,隐藏的阻塞I / O调用,计算密集型操作等)的影响。 。相反,基于线程的编程允许您假装您是唯一正在运行的处理程序。它的计算效率较低,并且仍然存在故障隔离问题,但推理起来更容易。 (随后,我开始进入Erlang,因为它本质上是试图烘焙带有错误隔离功能的用户空间线程到语言中,这甚至更好。)

我不知道现在高性能服务中的最新技术,我对这个游戏有些不了解。要点是,并非所有编程语言都具有相同的创造力,因为它们给程序员带来了不同的认知负担,并在运行时产生了不同的计算负担。我可以通过两种方式(协作调度与抢先式调度)之一使用现有的语言(当时为C ++),也可以使用旨在减轻折衷的另一种语言(Erlang)。

具有自动信用分配的命令式规范

如前所述,我们现在要指定的程序与过去指定的程序之间的差异是,我们希望我们的程序能够从经验中学习。与高性能服务一样,我们希望平衡程序员的认知负担与运行时施加的计算负担(也可能是运行时施加的统计负担;计算负担对应于诸如时间或空间之类的资源,而统计负担对应于数据资源)。

在当前范围内“AI summer”,一个流行的想法是 自动区分。完整的AD意味着基本上可以使用任何语言构造来定义函数,并且提供了用于计算函数相对于输入的梯度的计算“for free.”配备有正在计算(亚)可微函数的AD的语言可以从经验中学习,逐渐接近损失函数的局部最优值。深度学习工具包通过一些框架(例如, 链条机)在指定正向计算时积极追求允许任意语言构造的想法。

随着推理变得越来越复杂,使用任意语言构造的能力变得越来越重要。简单的推论(例如分类或排名)很容易推断,但除此之外,它很快成为以下方面的主要缺陷来源:1)指定如何使用机器学习模型的输出来合成完整的系统,以及2)指定如何运行该完整系统获得的数据将用于更新模型。

在结构化预测领域中,该问题显而易见。“结构化预测”当然是一个有点荒谬的名词“非线性系统分析”;在这两种情况下,最初都解决了该问题的一个简单版本(分别是分类和线性系统分析),然后为其他所有问题创建了一个总括术语。尽管如此,Hal Daume对结构化预测有一个很好的定义,它在一个示例上进行了多个预测,并且经历了(在决策中)共同损失。 (他也有一个 ku句版 这个定义。)

由于结构化预测中的推理很复杂,因此对于结构化预测本质上是重新定义了命令性规范和自动信用分配的思想。该技术概述于 Chang等人撰写的Arxiv论文。等,但是Chainer的粉丝会认出这是“define-by-run”用于结构化预测。 (请注意,这里的优化策略不是梯度下降,至少不是在正向计算上,而是策略梯度方法之类的东西,它会通过正向计算做出的预测转化为离散的信用分配问题。)

查看情景RL的一种方法是使用强盗反馈进行结构化预测:类似于监督学习,完全观察到结构化预测,因为可以计算给定特定输入的任何决策序列的损失。在强化学习中,您会获得强盗反馈,即,您仅了解与实际采取的决策顺序相关的损失。虽然这不是查看情节性RL的唯一方法,但它确实有助于与上一段中提到的论文的某些思想联系起来。

一个激励人的例子

这是一个希望可以澄清问题的示例。假设我们要构建一个交互式的问答系统,在该系统中用户提出问题,然后该系统可以有选择地向用户提出(澄清)问题或提供答案。我们可以将其视为一个偶然的RL问题,其中用户陈述是观察值,系统问题是动作,系统答案是更多动作,并且情节在我们提供答案后就结束了。

我想做的是指定类似此伪python的计算:
def interactive_qa_episode():
  q  = get_user_question()
  Qapairs = []
  sysaction = get_next_system_action(uq, Qapairs)
  while (sysaction.is_question):
    ua = get_user_answer(sysaction.utterance)
    Qapairs.append((sysaction,ua))
    sysaction = get_next_system_action(uq, Qapairs)
  deliverAnswer(sysaction.utterance)
很清楚这里发生了什么:我们得到一个用户问题,有条件地提出问题,然后给出答案。在机器学习出现之前,这种系统的实现者会尝试填写上述未指定的功能:特别是, get_next_system_action 手动指定很棘手。我们要做的是改为学习此功能。

使用装饰器来实现这一点将是很好的。首先,要学习,我们需要知道做的更好还是更坏的想法,因此假设给出答案后,可以通过某种方法来确定用户对会话的满意程度(即,ceterus perebus,应随所提问题的数量单调减少) ,以鼓励权宜之计):
@episodicRL
def interactive_qa_episode():
  q  = get_user_question()
  Qapairs = []
  sysaction = get_next_system_action(uq, Qapairs)
  while (sysaction.is_question):
    ua = get_user_answer(sysaction.utterance)
    Qapairs.append((sysaction,ua))
    sysaction = get_next_system_action(uq, Qapairs)
# this next line is the only change 至 the original function
  奖励 = deliverAnswer(sysaction.utterance) 
太容易了!伪代码是如此高效。我们甚至可以想象更新 奖励 装饰者多次跟踪奖励增量以改善信用分配。

现在开始进行一些魔术性元编程,并将其转换为使用RL算法训练的模型(例如,诸如q-learning之类的值迭代方法或诸如 强盗)。还是呢?我们仍然没有说要学习哪些功能以及哪些是手工指定的。默认值是手动指定的,因此我们将修饰一个功能。
@learnedFunction
def get_next_system_action(uq, Qapairs):
  ...
现在我们陷入一些棘手的问题。我们最终需要根据参数化模型(例如神经网络)来指定此功能;我们将不得不说从诸如 q Qapairs;我们将不得不说如何将模型的输出映射到实际决策上。为了保持前进,我们假设存在一组固定的系统问题和系统答案。
action_table = [ ... ] # list containing action mapping
@learnedFunction
def get_next_system_action(uq, Qapairs):
  not_allowed_action_ids = [ sysa.action_id for (sysa, _) in Qapairs ]
  action_id = categorical_choice(uq: q ,
                                 Qapairs: Qapairs,
                                 not_allowed_action_ids: not_allowed_action_ids,
                                 tag: 'nextsystemaction')
  return action_table[action_id]
categorical_choice 是从一组可能性中进行强制选择的表示。对于小型动作空间,可以将其直接实现为每个动作的输出,但是对于大型动作空间,可以通过具有信息检索样式级联管道的动作嵌入来实现。

很好吗?好吧,仍然存在一些问题。
  • 用于选择的最佳模型结构(即策略类)需要程序员指定一些规范,例如卷积文本网络与迭代注意力体系结构。理想情况下,此规范不同于推理规范,因此可以尝试许多建模思想。这就是tag参数的目的,是将其与学习参数的单独说明结合在一起。 (如果未提供,则可以在编译期间生成合理的默认标记。)
  • 如所示 以前的帖子,引导就是一切。所以最初的实现 get_next_system_action 需要提供。也许这可以简化为提供基础模型的初始设置,但是可能不取决于初始化方案。注意,如果初始化是通过模拟或从历史数据中脱离策略学习完成的,则可以通过简化I / O功能的模型来支持这些初始化 get_user_questionget_user_answer。另一个常见的情况是,将未学习的功能作为参考策略,学习的功能应与之竞争。
我不能和Chainer一起做吗? 有点。当然,如果您使用特定的RL算法。例如,q学习可将强化学习减少到回归,因此,如果您对该内联进行编码,您将获得Chainer可以处理的东西。但是,目标是在不泄漏学习算法细节的情况下指定推论,因此我宁愿不对该内联代码进行编码。另一种方法是编译 链条机,类似于c ++早期的cfront。

但最终,我希望有一个不同的编译策略。不仅仅是实现学习算法,还有更多的风险:存在所有提到的问题 我以前的帖子 这使我确信,实施应该能够利用强化学习服务。

没意见:

发表评论