论文翻译:构建健壮的系统(2)
读书笔记
Aug 14, 2023
type
Post
status
Published
date
Aug 14, 2023
slug
build-robust-system-translate-2
summary
本文是 SICP 的作者 Gerald Jay Sussman 教授的一篇文章,系统的阐述了他对生物系统的观察而得到的构建健壮系统的观点,读完受益匪浅,故尝试翻译记录于此 - 第二部分 (脑袋都要爆了,各种生物、计算机和物理的专业术语)
tags
论文
Architecture
category
读书笔记
icon
password
Property
Aug 16, 2023 07:29 AM

书接上文

约束泛化程序

假设有一个用于求解常微分方程系统的显式积分器,例如 Bulirsch-Stoer 算法,这样的积分器的ODE 描述是一个系统导数程序,它接收系统的状态并返回状态的导数。例如,驱动谐振子的系统导数接收一个包含时间、位置和速度组件的结构,并返回一个向量,包含 1 ()、速度和加速度。系统导数有三个参数:阻尼常数、无阻尼振荡频率的平方和驱动函数。自然频率由前两个参数的二次方程确定。自然频率的和是阻尼常数,自然频率的乘积是无阻尼振荡频率的平方。我们也可以为这样的系统定义一个 值。在任何特定的物理系统中,例如串联谐振电路,这些参数与电路的电感、电容和电阻之间存在关系。实际上,我们可以用许多方式指定系统导数,例如振荡频率、电容和 值。这并不比指定电感、电阻和电容更难。
如果我们有一组量和它们之间的关系,我们可以构建一个约束网络,该网络可以自动从其他值推导出一些量。通过将系统导数的参数包含在约束网络中,我们大大增加了其应用的通用性,而不会损失数值积分的效率。另一个好处是,我们可以使用约束传播过程来为我们提供模拟机制的多种替代视图:我们可以将弹簧、质量和阻尼器约束网络连接到我们的串联 RLC 约束网络,并思考我们的电感器中电流的惯性。支持这样一个约束导向的调用机制所需的基础设施是廉价的,而跟踪依赖关系所需的真值维护系统是实现上述依赖性导向回溯的同一机制。
但约束为我们提供的不仅仅是支持通用性。动态测试数据结构和硬件完整性的约束可以用来发现和示意系统的损坏。这样的机制可能能够封装损坏,使其不会扩散,并触发防御机制来对抗破坏者。此外,如果我们制作使用由约束控制的生成和测试机制构建自身的系统,这些约束会对结果的结构施加要求,我们可以构建能够自动修复某些形式的损坏的系统。

工程中的简并性

