傻蛋架构师才会导致错误放大的恶果。Pipeline 系统理应负负得正。
今天早上在想一个老话题,管式(pipieline)系统是错误放大(error propagation)还是负负得正?这个问题是模块化串行大系统的根本问题。如果是前者,pipeline就死定了。对于模块之间有依赖关系的管式系统,很多人第一个反应和批评就是错误放大,说后续模块依赖前面模块的话,乘法效应决定了错误不断放大,哪怕是一个底子不错的系统,随着串行模块的增加,到头来会错得一塌糊涂,不可收拾。
这是不了解目标导向的开发模式的缘故。目标导向的开发不再把每一层模块的开发看成是一个独立的过程,而是一个动态协调的 adaptive dev 开发过程。在这个模式下,系统的主流是负负得正,而不是错误放大,至少是前者的正效应远远压倒后者的负效应。错误放大不可收拾的担忧是杞人忧天。实际研发过程中不应该发生。
在这样的模式下,一个模块的绝对精度的意义远远没有这个模块的调适性、可维护性(包括可读性,维护门槛降低、维护过程容错性强、维护线索清晰等)重要。举个例子吧,如果决定中文切词作为中文分析 pipeline 的一个基础模块,那么这个切词的模块并不是如许多人想象的可以随时地 plug-in, 看见哪家开源了一个更高水平的 segmenter,于是拿来就用,期望它能加强中文分析的能力。
不是这样的。
以前说过,谁都不愿意 reinvent wheels,但是对于 NLP,模块(component technoogy)拿来就用不断翻新绝不是正道。最多是为了 feasibility study或做个 prototype 可以偷这个懒。正经做系统的人,必须一切自己来(home-made or built-in-house)。
可以利用开源的 code base 和其他资源,但前提是消化改造成自己系统的“有机”模块(integrated module)。至于 license 一个 component technology,不到万不得已,不要做。因为商业性 licensed 过来的东西,与开源社区的资源不同,他们为了自身的利益,往往把模块黑箱化,使得消化集成和扩展这个模块的能力受到极大限制,长久下来,不是好事儿。
所以 这就造成了一个 catch-22.
自然语言这个 monster,要做深度分析(deep parsing)和理解,牵涉的层面不少,每一样都自己做,门槛蛮高,研发时间长。如果想偷懒,利用一些现成的模块,又会消化不良。从词典到文法,从词法到句法,从句法到语义,从本体到逻辑,从实体到关系,从浅层到深层,等等等等,真地有点烦,有点难。如果再加上领域和语种这两条延长线,以及业务场景语义落地的对接,简直就是做不完的活儿,难免产生吾生也有涯对无涯的无奈。
最后,再回到一开始的话题:pipeline 负负得正而不是错误放大的诀窍在哪里?简单总结来说就是 task-driven integrated adaptive modular development。
可以举一个NLP历史的经典例子来形象地说明这一点。稍微做过几年 NLP 的,没有人不知道 Brill's POS Tagger 的吧。Brill 发明了一个极其简单透明的,他命名为 transformation-based 符号规则的学习算法。(这个NLP大牛一看就是语言学外行,居然敢在计算语言学领域用 transformation 这个词,与乔老爷的语言学革命的经典术语撞车。简直是冒犯。)Anyway,这个所谓的 transformation 的学习机制,就是一个典型的 pipieline 负负得正的例证。整个学习过程都是最终目标驱动的,譬如 Penn Tree 里面的 POS 标准训练集。每一条规则可以看成是一个 pipeline 系统的一个模块的缩影。这个学习也叫 error-driven,意思是说,在每一层规则的学习过程中,当时现场的产生错误最小的规则胜出。因此第一条规则注定是全局最优的规则,但同时也是错漏百出的规则,跟筛子一样,大路货娄住了,后面需要大量的规则去擦屁股。每一个后续的规则都是替前面擦屁股,纠正其错误,也创造新错误,就是这么一个不断的负负得正的过程,使得规则越来越精细,概括性越来越低,错误面越来越小。初始错误不是放大,而是在不断缩小中。这一切的黄金指南就是最终的 task 的定义。这样的 Pipeline 哪里会有什么错误放大。恰恰相反呢。结果呢,对于 POS,大约是学出来 200 多条规则,就好比 200 多个模块,被一个 pipeline串起来。一个 200 多层的系统,按照错误放大理论,哪怕每个模块达到 90+% 精准(接近人的精度),放大 200 层,也是一塌糊涂。好在,这里不是错误放大,而是负负得正。一切拜 task-driven 和 integrated adaptive learning (or dev) 所赐。
好了,我想广为流传的错误放大的顾虑可以放下了。错误放大会出现,那是设计者的问题。是傻蛋架构师才会导致的恶果,不是 pipeline 系统本身的问题。
【语义计算群补记】
白:
这和公司有什么样的人有关。如果公司没有有悟性的语言学家,改造的事儿还是免了的好。
李:
不改造就很难负负得正。不改造又要降低模块的副作用,只好外包。简单说就是前堵后补。开发维护就不是一个一体化的过程。初期的甜头会被后期没完没了的异物感消弭,用的时间越长越觉得不合算:还不如当初自己咬咬牙从头做起呢。
白:
我觉得关键不在于你能改他的代码,而在于他能送给你多于一个候选结果。应该促成这种多选结果的标准化。
李:
还是不好,主要是数据结构的对接存在异物反应。它给你个XML的多结果表达,这边还要转来转去。表达能力也受限制:这种限制包括,它内部使用的可能有用的信息被隐藏了,不作为标准输出;也包括它输出的XML表达性不够,不能表达一些扩展的需要。总之是水土不服。只有改造才能服了水土。
姜:
@wei “初期的甜头会被后期没完没了的异物感消弭,用的时间越长越觉得不合算:还不如当初自己咬咬牙从头做起呢。”。很能理解您的这种感悟!有时咬咬牙自己做了就做了。
李:
我一直懊悔我的英语系统用了Brill。自己写一个POS一定比Brill好用。当时偷了这么个懒,后面擦了十几年的屁股。它的输出输入也格格不入,所用词典的格式与我内部的也很难一体化。成了一个鸡肋。回想起来是免费惹的祸。当年觉得,免费的东西,不用是傻瓜。其实在国内做MT早就弄过POS,比起后面的工作,虽然琐碎,但门槛相对低。其所以不自己做,也不完全是时间压力,还有不占便宜是傻蛋的心理作祟。幸运的是中文还没有这么一个久负盛名的免费POS诱惑我,在中文开发的时候绕过了这个陷阱。
白:
一个从终端落地应用角度驱动的、不预设语言学立场的集成者,或许可以更好地处理异构资源的协同性。毕竟同行是冤家,非同行立场更客观。
Guo:
多候选(n-best)在夸领域场合是不够的。多候选永远是在某种“语言模型”下的结果。如果这个模型离目标太远,期望值就常常不在n-best里。这时候“负负得正”就变成了一个开放问题。
白:
将错就对,也是一个策略,当年用过。只要你这样错的,那么我就把对应的某个对的拉进来做候选。
Guo:
与识别相反,pos tagging在给定标记集合上是个封闭问题。负负得正就变得有意义了。关键区别是这个“错-对”对应是开放的还是封闭的。
白:
pos tagging和parse不是前后工序的关系,而是相互决定的关系。在parse未完成时,pos tagging只有概率,没有对错。如果不能唤醒低概率的候选,就形不成闭环。
李:
绕过了中文pos与segmenter双重陷阱 是大幸。特别是那些声称在这方面表现优异的机器学习模块。你再优异也没用,用户几乎无法重新训练,于是就是一个死系统。且不说还有其他水土不服的种种。
白:
市面上的Pos压根儿就不是为 deep parsing 后道工序而存在的。
冯:
BRILL的transformation与CHOMSKY的transformation是完全不同的概念,BRILL的transformation实际上就是改错。应当把二者分开。不要顾名思义。
白:
二者拧在一起是个怪物。
李:
我觉得他在命名的时候,还没意识到这个撞车,否则不会这样命名。改错可以用很多其他的命名办法,不该与语言学革命的术语撞车。记得第一次读Brill,被他这么用“革命口号”吓倒了。当代计算语言学学者不了解语言学的 太多了,这只是一个表现。
冯:
有兴趣的专家不妨读一读我的《现代语言学流派》增订本(商务印书馆),丰富您的语言学常识。
【相关】