在设计任何重要系统时,每个组件在每个细节层面都有许多实施方案被提出。然而,在最终交付的系统中,这种方案的多样性就丢失了,通常只有一个统一的方案被采纳和实施。就像在生态系统中一样,传统工程过程中多样性的缺失带来了严重的后果。
我们很少在程序中构建简并性,部分原因是它代价昂贵,部分原因是我们没有正式的机制来调控其使用。然而,在人工智能解决问题的世界中,有一种用于简并设计的机制:目标导向调用。这个想法是,我们不是指定“如何”实现一个目标,比如通过明确一个实现它的程序,而是指定“我们想要实现什么”,并将可以实现该目标的程序与目标链接起来。这种链接通常是通过模式匹配完成的,但这更多是偶然的,而不是绝对必需的。
如果有多种方式可以实现目标,那么选择适当程序的选择点就可以注册为回溯。当然,除了使用回溯搜索来选择实现目标的特定方式,还有其他方式可以调用简并方法。例如,我们可能希望并行运行几种可能的解决问题的方式,选择首先完成的那种。
假设我们有几个独立实现的程序,都设计用来解决同一类(模糊指定的)一般问题。假设每个设计都相当合格,并且在实际操作中可能遇到的大部分问题上都能正确工作。我们知道,通过将给定的程序组合成一个更大的系统,该系统独立地调用每个给定的程序并比较它们的结果,选择每个问题上的最佳答案,我们可以构建一个更健壮的系统。如果这个组合有独立的方式来确定哪些答案是可接受的,我们就处于非常好的状态。但即使我们只能投票,我们也能得到一个可以可靠覆盖更大解决方案空间的系统。此外,如果这样的系统可以自动记录一个设计的所有失败案例,那么这些操作反馈可以用来提高失败程序的表现。
这种简并设计策略可以应用于每个层次的细节之处。每个子系统的每个组件本身都可以进行冗余设计,而且实现者可以结构化地使用这些冗余设计。如果组件池本身在子系统之间共享,我们就能实现一种强大的受控冗余。甚至,我们还可以做得更好。我们可以提供一种机制,用于检查独立设计的子系统的中间结果的一致性,即使在一个子系统中没有任何特定值与另一个子系统中的特定值完全对应的情况下也是如此。
举一个简单的例子,假设我们有两个子系统,它们的目标是得到相同的结果,但是计算方式完全不同。假设设计者们达成共识,在设计的某个阶段,一个设计中两个变量的乘积必须与另一个设计中两个变量的和相同。一旦这四个相关值(///)都可用,就没有理由不立即计算这个谓词(),从而在运行时进行一致性检查,并向设计者提供有力的调试信息。这可以通过使用局部嵌入的约束网络来安排。
再者,如果我们制造的系统是通过使用受约束控制的生成和测试机制自我构建的,这些约束对结果的结构有所要求,我们将得到明显的天然简并性,因为通常情况下,约束会接受多个方案。此外,由于要构建的系统实例之间的环境差异,我们将自动从系统实例到系统实例得到变化。这种中性空间的变化将大大增强对入侵的抵抗力。

支持健壮和演进的基础设施

组合子

如果我们构建的系统是由一系列可以混合和匹配的组件组成的,这些组件可以组合成新的系列成员(通过遵循预定的互连标准协议),那么更大的需求变动可以更容易地通过高级组件的重新排列来解决。
但是,我们如何安排通过组合可以混合和匹配的组件系列的元素来构建我们的系统呢?一种方法是确定一组基本组件和一组组合子,这些组合子将组件组合成具有与基本组件相同接口的复合组件。这种组合子集有时是显式的,但更常见的是在数学符号中隐含的。
使用函数符号就是这样一种规则。一个函数有一个域,从中选择其参数,并有其可能值的范围。有些组合子可以跟其他函数组合产生新的函数。例如,函数 的组合是一个新的函数,它在 的域中取参数,在 的范围中产生值。如果两个函数有相同的域和范围,并且在它们的共同范围上定义了数学算法,那么我们可以定义这两个函数的和(或乘积)为在它们的共同域中给定参数时,是这两个函数在该参数处的值的和(或乘积)。允许一级过程(first-class procedures)的语言提供了支持这种组合方式的机制,但真正重要的是一个好的部件系列。
有一些我们在编程中可以使用的整个组合子系列,但我们通常不会考虑到这一点。张量是线性代数对具有多个参数的线性运算符的扩展。但这个想法更为通用:两个过程的“张量组合”只是一个新的过程,它接受一个数据结构,该数据结构结合了两个过程的参数。它将这些参数分配给两个过程,生成一个结合了两个过程值的数据结构。在编程中,需要解开数据结构,单独操作各部分,并重新打包结果是无处不在的(译者注:Divide and Conquer / Map and Reduce)。有许多这样常见的模式,将这些模式暴露和抽象到一个组合子的公共库中对我们是有利的。
当我们模拟物理系统时,我们可能会使用约束。物理系统有守恒定律,这些定律以双变量的形式表达,如扭矩和角速度,或电压和电流。这样的系统的原始约束和组合子稍微复杂一些,但有些已经详细地解决了。例如,Penfield 的 martha 系统提供了一套完整的组合子,用于以两端口部件形式表示的电路。

Continuation

(译者注:就不翻译了,是一个专有名词,意思是:程序执行到这里还需要做的工作)
现在有计算机语言提供对一级 continuation 的访问。单独来看,这可能看起来是一个非常晦涩的结构,但它使得可以来显著提高系统的健壮性的控制结构具有了多样性。
Continuation 是捕获到的计算的控制状态。如果调用 continuation,计算将在 continuation 所代表的地方继续。Continuation 可能代表将子表达式的值返回到包围表达式的求值过程的行为。然后,Continuation 就是一个过程,当它被调用时,它将自己的参数作为子表达式的值返回到包围表达式的求值过程中。Continuation 是一个一级对象,可以作为参数传递,作为值返回,并纳入数据结构。它可以被多次调用,允许计算带着 continuation 返回的不同值在特定时间点继续。
Continuation 给程序员提供了对时间的显式控制。计算可以在某个时刻被捕获和挂起,然后在任何未来的时间点被恢复和继续。这直接提供了 coroutines(协作式多任务处理),并且通过添加一个定时器中断机制,我们得到了分时处理(抢占式多任务处理)。

回溯和并发

Continuation 是个天然支持回溯的机制。可以做出一个选择,如果这个选择被证明是不合适的,可以做出另一个选择并计算出其结果。(我们多么希望现实生活中也有这个功能!)所以,在我们的平方根示例中,平方根程序应该返回两个平方根的 AMB,其中 AMB 是选择并返回其中一个的操作符,如果第一个被拒绝,它有提供另一个的选项。接收者可以继续使用给定的解决方案,但如果在某一点接收者发现其计算不满足某些约束,它可以 FAIL,导致 AMB 操作符修正其选择,并通过其 continuation 返回新的选择。本质上,Continuation 允许选择的生成器与选择的接收者/测试者进行协作。
如果解决子问题存在多种可能的方法,而只有部分方法适合解决更大的问题,那么在生成和测试机制中顺序尝试各种可能,是解决这个问题的唯一方式。例如,如果有些选择可能导致在测试过程中计算时间非常长(甚至可能是无限的),而其他选择可能会快速成功或失败,那么将每个选择分配给可能并行运行的线程是合适的。这需要一种方式让线程之间进行通信,也许还需要一种成功的线程终止其兄弟线程的方式。所有这些都可以通过使用 continuation 来实现,线程之间的通信可以围绕事务进行组织。

任意关联

在构建健壮系统时,能够为任何数据片段附加其他数据是一个至关重要的机制。元数据的附加是对用于支持可扩展通用操作的标签化的泛化。附加额外的标签,将数据与导致其派生的选择进行标记,可用于支持依赖导向的回溯(dependency-directed backtracking)。有时,适当地附加详细的审计历史以描述数据项的派生过程,允许稍后的流程将派生用于某一目的,或者为了评估派生的有效性以进行调试。对于许多任务,如法律论证,了解数据的来源是必要的:数据是在哪里收集的,如何收集的,由谁收集的,收集是否经过授权等等。一份证据的详细派生过程,展示了每个贡献的来源,可能对判断其在审判中是否可采纳至关重要。
数据项的关联可以通过多种机制实现,例如哈希表。但是实现可能有微妙之处。例如,商品的成本通常会依赖于不同的假设,而不只是商品的运输重量,因为它们可能具有相同的数值。如果计算系统没有为这两个相等的数值分别分配不同的令牌,系统就无法为不同商品的运输重量附加不同的标签。

可动态配置的接口

当实体们没有共同的语言时,它们如何进行交流呢?西蒙·柯比(Simon Kirby)的一个计算实验为我们提供了语言可能是怎么演化的一点线索。具体而言,柯比在一个非常简化的情境中展示了这么一种情境,如果我们有一个由代理者组成的社群,这些代理者分享一些语义结构(可能是通过共同的知觉体验),并且试图制定和使用规则来解析彼此关于共同经历的话语,那么社群最终会趋于收敛,以便成员们分享兼容的规则。虽然柯比的实验非常基础,但它确实为我们提供了一种通用机制的想法,用以促使不同的模块合作。
雅各布·比尔(Jacob Beal)延伸并泛化了柯比的研究工作。他构建并演示了一个系统,这个系统允许计算代理通过稀疏但不受控制的通信媒介学习互相交流。这个媒介具有许多冗余通道,但代理者没有通道的序号,甚至没有命名通道的能力。尽管如此,比尔先生采用了一种类似于卡尔文·穆尔斯(Calvin Mooers)的 Zatocoding(一种早期的哈希编码)的编码方案,其中待检索信息的描述在卡片边缘的凹槽分布中表示。他将媒介的稀疏性和冗余性转化为任意复杂性的可靠和可重构通信。比尔的方案允许多条消息通过叠加一次性传输,因为碰撞的概率很小。比尔向我们展示了这个问题的新见解,而这些结果可能在工程问题中有广泛的应用。

结论

严肃的工程学只有几千年的历史。我们试图刻意产生非常复杂且健壮的系统的尝试在最好的情况下也是不够成熟。我们还没有从生物进化在过去几十亿年中所学到的教训中获益。
我们更关注效率和正确性,而不是健壮性。这对于开发仅具备足够资源执行其功能的关键任务系统是明智的。然而,微电子技术的快速发展已经缓解了大多数应用的资源问题。我们越来越依赖计算和通信基础设施,并且对这些基础设施进行更加复杂攻击的能力不断发展,这使得我们必须将注意力转向健壮性。
我并不主张生物模仿;但是对生物系统的观察为我们提供了关于如何将健壮性原则纳入我们的工程实践的线索。这些原则中的许多与优化和正确性证明的既定实践直接相冲突。
作为持续构建人工智能符号系统的工作的一部分,我们顺带开发了可以用来支持健壮性设计原则的技术工具。例如,我们可以不再将回溯视为一种组织搜索的方法,而是可以将其用于增加复杂系统中组件的一般适用性,该系统可以根据特定约束自行构建。我相信我们可以追求这种新的综合方法,以获得更好的硬件/软件系统。
 
🎉
  • 论文
  • Architecture
  • 使用 Vault 保护应用的敏感数据
    论文翻译:构建健壮的系统