first commit

This commit is contained in:
张乾
2024-10-15 21:15:28 +08:00
parent 1b0c35dd30
commit 914d92856f
72 changed files with 3115 additions and 3 deletions

View File

@ -0,0 +1,138 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
02 以终为始:如何让你的努力不白费?
你好,我是郑晔。
今天内容的开始,我希望你可以先来思考一个问题:如果让你设计一个登录功能,你会怎么做?
我曾在公司内部做过这样一个练习,我扮演客户,让大家帮我设计一个登录功能。同事们一听就高兴了,登录不就是用户名加密码嘛,我熟啊,我还可以设计出验证码、找回密码、第三方登录等等功能。
更有个别动作快的同事甚至已经开始设计数据库表考虑用Redis做缓存了。整个过程下来大家彼此讨论得热火朝天唯一没人理会的就是我这个“客户”。
讨论结束,扮演客户的我告诉大家,作为一个“土豪”,我打算做一个打车软件,用户可以通过手机号接收验证码的方式进行登录。你可以想见,同事们一副“被套路了”的表情。是的,他们设计那套用户名密码登录完全是文不对题。
虽然这是一个简单的练习,但反映的却是我们日常面对的真实工作场景:许多人都是刚刚听到别人要求做的一个功能,就开始脑补接下来的一切。导致的结果,就是付出的努力毫无意义。
那么问题出在哪呢?因为我们欠缺了“以终为始”的思维习惯。
一种反直觉的思维方式
以终为始,就是在做事之前,先想想结果是什么样子的。
说起来很简单,但做到并不容易。因为我们习以为常的思维模式是线性而顺序的,第一步做完,做第二步;第二步做完,做第三步。
这也情有可原。我们人类都是从远古时代演化而来,在那个食不果腹的时代里,倒着思考的用途并不大,人们甚至不确定自己能否见到明天的太阳。几十万年的进化留给我们很多短视的行为和思考习惯,因为这样的做法最为节省能量,把目光放长远是需要额外消耗能量的。
“以终为始”是一种反直觉的思维方式,是大多数人不具备的。所以,日常生活中,我们看到很多有趣的现象。
比如,大学毕业时,有很多人想考研,如果你问他为什么要考研,得到的理由通常是为了找个好工作。但考研真的能帮他找个好工作吗?不一定,因为找工作和考研根本就不是同一棵技能树。
如果真的是想找个好工作,那你就应该了解工作的要求是什么,怎样才能掌握工作要求的技能。
从后面这个角度出发,你会发现考研只是通往工作诸多道路中的一条,其他的路径也是可以到达的。比如,你应该找个实习的地方锻炼一下职业技能。这就是“以终为始”思考问题的方式。
回到前面“设计登录功能”的例子,对比“以终为始”的思维,你也许会替我的同事抱不平,他们或许也有“以终为始”的思路,只不过,他们的“终”和我这个客户的“终”不一样罢了。这就要说到做软件,本质上是在构建一个“集体想象”。
想象的共同体
如果你读过尤瓦尔·赫拉利的《人类简史》或《未来简史》,有一个说法你一定不陌生:想象的共同体。作者认为,人类历史发展的一个重要因素是“集体想象”,无论是国家、宗教,还是法律、习俗,都是人们达成的“集体想象”。人类就是认同了这些“集体想象”的一个共同体。
我们这些做软件的人其实就是一个想象的共同体,这个“集体想象”就是我们要做的软件,任何想象都需要一个载体将其展现出来,我们编写软件的过程就是将这个“集体想象”落实的过程。
既然是“集体想象”,那么在载体将想象呈现出来之前,我们的想象很难统一起来,都或多或少存在差异。
所以任何事物都要经过两次创造一次是在头脑中的创造也就是智力上的或者第一次创造Mental/First Creation然后才是付诸实践也就是实际的构建或第二次创造Physical/Second Creation
我们在工作中遇到的很多问题,其实就是在于第一次创造没有做好,就进入到第二次创造。所以,我们在工作中会遇到很多“惊喜”,准确地说,是惊吓。
相比于第一次创造,第二次创造是一件成本很高的事。我们知道,软件开发最费时费力,一旦投入大量精力做出来,却发现与理解偏差甚大,所有人都会欲哭无泪。
所以,在动手做事之前,我们要在第一次创造上多下一些功夫,将相关各方的“集体想象”统一起来。以建筑为例,就是先在图纸上构思各种细节。对应到做软件,我们也可以做很多事,比如:
要给用户看产品的样子,可以用原型工具把它做出来,而不是非得把完整功能开发出来;
要呈现服务接口的样子,可以用模拟服务器搭出一个服务,而不用等后端全部开发完毕;
要让程序员知道要开发产品的细节,可以在任务上描述出软件各种场景给出的各种行为。
再回到前面“设计一个登录功能”的例子上,我的同事们在构建的其实是他们自己的想象,而不是我们共同的想象。这其中最大的一个区别就在于,没有人会为他们自己的想象买单的。
所以说,他们看到的“终”不是真正的终,只是一个自我的“终”,至于看到什么样的“终”,这取决于每个人的见识。
对做软件的人来说,我们应该把“终”定位成做一个对用户有价值的软件,能够为别人带来价值,自己的价值才能体现出来。
至此,你对“以终为始”已经有了一个初步的认识,有了这种思维方式,我们可以在工作中怎样运用它呢?
规划和发现
软件行业有很多英雄传说,一个人或者一个团队连续奋战一段时间,写好了一个软件,在上线前夜发现了一个问题,然后冒着“不成功便成仁”的风险,通宵达旦解决了问题,一战成名。
这种故事听起来让人热血沸腾,但仔细想想,为什么总在最后一刻发现问题?除了时间压力确实大的情况以外,大多数情况,他们还是一开始没有想好就动手了。
在团队内部,我一直坚持“以终为始”,让大家在执行任务之前,先倒着想想再动手规划,这样规划出来的工作更能瞄准真正的目标。举一个之前做产品的例子,当年在创业的时候,我们打算做一个物联网开发平台,但具体应该做成什么样子呢?
有了“以终为始”的思维,我们考虑的是别人会怎么用我们的平台。我们设计的方式是,用户到我们的网站,阅读相关文档,然后参考文档一步一步照着做。
这其中的一个关键点是:文档,特别是《起步走》的文档,这是用户接触我们这个平台的第一步,决定了他对我们产品的第一印象。
所以我们决定从写《起步走》这个文档开始这个文档描绘了用户怎样一步一步使用我们的开发平台完成第一个“Hello World”级别的应用。请注意这个时候我们一行代码都没有写。
写好了这个《起步走》文档,团队的所有人对于我们的平台要做成什么样子,已经有了一个比较初步的认识。更重要的是,我们可以拿着这个文档,去和外部的人讨论这个尚未出世的平台。
人类是一个擅长脑补的群体,一旦有人看到了这个文档,他就已经可以构想出这个平台已经存在的样子,进而给出各种各样的反馈:“我认为这个地方可以这样做”“我觉得那个地方可以改改”。
所有这些反馈都是真实的,因为他们已经“看到了”一个真实的东西。正是这些真实的反馈,让我们逐渐地锁定了目标。之后,我们才开始动手写代码。
“以终为始”的方式,不仅仅可以帮我们规划工作,还可以帮我们发现工作中的问题。
有一次,我的团队在开发一个大功能,要将现有的系统改造成支持多租户的系统。也就是说,别的商家可以到我们的平台上发起申请,拥有和我们现有平台一样的能力。
功能来了,各个团队将任务分解,然后就各忙各的去了。但我有着习惯性的不安,总担心丢点什么,于是催着项目经理梳理一下上线流程。
是的,上线流程,虽然我们的代码还没开发完,但是本着“以终为始”的态度,我们就假设各个部分已经开发好了,来想一想上线应该怎么做。
果不其然一梳理上线流程我们便发现了问题怎么识别不同的租户呢有人给出的方案是设置一个HTTP头。但谁来设置这个HTTP头呢没人仔细想过。于是一个潜在的问题就这样被发现了至少不用在未来为它加班了。至于解决方案作为程序员我们有的是办法。
事实上,在今天的软件开发实践中,已经有很多采用了“以终为始”原则的实践。
比如测试驱动开发。测试是什么?就是你这段代码的“终”,只有通过测试了,我们才有资格说代码完成了。当然,测试驱动开发想做好,并不是先写测试这么简单的。
比如持续集成,我们是要交付一个可运行的软件,倒着来想,最好的做法就是让软件一直处于可运行的状态,那就是持续地做集成。
概括地说,践行“以终为始”就是在做事之前,先考虑结果,根据结果来确定要做的事情。
这是“以终为始”这个内容版块的开篇,后面我会给你介绍这个原则在不同场景下的应用,也会引入一些现在行业内的最佳实践进行解析。相信会对你的实际工作有帮助。
总结时刻
有一段时间,网上流传着一个帖子,亚马逊 CTO 介绍亚马逊是如何开发一项产品的,简单来说,他们采用向后工作的方法,开发一项产品的顺序为:
写新闻稿;
写FAQ常见问题解答
写用户文档;
写代码。
今天我带你了解了“以终为始”的做事思路,回过头再来看这个帖子,相信你不难理解为什么亚马逊要这么做事情了。
人们习惯采用顺序思考的思维方式,几十万年的进化将这种思考模式刻在了我们的基因里。要成为更好的自己,我们要克服自身的不足,而这个做法很简单,那就是“以终为始”,做事倒着想,先考虑结果。
人类是一个想象的共同体,做软件的团队更是如此,而我们写出来的软件是我们将“集体想象”落地的载体。
任何事物都要经过两次创造一次是在头脑中的创造也就是智力上的或者第一次创造Mental/First Creation然后才是付诸实践也就是实际的或第二次创造Physical/Second Creation。我们应该在第一次创造上多下功夫统一集体想象让目标更明确。
“以终为始”的思维可以帮助我们更好地规划我们手头任务,也可以帮助我们发现过程中的问题。
如果今天的内容你只能记住一件事,那请记住:遇到事情,倒着想。
最后,我想请你思考一下,在实际的工作或生活中,你有运用“以终为始”的思维方式吗?帮助你解决过哪些问题?欢迎在留言区写下你的想法。
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。

View File

@ -0,0 +1,140 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
03 DoD的价值你完成了工作为什么他们还不满意
你好,我是郑晔。
在开始今天的讨论之前,我们先来看一个小故事。小李是一个程序员,有一天,项目经理老张来到他身边,和他商量一个功能特性的进度:
老张:这有一个任务需要完成,你看一下。-
小李:这个不难,两天就能做完,两天以后就能上线。
两天以后,老张又来到小李的身边验收工作:
老张:怎么样,做完了吗?今天能上线吗?-
小李:我的代码写完了。-
老张:测试人员测过了吗?-
小李:还没有。-
老张:那今天能测完吗?-
小李:那我就不知道了。-
老张:什么?我可是答应了业务的人,今天一定要上线的!
很明显,老张有些愤怒,而小李也有些委屈。于是,老张、小李和测试人员一起度过了一个不眠之夜。
听完这个故事,你有什么感想呢?先不急,我们继续看后面的故事。
又过了几天,老张又来找小李,给小李安排一个很简单的功能。在小李看来,一天就能搞定,而按照老张给出的时间表,小李有两天时间处理这个功能。小李心中暗喜:看来我可以“偷得浮生一日闲”了。
两天以后,老张又来检查工作。
老张:这个功能开发完了吗?-
小李:写完了,你看我给你演示一下。
小李熟练地演示了这个新写好的功能,这次老张很满意:
老张:做得不错。单元测试都写了吧?-
小李:啊?还要写单元测试吗?-
老张:要不为啥给你两天的时间?
怎么会这样?小李心里很委屈,自己明明已经很好地完成了工作,老张是不是故意在找自己的麻烦呢?
好,故事讲完了。是不是有些似曾相识的感觉呢?为什么小李辛辛苦苦地工作,老张却总能挑出毛病来呢?老张是不是来挑刺的呢?其实,老张才没那么闲,小李的委屈主要是因为他和老张对于“完成”有着不一样的理解。换句话说,他们之间存在一个理解的鸿沟。
理解的鸿沟
在这个模块里,我们讨论的主题是“以终为始”。那我们第一个问题就是,“终”到底是什么?在前面这个例子里,“终”就是“完成”,可是,小李认为他的活已经做完了,老张却认为他没做完。
怎么会这样?二人之所以有分歧,归根结底,就在于二人对“完成”的定义理解的不同。
在第一个故事里,作为项目经理,老张认为“完成”应该是“上线运行”,而程序员小李则认为“完成”是“功能代码编写完毕”。这中间存在的理解偏差,包括了测试人员的测试工作,可能还包括了运维人员的上线工作。
在第二个故事里,老张给了小李两天时间。小李认为这两天都是编写功能代码的,而老张想的是,小李应该自己写好功能代码和单元测试,可能还包括了功能测试,这中间的差异是测试代码的工作量。
因为双方的理解不一致,所以无论怎样努力,小李都不可能达成项目经理老张的要求,正所谓“南辕北辙”。
那该怎么办呢?小李会说,我又不是老张肚子里的蛔虫,怎么才能和他达成一致呢?答案很简单,既然双方的理解有差异,那就把这个差异弥合上,后面的问题便也不是问题了。
弥合差异的方式有很多,有一个最佳实践,它的名字叫 DoDDefinition of Done完成的定义从这个概念的名字便不难看出它就是为了解决软件开发中常见的“完成”问题而生的。
完成的定义
DoD 这个概念本身并不复杂,它就是告诉我们怎样算是完成了,尽量减少因为理解偏差造成的各种浪费。具体怎么做呢?就是团队在开始工作前,先制定 DoD。以前面的场景为例团队可以规定
特性开发完成,表示开发人员经过了需求澄清、功能设计、编写代码、单元测试,通过了测试人员的验收,确保代码处于一个可部署的状态,相关文档已经编写完毕。
开发完成,表示开发人员编写好功能代码,编写好单元测试代码,编写好集成测试代码,测试可以通过,代码通过了代码风格检查、测试覆盖率检查。
大家都是聪明人,一旦 DoD 确定好了,谁该做什么事就一目了然了。这个时候,如果小李说“我已经开发完了”,却只是写好了功能代码,那就别怪老张手下无情了。
好了,你已经知道 DoD 是什么了,它简单到让人一目了然,相信你很快就能知道该怎样把它用到你的工作里。不过,我们不仅要知道怎么用,还要知道怎样让 DoD 更好地发挥作用。
DoD 是一个清单清单是由一个个的检查项组成的用来检查我们的工作完成情况。DoD 的检查项,就是我们开发产品所需的一系列有价值的活动。比如:编写代码、编写测试代码、通过测试人员验收等。什么样的活动是有价值的,也许每个团队的认识是不同的。但如果你的团队认为除了功能代码,其他都没价值,也许这是个信号,说明你的团队整体上是缺乏职业素养的,在这样的团队工作,前景并不乐观。
DoD 的检查项应该是实际可检查的。你说代码写好了,代码在哪里;你说测试覆盖率达标了,怎么看到;你说你功能做好了,演示一下。
DoD 是团队成员间彼此汇报的一种机制。别把“汇报”想复杂了,最简单的汇报就是说一句“这个功能做完了”。当我们有了 DoD做事只有两种状态即“做完”和“没做完”。在团队协作中我们经常会听到有人说“这个事做完了80%”对不起那叫没做完根本没有80%做完的说法。
在前面的讨论中,我们所说的 DoD 只是从个人层面入手。在团队层面,我们也可以定义 DoD。
某个功能的 DoD比如这个功能特性已经开发完成经过产品负责人的验收处于一个可部署的状态。
一个迭代的 DoD比如这个迭代规划的所有功能已经完成。
一次发布的 DoD比如整个软件处于可发布的状态上线计划已经明确。
站在 DoD 的肩膀上
至此,我们只是从软件开发团队内部协作的角度来谈 DoD。但实际上它不仅局限在团队内部协作上如果你可以放开思路会发现 DoD 的思维在工作中用途非常广泛。比如,当我们需要和其他团队合作开发一个接口时,我们都知道第一步就是要把接口定义下来。
那么,怎样才算定义完成?很多团队认为落在字面上就够了。但是有了 DoD 的思维,我们定义接口,就会去明确定义可检查的检查项。那么在定义接口这件事上,什么才是“可检查”的呢?我们可以参照一个可运行的接口来进行评估。只要检查:
服务方提供的接口是不是和这个可运行的接口返回值是一样的;
调用方是否可以和这个可运行的接口配合使用。
谁错了,谁改去。你可能会问,应该参照哪些可运行的接口呢?这不难解决,现在模拟服务器的框架到处都是。如果你不介意的话,我的 Moco 就是这样一个开源项目,你可以看一下。
在协作中一旦确立好 DoD我们甚至可以通过流程把它固化下来从而更高效高质地完成工作。当然我们在工作生活中难免会有一些临时的工作它们没有复杂到需要一个流程但是也可以用 DoD 思维来高效地解决。比如:
经常会有人过来,让我帮忙做些事。运用 DoD 的思维,我首先会问他我具体要做哪些事,确认好细节(相当于定义好“检查项”),然后我就知道,这个忙我能帮到什么程度。
我请别人帮忙的时候,也会很清楚告诉他,哪些事是需要他做的,尽量减少不必要的误解。
DoD 是一个思维模式是一种尽可能消除不确定性达成共识的方式。我们本着“以终为始”的方式做事情DoD 让我们能够在一开始就把“终”清晰地定义出来。
人与人协作中经常会出现各种问题根本原因就是有太多因为理解差异造成的误解进而浪费了大量的时间而DoD 就是一种将容易产生歧义的理念落到实处的方法。
总结时刻
好,我们来总结一下今天学到的内容。首先,你应该知道,人与人协作,总会有这样或那样的理解差异。开始协作之前,我们最好先同步一下彼此的理解,确保之后不会因为理解不一致,而让协作方措手不及。
怎样解决大家的理解偏差呢,我介绍了 DoD完成的定义它是行业中的一种最佳实践能够在团队内部很好地同步大家对“完成”的理解。好的 DoD 是一个可以检查的清单,可以确保你不遗漏任何事情。
如果深入领会 DoD你会发现 DoD 可以灵活应用在不同的协作场景中。比如应用于个人工作、团队工作,甚至跨团队工作。当然,你也可以将它灵活地运用于各种生活场景,弥合人与人理解之间的差异,更好地协作与沟通。
如果今天的内容你只能记住一件事,那请记住:在做任何事之前,先定义完成的标准。
最后,我想请你回想一下,你在工作或生活中,是否发生过因为双方理解差异导致的问题或不快呢?有了 DoD 的概念以后,你是不是有了一些新的想法呢?欢迎在留言区留言。
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。

View File

@ -0,0 +1,141 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
04 接到需求任务,你要先做哪件事?
你好,我是郑晔。
我们书接上文,继续讲程序员小李的故事。这次小李接到一个新的需求,让他开发一个单点登录的服务,经过几天的奋战,他顺利地写完了所有的代码。正好产品经理小王路过他身边,顺便问了他一下。
小王:单点登录做得咋样了?-
小李:做完了,我给你演示一下。
小李演示了一遍自己做的功能,小王看上去很满意。
小王:不错。不过,怎么没有支持验证码?-
小李:为什么要做这个?-
小王:这不就是登录的一部分吗?-
小李:哪里规定要做验证码了?-
小王:现在做登录哪有不用验证码的?
我想你已经嗅到了双方谈话的火药味,这个时候如果双方都不能很好地控制自己的情绪,那接下来一场体力的较量可能就一触即发了。
为什么双方会有这么大的分歧呢?其中一个重要的原因是,开始实现这个需求之前,任务双方都没有清晰地定义好边界,没能把需求描述清楚。
需求描述的问题
在软件开发中,程序员做什么一般都由需求来定义。我们都知道,需求是软件开发的一个重要组成部分,但你可能并没有仔细想过,不同的需求描述方式,可能会影响我们程序员对需求的理解。
因为信息的传递是会衰减的你不可能把你理解的信息100%传递给另外一个人,而这中间,如何传递,也就是如何描述将直接决定衰减的比例。
很多公司的软件开发模式是基于功能列表的,这个列表“规定”了程序员要做的功能,各个组从产品经理那里领来开发列表,然后“照单抓药”开始写代码。但是,通常这种功能列表只是一些简单的描述,你并不能看到全局。
很多团队的一个状态就是,程序员们都知道要开发的功能是什么,但这个功能是谁在什么样的场景下使用的,很多人却回答不上来。如果你去问他为什么要开发这个功能,他通常会说:这是功能列表里规定的。
这种功能列表式的需求描述方式,将一个完整的需求敲成了碎片。 只有所有功能全部开发完成,对接在一起的时候,才是“破镜重圆”的时刻。
也就是说,不到最后一刻,大多数人并没有一个完整的图景,这就相当于看不到完整的“终”。顺着这个思路做下去,你会在最后关头遇到许多意料之外的问题,其结果必然是手忙脚乱。
根据这种基于功能列表的需求描述,每个组在安排工作的时候,都会按照自己的理解进行功能排列。
所以,当你的组完成了一个功能时,这个功能却可能上不了线,因为你还要依赖于其他组的工作,而这个组不巧,却刚好把相关的功能开发排在了后面。
这还只是两个组之间有依赖的情况,如果需要多个组协同,可以想象,状况会多么糟糕。
所以,当我们对产品经理说“时间不足,砍掉一些需求吧。”得到的答案肯定是,“对不起,做不到,因为需求已破碎,没办法调整。”
因此一些新的需求描述方式也就应运而生这其中用户故事User Story是我最喜欢的一种方式。它是站在用户的角度来描述了一个用户希望得到的功能关注用户在系统中完成一个动作需要经过怎样的路径。既然它是“故事”它就需要是一个完整的场景可以讲述出来。
“用户故事”有什么用?
我们先来以用户密码登录为例,看看用户故事长什么样?一个完整的用户故事大致包含以下几个部分:
标题,简要地说明这个用户故事的主要内容,比如:注册用户使用用户名密码登录。
概述,简要地介绍这个用户故事的主要内容,一般会用这样的格式:-
As a Role, I want to Activity, so that Business Value.-
意思就是:作为一个什么角色,要做什么样的事,以便达成一种怎样的效果。其中最重要的是,告诉别人为什么要做这件事,虽然只有一句话,却往往是很多人欠缺的思考,只知做,不知为何做。-
举个概述的例子:作为一个注册用户,我想要通过用户密码登录,以便我可以使用注册用户才能够使用的服务。
详述,详细地描述这个用户故事的完整流程,我们会把操作流程、用户界面等信息都放到这里。-
比如:用户使用正确用户名和密码登录,就可以登录成功;如果密码不正确,则登录页面提示用户“用户名密码不正确”。基本上,看到这个部分,程序员就可以在心中描绘出这个用户故事的样子了。-
超出范围的部分,比如:第三方登录不在范围内,这个部分主要是限定人们不要进一步发散。
验收标准,这个部分会描述一个正常使用的流程是怎样的,以及各种异常流程系统是如何给出响应的,这是程序员常常会欠缺的思考。它会把详述中很多叙述的部分变成一个具体的测试用例。比如,下面我给出的两个验收用例:-
正常场景给定一个注册用户张三其用户名是zhangsan密码是foobar当张三使用zhangsan 和 foobar 登录系统时,可以成功登录,登录成功后,跳转到用户中心。-
异常场景给定一个注册用户张三其用户名是zhangsan密码是foobar当张三使用zhangsan 和 wrong 登录系统时,登录失败,在登录页面上提示“用户名密码不正确”。
在前面的例子中,小张和小王之所以会对需求是否完成产生分歧,是因为大家对于需求完成的定义不同。对于这种情况,我们能怎么办呢?
这个模块的主题是“以终为始”现在你看到了用户故事是如何描述需求的你或许已经知道我要说什么了没错这里非常关键的一点就是“验收标准”。很多人学习用户故事认为最重要的是记住“As…, I want to …, so that …”这样的需求描述方式。
在我看来,无论采用哪种需求描述方式,这部分也都是能说清楚的。那我们要从用户故事中学到什么呢?我认为就是用户故事的关键点:验收标准,它可以清晰地定义出需求边界。
验收标准非常重要的一环是异常流程的描述。大部分程序员都擅长解决正常流程,而异常流程则是最容易忽略的,也是产生扯皮的关键环节。既然容易扯皮,我们就在一开始把它定义清楚。怎么才算做完需求呢?验收标准说了算。
采用用户故事之后,我经常在写完了主要流程之后,再去看一下验收标准,为自己的开发查缺补漏。因为我知道,那是标准,达不成就不算任务完成。
当我们说自己开发完成,可以交给测试人员测试时,我们需要照着验收标准给测试人员演示一遍,证明我们的系统确实能够跑通。这之后,测试人员才会把系统接手过去,做更系统的测试。
验收标准给出了这个需求最基本的测试用例,它保证了开发人员完成需求最基本的质量。如果你了解 BDDBehavior-Driven Development也就是“行为驱动开发”就可以按照验收标准中给出的内容编写验收测试用例了。
在实际工作中,许多产品经理把需求交给开发人员之前,很多细节是没想清楚的,那种功能列表式的需求常常只包含了正常路径,那些缺失的细节就是在后续的过程中,由开发人员补全的。用户故事就是一种固定的格式,让他们把这些应该想清楚的问题想清楚。
如果你的团队采用用户故事的格式进行需求描述固然好,如果不能,在功能列表中,补充验收标准也会极大程度地改善双方协作的效率。
你的角色
或许你会有这样的疑问,如果产品经理通过用户故事的方式,将需求实现细节都描绘得清清楚楚,那我们程序员的发挥空间在哪里?请注意,验收标准所给出实现细节应该是业务上的,程序员在这种问题上思考才是真正意义上的浪费时间,我们的发挥空间应该是在技术实现上。
然而,在现实情况中,很多团队做不到这种程度。
你会发现,我们在开发中之所以会“丢三落四”,很重要的一个原因是,在开发一个功能特性的时候,因为一些环节的缺失,我们不得已扮演了很多的角色,其中之一就是产品经理。你是一个专业的程序员,但大多数情况下,你却只是一个业余的产品经理,“丢三落四”就在所难免了。
或许你会说,我在一个小公司工作,公司没那么多人,没有专门的产品经理,只有我们几个“全世界都缺”的程序员,需求都是老板扔给我们的,谁来帮我们写验收标准呢?
没办法,答案只能是你自己。虽然你名义上是程序员,但当拿到一个需求的时候,你要做的事不是立即动手写代码,而是扮演产品经理的角色,分析需求,圈定任务范围。相信我,事前分析绝对比你拿一个写好的系统给老板,而他却告诉你这不是他想要的,好太多了。
另外我想提醒你注意的是,扮演不同角色的时候,我们的思考模式是不同的。还是以开发用户名密码登录为例,你想到的可能是:输入正确的用户名和密码可以正常登录,输入错误的用户名和密码不能登录,并且给出提示。
如果你只扮演开发人员的角色,想到这些就算不错了。但如果你扮演的是产品经理的角色,会从产品的角度进行思考,也就会看到不同的内容,比如:
登录是否需要验证码
是否需要第三方登录
用户名和密码的长度在系统内是否有限制
密码是否需要满足一定的规则
……
我知道,如果让你来填写,这个列表会更长。可能这并不是我们都需要完成的功能,但站在分析的角度,这都是我们要考虑的问题,一个登录功能,绝不仅仅是用户名和密码校验那么简单的。我们能想到这些,仅仅是因为我们正在扮演一个不同的角色。
所以,如果你要兼顾开发人员和产品经理两个角色,建议你先扮演好产品经理的角色,多花点时间把验收标准制定好,再回到开发人员的角色上去写代码。毕竟,最好维护的代码是没有写出来的代码。
总结时刻
需求,是软件开发中的一个关键环节,一旦需求理解出现问题,势必会造成大量的浪费。传统的功能列表只是简单罗列了要实现的功能,丢失了大量的上下文,会导致团队成员对于需求“只见树木不见森林”。
而在比较大的团队中,更是会将一个功能分拆到多个小团队中,每个人看到的只是功能碎片。于是,后来产生了其他的需求描述方式,比如用例和用户故事。
在实际的开发过程中,大量的分歧来自于对“需求完成”的定义。当我们把“以终为始”的原则应用在需求领域中,就会注意到,用户故事有一个非常重要的组成部分是验收标准。
验收标准不仅仅描述出了正常流程,也会关注到异常流程的处理,它也是我们验收测试用例的起点。一旦事先定义好验收标准,大量的扯皮工作就随之烟消云散了。
理解了验收标准的作用,即便我们不使用用户故事来定义需求,依然可以把用户故事中的关键点应用到自己的实践中,在功能列表的每个功能定义中,增加验收标准。
如果今天的内容你只能记住一件事,那请记住:在做任何需求或任务之前,先定好验收标准。
最后,我想请你回想一下,在实际工作中,你是如何澄清你的需求,或者因为需求不清晰给你造成了哪些困扰?欢迎在留言区写下你的想法。
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。

View File

@ -0,0 +1,110 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
09 你的工作可以用数字衡量吗?
你好,我是郑晔。
今天的分享从日常工作开始。请你回想一下,你每天到岗之后做的第一件事是什么呢?然后你来猜猜我的答案是什么?你可能猜不到,我每天到公司之后,第一件正事是看数字。
我现在服务于一家做数字资产的公司我们提供的是一个24小时运行的服务。从加入这家公司的第一天开始公司的人就给我不断灌输一个重要理念——看数字。在我座位的正前方摆着一个巨大的显示器上面展示着各种不断变换的曲线、柱状图和数字这些数字反映的是各种系统运行的指标。
我们就是每天看着这些指标,来发掘一些线上系统问题的,一旦某些指标出现自己不能理解的异常,就要着手调查。
你或许会纳闷,我们不是在探讨“以终为始”吗?怎么变成了一个关于监控的讨论呢?别急,我们确实还在讨论“以终为始”,因为数字是诠释“终”的最好方式。
我们前面讨论了各种“终”但通常靠语言定义的“终”或多或少都会存在一些模糊的地方也就是容易产生误解的地方。而数字却是一个明明白白的“终”。比如测试覆盖率要求100%即便你做到了99.9%,不达标就是不达标,没什么好说的,说破天也是不达标。
再比如之前内容我们讲到精益创业时提到了一个重要的反馈循环开发build-测量measure-认知learn。你会发现在这个循环中开发build是可控的认知learn必须是得到反馈之后才能有的。所以这里面最需要我们回答的问题是测量measure。而这里的测量最好的检验标准当然就是数字。
或许你会说,数字我们都很熟,还用讲吗?不过在我看来,你还真的未必习惯于使用数字。
熟悉而陌生的数字
从进化的角度来看,人们做事更多是依赖于直觉的。数字,是人类在非洲大草原上奔跑了许久之后才创造出来的东西。著名科普著作《从一到无穷大》的开篇有这么一个故事:
两个匈牙利贵族决定做一次数数的游戏,看谁说出的数字大。-
一个贵族说:“好,那你先说吧!”-
另一个绞尽脑汁想了好几分钟说了一个数字“3”。-
现在轮到第一个贵族苦思冥想了,他想了一刻钟,然后说:“好吧,你赢啦!”
这个故事听起来有些荒诞但一些非洲探险家证实在某些原始部族里不存在比3大的数词。如果问他们有几个孩子而这个数字大于3的话他就会回答“许多个”。
虽然我们中华民族是一个重视教育的民族,现在也都承认数学是一门重要的基础知识。但我们还是习惯性地观其大略,因为在日常生活领域里,除了买东西发工资,需要对数字斤斤计较的场合并不多。
历史的车轮在不停地滚滚向前,当今社会所面临的复杂度已经远远超过凭直觉就能把事情做好的程度。
一些人说自己靠直觉就能把事情做好其实这是一种误解因为那种所谓的直觉通常是一种洞见Insight洞见很大程度上依赖于一个人在一个领域长期的沉淀和积累而这其实是某种意义上的大数据。
我们都在说人类马上就要进入智能时代了。之所以这么说主要是现在人工智能技术不断地向前发展着。而人工智能作为一门在50年代就已经问世的技术直到最近几年才得到大踏步的前进主要归功于基础设施的发展。
在人工智能领域,基于统计的方法早就在学术界提了出来,但由于当时的技术条件所限,人们的数据采集和存储能力都有限,当时的“大”数据和今天的大数据完全不是一个量级的概念。
直到进入到互联网时代,随着处理数据量的增加,基础设施在不断地拓展,进而促使人们采集更多的数据,这个正向反馈也造就了今天的大数据。
原本因为缺乏足够数据支撑,难以施展拳脚的 AI 算法,在今天一下子有了足够的表演空间,从一个边缘角色成为了舞台中心的主角。
今天谈到人工智能,人们主要会谈三件事:算法、算力和数据。算法几乎是行业共有的,而算力在云计算普及的今天也不再是稀缺资源,所以,数据几乎成了兵家必争之“物”。于是,我们看到的现象是各个公司都在努力地搜集各种数据,让数据成为自己的竞争力。所以,在大方向上,数据采集是一个行业共识。
但是,作为这个世界上最了解数据价值的一批人,我们程序员只是在努力地把数据用于不断改善别人的生活,而对于自己日常工作的改善,则思考得少之又少。
我们更习惯的讨论方式依然是靠直觉。比如:增加了这个特性可能会让用户增长,做了这个调整应该会让系统的压力变小。
在一些简单的情形下,或者说大家信息对称、知识背景相差无几的情况下,这样的讨论是很容易得到认同的。而当事情复杂到一定程度时,简单地靠感觉是很难让人相信的。
所以,在我们的工作中,经常会发生的一个现象是,一个人说,我觉得这个有作用,另一个人说,我觉得那个没有。几个“觉得”下来,双方就开始进入了隔空对话的环节,谁也无法说服谁。
如果换成用数字的方式进行讨论,效果就会更好。有一次,为了改善用户体验,我们准备进行一次主页改版。产品团队希望在主页上加上大量的内容,而开发团队则认为太多的内容会导致主页加载变慢,进而造成用户体验下降。
正当这个对话即将进入“空对空”的讨论之时,我们找到了一个测量指标:主页加载速度。只要保证主页加载速度,产品团队就可以按照自己的理解来做调整。于是,一个即将不可挽回的讨论,变成了在一定约束条件下的讨论,双方谁也不再思维发散,讨论就能继续推进了。
如果你认同了数据本身的价值,那么再结合“以终为始”的理念,我们就应该在着手做一件事之前,先来想怎么去测量。无论是在讨论产品特性,还是功能开发,“信口雌黄”是容易的,落到数字上,人们就会多想一下,这是对彼此的约束。
从数字出发
前面的内容我们都是在说应该重视测量指标,重视数字。接下来,我就分享下几个我在实际工作中运用数字的案例,让你看看习惯用数字去思考问题之后,会拓宽哪些思考的维度。
首先是基于数字进行技术决策。有一次,我们打算做一个技术改进,给系统增加一些缓存,减轻数据库的压力。大家一起设计了两个技术方案。如果查询是特定的,我们就准备简单地在某些方法上加上缓存;如果查询是五花八门的,就准备用一个中间件,使用它的查询方案。
系统现在的情况到底是什么样的呢?我们发现并不能立刻回答这个问题。于是,大家决定在系统中增加一些统计指标,让数据给我们答案。然后根据数据反映出的情况,进行具体的决策。
其次是一个准备上线的案例。当时,我们是要做一个影响力比较大的系统升级,因为这是一个系统的关键模块,上下游系统都会受到影响。
谁也不能确定哪个模块会在上线过程中出问题。于是,设计了一个全新的数据面板,将相关的几个模块的核心指标都摆在上面。而我们要做的就是在上线的同时,观察这些指标的变化。
所幸的是,这次上线影响不大,几个指标一路平稳,而大家的信心就源自这些提前准备好的指标。
再次,看一个从数字中发现问题的例子。由于各种历史原因,我们的重点指标面板上,会有几个指标表示的是类似的东西。
比如,某个模块的处理能力,一个指标是站在这个模块内部度量的,而另一个指标则是由这个模块上下游系统度量的。在大多数情况下,它们的表现是一致的。结果有一天两者突然出现了很大的差异,内部度量表现依然良好,而外部度量则出现了很大的延迟。
于是,我们开始追问为什么。一路追寻下来,我们发现,是这个模块内部需要定期将内部状态持久化下来,而在那个时间段内,这个模块就会停止从上游读取数据。所以,在内部看一切正常,而外部看则延迟较大。随后,我们找到了方案,解决了这一问题。
最后再说一个行业中的例子,据我所知,行业里的某些公司已经开始做所谓的 AIOps也就是通过人工智能的方式从数据中发现更多运维的问题。无论哪种做法都是为了从数字中发现问题让系统更稳定。
我的一个同事有个观点非常值得玩味,他说,从数字上看,好的系统应该是“死水一潭”。
我是赞同这个观点的,因为出现波动尤其是大幅度波动,又不能给出一个合理解释的话,就说明系统存在着隐患。而让系统稳定,正是我们工作的一个重要组成部分。
回到这一讲的开头,我说每天工作中的一个重要组成部分就是看数字,其实就是在尝试着从数字的趋势中发现问题,如今团队已经习惯了“给个数字看看”这样的沟通方式,内部扯皮的机会也相应地减少了一些。
总结时刻
随着智能时代的来临,人类社会开始逐渐认识到数据的重要性。但我们这群 IT 人在通过数据为其他人服务的同时,却很少把数字化的思维带到自己的工作范围内。这也是工作中很多“空对空”对话的根源所在。
结合着“以终为始”的思考,如果我们可以在一开始,就设计好测量工作有效性的指标,那么就可以更有目的性地去工作了。
而如果我们习惯了用数字去思考,就可以在很多方面让数字帮助我们。我举了几个小例子,比如:基于数据进行技术决策、预先设定系统指标,以及发现系统中的问题等等。希望你也可以把数字思维带到你的日常工作中。
如果今天的内容你只记住一件事,那请记住:问一下自己,我的工作是不是可以用数字衡量。
最后,我想请你分享一下,你的工作中,有哪些应用数字解决问题的场景呢?欢迎在留言区写下你的想法。
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。

View File

@ -0,0 +1,143 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
10 迭代0_ 启动开发之前,你应该准备什么?
你好,我是郑晔。
关于“以终为始”,我们已经从各个方面讲了很多。你或许会想,既然我们应该有“以终为始”的思维,那么在项目刚开始,就把该准备的东西准备好,项目进展是不是就能稍微顺畅一点儿呢?
是这样的事实上这已经是一种常见的实践了。今天我们就来谈谈在一开始就把项目准备好的实践迭代0。
为什么叫迭代0呢在“敏捷”已经不是新鲜词汇的今天软件团队对迭代的概念已经不陌生了它就是一个完整的开发周期各个团队在迭代上的差别主要是时间长度有所不同。
一般来说第一个迭代周期就是迭代1然后是迭代2、迭代3依次排列。从名字上你就不难发现所谓迭代0就是在迭代1之前的一个迭代所以我们可以把它理解成开发的准备阶段。
既然迭代0是项目的准备阶段我们就可以把需要提前准备好的各项内容在这个阶段准备好。事先声明这里给出的迭代0它的具体内容只是基本的清单。在了解了这些内容之后你完全可以根据自己项目的实际情况扩展或调整这个清单。
我们来看看我为你准备的迭代0清单都包含了哪些内容。
需求方面
1. 细化过的迭代1需求
一个项目最重要的是需求而在迭代0里最重要的是弄清楚第一步怎么走。当我们决定做一个项目时需求往往是不愁的哪些需求先做、哪些需求后做这是我们必须做的决策。迭代0需要做的事就是把悬在空中的内容落到地上。
在需求做好分解之后,我们就会有一大堆待开发的需求列表。注意,这个时候需求只是一个列表,还没有细化。因为你不太可能这个时候把所有的内容细化出来。如果你做过 Scrum 过程,你的 backlog 里放的就是这些东西。
然后我们要根据优先级从中挑出迭代1要开发的需求优先级是根据我们要完成的最小可行产品minimum viable productMVP来确定的这个最小可行产品又是由我们在这个迭代里要验证的内容决定的。一环扣一环我们就得到了迭代1要做的需求列表。
确定好迭代1要做的需求之后接下来就要把这些需求细化了细化到可执行的程度。前面讲用户故事时我们已经说过一个细化需求应该是什么样子的这里的关键点就是要把验收标准定义清楚。
所以我们要在迭代0里根据优先级来确定迭代1要做的需求然后进行细化。
2.用户界面和用户交互
如果你的项目是一个有用户界面的产品给出用户界面自然也是要在迭代0完成的。另外还有一个东西也应该在迭代0定义清楚那就是用户交互。
我见过很多团队只给出用户界面,然后,让前端程序员或者 App 程序员根据界面去实现。程序员实现功能没问题,但定义交互并不是程序员这个角色的强项,它应该是需求的一部分。
如何让用户用着舒服,这是一门学问。我们在市面上看到很多难用的网站或 App基本上都是程序员按照自己习惯设计出来的。
现如今,我们可以很容易地在市面上找到画原型的工具,某些工具用得好的话,甚至可以达到以假乱真的地步。如果能再进一步的话,甚至可以用一些模拟服务器的工具,把整个交互的界面都做出来。作为 Moco 这个模拟服务器的开发者,我很清楚,一个原型可以达到怎样的高度。
所以一个有用户界面的项目需要在迭代0中给出用户界面和用户交互。
技术方面
1. 基本技术准备
技术方面需要在项目一开始就准备好的事比较多。其中有一些是你很容易想到的比如在进入迭代1开始写代码之前我们需要确定技术选型确定基本的技术架构等等。也许你还能想到数据库表结构也是这个阶段应该准备的。
确实,这些东西都应该是在一个项目初期准备的,也是大家容易想到的。接下来,我来补充一些大家可能会遗漏的。
持续集成
对于持续集成,我们通常的第一反应是搭建一个持续集成服务器。没错,但还不够。这里的重点其实是构建脚本。因为持续集成服务器运行的就是构建脚本。
那要把什么东西放在构建脚本里呢?最容易想到的是编译打包这样的过程。感谢现在的构建工具,它们一般还会默认地把测试也放到基本的构建过程中。
但仅有这些还是不够,我们还会考虑把更多的内容放进去,比如:构建 IDE 工程、代码风格检查、常见的 bug 模式检查、测试覆盖率等等。
持续集成还有一个很重要的方面,那就是持续集成的展示。为什么展示很重要?当你的持续集成失败时,你怎么发现呢?
一个简单的解决方案是:摆个大显示器,用一个 CI Monitor 软件,把持续集成的状态展示在上面。更有甚者,会用一个实体的灯,这样感官刺激更强一些。
在“以终为始”这个模块中,我们提到集成的部分时,只讲了要做持续集成,后面我们还会再次讲到持续集成,和你说说持续集成想做好,应该做成什么样子。
测试
测试是个很有趣的东西,程序员对它又爱又恨。一般来说,运行测试已经成为现在很多构建工具的默认选项,如果你采用的工具没有这个能力,建议你自己将它加入构建脚本。
让你为一个项目补测试,那是一件非常痛苦的事,如果在一开始就把测试作为规范加入进去的话,那么在边开发边写测试的情况下,相对来说,写测试痛苦度就低多了,团队成员也就容易遵守这样的开发规范。
把测试当作规范确定下来的办法就是把测试覆盖率加入构建脚本。
大多数团队提起测试,尤其是开发者测试,多半想到的都是单元测试和集成测试。把整个系统贯穿在一起的“端到端测试”却基本上交给其他人来做,也有不少团队是交给测试团队专门开发的特定程序来做。
在今天的软件开发中有一些更适合描述这类测试的方法比如BDD再比如Specification by Example。你可以简单地把它们理解成一种描述系统行为的方式。还有一点做得好的地方是有一些软件框架很好地支持了这种开发方法比如Cucumber。如果你有这种测试不妨也将它加入构建脚本。
2.发布准备
数据库迁移
如果你做的是服务器端开发,多半离不开与数据库打交道。只要是和数据库打交道,强烈建议你把数据库变更管理起来。
管理数据库变更的方式曾是很多团队面临的困扰。好在现在已经有了很多工具支持,比如,我最近喜欢的工具是 flyway它可以把每一次数据库变更都当作一个文件。这样一来我们就可以把数据库变更放到版本控制工具里面方便进行管理。
管理变更有几种不同的做法,一种是每个变更是一个文件,一种是每一次发布是一个文件。各有各的好处,你可以根据需要,自行选择。
发布
技术团队擅长做功能开发,但上线部署或打包发布却是很多团队在前期最欠考量的内容,也是很多团队手忙脚乱的根源。
如果一开始就把部署或发布过程自动化,那么未来的生活就会轻松很多。如果你采用的是 Docker就准备好第一个可以部署的 Dockerfile如果是自己部署就编写好 Shell 脚本。
其实你会发现上面提到的所有内容即便不在迭代0做在项目的各个阶段也会碰到。而且一般情况下即便你在迭代0把这些工作做好了后续依然要不断调整。但我依然建议你在迭代0把这些事做好因为它会给你的项目定下一个基调一个自动化的基调。
日常工作
最后我们来看一下如果在迭代0一切准备就绪你在迭代1应该面对的日常工作是什么样的。
你从已经准备好的任务卡中选了一张,与产品经理确认了一些你不甚清楚的几个细节之后,准备实现它。你从代码仓库更新了最新的代码,然后,开始动手写代码。
这个任务要在数据库中添加一个字段,你打开开发工具,添加了一个数据库迁移文件,运行了一下数据库迁移工具,一切正常,新的字段已经出现在数据库中。
这个任务很简单,你很快实现完了代码,运行一下构建脚本,代码风格检查有个错误,你顺手修复了它。再运行,测试通过了,但测试覆盖率不够,你心里说,偷懒被发现了。不过,这是小事,补几个测试就好了。一切顺利!
你又更新了一下代码,有几个合并的问题。修复之后,再运行构建脚本,全过,提交代码。
你伸了一个懒腰,完成任务之后,你决定休息片刻。忽然,持续集成的大屏幕红了,你的提交搞砸了。你立刻看了一下代码,有一个新文件忘提交了,你吐了一下舌头赶紧把这个文件补上了。不一会儿,持续集成大屏幕又恢复了代表勃勃生机的绿色。
你休息好了,准备开始拿下下一个任务。
这就是一个正常开发该有的样子在迭代0时将准备工作做好后续你的一切工作就会变得井然有序出现的简单问题会很快地被发现所有人都在一种有条不紊的工作节奏中。
总结时刻
在这一讲中我给你介绍了迭代0的概念它是在正式开发迭代开始之前进行一些基础准备的实践。我给了一份我自己的迭代0准备清单这份清单包含了需求和技术两个大方面你可以参照它设计你自己的迭代0清单。
根据我的经验,对比这个清单,大多数新项目都在一项或几项上准备得不够充分。即便你做的不是一个从头开始的项目,对照这个清单,也会发现项目在某些项上的欠缺,可以有针对性地做一些补充。
如果今天的内容你只记住一件事那么请记住设计你的迭代0清单给自己的项目做体检。
最后我想请你思考一下如果让你来设计迭代0清单它会包含哪些内容呢欢迎在留言区写下你的想法。
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。

View File

@ -0,0 +1,119 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
11 向埃隆·马斯克学习任务分解
你好,我是郑晔。
这次我们从一个宏大的话题开始:银河系中存在多少与我们相近的文明。我想,即便这个专栏的读者主力是程序员这个平均智商极高的群体,在面对这样一个问题时,大多数人也不知道从何入手。
我来做一个科普给大家介绍一下德雷克公式这是美国天文学家法兰克·德雷克Frank Drake于1960年代提出的一个公式用来推测“可能与我们接触的银河系内外星球高等文明的数量”。
下面,我要放出德雷克公式了,看不懂一点都不重要,反正我也不打算讲解其中的细节,我们一起来感受一下。
不知道你看了德雷克公式做何感想,但对于科学家们来说,德雷克公式最大的作用在于:它将一个原本毫无头绪的问题分解了,分成若干个可以尝试回答的问题。
随着观测手段的进步,我们对宇宙的了解越来越多,公式中大多数数值,都可以得到一个可以估算的答案。有了这些因子,人们就可以估算出银河系内可以与我们通信的文明数量。
虽然不同的估算结果会造成很大的差异,而且我们迄今为止也没能找到一个可以联系的外星文明,但这个公式给了我们一个方向,一个尝试解决问题的手段。
好吧,我并不打算将这个专栏变成一个科普专栏,之所以在这讲解德雷克公式,因为它体现了一个重要的思想:任务分解。
通过任务分解,一个原本复杂的问题,甚至看起来没有头绪的问题,逐渐有了一个通向答案的方向。而“任务分解”就是我们专栏第二模块的主题。
马斯克的任务分解
如果大家对德雷克公式有些陌生,我们再来看一个 IT 人怎样用任务分解的思路解决问题。
我们都知道埃隆·马斯克Elon Musk他既是电动汽车公司特斯拉Tesla的创始人同时还创建了太空探索公司 SpaceX。SpaceX 有一个目标是送100万人上火星。
美国政府曾经算过一笔账把一个人送上火星以现有技术是可实现的需要花多少钱呢答案是100亿美金。如果照此计算实现马斯克的目标送100万人上火星就要1万万亿。这是什么概念呢这笔钱相当于美国500年的GDP实在太贵了贵到连美国政府都无法负担。
马斯克怎么解决这个问题呢他的目标变了他准备把人均费用降到50万美元也就是一个想移民的人把地球房子卖了能够凑出的钱。原来需要100亿美金现在要降到50万美金需要降低2万倍。
当然降低2万倍依然是一个听起来很遥远的目标。所以我们关注的重点来了马斯克的第二步是把2万分解成20×10×100。这是一道简单的数学题也是马斯克三个重点的努力方向。
先看“20”现在的火星飞船一次只能承载5个人马斯克的打算是把火箭造大一点一次坐100人这样就等于把成本降低20倍。如果你关注新闻的话会发现 SpaceX 确实在进行这方面的尝试,
再来看“10”马斯克认为自己是私营公司效率高成本可以降到十分之一。他们也正在向这个方向努力SpaceX 的成本目前已经降到了同行的五分之一。
最后的“100”是什么呢就是回收可重复使用的火箭。如果这个目标能实现发射火箭的成本就只是燃料成本了。这也就是我们频频看到的 SpaceX 试飞火箭新闻的原因。
这么算下来,你是不是觉得,马斯克的目标不像最开始听到的那样不靠谱了呢?正是通过将宏大目标进行任务分解,马斯克才能将一个看似不着边际的目标向前推进。
软件开发的任务分解
好了,和大家分享这两个例子只是为了热热身,说明人类解决问题的方案是差不多的。当一个复杂问题摆在面前时,我们解决问题的一个主要思路是分而治之。
一个大问题,我们都很难给出答案,但回答小问题却是我们擅长的。所以,当我们学会将问题分解,就相当于朝着问题的解决迈进了一大步。
我们最熟悉的分而治之的例子,应该是将这个理念用在算法上,比如归并排序。将待排序的元素分成大小基本相同的两个子集,然后,分别将两个子集排序,最后将两个排好序的子集合并到一起。
一说到技术,大家就觉得踏实了许多,原来无论是外星人搜寻,还是大名鼎鼎的马斯克太空探索计划,解决问题时用到的思路都是大同小异啊!确实是这样。
那么,用这种思路解决问题的难点是什么呢?给出一个可执行的分解。
在前面两个例子里面,最初听到要解决的问题时,估计你和我一样,是一脸懵的。但一旦知道了分解的结果,立即会有一种“柳暗花明又一村”的感觉。你会想,我要是想到了这个答案,我也能做一个 SpaceX 出来。
但说到归并排序的时候,你的心里可能会有一丝不屑,这是一个学生级别的问题,甚至不值得你为此费脑子思考。因为归并排序你已经知道了答案,所以,你会下意识地低估它。
任务分解就是这样一个有趣的思想,一旦分解的结果出来,到了可执行的步骤,接下来的工作,即便不是一马平川,也是比原来顺畅很多,因为问题的规模小了。
在日常工作中,我们会遇到很多问题,既不像前两个问题那样宏大,也不像归并排序那样小,但很多时候,我们却忘记了将任务分解这个理念运用其中,给工作带来很多麻烦。
举一个例子有一个关于程序员的经典段子这个工作已经做完了80%剩下的20%还要用和前面的一样时间。
为什么我们的估算差别如此之大,很重要的一个原因就在于没有很好地分解任务,所以,我们并不知道要做的事情到底有多少。
前面我们在“为什么说做事之前要先进行推演?”文章中,讲到沙盘推演,这也是一个很好的例子,推演的过程就是一个任务分解的过程。上手就做,多半的结果都是丢三落四。你会发现,真正把工作完全做好,你落掉的工作也都要做,无论早晚。
与很多实践相反,任务分解是一个知难行易的过程。知道怎么分解是困难的,一旦知道了,行动反而要相对来说容易一些。
在“任务分解”这个主题下,我还会给你介绍一些实践,让你知道,这些最佳实践的背后思想就是任务分解。如果你不了解这些实践,你也需要知道,在更多的场景下,先分解任务再去做事情是个好办法。
也许你会说,任务分解并不难于理解,我在解决问题的过程中也是先做任务分解的,但“依然过不好这一生。”这就要提到我前面所说难点中,很多人可能忽略的部分:可执行。
可执行对于每个人的含义是不同的对于马斯克而言他把2万分解成20×10×100剩下的事情对他来说就是可执行的但如果你在 SpaceX 工作,你就必须回答每个部分究竟是怎样执行的。
同样,假设我们做一个 Web 页面,如果你是一个经验丰富的前端工程师,你甚至可能认为这个任务不需要分解,顶多就是再多一个获取网页资源的任务。
而我如果是一个新手,我就得把任务分解成:根据内容编写 HTML根据页面原型编写页面样式根据交互效果编写页面逻辑等几个步骤。
不同的可执行定义差别在于,你是否能清楚地知道这个问题该如何解决。
对于马斯克来说,他的解决方案可能是成立一个公司,找到这方面的专家帮助他实现。对你的日常工作来说,你要清楚具体每一步要做的事情,如果不能,说明任务还需要进一步分解。
比如,你要把一个信息存起来,假设你们用的是关系型数据库,对大多数人来说,这个任务分解就到了可执行的程度。但如果你的项目选用了一个新型的数据库,比如图数据库,你的任务分解里可能要包含学习这个数据库的模型,然后还要根据模型设计存储方案。
不过,在实际工作中,大多数人都高估了自己可执行粒度,低估任务分解的程度。换句话说,如果你没做过任务分解的练习,你分解出来的大部分任务,粒度都会偏大。
只有能把任务拆分得非常小,你才能对自己的执行能力有一个更清楚地认识,真正的高手都是有很强的分解能力。这个差别就相当于,同样观察一个物品,你用的是眼睛,而高手用的是显微镜。在你看来,高手全是微操作。关于这个话题,后面我们再来细聊。
一旦任务分解得很小,调整也会变得很容易。很多人都在说计划赶不上变化,而真正的原因就是计划的粒度太大,没法调整。
从当年的瀑布模型到今天的迭代模型,实际上,就是缩减一次交付的粒度。几周调整一次计划,也就不存在“计划赶不上变化”的情况了,因为我的计划也一直在变。
如今软件行业都在提倡拥抱变化,而任务分解是我们拥抱变化的前提。
总结时刻
我们从外星人探索和马斯克的火星探索入手,介绍了任务分解在人类社会诸多方面的应用,引出了分而治之这个人类面对复杂问题的基本解决方案。接着,我给你讲了这一思想在软件开发领域中的一个常见应用,分而治之的算法。
虽然我们很熟悉这一思想,但在日常工作中,我们却没有很好地应用它,这也使得大多数人的工作有很大改进空间。运用这一思想的难点在于,给出一个可执行的分解。
一方面,对复杂工作而言,给出一个分解是巨大的挑战;另一方面,面对日常工作,人们更容易忽略的是,分解的任务要可执行。每个人对可执行的理解不同,只要你清楚地知道接下来的工作该怎么做,任务分解就可以告一段落。
大多数人对于可执行的粒度认识是不足的,低估了任务分解的程度,做到好的分解你需要达到“微操作”的程度。有了分解得很小的任务,我们就可以很容易完成一个开发循环,也就让计划调整成为了可能。软件行业在倡导拥抱变化,而任务分解是拥抱变化的前提。
如果今天的内容你只记住一件事,那么请记住:动手做一个工作之前,请先对它进行任务分解。
最后,我想请你回想一下,你在实际工作中,有哪些依靠任务分解的方式解决的问题呢?欢迎在留言区写下你的想法。
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。

View File

@ -0,0 +1,96 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
18 Redis协议的请求和响应有哪些“套路”可循
你好,我是你的缓存课老师陈波,欢迎进入第 18 课时“Redis 协议分析”的学习本课时主要学习Redis的设计原则、三种响应模式、2种请求格式、5种响应格式。
Redis 协议
Redis 支持 8 种核心数据结构每种数据结构都有一系列的操作指令除此之外Redis 还有事务、集群、发布订阅、脚本等一系列相关的指令。为了方便以一种统一的风格和原则来设计和使用这些指令Redis 设计了 RESP即 Redis Serialization Protocol中文意思是 Redis 序列化协议。RESP 是二进制安全协议,可以供 Redis 或其他任何 Client-Server 使用。在 Redis 内部,还会基于 RESP 进一步扩展细节。
设计原则
Redis 序列化协议的设计原则有三个:
第一是实现简单;
第二是可快速解析;
第三是便于阅读。
Redis 协议的请求响应模型有三种,除了 2 种特殊模式,其他基本都是 ping-pong 模式,即 client 发送一个请求server 回复一个响应,一问一答的访问模式。
2 种特殊模式:
pipeline 模式,即 client 一次连续发送多个请求,然后等待 server 响应server 处理完请求后,把响应返回给 client。
pub/sub 模式。即发布订阅模式client 通过 subscribe 订阅一个 channel然后 client 进入订阅状态静静等待。当有消息产生时server 会持续自动推送消息给 client不需要 client 的额外请求。而且客户端在进入订阅状态后,只可接受订阅相关的命令如 SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE 和 PUNSUBSCRIBE除了这些命令其他命令一律失效。
Redis 协议的请求和响应也是有固定套路的。
对于请求指令,格式有 2 种类型。
当你没有 redis-client但希望可以用通用工具 telnet直接与 Redis 交互时Redis 协议虽然简单易于阅读,但在交互式会话中使用,并不容易拼写,此时可以用第一种格式,即 inline cmd 内联命令格式。使用 inline cmd 内联格式,只需要用空格分隔请求指令及参数,简单快速,一个简单的例子如 mget key1 key2\r\n。
第二种格式是 Array 数组格式类型。请求指令用的数组类型,与 Redis 响应的数组类型相同,后面在介绍响应格式类型时会详细介绍。
响应格式
Redis 协议的响应格式有 5 种,分别是:
simple strings 简单字符串类型,以 + 开头,后面跟字符串,以 CRLF即 \r\n结尾。这种类型不是二进制安全类型字符串中不能包含 \r 或者 \n。比如许多响应回复以 OK 作为操作成功的标志,协议内容就是 +OK\r\n 。
Redis 协议将错误作为一种专门的类型,格式同简单字符串类型,唯一不同的是以 -减号开头。Redis 内部实现对 Redis 协议做了进一步规范,减号后面一般先跟 ERR 或者 WRONGTYPE然后再跟其他简单字符串最后以 CRLF回车换行结束。这里给了两个示例client 在解析响应时,一旦发现 - 开头,就知道收到 Error 响应。
Integer 整数类型。整数类型以 开头后面跟字符串表示的数字最后以回车换行结尾。Redis 中许多命令都返回整数,但整数的含义要由具体命令来确定。比如,对于 incr 指令,:后的整数表示变更后的数值;对于 llen 表示 list 列表的长度,对于 exists 指令1 表示 key 存在0 表示 key 不存在。这里给个例子,:后面跟了个 1000然后回车换行结束。
bulk strings 字符串块类型。字符串块分头部和真正字符串内容两部分。字符串块类型的头部, 为 \( 开头,随后跟真正字符串内容的字节长度,然后以 CRLF 结尾。字符串块的头部之后,跟随真正的字符串内容,最后以 CRLF 结束字符串块。字符串块用于表示二进制安全的字符串,最大长度可以支持 512MB。一个常规的例子“\)6\r\nfoobar\r\n”对于空字串可以表示为 “\(0\r\n\r\n”NULL字串 “\)-1\r\n”。
Arrays 数组类型,如果一个命令需要返回多条数据就需要用数组格式类型,另外,前面提到 client 的请求命令也是主要采用这种格式。
Arrays 数组类型,以 * 开头,随后跟一个数组长度 N然后以回车换行结尾然后后面跟随 N 个数组元素,每个数组元素的类型,可以是 Redis 协议中除内联格式外的任何一种类型。
比如一个字符串块的数组实例,*2\r\n\(3\r\nget\r\n\)3\r\nkey\r\n。整数数组实例”*3\r\n:1\r\n:2\r\n:3\r\n”混合数组实例”*3\r\n :1\r\n-Bar\r\n$6\r\n foobar\r\n”空数组”0\r\n”NULL数组”-1\r\n”。
协议分类
Redis 协议主要分为 16 种,其中 8 种协议对应前面我们讲到的 8 种数据类型,你选择了使用什么数据类型,就使用对应的响应操作指令即可。剩下 8 种协议如下所示。
pub-sub 发布订阅协议client 可以订阅 channel持续等待 server 推送消息。
事务协议,事务协议可以用 multi 和 exec 封装一些列指令,来一次性执行。
脚本协议,关键指令是 eval、evalsha 和 script等。
连接协议,主要包括权限控制,切换 DB关闭连接等。
复制协议,包括 slaveof、role、psync 等。
配置协议config set/get 等,可以在线修改/获取配置。
调试统计协议,如 slowlogmonitorinfo 等。
其他内部命令,如 migratedumprestore 等。
Redis client 的使用及改进
由于 Redis 使用广泛,几乎所有主流语言都有对 Redis 开发了对应的 client。以 Java 语言为例,广泛使用的有 Jedis、Redisson 等。对于 Jedis client它的优势是轻量简洁便于集成和改造它支持连接池提供指令维度的操作几乎支持 Redis 的所有指令但它不支持读写分离。Redisson 基于 Netty 实现,非阻塞 IO性能较高而且支持异步请求和连接池还支持读写分离、读负载均衡它内建了 tomcat Session ,支持 spring session 集成,但 redisson 实现相对复杂。
在新项目启动时,如果只是简单的 Redis 访问业务场景,可以直接用 Jedis甚至可以简单封装 Jedis实现 master-slave 的读写分离方案。如果想直接使用读写分离,想集成 spring session 等这些高级特性,也可以采用 redisson。
Redis client 在使用中,需要根据业务及运维的需要,进行相关改进。在 client 访问异常时,可以增加重试策略,在访问某个 slave 异常时,需要重试其他 slave 节点。需要增加对 Redis 主从切换、slave 扩展的支持,比如采用守护线程定期扫描 master、slave 域名,发现 IP 变更,及时切换连接。对于多个 slave 的访问还需要增加负载均衡策略。最后Redis client 还可以与配置中心、Redis 集群管理平台整合,从而实时感知及协调 Redis 服务的访问。
至此,本节课的内容就讲完了。
在这几节课中,你首先学习了 Redis 的特性及基本原理,初步了解了 Redis 的数据类型、主进程/子进程、BIO 线程、持久化、复制、集群等;这些内容会在后续逐一深入学习。
然后,详细学习了 Redis 的数据类型了解了字符串、列表、集合、有序集合、哈希、位图、GEO 地理位置、HyperLogLog 基数统计,这 8 种核心数据类型的功能、特点、主要操作指令及应用场景。
接下来,你还熟悉了 Redis 协议,包括 Redis 协议的设计原则、三种响应模型2 种请求格式和 5 种响应格式。
最后,以 Java 语言为例,你还了解了 Redis client 的对比、选择及改进。
你可以参考这个思维导图,对这些知识点进行回顾和梳理。

View File

@ -0,0 +1,53 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
19 Redis系统架构中各个处理模块是干什么的
你好,我是你的缓存课老师陈波,欢迎进入第 19 课时“Redis 系统架构”的学习。
Redis 系统架构
通过前面的学习,相信你已经掌握了 Redis 的原理、数据类型及访问协议等内容。本课时,我将进一步分析 Redis 的系统架构,重点讲解 Redis 系统架构的事件处理机制、数据管理、功能扩展、系统扩展等内容。
事件处理机制
Redis 组件的系统架构如图所示,主要包括事件处理、数据存储及管理、用于系统扩展的主从复制/集群管理,以及为插件化功能扩展的 Module System 模块。
Redis 中的事件处理模块,采用的是作者自己开发的 ae 事件驱动模型,可以进行高效的网络 IO 读写、命令执行,以及时间事件处理。
其中,网络 IO 读写处理采用的是 IO 多路复用技术,通过对 evport、epoll、kqueue、select 等进行封装,同时监听多个 socket并根据 socket 目前执行的任务,来为 socket 关联不同的事件处理器。
当监听端口对应的 socket 收到连接请求后,就会创建一个 client 结构,通过 client 结构来对连接状态进行管理。在请求进入时,将请求命令读取缓冲并进行解析,并存入到 client 的参数列表。
然后根据请求命令找到 对应的redisCommand 最后根据命令协议对请求参数进一步的解析、校验并执行。Redis 中时间事件比较简单,目前主要是执行 serverCron来做一些统计更新、过期 key 清理、AOF 及 RDB 持久化等辅助操作。
数据管理
Redis 的内存数据都存在 redisDB 中。Redis 支持多 DB每个 DB 都对应一个 redisDB 结构。Redis 的 8 种数据类型,每种数据类型都采用一种或多种内部数据结构进行存储。同时这些内部数据结构及数据相关的辅助信息,都以 kye/value 的格式存在 redisDB 中的各个 dict 字典中。
数据在写入 redisDB 后,这些执行的写指令还会及时追加到 AOF 中追加的方式是先实时写入AOF 缓冲,然后按策略刷缓冲数据到文件。由于 AOF 记录每个写操作,所以一个 key 的大量中间状态也会呈现在 AOF 中,导致 AOF 冗余信息过多,因此 Redis 还设计了一个 RDB 快照操作,可以通过定期将内存里所有的数据快照落地到 RDB 文件,来以最简洁的方式记录 Redis 的所有内存数据。
Redis 进行数据读写的核心处理线程是单线程模型为了保持整个系统的高性能必须避免任何kennel 导致阻塞的操作。为此Redis 增加了 BIO 线程,来处理容易导致阻塞的文件 close、fsync 等操作,确保系统处理的性能和稳定性。
在 server 端存储内存永远是昂贵且短缺的Redis 中,过期的 key 需要及时清理,不活跃的 key 在内存不足时也可能需要进行淘汰。为此Redis 设计了 8 种淘汰策略,借助新引入的 eviction pool进行高效的 key 淘汰和内存回收。
功能扩展
Redis 在 4.0 版本之后引入了 Module System 模块,可以方便使用者,在不修改核心功能的同时,进行插件化功能开发。使用者可以将新的 feature 封装成动态链接库Redis 可以在启动时加载,也可以在运行过程中随时按需加载和启用。
在扩展模块中,开发者可以通过 RedisModule_init 初始化新模块,用 RedisModule_CreateCommand 扩展各种新模块指令,以可插拔的方式为 Redis 引入新的数据结构和访问命令。
系统扩展
Redis作者在架构设计中对系统的扩展也倾注了大量关注。在主从复制功能中psyn 在不断的优化,不仅在 slave 闪断重连后可以进行增量复制,而且在 slave 通过主从切换成为 master 后,其他 slave 仍然可以与新晋升的 master 进行增量复制,另外,其他一些场景,如 slave 重启后,也可以进行增量复制,大大提升了主从复制的可用性。使用者可以更方便的使用主从复制,进行业务数据的读写分离,大幅提升 Redis 系统的稳定读写能力。
通过主从复制可以较好的解决 Redis 的单机读写问题,但所有写操作都集中在 master 服务器,很容易达到 Redis 的写上限,同时 Redis 的主从节点都保存了业务的所有数据,随着业务发展,很容易出现内存不够用的问题。
为此Redis 分区无法避免。虽然业界大多采用在 client 和 proxy 端分区,但 Redis 自己也早早推出了 cluster 功能并不断进行优化。Redis cluster 预先设定了 16384 个 slot 槽,在 Redis 集群启动时,通过手动或自动将这些 slot 分配到不同服务节点上。在进行 key 读写定位时,首先对 key 做 hash并将 hash 值对 16383 ,做 按位与运算,确认 slot然后确认服务节点最后再对 对应的 Redis 节点,进行常规读写。如果 client 发送到错误的 Redis 分片Redis 会发送重定向回复。如果业务数据大量增加Redis 集群可以通过数据迁移,来进行在线扩容。

View File

@ -0,0 +1,116 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
20 Redis如何处理文件事件和时间事件
上一课时,我们学习了 Redis 的系统架构,接下来的几个课时我将带你一起对这些模块和设计进行详细分析。首先,我将分析 Redis 的事件驱动模型。
Redis 事件驱动模型
事件驱动模型
Redis 是一个事件驱动程序,但和 Memcached 不同的是Redis 并没有采用 libevent 或 libev 这些开源库而是直接开发了一个新的事件循环组件。Redis 作者给出的理由是尽量减少外部依赖而自己开发的事件模型也足够简洁、轻便、高效也更易控制。Redis 的事件驱动模型机制封装在 aeEventLoop 等相关的结构体中,网络连接、命令读取执行回复,数据的持久化、淘汰回收 key 等,几乎所有的核心操作都通过 ae 事件模型进行处理。
Redis 的事件驱动模型处理 2 类事件:
文件事件,如连接建立、接受请求命令、发送响应等;
时间事件,如 Redis 中定期要执行的统计、key 淘汰、缓冲数据写出、rehash等。
文件事件处理
Redis 的文件事件采用典型的 Reactor 模式进行处理。Redis 文件事件处理机制分为 4 部分:
连接 socket
IO 多路复用程序
文件事件分派器
事件处理器
文件事件是对连接 socket 操作的一个抽象。当端口监听 socket 准备 accept 新连接,或者连接 socket 准备好读取请求、写入响应、关闭时就会产生一个文件事件。IO 多路复用程序负责同时监听多个 socket当这些 socket 产生文件事件时,就会触发事件通知,文件分派器就会感知并获取到这些事件。
虽然多个文件事件可能会并发出现,但 IO 多路复用程序总会将所有产生事件的 socket 放入一个队列中,通过这个队列,有序的把这些文件事件通知给文件分派器。
IO多路复用
Redis 封装了 4 种多路复用程序,每种封装实现都提供了相同的 API 实现。编译时,会按照性能和系统平台,选择最佳的 IO 多路复用函数作为底层实现,选择顺序是,首先尝试选择 Solaries 中的 evport如果没有就尝试选择 Linux 中的 epoll否则就选择大多 UNIX 系统都支持的 kqueue这 3 个多路复用函数都直接使用系统内核内部的结构,可以服务数十万的文件描述符。
如果当前编译环境没有上述函数,就会选择 select 作为底层实现方案。select 方案的性能较差,事件发生时,会扫描全部监听的描述符,事件复杂度是 O(n)并且只能同时服务有限个文件描述符32 位机默认是 1024 个64 位机默认是 2048 个,所以一般情况下,并不会选择 select 作为线上运行方案。Redis 的这 4 种实现,分别在 ae_evport、ae_epoll、ae_kqueue 和 ae_select 这 4 个代码文件中。
文件事件收集及派发器
Redis 中的文件事件分派器是 aeProcessEvents 函数。它会首先计算最大可以等待的时间,然后利用 aeApiPoll 等待文件事件的发生。如果在等待时间内,一旦 IO 多路复用程序产生了事件通知,则会立即轮询所有已产生的文件事件,并将文件事件放入 aeEventLoop 中的 aeFiredEvents 结构数组中。每个 fired event 会记录 socket 及 Redis 读写事件类型。
这里会涉及将多路复用中的事件类型,转换为 Redis 的 ae 事件驱动模型中的事件类型。以采用 Linux 中的 epoll 为例,会将 epoll 中的 EPOLLIN 转为 AE_READABLE 类型,将 epoll 中的 EPOLLOUT、EPOLLERR 和 EPOLLHUP 转为 AE_WRITABLE 事件。
aeProcessEvents 在获取到触发的事件后,会根据事件类型,将文件事件 dispatch 派发给对应事件处理函数。如果同一个 socket同时有读事件和写事件Redis 派发器会首先派发处理读事件,然后再派发处理写事件。
文件事件处理函数分类
Redis 中文件事件函数的注册和处理主要分为 3 种。
连接处理函数 acceptTcpHandler
Redis 在启动时,在 initServer 中对监听的 socket 注册读事件,事件处理器为 acceptTcpHandler该函数在有新连接进入时会被派发器派发读任务。在处理该读任务时会 accept 新连接,获取调用方的 IP 及端口,并对新连接创建一个 client 结构。如果同时有大量连接同时进入Redis 一次最多处理 1000 个连接请求。
readQueryFromClient 请求处理函数
连接函数在创建 client 时,会对新连接 socket 注册一个读事件,该读事件的事件处理器就是 readQueryFromClient。在连接 socket 有请求命令到达时IO 多路复用程序会获取并触发文件事件然后这个读事件被派发器派发给本请求的处理函数。readQueryFromClient 会从连接 socket 读取数据,存入 client 的 query 缓冲,然后进行解析命令,按照 Redis 当前支持的 2 种请求格式,及 inline 内联格式和 multibulk 字符块数组格式进行尝试解析。解析完毕后client 会根据请求命令从命令表中获取到对应的 redisCommand如果对应 cmd 存在。则开始校验请求的参数,以及当前 server 的内存、磁盘及其他状态,完成校验后,然后真正开始执行 redisCommand 的处理函数,进行具体命令的执行,最后将执行结果作为响应写入 client 的写缓冲中。
命令回复处理器 sendReplyToClient
当 redis需要发送响应给client时Redis 事件循环中会对client的连接socket注册写事件这个写事件的处理函数就是sendReplyToClient。通过注册写事件将 client 的socket与 AE_WRITABLE 进行间接关联。当 Client fd 可进行写操作时,就会触发写事件,该函数就会将写缓冲中的数据发送给调用方。
Redis 中的时间事件是指需要在特定时间执行的事件。多个 Redis 中的时间事件构成 aeEventLoop 中的一个链表,供 Redis 在 ae 事件循环中轮询执行。
Redis 当前的主要时间事件处理函数有 2 个:
serverCron
moduleTimerHandler
Redis 中的时间事件分为 2 类:
单次时间,即执行完毕后,该时间事件就结束了。
周期性事件,在事件执行完毕后,会继续设置下一次执行的事件,从而在时间到达后继续执行,并不断重复。
时间事件主要有 5 个属性组成。
事件 IDRedis 为时间事件创建全局唯一 ID该 ID 按从小到大的顺序进行递增。
执行时间 when_sec 和 when_ms精确到毫秒记录该事件的到达可执行时间。
时间事件处理器 timeProc在时间事件到达时Redis 会调用相应的 timeProc 处理事件。
关联数据 clientData在调用 timeProc 时,需要使用该关联数据作为参数。
链表指针 prev 和 next它用来将时间事件维护为双向链表便于插入及查找所要执行的时间事件。
时间事件的处理是在事件循环中的 aeProcessEvents 中进行。执行过程是:
首先遍历所有的时间事件。
比较事件的时间和当前时间,找出可执行的时间事件。
然后执行时间事件的 timeProc 函数。
执行完毕后,对于周期性时间,设置时间新的执行时间;对于单次性时间,设置事件的 ID为 -1后续在事件循环中下一次执行 aeProcessEvents 的时候从链表中删除。

View File

@ -0,0 +1,31 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
21 Redis读取请求数据后如何进行协议解析和处理
你好,我是你的缓存课老师陈波,欢迎进入第 21 课时“Redis 协议解析及处理”的学习。上一课时,我们学习了 Redis 事件驱动模型,接下来,看一下 Redis 是如何进行协议解析及处理的。
Redis 协议解析及处理
协议解析
上一课时讲到,请求命令进入,触发 IO 读事件后。client 会从连接文件描述符读取请求,并存入 client 的 query buffer 中。client 的读缓冲默认是 16KB读取命令时如果发现请求超过 1GB则直接报异常关闭连接。
client 读取完请求命令后,则根据 query buff 进行协议解析。协议解析时,首先查看协议的首字符。如果是 *,则解析为字符块数组类型,即 MULTIBULK。否则请求解析为 INLINE 类型。
INLINE 类型是以 CRLF 结尾的单行字符串,协议命令及参数以空格分隔。解析过程参考之前课程里分析的对应协议格式。协议解析完毕后,将请求参数个数存入 client 的 argc 中,将请求的具体参数存入 client 的 argv 中。
协议执行
请求命令解析完毕,则进入到协议执行部分。协议执行中,对于 quit 指令,直接返回 OK设置 flag 为回复后关闭连接。
对于非 quit 指令,以 client 中 argv[0] 作为命令,从 server 中的命令表中找到对应的 redisCommand。如果没有找到 redisCommand则返回未知 cmd 异常。如果找到 cmd则开始执行 redisCommand 中的 proc 函数,进行具体命令的执行。在命令执行完毕后,将响应写入 client 的写缓冲。并按配置和部署,将写指令分发给 aof 和 slaves。同时更新相关的统计数值。

View File

@ -0,0 +1,37 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
25 Redis是如何处理容易超时的系统调用的
本课时我们主要学习通过 BIO 线程解决处理容易超时的系统调用问题,以及 BIO 线程处理的任务与处理流程等内容。
BIO 线程简介
Redis 在运行过程中,不可避免的会产生一些运行慢的、容易引发阻塞的任务,如将内核中的文件缓冲同步到磁盘中、关闭文件,都会引发短时阻塞,还有一些大 key如一些元素数高达万级或更多的聚合类元素在删除时由于所有元素需要逐一释放回收整个过程耗时也会比较长。而 Redis 的核心处理线程是单进程单线程模型所有命令的接受与处理、数据淘汰等都在主线程中进行这些任务处理速度非常快。如果核心单线程还要处理那些慢任务在处理期间势必会阻塞用户的正常请求导致服务卡顿。为此Redis 引入了 BIO 后台线程,专门处理那些慢任务,从而保证和提升主线程的处理能力。
Redis 的 BIO 线程采用生产者-消费者模型。主线程是生产者生产各种慢任务然后存放到任务队列中。BIO 线程是消费者,从队列获取任务并进行处理。如果生产者生产任务过快,队列可用于缓冲这些任务,避免负荷过载或数据丢失。如果消费者处理速度很快,处理完毕后就可以安静的等待,不增加额外的性能开销。再次,有新任务时,主线程通过条件变量来通知 BIO 线程,这样 BIO 线程就可以再次执行任务。
BIO 处理任务
Redis 启动时,会创建三个任务队列,并对应构建 3 个 BIO 线程,三个 BIO 线程与 3 个任务队列之间一一对应。BIO 线程分别处理如下 3 种任务。
close 关闭文件任务。rewriteaof 完成后,主线程需要关闭旧的 AOF 文件,就向 close 队列插入一个旧 AOF 文件的关闭任务。由 close 线程来处理。
fysnc 任务。Redis 将 AOF 数据缓冲写入文件内核缓冲后,需要定期将系统内核缓冲数据写入磁盘,此时可以向 fsync 队列写入一个同步文件缓冲的任务,由 fsync 线程来处理。
lazyfree 任务。Redis 在需要淘汰元素数大于 64 的聚合类数据类型时,如列表、集合、哈希等,就往延迟清理队列中写入待回收的对象,由 lazyfree 线程后续进行异步回收。
BIO 处理流程
BIO 线程的整个处理流程如图所示。当主线程有慢任务需要异步处理时。就会向对应的任务队列提交任务。提交任务时,首先申请内存空间,构建 BIO 任务。然后对队列锁进行加锁,在队列尾部追加新的 BIO 任务,最后尝试唤醒正在等待任务的 BIO 线程。
BIO 线程启动时或持续处理完所有任务,发现任务队列为空后,就会阻塞,并等待新任务的到来。当主线程有新任务后,主线程会提交任务,并唤醒 BIO 线程。BIO 线程随后开始轮询获取新任务,并进行处理。当处理完所有 BIO 任务后,则再次进入阻塞,等待下一轮唤醒。

View File

@ -0,0 +1,43 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
26 如何大幅成倍提升Redis处理性能
本课时我们主要学习如何通过 Redis 多线程来大幅提升性能,涉及主线程与 IO 线程、命令处理流程,以及多线程方案的优劣等内容。
主线程
Redis 自问世以来,广受好评,应用广泛。但相比, Memcached 单实例压测 TPS 可以高达百万,线上可以稳定跑 20~40 万而言Redis 的单实例压测 TPS 不过 10~12 万,线上一般最高也就 2~4 万,仍相差一个数量级。
Redis 慢的主要原因是单进程单线程模型。虽然一些重量级操作也进行了分拆,如 RDB 的构建在子进程中进行,文件关闭、文件缓冲同步,以及大 key 清理都放在 BIO 线程异步处理,但还远远不够。线上 Redis 处理用户请求时,十万级的 client 挂在一个 Redis 实例上,所有的事件处理、读请求、命令解析、命令执行,以及最后的响应回复,都由主线程完成,纵然是 Redis 各种极端优化,巧妇难为无米之炊,一个线程的处理能力始终是有上限的。当前服务器 CPU 大多是 16 核到 32 核以上Redis 日常运行主要只使用 1 个核心,其他 CPU 核就没有被很好的利用起来Redis 的处理性能也就无法有效地提升。而 Memcached 则可以按照服务器的 CPU 核心数,配置数十个线程,这些线程并发进行 IO 读写、任务处理,处理性能可以提高一个数量级以上。
IO 线程
面对性能提升困境,虽然 Redis 作者不以为然,认为可以通过多部署几个 Redis 实例来达到类似多线程的效果。但多实例部署则带来了运维复杂的问题,而且单机多实例部署,会相互影响,进一步增大运维的复杂度。为此,社区一直有种声音,希望 Redis 能开发多线程版本。
因此Redis 即将在 6.0 版本引入多线程模型,当前代码在 unstable 版本中6.0 版本预计在明年发版。Redis 的多线程模型,分为主线程和 IO 线程。
因为处理命令请求的几个耗时点,分别是请求读取、协议解析、协议执行,以及响应回复等。所以 Redis 引入 IO 多线程并发地进行请求命令的读取、解析以及响应的回复。而其他的所有任务如事件触发、命令执行、IO 任务分发,以及其他各种核心操作,仍然在主线程中进行,也就说这些任务仍然由单线程处理。这样可以在最大程度不改变原处理流程的情况下,引入多线程。
命令处理流程
Redis 6.0 的多线程处理流程如图所示。主线程负责监听端口,注册连接读事件。当有新连接进入时,主线程 accept 新连接,创建 client并为新连接注册请求读事件。
当请求命令进入时,在主线程触发读事件,主线程此时并不进行网络 IO 的读取,而将该连接所在的 client 加入待读取队列中。Redis 的 Ae 事件模型在循环中,发现待读取队列不为空,则将所有待读取请求的 client 依次分派给 IO 线程,并自旋检查等待,等待 IO 线程读取所有的网络数据。所谓自旋检查等待,也就是指主线程持续死循环,并在循环中检查 IO 线程是否读完,不做其他任何任务。只有发现 IO 线程读完所有网络数据,才停止循环,继续后续的任务处理。
一般可以配置多个 IO 线程,比如配置 4~8 个,这些 IO 线程发现待读取队列中有任务时,则开始并发处理。每个 IO 线程从对应列表获取一个任务,从里面的 client 连接中读取请求数据,并进行命令解析。当 IO 线程完成所有的请求读取,并完成解析后,待读取任务数变为 0。主线程就停止循环检测开始依次执行 IO 线程已经解析的所有命令,每执行完毕一个命令,就将响应写入 client 写缓冲,这些 client 就变为待回复 client这些待回复 client 被加入待回复列表。然后主线程将这些待回复 client轮询分配给多个 IO 线程。然后再次自旋检测等待。
然后 IO 线程再次开始并发执行,将不同 client 的响应缓冲写给 client。当所有响应全部处理完后待回复的任务数变为 0主线程结束自旋检测继续处理后续的任务以及新的读请求。
Redis 6.0 版本中新引入的多线程模型,主要是指可配置多个 IO 线程,这些线程专门负责请求读取、解析,以及响应的回复。通过 IO 多线程Redis 的性能可以提升 1 倍以上。
多线程方案优劣
虽然多线程方案能提升1倍以上的性能但整个方案仍然比较粗糙。首先所有命令的执行仍然在主线程中进行存在性能瓶颈。然后所有的事件触发也是在主线程中进行也依然无法有效使用多核心。而且IO 读写为批处理读写,即所有 IO 线程先一起读完所有请求,待主线程解析处理完毕后,所有 IO 线程再一起回复所有响应,不同请求需要相互等待,效率不高。最后在 IO 批处理读写时,主线程自旋检测等待,效率更是低下,即便任务很少,也很容易把 CPU 打满。整个多线程方案比较粗糙,所以性能提升也很有限,也就 1~2 倍多一点而已。要想更大幅提升处理性能,命令的执行、事件的触发等都需要分拆到不同线程中进行,而且多线程处理模型也需要优化,各个线程自行进行 IO 读写和执行,互不干扰、等待与竞争,才能真正高效地利用服务器多核心,达到性能数量级的提升。

View File

@ -0,0 +1,53 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
27 Redis是如何进行主从复制的
本课时我们主要学习 Redis 复制原理,以及复制分析等内容。
Redis 复制原理
为了避免单点故障,数据存储需要进行多副本构建。同时由于 Redis 的核心操作是单线程模型的,单个 Redis 实例能处理的请求 TPS 有限。因此 Redis 自面世起,基本就提供了复制功能,而且对复制策略不断进行优化。
通过数据复制Redis 的一个 master 可以挂载多个 slave而 slave 下还可以挂载多个 slave形成多层嵌套结构。所有写操作都在 master 实例中进行master 执行完毕后,将写指令分发给挂在自己下面的 slave 节点。slave 节点下如果有嵌套的 slave会将收到的写指令进一步分发给挂在自己下面的 slave。通过多个 slaveRedis 的节点数据就可以实现多副本保存,任何一个节点异常都不会导致数据丢失,同时多 slave 可以 N 倍提升读性能。master 只写不读,这样整个 master-slave 组合,读写能力都可以得到大幅提升。
master 在分发写请求时,同时会将写指令复制一份存入复制积压缓冲,这样当 slave 短时间断开重连时,只要 slave 的复制位置点仍然在复制积压缓冲,则可以从之前的复制位置点之后继续进行复制,提升复制效率。
主库 master 和从库 slave 之间通过复制 id 进行匹配,避免 slave 挂到错误的 master。Redis 的复制分为全量同步和增量同步。Redis 在进行全量同步时master 会将内存数据通过 bgsave 落地到 rdb同时将构建 内存快照期间 的写指令,存放到复制缓冲中,当 rdb 快照构建完毕后master 将 rdb 和复制缓冲队列中的数据全部发送给 slaveslave 完全重新创建一份数据。这个过程,对 master 的性能损耗较大slave 构建数据的时间也比较长,而且传递 rdb 时还会占用大量带宽对整个系统的性能和资源的访问影响都比较大。而增量复制master 只发送 slave 上次复制位置之后的写指令,不用构建 rdb而且传输内容非常有限对 master、slave 的负荷影响很小,对带宽的影响可以忽略,整个系统受影响非常小。
在 Redis 2.8 之前Redis 基本只支持全量复制。在 slave 与 master 断开连接,或 slave 重启后,都需要进行全量复制。在 2.8 版本之后Redis 引入 psync增加了一个复制积压缓冲在将写指令同步给 slave 时,会同时在复制积压缓冲中也写一份。在 slave 短时断开重连后上报master runid 及复制偏移量。如果 runid 与 master 一致,且偏移量仍然在 master 的复制缓冲积压中,则 master 进行增量同步。
但如果 slave 重启后master runid 会丢失,或者切换 master 后runid 会变化,仍然需要全量同步。因此 Redis 自 4.0 强化了 psync引入了 psync2。在 pysnc2 中,主从复制不再使用 runid而使用 replid即复制id 来作为复制判断依据。同时 Redis 实例在构建 rdb 时,会将 replid 作为 aux 辅助信息存入 rbd。重启时加载 rdb 时即可得到 master 的复制 id。从而在 slave 重启后仍然可以增量同步。
在 psync2 中Redis 每个实例除了会有一个复制 id 即 replid 外,还有一个 replid2。Redis 启动后,会创建一个长度为 40 的随机字符串,作为 replid 的初值,在建立主从连接后,会用 master的 replid 替换自己的 replid。同时会用 replid2 存储上次 master 主库的 replid。这样切主时即便 slave 汇报的复制 id 与新 master 的 replid 不同,但和新 master 的 replid2 相同,同时复制偏移仍然在复制积压缓冲区内,仍然可以实现增量复制。
Redis 复制分析
在设置 master、slave 时,首先通过配置或者命令 slaveof no one 将节点设置为主库。然后其他各个从库节点,通过 slaveof \(master_ip \)master_port将其他从库挂在到 master 上。同样方法,还可以将 slave 节点挂载到已有的 slave 节点上。在准备开始数据复制时slave 首先会主动与 master 创建连接,并上报信息。具体流程如下。
slave 创建与 master 的连接后,首先发送 ping 指令,如果 master 没有返回异常,而是返回 pong则说明 master 可用。如果 Redis 设置了密码slave 会发送 auth $masterauth 指令,进行鉴权。当鉴权完毕,从库就通过 replconf 发送自己的端口及 IP 给 master。接下来slave 继续通过 replconf 发送 capa eof capa psync2 进行复制版本校验。如果 master 校验成功。从库接下来就通过 psync 将自己的复制 id、复制偏移发送给 master正式开始准备数据同步。
主库接收到从库发来的 psync 指令后则开始判断可以进行数据同步的方式。前面讲到Redis 当前保存了复制 idreplid 和 replid2。如果从库发来的复制 id与 master 的复制 id即 replid 和 replid2相同并且复制偏移在复制缓冲积压中则可以进行增量同步。master 发送 continue 响应,并返回 master 的 replid。slave 将 master 的 replid 替换为自己的 replid并将之前的复制 id 设置为 replid2。之后master 则可继续发送,复制偏移位置 之后的指令,给 slave完成数据同步。
如果主库发现从库传来的复制 id 和自己的 replid、replid2 都不同或者复制偏移不在复制积压缓冲中则判定需要进行全量复制。master 发送 fullresync 响应,附带 replid 及复制偏移。然后, master 根据需要构建 rdb并将 rdb 及复制缓冲发送给 slave。
对于增量复制slave 接下来就等待接受 master 传来的复制缓冲及新增的写指令,进行数据同步。
而对于全量同步slave 会首先进行,嵌套复制的清理工作,比如 slave 当前还有嵌套的 子slave则该 slave 会关闭嵌套 子slave 的所有连接并清理自己的复制积压缓冲。然后slave 会构建临时 rdb 文件,并从 master 连接中读取 rdb 的实际数据,写入 rdb 中。在写 rdb 文件时,每写 8M就会做一个 fsync操作 刷新文件缓冲。当接受 rdb 完毕则将 rdb 临时文件改名为 rdb 的真正名字。
接下来slave 会首先清空老数据,即删除本地所有 DB 中的数据,并暂时停止从 master 继续接受数据。然后slave 就开始全力加载 rdb 恢复数据,将数据从 rdb 加载到内存。在 rdb 加载完毕后slave 重新利用与 master 的连接 socket创建与 master 连接的 client并在此注册读事件可以开始接受 master 的写指令了。此时slave 还会将 master 的 replid 和复制偏移设为自己的复制 id 和复制偏移 offset并将自己的 replid2 清空因为slave 的所有嵌套 子slave 接下来也需要进行全量复制。最后slave 就会打开 aof 文件,在接受 master 的写指令后,执行完毕并写入到自己的 aof 中。
相比之前的 syncpsync2 优化很明显。在短时间断开连接、slave 重启、切主等多种场景只要延迟不太久复制偏移仍然在复制积压缓冲均可进行增量同步。master 不用构建并发送巨大的 rdb可以大大减轻 master 的负荷和网络带宽的开销。同时slave 可以通过轻量的增量复制,实现数据同步,快速恢复服务,减少系统抖动。
但是psync 依然严重依赖于复制缓冲积压,太大会占用过多内存,太小会导致频繁的全量复制。而且,由于内存限制,即便设置相对较大的复制缓冲区,在 slave 断开连接较久时,仍然很容易被复制缓冲积压冲刷,从而导致全量复制。

View File

@ -0,0 +1,135 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
28 如何构建一个高性能、易扩展的Redis集群
通过上一课时的学习,我们知道复制功能可以 N 倍提升 Redis 节点的读性能,而集群则可以通过分布式方案来 N 倍提升 Redis 的写性能。除了提升性能之外Redis 集群还可以提供更大的容量,提升资源系统的可用性。
Redis 集群的分布式方案主要有 3 种。分别是 Client 端分区方案Proxy 分区方案,以及原生的 Redis Cluster 分区方案。
Client 端分区
Client 端分区方案就是由 Client 决定数据被存储到哪个 Redis 分片,或者由哪个 Redis 分片来获取数据。它的核心思想是通过哈希算法将不同的 key 映射到固定的 Redis 分片节点上。对于单个 key 请求Client 直接对 key 进行哈希后,确定 Redis 分片,然后进行请求。而对于一个请求附带多个 key 的场景Client 会首先将这些 key 按哈希分片进行分类,从而将一个请求分拆为多个请求,然后再分别请求不同的哈希分片节点。
Client 通过哈希算法将数据进行分布,一般采用的哈希算法是取模哈希、一致性哈希和区间分布哈希。前两种哈希算法之前的课程已有详细分析,此处不在赘述。对于区间分布哈希,实际是一种取模哈希的变种,取模哈希是哈希并取模计算后,按哈希值来分配存储节点,而区间哈希是在哈希计算后,将哈希划分为多个区间,然后将这些区间分配给存储节点。如哈希后分 1024 个哈希点,然后将 0~511 作为分片 1将 512~1023 作为分片 2。
对于 Client 端分区,由于 Redis 集群有多个 master 分片,同时每个 master 下挂载多个 slave每个 Redis 节点都有独立的 IP 和端口。如果 master 异常需要切换 master或读压力过大需要扩展新的 slave这些都会涉及集群存储节点的变更需要 Client 端做连接切换。
为了避免 Client 频繁变更 IP 列表,可以采用 DNS 的方式来管理集群的主从。对 Redis 集群的每个分片的主和从均采用不同 DNS 域名。Client 通过域名解析的方式获取域名下的所有 IP然后来访问集群节点。由于每个分片 master 下有多个 slaveClient 需要在多个 slave 之间做负载均衡。可以按照权重建立与 slave 之间的连接,然后访问时,轮询使用这些连接依次访问,即可实现按权重访问 slave 节点。
在 DNS 访问模式下Client 需要异步定时探测主从域名,如果发现 IP 变更,及时与新节点建立连接,并关闭老连接。这样在主库故障需要切换时,或者从库需要增加减少时,任何分片的主从变化,只需运维或管理进程改一下 DNS 下的 IP 列表,业务 Client 端不需要做任何配置变更,即可正常切换访问。
Client 端分区方案的优点在于分区逻辑简单配置简单Client 节点之间和 Redis 节点之间均无需协调,灵活性强。而且 Client 直接访问对应 Redis 节点,没有额外环节,性能高效。但该方案扩展不便。在 Redis 端,只能成倍扩展,或者预先分配足够多的分片。在 Client 端,每次分片后,业务端需要修改分发逻辑,并进行重启。
Proxy 端分区
Proxy 端分区方案是指 Client 发送请求给 Proxy 请求代理组件Proxy 解析 Client 请求,并将请求分发到正确的 Redis 节点,然后等待 Redis 响应,最后再将结果返回给 Client 端。
如果一个请求包含多个 keyProxy 需要将请求的多个 key按分片逻辑分拆为多个请求然后分别请求不同的 Redis 分片接下来等待Redis响应在所有的分拆响应到达后再进行聚合组装最后返回给 Client。在整个处理过程中Proxy 代理首先要负责接受请求并解析,然后还要对 key 进行哈希计算及请求路由,最后还要将结果进行读取、解析及组装。如果系统运行中,主从变更或发生扩缩容,也只需由 Proxy 变更完成,业务 Client 端基本不受影响。
常见的 Proxy 端分区方案有 2 种,第一种是基于 Twemproxy 的简单分区方案第二种是基于Codis 的可平滑数据迁移的分区方案。
Twemproxy 是 Twitter 开源的一个组件,支持 Redis 和 Memcached 协议访问的代理组件。在讲分布式 Memecached 实战时我曾经详细介绍了它的原理和实现架构此处不再赘述。总体而言Twemproxy 实现简单、稳定性高,在一些访问量不大且很少发生扩缩的业务场景中,可以很好的满足需要。但由于 Twemproxy 是单进程单线程模型的,对包含多个 key 的 mutli 请求,由于需要分拆请求,然后再等待聚合,处理性能较低。而且,在后端 Redis 资源扩缩容,即增加或减少分片时,需要修改配置并重启,无法做到平滑扩缩。而且 Twemproxy 方案默认只有一个代理组件,无管理后端,各种运维变更不够便利。
而 Codis 是一个较为成熟的分布式 Redis 解决方案。对于业务 Client 访问,连接 Codis-proxy 和连接单个 Redis 几乎没有区别。Codis 底层除了会自动解析分发请求之外,还可以在线进行数据迁移,使用非常方便。
Codis 系统主要由 Codis-server、Codis-proxy、Codis-dashboard、Zookeeper 等组成。
Codis-server 是 Codis 的存储组件,它是基于 Redis 的扩展,增加了 slot 支持和数据迁移功能,所有数据存储在预分配的 1024 个 slot 中,可以按 slot 进行同步或异步数据迁移。
Codis-proxy 处理 Client 请求,解析业务请求,并路由给后端的 Codis-server group。Codis 的每个 server group 相当于一个 Redis 分片,由 1 个 master 和 N 个从库组成。
Zookeeper 用于存储元数据,如 Proxy 的节点,以及数据访问的路由表。除了 ZookeeperCodis 也支持 etcd 等其他组件,用于元数据的存储和通知。
Codis-dashboard 是 Codis 的管理后台可用于管理数据节点、Proxy 节点的加入或删除还可用于执行数据迁移等操作。Dashboard 的各项变更指令通过 Zookeeper 进行分发。
Codis 提供了功能较为丰富的管理后台,可以方便的对整个集群进行监控及运维。
Proxy 端分区方案的优势,是 Client 访问逻辑和 Redis 分布逻辑解耦,业务访问便捷简单。在资源发生变更或扩缩容时,只用修改数量有限的 Proxy 即可,数量庞大的业务 Client 端不用做调整。
但 Proxy 端分区的方案,访问时请求需要经过 Proxy 中转,访问多跳了一级,性能会存在损耗,一般损耗会达到 5~15% 左右。另外多了一个代理层,整个系统架构也会更复杂。
Redis Cluster 分区
Redis 社区版在 3.0 后开始引入 Cluster 策略,一般称之为 Redis-Cluster 方案。Redis-Cluster 按 slot 进行数据的读写和管理,一个 Redis-Cluster 集群包含 16384 个 slot。每个 Redis 分片负责其中一部分 slot。在集群启动时按需将所有 slot 分配到不同节点,在集群系统运行后,按 slot 分配策略,将 key 进行 hash 计算,并路由到对应节点 访问。
随着业务访问模型的变化Redis 部分节点可能会出现压力过大、访问不均衡的现象,此时可以将 slot 在 Redis 分片节点内部进行迁移以均衡访问。如果业务不断发展数据量过大、TPS过高还可以将 Redis 节点的部分 slot 迁移到新节点,增加 Redis-Cluster 的分片,对整个 Redis 资源进行扩容,已提升整个集群的容量及读写能力。
在启动 Redis 集群时,在接入数据读写前,可以通过 Redis 的 Cluster addslots 将 16384 个 slot 分配给不同的 Redis 分片节点,同时可以用 Cluster delslots 去掉某个节点的 slot用 Cluster flushslots 清空某个节点的所有 slot 信息,来完成 slot 的调整。
Redis Cluster 是一个去中心化架构,每个节点记录全部 slot 的拓扑分布。这样 Client 如果把 key 分发给了错误的 Redis 节点Redis 会检查请求 key 所属的 slot如果发现 key 属于其他节点的 slot会通知 Client 重定向到正确的 Redis 节点访问。
Redis Cluster 下的不同 Redis 分片节点通过 gossip 协议进行互联,使用 gossip 的优势在于该方案无中心控制节点这样更新不会受到中心节点的影响可以通过通知任意一个节点来进行管理通知。不足就是元数据的更新会有延时集群操作会在一定的时延后才会通知到所有Redis。由于 Redis Cluster 采用 gossip 协议进行服务节点通信,所以在进行扩缩容时,可以向集群内任何一个节点,发送 Cluster meet 指令将新节点加入集群然后集群节点会立即扩散新节点到整个集群。meet 新节点操作的扩散,只需要有一条节点链能到达集群各个节点即可,无需 meet 所有集群节点,操作起来比较便利。
在 Redis-Cluster 集群中key 的访问需要 smart client 配合。Client 首先发送请求给 Redis 节点Redis 在接受并解析命令后,会对 key 进行 hash 计算以确定 slot 槽位。计算公式是对 key 做 crc16 哈希,然后对 16383 进行按位与操作。如果 Redis 发现 key 对应的 slot 在本地,则直接执行后返回结果。
如果 Redis 发现 key 对应的 slot 不在本地,会返回 moved 异常响应,并附带 key 的 slot以及该 slot 对应的正确 Redis 节点的 host 和 port。Client 根据响应解析出正确的节点 IP 和端口,然后把请求重定向到正确的 Redis即可完成请求。为了加速访问Client 需要缓存 slot 与 Redis 节点的对应关系,这样可以直接访问正确的节点,以加速访问性能。
Redis-Cluster 提供了灵活的节点扩缩容方案,可以在不影响用户访问的情况下,动态为集群增加节点扩容,或下线节点为集群缩容。由于扩容在线上最为常见,我首先来分析一下 Redis-Cluster 如何进行扩容操作。
在准备对 Redis 扩容时,首先准备待添加的新节点,部署 Redis配置 cluster-enable 为 true并启动。然后运维人员通过client连接上一个集群内的 Redis 节点,通过 cluster meet 命令将新节点加入到集群,该节点随后会通知集群内的其他节点,有新节点加入。因为新加入的节点还没有设置任何 slot所以不接受任何读写操作。
然后,将通过 cluster setslot \(slot importing 指令,在新节点中,将目标 slot 设为 importing 导入状态。再将 slot 对应的源节点,通过 cluster setslot \)slot migrating 将源节点的 slot 设为 migrating 迁移导出状态。
接下来,就从源节点获取待迁移 slot 的 key通过 cluster getkeysinslot \(slot \)count 命令,从 slot 中获取 N 个待迁移的 key。然后通过 migrate 指令,将这些 key 依次逐个迁移或批量一次迁移到目标新节点。对于迁移单个 key使用指令 migrate \(host \)port \(key \)dbid timeout如果一次迁移多个 key在指令结尾加上 keys 选项,同时将多个 key 放在指令结尾即可。持续循环前面 2 个步骤,不断获取 slot 里的 key然后进行迁移最终将 slot 下的所有数据都迁移到目标新节点。最后通过 cluster setslot 指令将这个 slot 指派给新增节点。setslot 指令可以发给集群内的任意一个节点这个节点会将这个指派信息扩散到整个集群。至此slot 就迁移到了新节点。如果要迁移多个 slot可以继续前面的迁移步骤最终将所有需要迁移的 slot 数据搬到新节点。
这个新迁移 slot 的节点属于主库,对于线上应用,还需要增加从库,以增加读写能力及可用性,否则一旦主库崩溃,整个分片的数据就无法访问。在节点上增加从库,需要注意的是,不能使用非集群模式下的 slaveof 指令,而要使用 cluster replication才能完成集群分片节点下的 slave 添加。另外对于集群模式slave 只能挂在分片 master 上slave 节点自身不能再挂载 slave。
缩容流程与扩容流程类似,只是把部分节点的 slot 全部迁移走,然后把这些没有 slot 的节点进行下线处理。在下线老节点之前,需要注意,要用 cluster forget 通知集群集群节点要从节点信息列表中将目标节点移除同时会将该节点加入到禁止列表1 分钟之内不允许再加入集群。以防止在扩散下线节点时,又被误加入集群。
Redis 社区官方在源代码中也提供了 redis-trib.rb作为 Redis Cluster 的管理工具。该工具用 Ruby 开发所以在使用前需要安装相关的依赖环境。redis-trib 工具通过封装前面所述的 Redis 指令,从而支持创建集群、检查集群、添加删除节点、在线迁移 slot 等各种功能。
Redis Cluster 在 slot 迁移过程中获取key指令以及迁移指令逐一发送并执行不影响 Client 的正常访问。但在迁移单条或多条 key 时Redis 节点是在阻塞状态下进行的也就是说Redis 在迁移 key 时,一旦开始执行迁移指令,就会阻塞,直到迁移成功或确认失败后,才会停止该 key 的迁移从而继续处理其他请求。slot 内的 key 迁移是通过 migrate 指令进行的。
在源节点接收到 migrate \(host \)port \(key \)destination-db 的指令后,首先 slot 迁移的源节点会与迁移的目标节点建立 socket 连接,第一次迁移,或者迁移过程中,当前待迁移的 DB 与前一次迁移的 DB 不同,在迁移数据前,还需要发送 select $dbid 进行切换到正确的 DB。
然后,源节点会轮询所有待迁移的 key/value。获取 key 的过期时间,并将 value 进行序列化,序列化过程就是将 value 进行 dump转换为类 rdb 存储的二进制格式。这个二进制格式分 3 部分。第一部分是 value 对象的 type。第二部分是 value 实际的二进制数据;第三部分是当前 rdb 格式的版本,以及该 value 的 CRC64 校验码。至此,待迁移发送的数据准备完毕,源节点向目标节点,发送 restore-asking 指令将过期时间、key、value 的二进制数据发送给目标节点。然后同步等待目标节点的响应结果。
目标节点对应的client收到指令后如果有 select 指令,就首先切换到正确的 DB。接下来读取并处理 resotre-asking 指令,处理 restore-asking 指令时,首先对收到的数据进行解析校验,获取 key 的 ttl校验 rdb 版本及 value 数据 cc64 校验码,确认无误后,将数据存入 redisDb设置过期时间并返回响应。
源节点收到目标节点处理成功的响应后。对于非 copy 类型的 migrate会删除已迁移的 key。至此key 的迁移就完成了。migrate 迁移指令,可以一次迁移一个或多个 key。注意整个迁移过程中源节点在发送 restore-asking 指令后,同步阻塞,等待目标节点完成数据处理,直到超时或者目标节点返回响应结果,收到结果后在本地处理完毕后序事件,才会停止阻塞,才能继续处理其他事件。所以,单次迁移的 key 不能太多,否则阻塞时间会较长,导致 Redis 卡顿。同时,即便单次只迁移一个 key如果对应的 value 太大,也可能导致 Redis 短暂卡顿。
在 slot 迁移过程中,不仅其他非迁移 slot 的 key 可以正常访问,即便正在迁移的 slot它里面的 key 也可以正常读写,不影响业务访问。但由于 key 的迁移是阻塞模式,即在迁移 key 的过程中,源节点并不会处理任何请求,所以在 slot 迁移过程中,待读写的 key 只有三种存在状态。
尚未被迁移,后续会被迁走;
已经被迁移;
这个 key 之前并不存在集群中,是一个新 key。
slot 迁移过程中,对节点里的 key 处理方式如下。
对于尚未被迁移的 key即从 DB 中能找到该 key不管这个 key 所属的 slot 是否正在被迁移,都直接在本地进行读写处理。
对于无法从 DB 中找到 value 的 key但key所属slot正在被迁移包括已迁走或者本来不存在的 key 两种状态Redis 返回 ask 错误响应,并附带 slot 迁移目标节点的 host 和 port。Client 收到 ask 响应后,将请求重定向到 slot 迁移的新节点,完成响应处理。
对于无法从 DB 中找到 value 的 key且 key 所在的 slot 不属于本节点,说明 Client 发送节点有误,直接返回 moved 错误响应,也附带上 key 对应节点的 host 和 port由 Client 重定向请求。
对于 Redis Cluster 集群方案,由社区官方实现,并有 Redis-trib 集群工具,上线和使用起来比较便捷。同时它支持在线扩缩,可以随时通过工具查看集群的状态。但这种方案也存在不少弊端。首先,数据存储和集群逻辑耦合,代码逻辑复杂,容易出错。
其次Redis 节点要存储 slot 和 key 的映射关系,需要额外占用较多内存,特别是对 value size 比较小、而key相对较大的业务影响更是明显。
再次key 迁移过程是阻塞模式,迁移大 value 会导致服务卡顿。而且,迁移过程,先获取 key再迁移效率低。最后Cluster 模式下,集群复制的 slave 只能挂载到 master不支持 slave 嵌套,会导致 master 的压力过大,无法支持那些,需要特别多 slave、读 TPS 特别大的业务场景。

View File

@ -0,0 +1,67 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
33 如何为秒杀系统设计缓存体系?
本课时我们具体讲解如何为秒杀系统设计缓存体系。
秒杀系统分析
互联网电商为了吸引人气经常会对一些商品进行低价秒杀售卖活动。比如几年前小米的不定期新品发售又如当前每年定期举行双11、双12中的特价商品售卖。秒杀售卖时大量消费者蜂拥而至给电商带来了极大的人气也给电商背后的服务系统带来了超高的并发访问负荷。
在不同电商、不同的秒杀活动,秒杀系统售卖的商品、销售策略大不相同,但秒杀背后的秒杀系统却有很大的相似性,基本都有以下这些共同特点。
首先,秒杀业务简单,每个秒杀活动售卖的商品是事先定义好的,这些商品有明确的类型和数量,卖完即止。
其次,秒杀活动定时上架,而且会提供一个秒杀入口,消费者可以在活动开始后,通过这个入口进行抢购秒杀活动。
再次,秒杀活动由于商品售价低廉,广泛宣传,购买者远大于商品数,开始售卖后,会被快速抢购一空。
最后,由于秒杀活动的参与者众多,远超日常访客数量,大量消费者涌入秒杀系统,还不停的刷新访问,短时间内给系统带来超高的并发流量,直到活动结束,流量消失。
分析了秒杀系统的特点,很容易发现,秒杀系统实际就是一个有计划的低价售卖活动,活动期间会带来 N 倍爆发性增长的瞬时流量,活动后,流量会快速消失。因此,秒杀活动会给后端服务带来如下的技术挑战。
首先,秒杀活动持续时间短,但访问冲击量大,秒杀系统需要能够应对这种爆发性的类似攻击的访问模型。
其次,业务的请求量远远大于售卖量,大部分是最终无法购买成功的请求,秒杀系统需要提前规划好处理策略;
而且,由于业务前端访问量巨大,系统对后端数据的访问量也会短时间爆增,需要对数据存储资源进行良好设计。
另外,秒杀活动虽然持续时间短,但活动期间会给整个业务系统带来超大负荷,业务系统需要制定各种策略,避免系统过载而宕机。
最后,由于售卖活动商品价格低廉,存在套利空间,各种非法作弊手段层出,需要提前规划预防策略。
秒杀系统设计
在设计秒杀系统时,有两个设计原则。
首先,要尽力将请求拦截在系统上游,层层设阻拦截,过滤掉无效或超量的请求。因为访问量远远大于商品数量,所有的请求打到后端服务的最后一步,其实并没有必要,反而会严重拖慢真正能成交的请求,降低用户体验。
其次,要充分利用缓存,提升系统的性能和可用性。
秒杀系统专为秒杀活动服务,售卖商品确定,因此可以在设计秒杀商品页面时,将商品信息提前设计为静态信息,将静态的商品信息以及常规的 CSS、JS、宣传图片等静态资源一起独立存放到 CDN 节点,加速访问,且降低系统访问压力。
在访问前端也可以制定种种限制策略,比如活动没开始时,抢购按钮置灰,避免抢先访问,用户抢购一次后,也将按钮置灰,让用户排队等待,避免反复刷新。
用户所有的请求进入秒杀系统前,通过负载均衡策略均匀分发到不同 Web 服务器,避免节点过载。在 Web 服务器中,首先进行各种服务预处理,检查用户的访问权限,识别并发刷订单的行为。同时在真正服务前,也要进行服务前置检查,避免超售发生。如果发现售出数量已经达到秒杀数量,则直接返回结束。
秒杀系统在处理抢购业务逻辑时,除了对用户进行权限校验,还需要访问商品服务,对库存进行修改,访问订单服务进行订单创建,最后再进行支付、物流等后续服务。这些依赖服务,可以专门为秒杀业务设计排队策略,或者额外部署实例,对秒杀系统进行专门服务,避免影响其他常规业务系统。
在秒杀系统设计中,最重要的是在系统开发之初就进行有效分拆。首先分拆秒杀活动页面的内容,将静态内容分拆到 CDN动态内容才通过接口访问。其次要将秒杀业务系统和其他业务系统进行功能分拆尽量将秒杀系统及依赖服务独立分拆部署避免影响其他核心业务系统。
由于秒杀的参与者远大于商品数,为了提高抢购的概率,时常会出现一些利用脚本和僵尸账户并发频繁调用接口进行强刷的行为,秒杀系统需要构建访问记录缓存,记录访问 IP、用户的访问行为发现异常访问提前进行阻断及返回。同时还需要构建用户缓存并针对历史数据分析提前缓存僵尸强刷专业户方便在秒杀期间对其进行策略限制。这些访问记录、用户数据通过缓存进行存储可以加速访问另外对用户数据还进行缓存预热避免活动期间大量穿透。
在业务请求处理时,所有操作尽可能由缓存交互完成。由于秒杀商品较少,相关信息全部加载到内存,把缓存暂时当作存储用,并不会带来过大成本负担。
为秒杀商品构建商品信息缓存并对全部目标商品进行预热加载。同时对秒杀商品构建独立的库存缓存加速库存检测。这样通过秒杀商品列表缓存进行快速商品信息查询通过库存缓存可以快速确定秒杀活动进程方便高效成交或无可售商品后的快速检测及返回。在用户抢购到商品后要进行库存事务变更进行库存、订单、支付等相关的构建和修改这些操作可以尽量由系统只与缓存组件交互完成初步处理。后续落地等操作必须要入DB库的操作可以先利用消息队列机记录成交事件信息然后再逐步分批执行避免对 DB 造成过大压力。
总之,在秒杀系统中,除了常规的分拆访问内容和服务,最重要的是尽量将所有数据访问进行缓存化,尽量减少 DB 的访问,在大幅提升系统性能的同时,提升用户体验。

View File

@ -0,0 +1,79 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
34 如何为海量计数场景设计缓存体系?
在上一课时我们讲解了如何为秒杀系统进行缓存设计,在本课时我们将具体讲解如何为海量计数场景设计缓存服务。
计数常规方案
计数服务在互联网系统中非常常见用户的关注粉丝数、帖子数、评论数等都需要进行计数存储。计数的存储格式也很简单key 一般是用户 uid 或者帖子 id 加上后缀value 一般是 8 字节的 long 型整数。
最常见的计数方案是采用缓存 + DB 的存储方案。当计数变更时,先变更计数 DB计数加 1然后再变更计数缓存修改计数存储的 Memcached 或 Redis。这种方案比较通用且成熟但在高并发访问场景支持不够友好。在互联网社交系统中有些业务的计数变更特别频繁比如微博 feed 的阅读数,计数的变更次数和访问次数相当,每秒十万到百万级以上的更新量,如果用 DB 存储,会给 DB 带来巨大的压力DB 就会成为整个计数服务的瓶颈所在。即便采用聚合延迟更新 DB 的方案,由于总量特别大,同时请求均衡分散在大量不同的业务端,巨大的写压力仍然是 DB 的不可承受之重。因此这种方案只适合中小规模的计数服务使用。
在 Redis 问世并越来越成熟后,很多互联网系统会直接把计数全部存储在 Redis 中。通过 hash 分拆的方式,可以大幅提升计数服务在 Redis 集群的写性能,通过主从复制,在 master 后挂载多个从库,利用读写分离,可以大幅提升计数服务在 Redis 集群的读性能。而且 Redis 有持久化机制,不会丢数据,在很多大中型互联网场景,这都是一个比较适合的计数服务方案。
在互联网移动社交领域,由于用户基数巨大,每日发表大量状态数据,且相互之间有大量的交互动作,从而产生了海量计数和超高并发访问,如果直接用 Redis 进行存储,会带来巨大的成本和性能问题。
海量计数场景
以微博为例,系统内有大量的待计数对象。如从用户维度,日活跃用户 2 亿+,月活跃用户接近 5 亿。从 Feed 维度,微博历史 Feed 有数千亿条,而且每日新增数亿条的新 Feed。这些用户和 Feed 不但需要进行计数,而且需要进行多个计数。比如,用户维度,每个用户需要记录关注数、粉丝数、发表 Feed 数等。而从 Feed 维度,每条 Feed 需要记录转发数、评论数、赞、阅读等计数。
而且,在微博业务场景下,每次请求都会请求多个对象的多个计数。比如查看用户时,除了获取该用户的基本信息,还需要同时获取用户的关注数、粉丝数、发表 Feed 数。获取微博列表时,除了获取 Feed 内容,还需要同时获取 Feed 的转发数、评论数、赞数,以及阅读数。因此,微博计数服务的总访问量特别大,很容易达到百万级以上的 QPS。
因此,在海量计数高并发访问场景,如果采用缓存 + DB 的架构,首先 DB 在计数更新就会存在瓶颈,其次,单个请求一次请求数十个计数,一旦缓存 miss穿透到 DBDB 的读也会成为瓶颈。因为 DB 能支撑的 TPS 不过 3000~6000 之间,远远无法满足高并发计数访问场景的需要。
采用 Redis 全量存储方案,通过分片和主从复制,读写性能不会成为主要问题,但容量成本却会带来巨大开销。
因为,一方面 Redis 作为通用型存储来存储计数,内存存储效率低。以存储一个 key 为 long 型 id、value 为 4 字节的计数为例Redis 至少需要 65 个字节左右,不同版本略有差异。但这个计数理论只需要占用 12 个字节即可。内存有效负荷只有 1265=18.5%。如果再考虑一个 long 型 id 需要存 4 个不同类型的 4 字节计数,内存有效负荷只有 (8+16)/(65*4)= 9.2%。
另一方面Redis 所有数据均存在内存,单存储历史千亿级记录,单份数据拷贝需要 10T 以上,要考虑核心业务上 1 主 3 从,需要 40T 以上的内存,再考虑多 IDC 部署,轻松占用上百 T 内存。就按单机 100G 内存来算,计数服务就要占用上千台大内存服务器。存储成本太高。
海量计数服务架构
为了解决海量计数的存储及访问的问题,微博基于 Redis 定制开发了计数服务系统,该计数服务兼容 Redis 协议,将所有数据分别存储在内存和磁盘 2 个区域。首先,内存会预分配 N 块大小相同的 Table 空间,线上一般每个 Table 占用 1G 字节,最大分配 10 个左右的 Table 空间。首先使用 Table0当存储填充率超过阀值就使用 Table1依次类推。每个 Table 中key 是微博 idvalue 是自定义的多个计数。
微博的 id 按时间递增,因此每个内存 Table 只用存储一定范围内的 id 即可。内存 Table 预先按设置分配为相同 size 大小的 key-value 槽空间。每插入一个新 key就占用一个槽空间当槽位填充率超过阀值就滚动使用下一个 Table当所有预分配的 Table 使用完毕,还可以根据配置,继续从内存分配更多新的 Table 空间。当内存占用达到阀值,就会把内存中 id 范围最小的 Table 落盘到 SSD 磁盘。落盘的 Table 文件称为 DDB。每个内存 Table 对应落盘为 1 个 DDB 文件。
计数服务会将落盘 DDB 文件的索引记录在内存,这样当查询需要从内存穿透到磁盘时,可以直接定位到磁盘文件,加快查询速度。
计数服务可以设置 Schema 策略,使一个 key 的 value 对应存储多个计数。每个计数占用空间根据 Schema 确定,可以精确到 bit。key 中的各个计数,设置了最大存储空间,所以只能支持有限范围内的计数。如果计数超过设置的阀值,则需要将这个 key 从 Table 中删除,转储到 aux dict 辅助词典中。
同时每个 Table 负责一定范围的 id由于微博 id 随时间增长而非逐一递增Table 滚动是按照填充率达到阀值来进行的。当系统发生异常时,或者不同区域网络长时间断开重连后,在老数据修复期间,可能在之前的 Table 中插入较多的计数 key。如果旧 Table 插入数据量过大,超过容量限制,或者持续搜索存储位置而不得,查找次数超过阀值,则将新 key 插入到 extend dict 扩展词典中。
微博中的 feed 一般具有明显的冷热区分,并且越新的 feed 越热,访问量越大,越久远的 feed 越冷。新的热 key 存放内存 Table老的冷 key 随所在的 Table 被置换到 DDB 文件。当查询 DDB 文件中的冷 key 时,会采用多线程异步并行查询,基本不影响业务的正常访问。同时,这些冷 key 从 DDB 中查询后,会被存放到 LRU 中,从而方便后续的再次访问。
计数服务的内存数据快照仍然采用前面讲的 RDB + 滚动 AOF 策略。RDB 记录构建时刻对应的 AOF 文件 id 及 pos 位置。全量复制时master 会将磁盘中的 DDB 文件,以及内存数据快照对应的 RDB 和 AOF 全部传送给 slave。
在之后的所有复制就是全增量复制slave 在断开连接,再次重连 master 时,汇报自己同步的 AOF 文件 id 及位置master 将对应文件位置之后的内容全部发送给 slave即可完成同步。
计数服务中的内存 Table 是一个一维开放数据,每个 key-value 按照 Schema 策略占用相同的内存。每个 key-value 内部key 和多个计数紧凑部署。首先 8 字节放置 long 型 key然后按Schema 设置依次存放各个计数。
key 在插入及查询时,流程如下。
首先根据所有 Table 的 id 范围,确定 key 所在的内存 Table。
然后再根据 double-hash 算法计算 hash用 2 个 hash 函数分别计算出 2 个 hash 值,采用公示 h1+N*h2 来定位查找。
在对计数插入或变更时,如果查询位置为空,则立即作为新值插入 key/value否则对比 key如果 key 相同,则进行计数增减;如果 key 不同,则将 N 加 1然后进入到下一个位置继续进行前面的判断。如果查询的位置一直不为空且 key 不同,则最多查询设置的阀值次数,如果仍然没查到,则不再进行查询。将该 key 记录到 extend dict 扩展词典中。
在对计数 key 查找时,如果查询的位置为空,说明 key 不存在,立即停止。如果 key 相同,返回计数,否则 N 加 1继续向后查询如果查询达到阀值次数没有遇到空且 key 不同,再查询 aux dict 辅助字典 和 extend dict 扩展字典,如果也没找到该 key则说明该 key 不存在,即计数为 0。
海量计数服务收益
微博计数服务,多个计数按 Schema 进行紧凑存储,共享同一个 key每个计数的 size 按 bit 设计大小,没有额外的指针开销,内存占用只有 Redis 的 10% 以下。同时,由于 key 的计数 size 固定,如果计数超过阀值,则独立存储 aux dict 辅助字典中。
同时由于一个 key 存储多个计数,同时这些计数一般都需要返回,这样一次查询即可同时获取多个计数,查询性能相比每个计数独立存储的方式提升 3~5 倍。

View File

@ -0,0 +1,81 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
002 精读2017年KDD最佳研究论文
前面我们介绍过KDD大会的时间检验奖每年大会的另外一个亮点奖项就是最佳论文奖有两类一类是最佳研究论文一类是最佳应用数据科学论文。今天我就先来说说前者。
大会每年都会在众多的学术研究论文中选择最有新意和价值的研究论文评选出最佳研究论文的第一名和第二名。从过去十多年的经验来看KDD历年的最佳研究论文都会对之后很多领域的研究有开创性的影响。因此不论是从阅读经典文献的角度还是从学习最新研究成果的角度来说认真分析和探讨每年的最佳研究论文都是一个不错的选择。
今天我就带你认真剖析一下KDD 2017年的最佳研究论文《通过挖掘类比关系加速创新》Accelerating Innovation Through Analogy Mining
作者群信息介绍
第一作者汤姆·霍普Tom Hope来自耶路撒冷的希伯来大学The Hebrew University of Jerusalem计算机博士在读第三年。同时他还是英特尔以色列的资深数据科学员对深度学习的很多方面都有研究。目前他正在写一本基于TensorFlow的深度学习简明技术书籍。
第四作者达夫娜·沙哈夫Dafna Shahaf是霍普的博士导师目前在希伯来大学计算机系任助理教授。达夫娜于2012年从卡内基梅隆大学博士毕业。她曾经在微软研究院以及富士通公司实习并在斯坦福大学攻读博士后。达夫娜的论文曾获得2010年的KDD最佳研究论文可以说她一直站在机器学习研究的前沿。
第二作者乔尔Joel Chan是来自卡内基梅隆大学人机交互学院的科学家。乔尔于2014年从匹兹堡大学毕业获得认知心理学博士学位。他一直在人机交互领域进行研究。
第三作者安尼凯特·科图Aniket Kittur是来自卡内基梅隆大学人机交互学院的副教授。他于2009年从加州大学洛杉矶分校毕业获得认知心理学博士学位之后就一直在卡内基梅隆大学任教。
从整个作者群的情况来看,这篇文章是一个比较典型的机器学习技术与人机交互领域的交叉成果。
论文的主要贡献
我们先来看一下这篇文章的主要贡献。当然,要想深入理解这篇文章的贡献,我们还要先弄明白,这篇文章主要解决的是一个什么场景下的问题。
这篇文章主要阐述了帮助创新的一个重要步骤,那就是如何找到合适并且有效的类比案例。什么叫作类比案例?在人类发展的历史上,特别是科学技术的革新历程中,有很多重要的发明发现,都是因为当时的科学家借鉴了一些类似场景中的解决方案,或者是从这些场景中获取了灵感,进一步做出了创新。在这个步骤中,从类似场景中借鉴往往被称作类比。
比如,莱特兄弟从自行车中得到灵感,制造出了可以滑行的飞行器。再比如,诺贝尔生理学或医学奖得主萨尔瓦多·卢里亚从对赌博机运行的观察中,进一步发现了细菌基因突变的规律。同时,类比在很多国家的法律,不管是成文中或者是运行中都比比皆是。因此,我们可以看到,如何找到合适的类比,并能从中获取灵感,可能就是创新的一个关键因素。
时至互联网时代的今天我们已经有很多数据库、文献库可以作为获取类比灵感的重要数据源泉。比如谷歌学术搜索Google Scholar有上百万的论文和专利信息OpenIDEO有上百份关于社会问题的分析Quirky有超过两百万份的产品构想InnoCentive有超过四万份的社会、政治以及科技方面的解决方案美国专利局有超过九百万份的专利信息类似的例子还有很多。
与此同时,海量数据也为寻找灵感带来了很大的挑战。如何才能在这些百万级别的信息库里快速定位可能的类比场景,就成了阻碍创新速度的一大瓶颈。
找到类比场景有一个很大的挑战,那就是如何定义“类似”或者“相似”。如果仅从一些表面特征来看,很多场景之间的相似程度是很低的。因此,好的类比场景一定是能够找到,刨除表象相似以外的深层次相似的案例。另外一个挑战就是,寻找类比场景中,是否能有一个非常丰富的数据结构来支持推理,如果有,往往就能有比较简单的方法。
这篇论文的重点是如何从海量的无结构或者弱结构的文本数据中找到这样的类比。我们可以看出,这确实是一个巨大的挑战。
理解了这篇论文的目的,我这里就直接给你总结一下它的贡献,那就是提出了一种自动的在海量无结构的文本数据中挖掘类比场景的方法。这篇文章关注的是产品信息数据。作者们通过实际的数据,验证了提出的方法比之前的一些文本处理方法更加有效。
论文的核心方法
了解了这篇文章的目的和贡献后,接下来,我就来剖析一下作者们究竟提出了一个什么方法。
首先作者们提出了一组叫“目的”Purpose和“机制”Mechanism的概念。什么叫“目的”呢那就是当前的产品是要解决什么问题的。什么叫“机制”呢那就是当前的产品是使用什么手段或者方法来解决这个问题的。对于一个产品如果我们能够明确这个产品的目的和机制找到类比就变得更加容易。
比如,我们可以针对某一个问题,相同的目的,采用不同的机制或者对不同的问题采用相同的机制。作者们认为,这种对产品信息的分类符合很多工程设计的过程,是创新过程中的一个必要环节。
有了这种想法以后很自然的下一个步骤就是如何从数据中学习到目的和机制如何自动挖掘出海量产品信息的目的和机制。要想学习到这样的信息作者们提出了一种依靠标签数据的监督学习Supervised Leanring机制。具体说来作者们把文本信息中的每句话、短语交给亚马逊土耳其机器人Amazon Mechanical Turk上的在线工人来标注每个文本信息是目的信息还是机制信息。也就是说作者们依靠有标注的数据来训练提出的算法。
首先我们有一组文本每组文本都有这些文本的原始文字。针对每个文档我们都收集K个目的标注和K个机制标注。这时我们定义一组“目的标注”Purpose Annotation向量其实也就是一组0或者1的向量。当文本原始文字中的某个字被标识为目的的时候这个向量的相应元素置1反之置0。类似的我们也可以定义“机制标注”Mechanism Annotation向量。因为我们有K个标注因此我们也有相应的K个“目的标注”向量和“机制标注”向量。这两组向量可以说是原始标签信息的一种向量的表达。
下一步就是从每一个有标签信息的文档里产生唯一的目的向量和机制向量。这篇文章采用的方法是利用每个单词的嵌入向量Embedding来获得这个唯一的向量。
具体方法是这样的首先针对每一个标注总共有K个我们收集属于这个标注的单词的嵌入向量并把这些嵌入向量都拼接起来。然后计算这组拼接好的向量所对应单词的TF-IDF值Term FrequencyInverse Document Frequency词频-逆向文件频率并且取TF-IDF值最高的一些单词相对应的嵌入向量加权平均以后就得到了相应的唯一的目的向量或者是机制向量。作者们发现这种利用TF-IDF值加权的方法可以更加有效地表达文本的各种重要信息。注意这个步骤是依赖于文档标签的也就是说我们只能对训练数据进行这样的构造。
到目前为止我们描述了如何从文本到达文本对应的目的向量和机制向量的步骤。那么如何基于这样的数据以及向量来对未知的文档进行提取目的向量和机制向量呢文章采用了深度模型RNNRecurrent Neural Network循环神经网络具体说来是双向的RNN并且有一个GRUGated Recurrent Unit门控循环单元
这里我就不复述细节了,总体的思路就是,根据文档的嵌入向量信息,我们希望得到一组文档的隐含表达(中间参数),然后可以从这个隐含表达来预测目的向量和机制向量。注意,因为需要预测两组目标,目的向量和机制向量,因此,这里至少需要分别有两组参数。
除了预测文档的目的向量和机制向量以外,作者们还提出了一个用少数关键词来解释这两组变量的机制。具体说来,就是设立一个新的学习目标函数,希望通过少数关键词所对应的嵌入向量来重构目的向量或者机制向量,让得到的误差最小。
方法的实验效果
作者们使用了Quirky数据集通过亚马逊土耳其机器人标注了八千多组产品信息。首先检测了利用学习到的目的向量和机制向量是否能够比较容易地从海量数据中提取相应的类比信息。这里作者们直接利用把目的向量和机制向量拼接的方式来表达问题。答案是效果非常显著。在前1%到25%的提取结果中精度Precision和召回Recall都比之前的标准文本处理方法比如LDA、TF-IDF、全局的嵌入向量要好10%到20%,可以说这是非常有效果的。
作者们还测试了通过提出的方法是否能够为用户推荐比较好的类比场景。这里文章又找了38个亚马逊土耳其机器人的虚拟工人来为12个产品思路打分。在不知道推荐方法的情况下虚拟工人认为这篇文章提出的方法能够推荐更有新意、在深层次上更加类似的场景。这也部分解决了我们前面说到的文章希望解决的问题。
小结
今天我为你讲了KDD 2017年的年度最佳研究论文这篇论文提出了一种自动的方法来挖掘类比信息为快速创新铺平道路。一起来回顾下要点第一我简单地介绍了这篇文章的作者群信息帮你了解到这篇文章是机器学习和人机交互研究的一个结合。第二我详细地介绍了这篇文章想要解决的问题以及贡献。第三我简要地介绍了文章所提出方法的核心内容。
最后,给你留一个思考题,这篇文章提出的是使用标注信息来获取目的向量和机制向量,我们有没有办法能够不使用标注信息,采用完全无监督的方式呢?
拓展阅读Accelerating Innovation Through Analogy Mining

View File

@ -0,0 +1,78 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
003 精读2017年KDD最佳应用数据科学论文
周一我们讲了2017年KDD最佳研究论文今天我们继续来聊今年的KDD最佳应用数据科学论文。
与研究类论文不同的是KDD的应用类学术论文更加强调论文所描述的方法或者系统在实际应用中发挥的作用。比如很多论文都是对现有的已部署的系统进行总结对工业界的很多研究人员和工程师往往都有不小的借鉴意义。和研究类论文一样从阅读经典文献和学习最新研究成果的角度我们都应该认真分析和探讨每年的最佳应用类论文。
2017年KDD最佳应用数据科学论文题目是《HinDroid基于结构性异构信息网络的智能安卓恶意软件检测系统》HinDroid: An Intelligent Android Malware Detection System Based on Structured Heterogeneous Information Network。可以说2017年是信息安全备受关注的一年2016年美国大选过程中传出了种种关于俄罗斯利用黑客入侵大选候选人的新闻让整个社会对信息安全的话题变得异常敏感。这是一篇有关如何智能地分析安卓恶意软件的论文真是非常应景。
作者群信息介绍
文章的第一作者和第二作者都来自西弗吉尼亚大学West Virginia University的计算机科学与电气工程系。第一作者Shifu Hou是该系的博士生先后发表过多篇论文。第二作者叶艳芳Yanfang Ye是该系的助理教授。叶艳芳2010年从厦门大学博士毕业先后在金山公司和科摩多Comodo Security Solutions从事信息安全方面的研究和开发工作。2013年她加入西弗吉尼亚大学任教。这篇KDD论文因为第一作者也是在读学生因此也是最佳学生论文。
第三作者宋阳秋Yangqiu Song是来自香港科技大学的计算机系助理教授。宋阳秋有丰富的学术和工业界经历。2016年加入香港科技大学在这之前曾经在西弗吉尼亚大学任教。2012年到2015年之间他曾在伊利诺伊大学香槟分校、香港科技大学、华为诺亚方舟实验室等地访问。2009年到2012年曾在微软亚洲研究院和IBM研究院工作。2009年于清华大学博士毕业。
最后一位作者是土耳其企业家米勒夫·阿杜勒哈尤格鲁Melih Abdulhayoğlu。他是科摩多Comodo的CEO于1998年创立了公司。这篇论文挂了他的名字是因为使用了科摩多的数据。
论文的主要贡献
我们首先来看一下这篇文章的主要贡献。类似地,按照我们周一分析最佳研究论文的思路,首先必需弄明白,这篇文章主要解决了什么场景下的问题。
这篇文章希望解决的问题描述起来很直观那就是如何有效地监测安卓手机系统下的恶意软件。经预测到2019年全球的手机市场中77.7%将是智能手机这里面安卓系统的市场占有率至少是80%。由于安卓系统的开放性以及分散的各类安卓手机软件市场对安卓软件进行监控和分析存在很大难度。各类恶意软件在安卓生态系统中可以说层出不穷比如Geinimi、DroidKungfu以及Lotoor等等。更悲观的统计来自赛门铁克Symantec的《互联网安全威胁报告》认为五分之一的安卓软件是恶意软件。
之前很多恶意软件的分析和检测都是基于某种“指纹签字”技术,然而这种技术常常被恶意软件开发者的新手段绕过。因此,寻找更加复杂有效的检测方式就成了各种信息安全公司所追逐的目标。
这篇论文的主要贡献是根据安卓的API提出了一种新的基于结构性异构信息网络的方法来对安卓程序的API模式进行更加复杂的建模从而能够理解整个安卓程序的语义。作者们还采用了多核学习Multi-Kernel Learning的方法在结构性异构信息网络的基础上对程序语义模式进行分类。
最后,文章提出的方法在科摩多的真实数据上达到了非常高的准确度,远远优于现在的一些主流方法。并且,科摩多已经在产品中部署了这个方法。
论文的核心方法
了解了这篇文章的目的和贡献,接下来,我就来剖析一下作者们提出的方法。
首先需要将安卓的程序代码转换为可以分析的形式。一般来说安卓的软件被打包为后缀名为Dex的Dalivik执行文件这个执行文件无法被直接分析。于是需要把这个执行文件通过一个叫 Smali 的反汇编器解析成Smali代码。这个时候软件的语义就能够通过Smali代码来解析了。作者们从Smali代码中提取所有的API调用通过对API的分析来对程序行为建模。
下一步就是要从繁复的API调用中摸索出这里面的规律。作者们这个时候构建了四类矩阵来表达API和某个App之间的基本特征
某一个App是否包含了某一个API
某两个API是否同时出现在某一段代码中
某两个API是否出现在同一个App中
某两个API是否使用了相同的调用方法。
可以看出这些矩阵可以抓住API和App之间的一个基本信息还可以抓住一系列API同时出现进行某种操作的特征信息。这些矩阵成了发现高阶规律的基础。
为了发现更加复杂的规律作者们在这里引入了一个工具叫异构信息网络。异构信息网络的概念最早由伊利诺伊大学香槟分校的数据挖掘权威韩家炜Jiawei Han和他当时的学生孙怡舟Yizhou Sun目前在加州大学洛杉矶分校任教提出。异构信息网络的核心思想就是希望能够表达一系列实体Entity之间的复杂规律。
传统的方法把实体表达成图Graph的节点实体之间的关系表达成节点之间的链接。这样的方式忽略了实体本身的不同以及关系的类型也有所不同。异构信息网络就是更加完整和系统地表达多种不同实体和实体关系的一种建模工具。在这篇文章中有两类实体App和API调用有四类关系和刚才定义的矩阵相同。而刚才定义的矩阵其实就是这四类关系所对应的图的邻接矩阵。
把App和API的关系描述成为异构信息网络以后下面的工作就是定义更高阶的规律关系。为了更好地定义这些复杂关系作者们使用了一个叫元路径Meta-Path的工具。元路径其实是提供一个描述性的模板语言来定义高阶关系。
比如我们可以定义一个从App到API再到App的“路径”用于描述两个App可能都含有相同的API调用。这个路径就可以帮助我们从最开始的四个矩阵推出更加复杂的矩阵来表达一些信息。那么根据人们的领域知识这里就是安全领域作者们就定义了多达16种元路径从而全面捕捉App和API之间的各种关系。
利用异构信息网络和元路径构建了程序的语义表达后下一步就是进行恶意软件的判别。这里作者们采用了多核学习的思想。简而言之就是把之前通过元路径所产生的新矩阵看作一个“核”。这里的多核学习就是要学习一个线性的分类器特征是每个App到某一个核的一个非线性转换这个转换是在学习过程中得到的。换句话说这个多核学习的流程要同时学习一个分类器来判断一个程序是不是恶意程序还需要在这个过程中学习从App到核的转换。
方法的实验效果
作者们使用了科摩多的数据集收集了2017年两个月里1834个App的信息。正常程序和恶意程序几乎各一半。另外还有一个数据集包含3万个App信息也几乎是正例负例各一半。从实验结果来看结合了16个定义好的元路径的多核学习能够实现高达98%的F1值。F1值可以认为是精度和召回的一个平衡同时准确率也是高达98%。
文章还比较了一些其他比较流行的方法比如神经网络、朴素贝叶斯Naïve Bayes分类器、决策树以及支持向量机这些方法基本的F1值都在85%和95%之间和文章提到的方法有较大差距。另外文章还和现在的一些商业软件比如Norton、Lookout、CM做了比较。这些商业软件的准确度也在92%上下徘徊。因此,文章所采用的方法的确比之前的很多方法都更有效果。
小结
今天我为你讲了KDD 2017年的最佳应用类论文。这篇论文提出了如何来分析安卓手机软件的行为进而检测手机应用是否是恶意软件。一起来回顾下要点第一简要介绍了这篇文章的作者群信息。第二详细介绍了这篇文章要解决的问题以及贡献。第三简要分析了文章提出方法的核心内容 。
总结一下,文章解决的问题就是如何有效监测安卓手机系统下的恶意软件,主要贡献是提出了一种新的基于结构性异构信息网络的方法,来理解安卓程序的语义。使用元路径的工具定义复杂关系,还采用了多核学习的方法完成恶意软件的判别。论文使用科摩多的数据集,验证了所提出的方法比当下流行的一些其他方法都更加有效。
最后,给你留一个思考题,文章中提到的多核学习方法这个步骤,是不是必需的?能否换成其他方法呢?
拓展阅读HinDroid: An Intelligent Android Malware Detection System Based on Structured Heterogeneous Information Network

View File

@ -0,0 +1,80 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
007 精读2017年ICCV最佳研究论文
ICCVInternational Conference on Computer Vision国际计算机视觉大会是每两年举办一次的计算机视觉顶级会议。从1987年开始举办已经有30年的历史。2017年的ICCV大会于10月22日至29日在意大利的水城威尼斯举行。
在每届ICCV大会上都会从众多学术论文中挑选出两篇最有新意和价值的论文作为最佳研究论文和最佳学生论文。ICCV的最佳论文奖又叫作“马尔奖项”Marr Prize是为了纪念英国的心理学家和神经科学家大卫·马尔David Marr而设计的奖项。马尔将心理学、人工智能和神经生理学的研究成果结合起来提出了全新的关于视觉处理的理论他被认为是计算神经科学的创始人。
今天我就来带你认真剖析一下ICCV 2017年的最佳研究论文“Mask R-CNN”。这篇论文是一个集大成的工作介绍了一个新的方法可以用于同时解决图像的“物体识别”Object Detection、“语义分割”Semantic Segmentation和“数据点分割”Instance Segmentation的工作。
什么意思呢?通俗地讲,那就是给定一个输入的图像,利用这篇论文提出的模型可以分析这个图像里究竟有哪些物体,比如是一只猫,还是一条狗;同时能够定位这些物体在整个图像中的位置;并且还能针对图像中的每一个像素,知道其属于哪一个物体,也就是我们经常所说的,把物体从图像中“抠”出来。
作者群信息介绍
这篇论文的作者全部来自Facebook的人工智能研究院Facebook AI Research
第一作者就是近几年在计算机视觉领域升起的学术之星何恺明博士Kaiming He。他于2016年加入Facebook人工智能研究院之前在微软亚洲研究院进行计算机视觉的研究工作他还是CVPR 2016年和CVPR 2009年的最佳论文得主。目前何恺明在计算机视觉领域有三项重大贡献。
第一他与其他合作者发明的ResNet从2016年以来成为了计算机视觉深度学习架构中的重要力量被应用到了计算机视觉以外的一些领域比如机器翻译和AlphaGo等相关论文引用数超过5千次。
第二他与其他合作者开发的Faster R-CNN技术发表于NIPS 2015上是图像物体识别和语义分析的重要技术手段也是今天我们要讨论的这篇论文的基础论文引用数超过2千次。
第三他与其他合作者在ICCV 2015年发表论文《深入研究整流器在ImageNet分类上超越人类水平》Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification研究了一种改进的ReLURectified Linear Unit线性整流函数又称修正线性单元结构从而达到了更好的效果论文引用数近2千次。
第二作者乔治亚⋅吉克里奥夏里Georgia Gkioxari目前是Facebook人工智能研究院的博士后研究员。乔治亚可以说是师出名门在Facebook工作之前才从加州大学伯克利毕业师从计算机视觉泰斗吉腾德拉⋅马利克Jitendra Malik。乔治亚之前还分别在谷歌大脑和谷歌研究院实习过。在过去几年中乔治亚在计算机视觉界已经发表了多篇高质量论文。
第三作者皮奥特⋅多拉Piotr Dollár是Facebook人工智能研究院的一名经理。2007年从加州大学圣地亚哥分校获得博士学位2014年加入Facebook这之前在微软研究院工作。皮奥特长期从事计算机视觉的研究工作。
最后一个作者罗斯⋅吉尔什克Ross Girshick是Facebook人工智能研究院的一名科学家。他于2012年毕业于芝加哥大学获得计算机博士。罗斯之前也在微软研究院工作也曾在计算机视觉泰斗吉腾德拉的实验室里担任博士后的研究工作。
论文的主要贡献
我们首先来看一下这篇文章的主要贡献。还是要先去理解,这篇文章主要解决的是一个什么场景下的问题。
刚才我们已经简单地谈到了这篇文章要解决的问题就是对输入图像的物体识别、语义分割以及数据点分割是这三个任务的一个集成。在之前的一个工作中“Faster R-CNN”[1]已经解决了前两个任务。那么这篇论文其实就是Faster R-CNN在逻辑上的一个扩展。然而这个扩展也并不是那么显而易见的。为了解决数据点分割的任务Mask R-CNN提出了深度学习网络结构上的一个创新这是本篇论文的一个重要贡献。
本文提出的模型不仅在数据点分割的标准数据集COCO上表现强劲击败所有之前提出的模型以外还能够很容易地扩展到其他的任务中比如“人体形态估计”Human Pose Estimation从而奠定了Mask R-CNN作为一个普适性框架的地位。
论文的核心方法
要想理解Mask R-CNN的核心思想我们就必须先简要理解Faster R-CNN的一些基本原理。刚才说到了Mask R-CNN就是在其之上的一种改进和延伸。
Faster R-CNN对于每一个输入图像中的每一个候选物体都会有两个输出一个是候选物体的标签比如猫、狗、马等还有一个就是一个矩形框Bounding Box用于表达这个物体在图像中的位置。第一个标签输出是一个分类问题Classification而第二个位置预测则是一个回归问题Regression
Faster R-CNN分为两个阶段Stage。第一个阶段叫作“区域提交网络”Region Proposal Network目的是从图像中提出可能存在的候选矩形框。第二个阶段从这些候选框中使用一个叫“RoIPool”的技术来提取特征从而进行标签分类和矩形框位置定位这两个任务。这两个阶段的一些特性可以共享。
区域提交网络的大体流程是这样的。最原始的输入图像经过经典的卷积层变换之后形成了一个图像特征层。在这个新的图像特征层上模型使用了一个移动的小窗口Sliding Window来对区域进行建模。这个移动小窗口有这么三个任务需要考虑。
首先移动小窗口所覆盖的特征经过一个变换达到一个中间层然后经过这个中间层直接串联到两个任务也就是物体的分类和位置的定位。其次移动的小窗口用于提出一个候选区域有时候也叫ROI也就是矩形框。而这个矩形框也参与刚才所说的定位信息的预测。
当区域提交网络“框”出了物体的大致区域和类别之后模型再使用一个“物体检测”Object Detection的网络来对物体进行最终的检测。在这里物体检测实际是使用了Fast R-CNN[2]的架构。所以也就是为什么Faster R-CNN的名字里用“Faster”来做区分。Faster R-CNN的贡献在于区域提交网络和Fast R-CNN的部分也就是物体检测的部分达到了共享参数或者叫共享网络架构这样也就起到了加速的作用。
Mask R-CNN在第一部分完全使用Faster R-CNN所提出的区域提交网络在此基础上对第二部分进行了更改。也就是说不仅仅在第二部分输出区域的类别和框的相对位置同时还输出具体的像素分割。然而和很多类似工作的区别是像素分割、类别判断、位置预测是三个独立的任务并没有互相的依赖这是作者们认为Mask R-CNN能够成功的一个重要的关键。对比之前的一些工作像素分割成了类别判断的依赖从而导致这几个任务之间互相干扰。
Mask R-CNN在进行像素分割的时候因为要在原始的图像上进行分割因此需要在整个流程中保留原始图像的位置关系。这个需求是类别判断和位置预测所不具备的。而在Faster R-CNN中因为不需要这个需求因此类别判断和位置预测所依赖的信息是一个压缩过后的中间层。那么很明显Mask R-CNN依靠这个压缩层就不够了。在这篇文章中作者们提出了一个叫RoIAlign的技术来保证中间提取的特征能够反映在最原始的像素中。如果对这部分内容感兴趣建议你去细读文章。
方法的实验效果
作者们使用Mask R-CNN在目前流行的图像物体检测任务数据集COCO 2015和COCO 2016上做了检测相对于之前的这两个竞赛的冠军实验结果表明Mask R-CNN的精度都大幅度增加。在一个“平均精度”Average Precision的度量上Mask R-CNN比COCO 2015的最佳结果好了近13%而比COCO 2016的最佳结果好了4%可以说效果非常明显。在实验结果中作者们非常细致地测试了整个Mask R-CNN中每一个部件的效果。其中把三个任务分开、以及RoIAlign方法都有非常显著的作用证明了这些模型组件是优秀结果的必要步骤。
小结
今天我为你讲了ICCV 2017年的最佳研究论文这篇文章介绍了目前在图像物体识别中的最新算法Mask R-CNN的大概内容。
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息。第二,我们详细介绍了这篇文章要解决的问题以及贡献 。第三,我们简要地介绍了文章提出方法的核心内容 。
最后给你留一个思考题你觉得为什么Mask R-CNN包括之前的一些工作要把物体检测的工作分为两步第一步先分析一个大的矩形框第二步进行物体检测这两步都是必要的吗
参考文献
Shaoqing Ren, Kaiming He, Ross Girshick, and Jian Sun. Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks. IEEE Trans. Pattern Anal. Mach. Intell. 39, 6 (June 2017), 1137-1149, 2017.
Ross Girshick. Fast R-CNN. Proceedings of the 2015 IEEE International Conference on Computer Vision (ICCV) (ICCV 15). IEEE Computer Society, Washington, DC, USA, 1440-1448, 2015.

View File

@ -0,0 +1,75 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
008 精读2017年ICCV最佳学生论文
周一我们认真剖析了ICCV 2017年的最佳研究论文“Mask R-CNN”。今天我们来分享ICCV 2017的最佳学生论文《焦点损失用于密集物体检测》Focal Loss for Dense Object Detection
可以说这篇文章是我们周一分享的最佳论文的孪生兄弟。首先这篇论文的作者群也基本是Facebook人工智能研究院的班底。其次这篇文章解决的问题也很类似也是物体识别和语义分割只是不解决数据点分割的问题。
作者群信息介绍
除第一作者外这篇论文的作者都来自Facebook的人工智能研究院。
第一作者林仓义Tsung-Yi Lin目前在谷歌大脑Google Brain团队工作发表论文的时候在Facebook人工智能研究院实习。林仓义在台湾国立大学获得本科学位在加州大学圣地亚哥分校获得硕士学位2017年刚从康奈尔大学博士毕业。博士期间他师从计算机视觉专家塞尔盖⋅比隆基Serge Belongie发表了多篇高质量的计算机视觉论文。
第二作者皮里亚⋅高耶Priya Goyal是Facebook人工智能研究院的一名研究工程师。在加入Facebook之前皮里亚从印度理工大学获得了学士和硕士学位。
第三作者罗斯⋅吉尔什克Ross Girshick第四作者何恺明还有最后一个作者皮奥特⋅多拉Piotr Dollár这三位作者也是周一的最佳研究论文的作者我们已经介绍过了你可以回去再了解一下。
论文的主要贡献
我们首先来看一下这篇文章的主要贡献。
刚才我们已经简单地谈到了,这篇文章要解决的问题,就是对输入图像进行物体识别和语义分割这两个任务。对于这个问题有两种主要的思路,这两个思路都在不断地发展。
第一种思路,那就是直接从输入图像入手,希望能够从输入图像中提取相应的特征,从而能够直接从这些特征中判断当前的图像区域是否属于某个物体,然后也能够一次性地找到矩形框的位置用于定位这个物体。
这种思路虽然直观但有一个致命的问题那就是对于一个输入图像来说大量的区域其实并不包含目标物体因此也就可以被认为是学习过程中的“负例”Negative Instance。如何有效地学习这么一个“不均衡”Imbalanced的数据集是这种思路需要考虑的问题。
因为这个因素,研究者们就开始思考另外一种思路,那就是先学习一个神经网络用于找到一些候选区域,然后在第二个阶段根据候选区域再去最终确定物体的类别和矩形框的位置。
在最近几年的实际评测中基于两个阶段Two-stage的模型包括我们在上一篇分享中提到的Faster R-CNN以及其他变种一般都有比较好的表现。而基于一个阶段One-stage的模型在这篇文章发布之前还不能达到两个阶段模型的水平。
本篇文章提出了一个新的目标函数叫作“焦点损失”Focal Loss用于取代传统的“交叉熵”Cross Entropy的目标函数。这个新目标函数的主要目的就是让一个阶段模型能够在正负例比例非常不协调的情况下依然能够训练出较好的模型从而使得一个阶段模型在效果上能够和两个阶段模型媲美。同时文章还提出了一种比较简单易用的深度网络结构可以简单地训练出整个模型。
论文的核心方法
在这一节,我们来讲一讲“焦点损失”的含义。因为这是一个新的目标函数,建议你还是阅读原文来理解这个目标函数的数学性质。这里,我们针对这个新的目标函数进行一个高度概括性的解释。
我们从普通的二分分类问题中常用的交叉熵我们简称为CE目标函数说起。首先我们认为模型预测类别是正例的概率是P。CE目标函数基本上可以认为是这个概率的对数的负数也就是在机器学习中经常使用的“负对数似然”Negative Log Likelihood。模型的目的是最小化“负对数似然”从而学习模型参数。
作者们观测到这么一个现象那就是CE目标函数在P是一个比较大的数值时比如大于0.5的时候依然会有一个“损失”Loss。什么意思呢就是说某一个数值点我们现在已经知道它可能是正例的可能性大于0.5了,也就是我们其实已经大体知道这个结果了,但是目标函数依然认为学习算法需要去对这个数据点进行作用,从而减少这个“损失”。
这其实也就是整个问题的核心那就是传统的CE目标函数并没有指导机器学习算法用在“应该使劲”的地方而是分散到了一些原本已经不需要再去关注的数据点上。当然这也就造成了学习困难的局面。
这篇文章提出的“焦点损失”对CE进行了一个看上去很小的改动那就是在CE目标函数的“负对数似然”之前乘以一个“相反概率”的系数并且这个系数有一个指数参数去调节这个系数的作用。如果你对这个内容感兴趣建议你参考原论文查看细节。如果对细节不感兴趣那重点理解这个目标函数的作用就可以了。
“焦点损失”有两个性质。第一当一个数据点被分错类的时候并且这个数据点的真实概率很小那么损失依然和CE类似。当一个数据点的真实概率趋近1也就是原本算法就可以比较自信的时候损失会相对于CE变小。第二刚才所说的系数起到了一个调节作用决定究竟需要对哪些“容易分类的数据点”降低损失到什么程度。
文章在新的“焦点损失”的基础上提出了一个新的网络结构叫RetinaNet使用一个阶段的思路来解决物体检测和语义分割的任务。这里我简要概括一下RetinaNet的一些特点。
第一RetinaNet使用了ResNet来从原始的输入图像中抽取基本的图像特性。
第二文章采用了一种叫FPNFeature Pyramid Net的网络架构来对图像的不同分辨率或者不同大小的情况进行特性抽取。
第三和Faster R-CNN相似的RetinaNet也是用了Anchor的思想也就是说从小的一个移动窗口中去寻找一个比较大的矩形框的可能性。
最后RetinaNet把从FPN抽取出来的特性用于两个平行的网络结构一个用于物体分类一个用于矩形框的定位。这一点很类似两个阶段模型的做法。
方法的实验效果
作者们使用RetinaNet在目前流行的图像物体检测任务数据集COCO上做了检测。首先RetinaNet的“平均精度” Average Precision要好于之前的所有一个阶段模型初步验证了提出的目标函数和网络架构的优越性。并且在实验中作者们分别使用了不同的“焦点损失”指数参数来展示这个参数对于结果的重要性。同时作者们还展示了RetinaNet能够比Faster R-CNN这种经典的两阶段模型以及一些变种在实验结果上至少持平甚至要更好。
小结
今天我为你讲了2017年ICCV的最佳学生论文这篇文章介绍了目前在图像物体识别中的最新目标函数“焦点损失”的大概内容。
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息。第二,我们分析了这篇文章要解决的问题和主要贡献 。第三,我们详细介绍了文章提出方法的核心内容 。
最后,给你留一个思考题,除了这篇文章介绍的更改目标函数的方法,针对不平衡的数据集,你觉得还有哪些通常使用的方法?

View File

@ -0,0 +1,69 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
009 如何将深度强化学习应用到视觉问答系统?
本周我们一起来剖析ICCV 2017的论文周一和周三分别讲了最佳研究论文和最佳学生论文。今天我们来分享一篇完全不同的文章题目是《使用深度强化学习研究协作性视觉对话机器人》Learning Cooperative Visual Dialog Agents with Deep Reinforcement Learning讲的是如何通过“深度强化学习”来解决视觉问答系统。
作者群信息介绍
第一作者阿布谢克·达斯Abhishek Das是一名来自佐治亚理工大学的在读博士生。他于2017年和2018年在Facebook人工智能研究院实习已经获得了Adobe的研究奖学金和Snapchat的研究奖学金可以说是一名非常卓越的博士生。之前在智能系统特别是在利用强化学习研究智能机器人会话系统的领域已经发表了多篇论文。
共同第一作者萨特维克·库托儿Satwik Kottur来自卡内基梅隆大学博士第四年研究领域为计算机视觉、自然语言和机器学习。2016年暑假他在Snapchat的研究团队实习研究对话系统中的个性化问题。2017年暑假在Facebook研究院实习研究视觉对话系统。近两年萨特维克已在多个国际顶级会议如ICCV 2017、ICML 2017、IJCAI 2017、CVPR 2017、NIPS 2017以及EMNLP 2017发表了多篇高质量研究论文可以说是一颗冉冉升起的学术新星。
第三作者何塞·毛拉José M. F. Moura是萨特维克在卡内基梅隆大学的导师。何塞是美国工程院院士和IEEE院士长期从事信号处理以及大数据、数据科学的研究工作。他当选2018年IEEE总裁负责IEEE下一个阶段的发展。
第四作者斯特凡·李Stefan Lee是来自乔治亚理工大学的研究科学家之前在弗吉尼亚理工大学任职长期从事计算机视觉、自然语言处理等多方面的研究。斯特凡2016年博士毕业于印第安纳大学计算机系。
第五作者德鲁·巴塔Dhruv Batra目前是Facebook研究院的科学家也是乔治亚理工大学的助理教授。德鲁2010年博士毕业于卡内基梅隆大学2010年到2012年在位于芝加哥的丰田理工大学担任研究助理教授2013年到2016年在弗吉尼亚大学任教。德鲁长期从事人工智能特别是视觉系统以及人机交互系统的研究工作。文章的第四作者斯特凡是德鲁长期的研究合作者他们一起已经发表了包括本文在内的多篇高质量论文。
论文的主要贡献
我们首先来看一下这篇文章的主要贡献,理解这篇文章主要解决了什么场景下的问题。
这篇论文是建立在这么一个虚拟“游戏”Game的基础上的。
首先我们有两个“机器人”Agent一个叫“Q机器人”Q-Bot一个叫“A机器人”A-Bot。这个游戏的规则是这样的。一开始A机器人得到一张图片IQ机器人一开始得到I的一个文字描述c而并不知道图片本身。然后Q机器人开始问A机器人关于图片的各种问题A机器人听到问题之后进行作答帮助Q机器人更进一步理解图片。Q机器人最终的目的是能够把这个图片“猜到”也就是说能够把图片从一个数据库中“提取”Retrieve出来。当然在实际的操作中这一步可以是去衡量Q机器人对于图像的理解也就是“描述图像的向量”和“真实图像的描述向量”的差距差距越小说明越成功。
那么你可以看到这其实是一个很难的问题。Q机器人必须从A机器人提供的图像文字描述中寻找线索并且能够提出有意义的问题。而A机器人必须了解Q机器人到目前为止究竟理解什么信息才能帮助Q机器人成功。
整个游戏或者叫任务常常被称作是“协作性的视觉对话系统”Cooperative Visual Dialog System。这篇文章的主要贡献就是第一个利用深度加强学习来对这样一个系统进行建模并且与之前的非加强学习模型相比提出的解决方案极大地提高了准确度。
论文的核心方法
那么,既然要把整个问题使用深度强化学习来建模,我们肯定就需要定义强化学习的一些构件。
第一我们来看看模型的“动作”Action。两个机器人的动作空间就是自然语言的词汇表。因为在这个游戏或者说在强化学习的每一轮中两个机器人都是需要根据现在的状态来进行下一步的动作也就是问问题的语句。这是一个离散的动作空间。除此以外Q机器人还需要在每一轮之后对自己理解的图像向量进行更新。那么这是一个连续的动作空间。
第二我们来看看模型的“状态”State。对于Q机器人来说每一轮的状态是一个这些信息的集合包括最初的A机器人提供的图像的描述以及到目前为止所有轮问答的每一句话。而A机器人的状态空间则包括最初的图像本身图像的描述以及到目前为止所有轮的对话。
第三我们来看看模型的“策略”Policy。对A机器人和Q机器人来说都是要根据现在的状态来评估下面的语句的可能性。这里评估的机制其实分别用两个神经网络来学习A机器人和Q机器人的策略。同时Q机器人还需要有一个神经网络来根据现有的A机器人的回答来更新对图像的一个认识。
第四我们来看一看模型的“环境”Environment和“回馈”Reward。在这个游戏里两个机器人都会得到一样的回馈而这个回馈的根据是Q机器人对图像的认识所表达的向量和图像的真实表达向量的一个距离或者更加准确地说是距离的变化量。
以上就是整个模型的设置。
那么我们来看两个模型策略神经网络的一些细节。首先对于Q机器人来说有这么四个重要的部件。第一Q机器人把当前轮自己问的问题和A给的回答当做一个组合用LSTM进行编码产生一个中间变量F。第二当前步骤的F和以前的所有F都结合起来再经过一个LSTM产生一个中间变量S。然后第三步我们根据这个S来产生下一步的语句以及当前对图像的一个重新的认识。也就是说F其实就是一个对历史所有状态的描述而S则是一个压缩了的当前描述信息并且我们使用S来作为下一步的一个跳板。A机器人的策略神经网络的架构非常类似这里就不赘述了区别在于不需要去产生图像的理解。
整个模型采用了目前深度强化学习流行的REINFORCE算法来对模型的参数进行估计。
这篇文章其实有不少技术细节,我们在今天的分享里只能从比较高的维度帮助你进行总结,如果有兴趣一定要去阅读原文。
方法的实验效果
作者们在一个叫VisDial的数据集上做了实验。这个数据集有6万8千幅图像是从我们之前提到过的COCO数据集里抽取出来的并且提供了超过68万对问答。可以说这个数据集还是比较大型的。
文章比较了利用普通的监督学习以及“课程学习”Curriculum Learning的方法。从效果来看强化学习的效果还是很明显的。最直接的效果是强化学习能够产生和真实对话相近的对话效果而其他的办法比如监督学习则基本上只能产生“死循环”的对话效果不理想。不过从图像提取的角度来讲强化学习虽然比监督学习的效果好但是差距并不是特别明显基本上可以认为目前的差距依然是在误差范围内的。
小结
今天我为你讲了ICCV 2017的一篇有意思的文章。这篇文章介绍了如何利用深度强化学习来搭建一个模型去理解两个机器人的对话并能够理解图像信息。
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息。第二,我们详细介绍了这篇文章要解决的问题以及贡献 。第三,我们重点介绍了的文章提出方法核心内容 。
最后,给你留一个思考题,你认为把强化学习用在这样的对话场景中,难点是什么?

View File

@ -0,0 +1,63 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
010 精读2017年NIPS最佳研究论文之一如何解决非凸优化问题
机器学习与人工智能领域的顶级会议NIPSConference on Neural Information Processing Systems神经信息处理系统大会从1987年开始举办已经有30多年的历史。NIPS 2017大会于2017年12月4日到9日在美国加利福尼亚州的长滩Long Beach举行。
每年大会都会在众多的学术论文中挑选出几篇最有新意和价值的论文作为最佳研究论文。在NIPS 2017上一共有三篇论文获得了最佳论文的称号。今天我就来带你认真剖析一下其中的一篇《具有凸目标的基于方差的正则化》Variance-based Regularization with Convex Objectives。这篇论文的两位作者都是来自斯坦福大学的学者。
这篇文章理论性很强主要研究的是一种“健壮的优化问题”Robust Optimization也就是说我们在优化一个“损失函数”Loss Function的时候不仅要考虑损失函数的“均值”Mean还要考虑损失函数的“方差”Variance。然而一个既要考虑均值又要考虑方差的综合的损失函数往往是一个“非凸”Non Convex的问题。对于一般的非凸优化问题来说我们往往不能找到一个全局的最优解甚至是找到局部最优解也很困难。这篇文章就是要来解决这么一个问题。
作者群信息介绍
第一作者洪升⋅南空Hongseok Namkoong是斯坦福大学“运筹学”Operations Research的一名在读博士研究生。他的导师分别是约翰⋅达齐John C. Duchi和彼得⋅格林Peter W. Glynn。2013年到斯坦福之前南空在韩国的韩国科学与技术高级研究所Korea Advanced Institute of Science and Technology有时候又称为KAIST获得工业工程和数学学士学位。最近两三年南空已经在发表了两篇NIPS的文章包括这篇最佳论文以及一篇ICML的论文。
第二作者约翰⋅达齐John C. Duchi是南空的导师之一。达奇可以说是师出名门他于2007年从斯坦福本科毕业接着在斯坦福跟随机器学习权威达菲⋅科勒Daphne Koller拿到了计算机科学的硕士学位然后又到加州大学伯克利分校跟随统计学习权威迈克尔⋅乔丹Michael Jordan拿到了计算机科学的博士学位。在博士阶段的暑假里达奇还到Google研究院中追随约然⋅辛格Yoram Singer积累了非常有价值的实习经验。之后他来到了斯坦福大学担任统计和电气电子工程系的助理教授。
有了这些良好的基础达奇的学术成绩也是非常扎实。他于2010年获得了ICML最佳论文奖。紧接着2011年在Google实习期间的工作AdaGrad成为了现在机器学习优化领域的经典算法这个工作的论文有超过2500次的引用而且也是深度学习优化算法的一个重要基础。目前达奇所有论文的引用数超过6千次。
论文的主要贡献
我们首先来看一下这篇文章的主要贡献,理解文章主要解决了一个什么场景下的问题。
很多机器学习问题其实都可以最终归结于优化一个目标函数Objective Function或者有时候叫做损失函数Loss Function的问题。针对训练数据集上损失函数的优化即最大化或最小化并且在测试集上表现优异是可以被证明为最终能够较好“泛化”Generalization的一种体现。
那么通常情况下这个损失函数都是针对均值的一个描述比如在整个训练数据集上的平均误差或者说在整个训练数据集上的平均准确度。然而我们都知道在一些很“偏斜”Skewed的数据分布上均值并不是很好的一个数据描述。即便我们的函数能够在“平均”的情况下优化一个损失函数这个函数也有可能在一些甚至大部分数据点上表现得不尽如人意。
于是,研究人员就引入了“健壮的优化问题”。也就是我们希望损失函数在更多的点上有优异的表现。那么,损失函数的健壮性是用损失函数的方差来衡量的。也就是说,我们希望损失函数在不同数据点上的波动要小。
有了这个概念之后,下一步就显得比较自然了,那就是把损失函数的均值部分,也就是我们通常要做的部分和有方差的部分串联起来,形成一个新的目标函数。这个目标函数有两个部分,第一部分就是均值部分,第二个部分就是方差的部分,中间有一个自由的参数,把这两个部分衔接起来。这样,我们就有了一个既考虑均值又考虑方差的新的健壮化的优化问题。
然而一个既要考虑均值又要考虑方差的综合的损失函数往往是一个“非凸”Non Convex的问题。什么叫做非凸函数一个“凸”Convex问题可以简单理解为函数只有唯一的最小值并且我们具备有效算法来找到这个最小值。而对于非凸问题来说我们往往不能找到一个全局的最优解或者找到局部最优解也很困难。
健壮优化问题已经在之前的研究中提了出来,那么这篇文章的主要贡献在于,为健壮优化问题找到了一个“凸”问题的逼近表达,并基于此提出了一个优化算法,解决了这个新提出的凸问题的近似解。
这里,值得注意的一点是,对于非凸问题提出凸问题的近似表达,是解决非凸问题的一个重要思路。有很多经典的非凸问题,都是通过凸问题的近似来得到解决或者部分解决的。从这个思路来说,这篇文章是延续了解决这种问题的一贯的策略。
论文的核心方法
这篇论文的核心方法以及证明都有很强的理论性需要有一定的数学功底和类似研究背景才能更好地理解。如果对文章内容有兴趣建议不仅要阅读原本的NIPS论文还需要去阅读其附加的文档一共有50多页才能比较全面地理解这篇文章的细节。我们在这里仅仅从概念上做一个高度浓缩的概括。
作者们在文章中提出了一种叫“健壮化的正则风险”Robustly Regularized Risk的目标函数。这个新的目标函数是建立在一个叫“经验分布”Empirical Distribution上的“散度”Divergence。而这个新的健壮化正则风险是一个凸问题。
直白一点说这个健壮化的正则风险可以被认为是一个包含两项的式子这两项是在数据集上的损失函数的期望加上一个损失函数的方差。在这个新的两项的式子中期望和方差都是定义在数据的经验分布上的。于是这样就把这个新提出的风险式子和我们实际需要解决的问题挂上了钩。当然后面大段的论文就是要证明这两个式子之间的差距到底有多少是不是新的式子提供了一个比较“紧”的“界限“Bound
紧接着,这篇文章其实讨论了这个健壮化的正则风险可以写成一个更加简单的优化问题,然后文章在附录中提供了这个简化后的优化问题的求解。
方法的实验效果
虽然这篇文章的核心内容是一个理论结果或者是算法革新。但是这篇文章依然是在两个数据集中做了实验一个是在UCI ML的数据集上展示了提出的新的健壮化的目标函数达到了比一般的目标函数更好的效果另外一个则是在RCV1文本分类的问题上比一般的优化目标函数有更好的效果。
小结
今天我为你讲了NIPS 2017年的最佳研究论文之一文章非常理论化。文章的一个核心观点是希望能够通过对损失函数的均值和方差同时建模从而达到让目标函数健壮化的目的。
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息。第二,我们详细介绍了这篇文章要解决的问题以及贡献 。第三,我们简要地介绍的文章提出方法的核心内容 。
最后,给你留一个思考题,要想控制目标函数的预测结果的方差,除了本文提出的把均值和方差都设计到目标函数里,还有没有别的方法?

View File

@ -0,0 +1,57 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
015 WSDM 2018论文精读深度学习模型中如何使用上下文信息
今天我们继续来精读WSDM 2018的一篇论文《隐含交叉在循环推荐系统中利用上下文信息》Latent Cross: Making Use of Context in Recurrent Recommender Systems。这篇文章同样出自谷歌团队其核心思想是希望通过深度模型来模拟并实现在推荐系统中广泛使用的“交叉特征”Cross Feature的效果。
作者群信息介绍
这篇论文的所有作者都来自谷歌,我们这里对其中的主要作者做一个简单介绍。
文章的第一作者亚力克斯·布伦特Alex Beutel是谷歌的资深科学家于2016年加入谷歌。布伦特毕业于卡内基梅隆大学获得计算机科学博士学位师从机器学习的权威亚力克斯·斯莫拉Alex Smola
最后一位作者艾德·池Ed H. Chi是谷歌的主任科学家他拥有39项专利已经发表了110多篇论文。在加入谷歌之前池是帕罗奥图Palo Alto研究中心的主任研究员。池毕业于明尼苏达大学获得计算机科学博士学位。
论文的主要贡献
我们首先来看这篇文章的主要贡献,梳理文章主要解决了一个什么场景下的问题。
推荐系统经常需要对当下的场景进行建模有时候这些场景被称作“上下文”Context。在过去比较传统的方法中已经有不少方法是探讨如何利用上下文信息进行推荐的比如使用“张量”Tensor的形式进行建模还有一些方法是利用对时间特性的把握从而对上下文信息进行处理。
近些年,随着深度学习的发展,越来越多的深度学习模型被应用到推荐系统领域中,但还没有直接探究如何在深度学习模型中使用上下文。这篇文章就想在这一方面做一个尝试。
这里面有一个比较棘手的问题。过去,这样的上下文常常使用“交叉特性”,也就是两个特征的乘积成为一个新的特征。这样的方法在矩阵分解或者张量分解的模型中得到了非常广泛的使用。然而在深度学习中,过去的经验是不直接使用这样的特性。但是,在上下文非常重要的推荐系统中,不使用交叉特性的的结果,往往就是效果不尽如人意。
这篇文章提出了一个叫“隐含交叉”Latent Cross的概念直接作用在嵌入Embedding这一层从而能够在深度模型的架构上模拟出“交叉特性”的效果。
论文的核心方法
作者们首先探讨了推荐系统中一个常见的特性那就是利用交叉特性来达到一个“低维”Low-Rank的表达方式这是矩阵分解的一个基本假设。比如每一个评分Rating都可以表达成一个用户向量和物品向量的点积。
那么作者们就提出了这样一个问题作为深度学习的基石前馈神经网络Feedforward Neural Network是否能够很好地模拟这个结构呢
通过模拟和小规模实验作者们从经验上验证了深度学习的模型其实并不能很好地抓住这样的交叉特性所带来的“低维”表达。实际上深度学习模型必须依赖更多的层数和更宽的层数才能得到相同的交叉特性所达到的效果。对于这一点我们或多或少会感到一些意外。同时作者们在传统的RNN上也作了相应的比较这里就不复述了。
得到了这样的结果之后,作者们提出了一个叫作“隐含交叉”的功能。这个功能其实非常直观。传统的深度学习建模,是把多种不同的信息输入直接拼接在一起。“隐含交叉”是让当前的普通输入特性和上下文信息进行乘积,从而直接对“交叉特性”进行建模。
这样做的好处是不言而喻的。之前,我们寄希望于深度学习模型自身能够学习到这样的交叉关系。而现在,作者们直接让上下文信息作用于输入信息和其他的中间特征,使得上下文信息的作用得到了提升。
这篇文章提出的办法可以说是第一个尝试解决传统推荐系统的一些想法,使之移植到深度学习的语境中。
方法的实验效果
这篇文章使用了谷歌的Youtube数据来做实验。作者们比较了一系列的方法得出的结论是RNN配合“隐含交叉”比仅仅使用RNN的效果要好2%~3%,这个提升已经是一个非常可观的数字了。
小结
今天我为你讲了WSDM 2018的一篇来自谷歌团队的文章这篇文章介绍了在传统推荐系统的模型中比如矩阵分解等都有的交叉特性如何应用在深度学习中。
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息;第二,我们详细介绍了这篇文章要解决的问题以及贡献 ;第三,我们分析了文章提出方法的核心内容以及实验结果。
最后,给你留一个思考题,深度学习模型在默认状态下并不能很好地抓住交叉特性,这是深度模型的问题吗?

View File

@ -0,0 +1,69 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
016 The Web 2018论文精读如何对商品的图片美感进行建模
“万维网大会”The Web Conference 2018前身叫作“国际万维网大会”International World Wide Web Conference从1994年开始举办已有20多年的历史了在Google学术排名上是“信息系统”排名第一的国际顶级学术会议。
从万维网大会最初举办开始,这个会议就成为了互联网方面独一无二的权威学术会议。会议包含搜索、推荐、广告、数据库、信息提取、互联网安全等诸多领域的优秀论文,每年都吸引着上千名世界各地的学者和工程师来分享他们的最新研究成果。
2018年的万维网大会于4月23日~27日在法国里昂举行。整个会议收录了171篇论文还有27个研讨班Workshop、19个讲座Tutorial、61个展板论文Poster和30个演示Demo
万维网大会的一大特点就是论文成果涵盖了非常广的领域。要在这些论文中找到有价值的学习信息是一件非常耗时、很辛苦的任务。这里给你分享几篇我认为今年这个会议上最有价值的论文,希望能起到抛砖引玉的作用。
今天我们就来看一篇优秀论文提名题目是《基于美感的服装推荐》Aesthetic-based Clothing Recommendation。这篇论文一共有六位作者除了两位分别来自新加坡国立大学和美国的埃默里大学之外绝大多数作者都来自清华大学。
论文的主要贡献
在现代的电商推荐系统中商品特别是服装服饰的图片其美观和质量是用户进行购买决策的关键参考因素。不少过去的商品推荐系统已经考虑了图片的属性特别是尝试同时利用图片信息和文字信息来实现多模Multi-Modal数据理解的目的从而能够进行更加智能的推荐。不过当前的大多数方案都只是考虑基本的图片特性。
从思路上来说大多数的类似工作都是利用某种深度神经网络提取图片特性然后和其他特性例如我们说过的文本信息加以组合从而能够扩宽我们对商品信息的提取。这样提取出来的图像特征自然没有显式地对图像的“美感”Aesthetic进行建模。
这篇文章的作者们认为,商品图片的“美感”是非常重要的属性,针对美感进行建模会有更显著的商品推荐效果。概括来说,这篇论文的一个贡献就是提供了一种模型,来对图片的美感和一般性的图片语义特性同时进行建模。这是一个在过去的工作中都没有的创新点,我们接下来会详细说明一这个模型的架构。
当作者们提取出了图片的美感信息以后接下来的一个问题就是如何利用这些特性。这篇论文使用了张量分解Tensor Factorization的思路。我们在前面介绍推荐系统的时候曾经提到过张量分解是一种很有效且常用的利用上下文语义信息的推荐模型。和一些之前的工作类似这里作者们采用了三维的张量来表达用户、商品和时间之间的关系。同时作者们还把图片信息有效地结合到了张量分解中从而能够利用美感信息来影响推荐结果。
论文的核心方法
了解了这篇论文的大体思路以后,我们现在来看看论文的第一个核心部件:如何利用深度神经网络来提取图片的美感信息?
首先这篇论文提出的模型假设对于每一个商品我们都有一个综合的美感标签并且还有一个细节标签来表达这个商品图案的“图像风格”Style。美感的综合标签是一个1~10的打分而图像风格则是文字的图像特征比如“高曝光”、“对比色”等。那么我们需要一个神经网络模型来同时对美感标签和细节的图像风格进行建模。
具体来说文章提出的模型分为了两个层次。第一个层次是用来解释细节的图像风格。在本文采用的数据中一共有14种图像风格作者们就用了14个子网络Sub Network来针对这些风格。每个风格都对应一个独立的子神经网络。每一个子神经网络都是标准的“卷积网络”CNN。他们的目标是尽可能地学习到特性来表示每个细节的图像风格。
当我们有了第一层的14个子网络之后再把这些子网络学习到的特性都整合起来形成中间特性层然后再经过一个卷积网络从而学习到一个对商品的整体美感评分进行解释的神经网络。
在文章中作者们提到这两个层次的神经网络并不是分类进行训练的而是在一个整体中进行训练。意思就是说我们同时训练底层的针对图像风格的14个子网络的参数以及高层次的针对美感评分的网络的参数。
当我们得到了图片的美感信息之后,下一步,就来看一下如何利用张量分解来进行商品推荐。
相比于传统的张量分解在这篇文章中作者们提出了一种新颖的针对商品推荐的张量表达模式叫作“动态协同过滤”Dynamic Collaborative Filtering或简称 DCF。
DCF认为每一个用户对于某个商品的购买取决于两个方面的因素。第一用户是否对这个商品有喜好。第二这个商品是不是符合时间维度上面的“流行度”。作者们认为只有当这两个条件同时满足的时候也就是用户喜欢某个当季的商品时才会做出购买的决定。因此作者们使用了两个矩阵分解来分别代表这两个假设。
第一个矩阵分解是针对用户和商品这个矩阵,这里我们会学习到用户对商品的喜好度。第二个矩阵分解是针对时间和商品这个矩阵,这里我们会学习到时间和商品的流行度。然后,作者把这两个矩阵分解(或者说是把两个矩阵)相乘,这就得到了一个张量,来表达用户在时间维度上对商品的喜好。
那么,如何把刚才学习到的图片美感信息给融入到这个新的张量学习框架下呢?作者们是这么做的,针对我们刚才所说的两个矩阵分解进行“扩展”。
刚才我们说,这个张量分解是基于一个假设,那就是用户在时间维度上的购买决定取决于,用户是否对这个商品有喜好,以及这个商品是不是符合时间维度上面的“流行度”。我们用了两个矩阵分解来表达这两个假设。每一个矩阵分解都是把一个大的矩阵分解成两个向量,比如用户和商品的矩阵就被分解为用户特性和商品特性。
基于此,作者们就在这个用户和商品的矩阵后面,再加上一个商品和图片美感信息矩阵,用来混合这两种信息。也就是说,我们刚才的第一个假设,用户对商品的好感,就被扩展成了两个矩阵的加和,用户和商品矩阵以及商品和图片信息矩阵,这两个矩阵的加和依然是一个矩阵。同理,时间和商品的流行度,被扩展成了时间和商品矩阵以及商品和图片信息矩阵的加和。也就是说,新的模型是两个矩阵的乘积组成的张量分解,而这里的每个矩阵分别又是两个矩阵的加和。这就是作者们最终提出的模型。
方法的实验效果
作者们在亚马逊的衣服数据集上做了实验来验证模型的有效性。这个亚马逊的数据集由将近四万的用户、两万多的商品和超过二十七万的购买信息构成。除了这篇文章提出的模型以外作者们还比较了一些其他算法例如完全随机的算法、只推荐最流行的商品、传统的矩阵分解模型以及只有基本图像信息但没有美感信息的算法。文章汇报了排序的精度NDCG以及“召回”Recall等指标。
从实验效果来看,这篇文章提出的模型要明显好于矩阵分解以及只有基本图像信息的算法,表明针对产品的图像美感进行建模是有价值的。并且,作者们提出的新的张量分解方法也被证明是切实有效的。
小结
今天我为你讲了今年万维网大会的一篇优秀论文。文章介绍了如何对商品的图片美感进行建模,以及如何把提取到的信息融入到一个基于张量分解的推荐系统中。
一起来回顾下要点:第一,我们详细介绍了这篇文章要解决的问题以及贡献;第二,我们简要地介绍了文章提出方法的核心内容;第三,我们简单分享了一下模型的实验成果。
最后,给你留一个思考题,有没有在没有标签情况下对图片的美感进行建模的呢?

View File

@ -0,0 +1,67 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
020 SIGIR 2018论文精读如何利用对抗学习来增强排序模型的普适性
今天我们继续来精读SIGIR 2018国际信息检索研究与发展大会的论文今天分享的是本次大会的最佳短论文标题是《使用对抗学习实现神经排序模型的跨领域正则化》Cross Domain Regularization for Neural Ranking Models using Adversarial Learning
非常有必要先简单介绍一下这篇文章的作者群,可以说这是一个“明星阵容”。
第一作者丹尼尔·科恩Daniel Cohen来自马萨诸塞大学阿默斯特分校University of Massachusetts Amherst是计算机科学系的博士生。2017年曾经在微软研究院剑桥分部实习。这篇获奖论文就是实习项目的总结。在深度学习模型在信息检索的应用这个方向上科恩已经发表了多篇论文。
第二作者巴斯卡·米特拉Bhaskar Mitra是微软研究院剑桥分部的主任级科学家。近些年米特拉在信息检索领域很活跃并且极力推动深度学习在这个领域的发展他在这个领域发表了很多篇论文在过去几年的很多学术会议上也主持了多个关于深度学习和信息检索相结合的讲座。
第三作者卡特娜·霍夫曼Katja Hofmann也是来自微软研究院剑桥分部的科学家。霍夫曼在信息检索领域的主要贡献是研究在线排序学习。
论文的最后一位作者布鲁斯·克罗夫特W. Bruce Croft是信息检索领域的学术权威也是科恩的博士导师。他是美国ACM院士还是信息检索领域最高学术荣誉奖杰拉德·索尔顿Gerard Salton奖的获得者。
论文的主要贡献
这篇论文主要涉及了这么两个重要概念的结合。第一个概念是“跨领域”Cross Domain信息检索第二个概念就是“对抗学习”Adversarial Learning
跨领域信息检索主要是指我们需要对一个以上的领域进行搜索。这里所说的领域主要是指不太相同,或者非常不同的文档集合。例如,如果我们要针对体育新闻、金融新闻等进行搜索,这里的“体育”和“金融”就是不同的领域。
跨领域信息检索的核心挑战是我们如何针对不同的领域都能进行有效搜索。比如,如果我们的排序算法本身或者其特性依赖于某个特定领域的信息,例如关于体育方面的搜索,需要依赖体育运动员的名字,那这种信息肯定会很少甚至完全不出现在另外一个领域。因此,依靠某些领域特有的信息很难做到真正的跨领域信息检索。
这篇文章的贡献是作者们认为,想要对跨领域的信息进行较好地检索,就需要训练这样的排序模型:不容易被某一个特定的领域所主导,同时也尽量不偏好某一个特定领域的具体信息。
如何实现这个目的呢?作者们使用了一种叫做“对抗学习”的技术。这也是这篇论文能够获奖的重要原因。
我在这里简单介绍一下对抗学习的原理。对抗学习最初的思想来自于利用深度产生模型解决计算机视觉中的一系列问题。最基本的对抗学习的模式主要是用于产生数据,而且是比较复杂的数据,例如图像。
对抗学习有两个模块,一个模块叫产生器,一个模块叫判别器。产生器的作用就是产生数据。判别器的作用是判断产生的数据是不是“真实数据”。产生器的最终目的是产生能够以假乱真的数据来扰乱判别器的判断能力。判别器的最终目的是不断提高判断能力从而能够分辨出数据的真假。
当然,最初的时候,产生器产生数据来源于随机噪声,因此判别器可以很容易地判断数据的真假。但是慢慢的,产生器产生的数据就会越来越接近真实数据,而判别器也很快在这个过程中得到优化,从而能够判别数据的真假。当然,这是一个动态的过程,最终,判别器和产生器的状态会稳定下来。
对抗学习这种思想最初被提出来的时候,主要是应用在计算机视觉领域中,用来产生以假乱真的图片。之后,这种技术被广泛应用到人工智能的各个领域。
这篇论文最大的一个贡献,就是利用了对抗学习的理念来增强学习到的排序模型的普适性,从而尽量避免学习到仅仅对一个领域有用的信息。
论文的核心方法
具体来说,这篇文章提出了这样一种方法。首先,我们有两套模型,一套是用于学习查询关键词和文档之间的相关关系的;一套是对抗学习的模型。然后,这两套模型的首要任务是更加精准地针对相关的文档和不相关的文档进行建模。这是整个框架里最主要的目标函数。
文章提出框架中新的模块是利用对抗学习来分别产生相关的和不相关的文档。具体来说,某一种类型的文档就像我们刚才提到的图片一样,我们希望能够利用产生器来进行产生这类数据。当然,我们依然需要判别器来引导产生器的工作。
在这篇文章中,相关的概念主要是看一个文档是不是某一个领域的。也就是说,我们希望对抗学习能够帮助识别某一个文档是不是来自于一个领域。当对抗学习模型被训练好的时候,对于查询关键词和文档的相关模型,我们就会利用一种叫做“梯度反转”的技术,强行偏离模型希望去拟合某一个领域的倾向。
从网络结构上看文章提出的模型查询关键词和文档都需要经过卷积层、提取层等变换然后进行俗称的“哈达马积”Hadamard product其实就是对应项乘积。这样文档和查询关键词所提取出来的隐含特征就结合在一起。这个结果之后又经过一系列稠密层的变换最终预测一个相关度的标签。对于对抗学习模型来说对抗中的判别器从刚才所说的架构中提取一些中间层作为输入然后判断这个文档是不是出现在某个领域中。
实验结果
在一个雅虎的搜索数据集以及另外两个数据集上,作者们对论文所提出的模型进行了实验。实验主要是看如果我们在某一个领域上训练出的模型,会不会在另外一个领域上表现优异。
一个不令人意外的结果是如果我们在全部领域上进行训练数据自然在所有的领域上效果都不错。当然文章展示了如果利用文章提出的方法针对某一个领域比如运动类文档在训练的时候完全移除所有的文档在测试集上依然有不错的表现。实验的结果比不进行对抗训练的效果要好5%以上。
小结
今天我为你讲了今年SIGIR上的最佳短论文。
一起来回顾下要点:第一,这篇论文主要涉及了两个概念,分别是跨领域信息检索和对抗学习,我们详细介绍了这篇文章的主要贡献,就是利用对抗学习的理念来增强所学排序模型的普适性;第二,我们简要地介绍了文章提出的方法核心内容,训练两套模型,利用对抗学习来分别产生相关的和不相关的文档;第三,我们简单介绍了论文的实验结果,进行对抗训练会有更好的效果。
最后,给你留一个思考题,除了使用对抗训练,你还能想到什么方法,能够比较好地学习到不属于某一个特定领域信息的排序模型?

View File

@ -0,0 +1,67 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
021 SIGIR 2018论文精读如何对搜索页面上的点击行为进行序列建模
今天我们将继续来精读SIGIR 2018的论文。
我们已经分享了SIGIR 2018的最佳论文介绍了如何对推荐系统中的偏差进行建模从而能够利用这种对偏差的理解来更加准确地对待基于流行度的推荐结果。周一我们分享了本次大会的最佳短论文主要讲了如何利用对抗学习的技术来让学习的排序模型更加“健壮”可以被应用到多个领域上。
今天我们分享的论文题目是《页面搜索的点击序列模型》A Click Sequence Model for Web Search
文章的第一作者阿列克谢·博里索夫Alexey Borisov来自俄罗斯的搜索引擎Yandex并且在阿姆斯特丹大学攻读博士学位。之前他已经发表过了多篇关于“点击模型”Click Model和深度学习模型结合的论文。
文章的第二作者马丁·万德纳Martijn Wardenaar、第三作者伊雅·马尔科夫Ilya Markov和最后的作者马顿·德里克Maarten de Rijke也都来自阿姆斯特丹大学。其中马顿是荷兰的计算机科学家欧洲的信息检索学术权威并且还是荷兰皇家科学院成员。
论文的主要贡献
我先对这篇论文的核心思想做一个提炼,就是利用深度学习模型,来对用户在搜索页面上的点击行为进行建模。
传统上这种对用户在搜索页面上的点击行为进行建模的思路就是“点击模型”。从2000年开始对点击模型的研究就成为了信息检索以及搜索领域中一个非常活跃的课题。在最近10年的时间里研究人员提出了几十种不同的点击模型。总体来说不同的点击模型主要是对不同的用户行为进行编码从而能够更加准确地对用户的点击行为进行预测。
在很多传统的点击模型中,为了简化模型,经常使用的一个假设是:针对每一个查询关键词,用户在搜索结果页只进行一次点击。在这种简化了的假设下,研究人员对用户的浏览、点击以及页面的偏差(例如位置偏差)进行建模,就会变得更加容易。然而,在很多场景中,这种假设就显得过于简化了。在同一个查询关键词的搜索结果页面下,很多用户都会点击多个结果。因此,对于多个点击结果的建模就变得重要起来。
这篇论文就是针对用户在搜索页面上的点击行为进行了序列建模,使得我们可以轻松地对每一个搜索页面进行预测,比如会有多少点击以及在什么位置点击等。
同时这篇论文还有一个贡献就是利用了深度学习中的循环神经网络RNN来对查询关键词的结果进行建模扩宽了传统的完全基于概率建模的点击模型在深度学习时代下的表现力。
论文的核心方法
论文提出方法的核心思路是针对每一个查询关键词,模型需要对所有可能的点击序列进行建模。这个任务是通过构建一个神经网络来完成的。
具体来说文章提出的模型有两个重要的模块编码器Encoder和解码器Decoder
编码器的作用是利用查询关键词和搜索结果为输入生成它们的“嵌入向量”Embedding Vector。近年来嵌入向量是深度学习建模中的一个重要技术手段它的目的往往是先把一些离散变量信息转化为连续信息。在这里查询关键词和搜索结果都可以首先表征为离散的输入信息然后需要映射到一个共同的语义空间。这可以被认为是一个中间结果或者在概率模型中这往往被认为是一个隐含变量。
解码器的作用是根据这个中间的嵌入向量表达下的查询关键词和搜索结果,然后决定在哪一个位置上可能会或者不会发生点击。这其实就是一个多类的分类问题。那么,怎么才能让解码器终止在某一个状态呢?作者们引入了一个特殊的符号代表序列的终止。这样,解码器也需要预测是否需要终止。类似的对解码器的操作在深度序列建模中十分常见。
可以说,作者们在设计编码器和解码器的结构上也是费了一番功夫的。
对于编码器而言,作者们认为一个好的嵌入向量必须包含当前的结果信息,以及当前结果周围的结果,或者说是上下文的信息,以及查询关键词的信息。这样,可以把每一个搜索结果都当做是一个独立的单元,有着足够丰富的信息来进行后面的建模。
因此,作者们首先把查询关键词和每一个搜索结果转换成为第一个层次的嵌入向量,组成一个大的第一层次的嵌入向量。然后,作者们利用这个第一层次的嵌入向量,并且引入了循环神经网络,来对当前结果前后的结果进行了两次编码,一次正向,一次逆向,从而形成了第二层次的嵌入向量。这个第二层次的嵌入向量就是最终表征每一个搜索结果的向量。
对于解码器而言作者们利用了“关注”Attention机制来对每一个搜索结果施加不同的权重或者说是关注度。每个时间点也就是每一次做“是否要点击”的决策之后都会重新生成一个关注向量或者说是一组新的关注权重。这里的核心是一个循环神经网络自己更新内部的状态变量并且根据关注向量以及输入的嵌入向量来预测下面一个点击的位置。
有了编码器和解码器之后一个难点是如何生成最有可能的点击序列。我们刚才提到了整个模型其实可以预测多种不同的点击序列。因此生成最优可能的K个序列就成为了必要的一个步骤。在这篇文章里作者们利用了“集束搜索”Beam Search的方法来近似生成最佳的K个序列在文章中K的值是1024。
模型的训练采用了标准的SGD以及Adam优化法同时作者们还采用了“梯度裁剪”Gradient Clipping的方式来防止在优化过程中发生“爆炸问题”Gradient Clipping
实验结果
作者们在Yandex俄罗斯的搜索引擎数据上进行了实验。因为之前没有类似的模型因此文章并没有可以直接比较的其他模型。作者们主要进行评估的地方是看历史数据中已经发生的点击序列会不会被正确预测出会不会出现在K个模型认为最有可能发生的点击序列中。这也就是作者们为什么选择K等于1024的原因因为在这种情况下接近97%的历史序列都在模型的预测序列中。
作者们还评估了模型能否预测出总的点击次数等一系列和点击预测有关的任务论文中提出的模型都能够以接近1的概率预测所有的点击并击败一些过去的基于概率的点击模型。可以说提出的模型的确可以对用户在搜索页面的点击行为进行有效的建模。
小结
今天我为你讲了今年SIGIR 2018的一个篇精彩论文。
一起来回顾下要点:第一,我们详细介绍了这篇文章要解决的问题以及贡献,主要是对用户在搜索页面上的点击行为进行序列建模;第二,我们简要介绍了文章提出方法的核心内容,主要是编码器和解码器两个模块;第三,我们简单介绍了论文的实验结果。
最后,给你留一个思考题,如果针对多个连续的查询关键词的点击行为进行建模,你能否用这篇论文提出的思路来扩展模型呢?

View File

@ -0,0 +1,71 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
022 CVPR 2018论文精读如何研究计算机视觉任务之间的关系
今年6月18 日~22日计算机视觉和模式识别大会CVPRConference on Computer Vision and Pattern Recognition在美国的盐湖城举行。CVPR大会从1985年开始举办已经有30多年的历史是计算机视觉领域的顶级会议。
最近几年CVPR大会发展成为了人工智能领域的盛会。受人工智能浪潮的影响大会的投稿数量和参会人数都有了明显增加。大会今年共收到了3300份论文投稿录取了979篇录取率将近30%。最终选出了70篇论文做口头报告224篇论文做快速汇报。近两年的参会人数都保持着近1千人的增长势头而今年更是达到了6千多人是2014年参会人数的3倍多。同时大会的审稿人也达到了惊人的1万人。
除了主会议以外CVPR大会还组织了21个讲座48个研讨班和博士论坛有超过115家公司的赞助。
想要在这么多论文里找到最有价值、最有影响力的信息可以说是大海捞针。我在这里为你精选了三篇今年CVPR的论文希望能够起到抛砖引玉的作用。
今天我们来分享大会的最佳论文题目是——Taskonomy: Disentangling Task Transfer Learning。
我先来简单介绍下论文的作者群。
第一作者阿米尔·扎米尔Amir R. Zamir目前是斯坦福大学和加州大学伯克利分校的博士后研究员已经在计算机视觉领域发表了30多篇论文还获得过CVPR 2016的最佳学生论文奖。
第二作者亚历山大·萨克斯Alexander Sax刚刚从斯坦福大学计算机系硕士毕业即将前往加州大学伯克利分校攻读博士已经以硕士生的身份发表了两篇CVPR论文。
第三作者沈博魁刚从斯坦福大学计算机系本科毕业即将在本校继续攻读博士。尽管是本科刚刚毕业他已经发表了2篇CVPR论文和1篇ICCV论文。
第四作者利昂奈达·圭巴斯Leonidas Guibas是斯坦福大学计算机系教授也是ACM和IEEE院士还是美国工程院和科学院院士。他的博士导师是图灵奖获得者高德纳Donald Knuth
第五作者吉腾德拉·马立克Jitendra Malik是加州大学伯克利分校计算机系教授也是ACM和IEEE院士并且是美国工程院以及科学院院士。马立克是计算机视觉方向的学术权威。
最后一位作者西尔维奥·萨瓦瑞斯Silvio Savarese是斯坦福大学计算机系的教授。他的研究方向是计算机视觉和计算机图形学。我们对华人学者李飞飞都很熟悉萨瓦瑞斯是李飞飞的丈夫。
论文的主要贡献
概括来说这篇论文主要是研究了计算机视觉任务之间的关系并且提出了一个计算框架能够定量地学习到这些任务之间的相似度。同时这些相似的任务可以帮助数据较少的任务达到比较好的效果。这其实就是迁移学习Transfer Learning的核心思想如何从已经学习到的任务或者领域迁移到数据较少、学习更加困难的任务或者领域。
很多研究人员在平时的研究过程中可能都会有这样的感觉一些计算机视觉任务之间有某种逻辑或者直觉上的联系。例如在计算机视觉界像物体识别Object Recognition、景深估计Depth Estimation、边界发掘Edge Detection以及姿势估计Pose Estimation这些任务大家都普遍认为它们是有关系的一系列任务。但是有一些视觉任务之间的关系则显得没有那么直观比如边界发掘和光影Shading如何帮助姿势估计就不得而知了。
如果我们单独来解决每一类任务,必然会有很大的挑战。这篇论文其实展示了,很多任务之间是有关联性的,而利用这些任务的关联性其实可以带来数据上的巨大便利。也就是说,我们可以利用更少的数据来学习到更多的任务。从这个角度来看,迁移学习也为新任务带来了希望,当我们没有大量的人工标注的数据时,依然能够在新任务上获得有效的结果。
这篇论文的另外一个重要贡献是提出了一个计算框架,这个框架并不需要事先准备的知识,比如人为地决定哪两个任务之间是有关联的,或者说,并不像之前的一些利用概率建模的方法,需要对任务之间的结构加以一个先验概率。这篇论文提出的框架完全从数据和结果的角度出发,从而避免了这些先验信息的不完整和不准确。
论文的核心方法
这篇论文提出的方法由四个组成部分,分别是:任务相关的建模、迁移建模、任务与任务关系归一化以及最后计算任务的关系图谱。每一个组成部分都有不同的目标。
首先,我们需要建立的是每一个独立任务自己的一个模型。这些模型有两个任务:第一就是尽可能地提高对自身任务的精度;第二就是在这个过程中,尽可能提取有代表性的中间表征结果,能够有助于迁移学习。
第二个部分就是迁移建模。这个部分主要是利用第一部分学习到的中间表现层,然后再在目标任务上学习到从原本的表现层到任务目标的迁移。这里面,除了一个原表现层,或者是原任务可以借鉴以外,作者们提出还可以利用多个原任务,来达到提升效果的目的。这样也就把多个任务和一个目标任务关联了起来。
第三个部分是任务关系的归一化。这一部分其实是这篇文章的一个亮点。当我们得到迁移学习的结果以后我们就可以利用每两个任务之间的关系来获得一个矩阵这个矩阵就完全表征了所有任务的联系。然而如果直接利用任务的迁移损失函数的值来刻画两个任务之间的关系那么每两个任务之间的这个数值其实是没办法直接比较的。如果我们采用机器学习界归一化数据的办法比如把数据归一到0和1之间也是不行的因为这样就完全没有考虑损失函数变化的速度和目标任务精度之间的关系。
所以,这篇论文的作者们提出了一种按照次序来做归一化的方法。简单来说,就是不再看两个任务之间的绝对的迁移数值,而是看在测试集上哪一个原任务相比于其他任务能够更多地获取目标任务的精度。这样所有的任务就可比了。总之,任务关系归一化的目的就是构建了任务与任务之间关系的矩阵。
最后一个部分的目的就是从这个关系矩阵中提取出所有任务的一个真正的关系图谱。也就是说我们希望从一个完全的全连通图找到一个最有价值的子图。在这里作者们采用了一种叫作“布尔值整数规划”Boolean Integer Programming的方法在一些限制条件下挖掘出了一个有代表性的子图。
实验结果
作者们提出了一个有4百多万张图片的新的数据集。在这个数据集里有26个计算机视觉任务。从实验中作者们挖掘出了这样一些情况例如3D的、2D的任务自然被归类到了一起而其他的例如上下文分割、景象识别这种高层次的任务则被分在了一起。
为了研究这种挖掘出的结构是否真的能够实现迁移学习的目的,作者们还把不同的两两任务随机组合在一起,也就是某种随机任务的图谱,按照学习到的结构进行迁移学习,看是不是比随机结果要好。答案是,的确要好很多。在这篇论文里,作者们展示了学习到的结构不仅能够帮助目标任务提升性能,而且在任务之间关系的解释性上效果也非常不错。
小结
今天我为你讲了CVPR 2018的最佳论文。
一起来回顾下要点:第一,我们详细介绍了这篇文章要解决的问题以及贡献,论文研究了计算机视觉任务之间的关系,并且提出了一个计算框架,能够起到迁移学习的作用;第二,我们简要介绍了文章提出的核心方法,主要有四个组成部分;第三,我们简单介绍了论文的实验结果。
最后,给你留一个思考题,当前挖掘的关系主要是任务的两两关系,能否有一个方法挖掘任务的高维度关系,比如三个任务之间的关系?

View File

@ -0,0 +1,79 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
023 CVPR 2018论文精读如何从整体上对人体进行三维建模
今天我们来分享CVPR大会的最佳学生论文标题是《全方位捕捉用于跟踪面部表情手势和身体运动的3D变形模型》Total Capture: A 3D Deformation Model for Tracking Faces, Hands and Bodies
很多学术会议都利用最佳学生论文这个奖项来鼓励学生参与学术研究活动,所以这个奖项的一般要求是第一作者必须是在校学生。
这篇论文的作者群来自卡内基梅隆大学。
第一作者周寒星Hanbyul Joo是来自韩国的学者目前在卡内基梅隆大学机器人学院The Robotics Institute攻读博士。他的博士论文方向是“计算行为科学”Computational Behavioral Science。他已经在计算机视觉方向发表了多篇CVPR、ICCV论文。
第二作者托马斯·西蒙Tomas Simon也是卡内基梅隆大学机器人学院的博士生。他的研究方向是“三维运动的时空建模”Spatiotemporal Modeling of 3D Motion
最后一位作者是这两位学生的导师亚瑟尔·舍艾克Yaser Sheikh是机器人学院的教授。
论文的主要贡献
这篇论文想要解决的问题很直观那就是希望对人体进行三维建模并且能够跟踪Track人体的行为以及活动。
这个任务看似简单,但有不少难点。
首先过去绝大多数的对人体进行三维建模的工作都是针对人体的不同部分分别进行的比如对脸部、对身体和对手部分别建模。在对这些部位进行建模的时候整体的设定都不太一样。例如对脸部的建模一般是近景Close-Up而对身体则主要是看身体的整体行动。也就是对于人体不同部位的建模经常在不同的尺度下进行那就无法把各个部分的建模很容易地对接上。
其次,还有一些人体的部位,过去并没有太多专门的建模工作,比如针对头发和脚,但这些在全身的建模中也是必不可少的部分。
这篇论文就加入了对头发和脚这些部分建模的讨论提供了对人体从整体上进行建模的一个框架。确切地说论文提供了两个模型一个叫“弗兰肯斯坦”Frankenstein一个叫“亚当”Adam
“弗兰肯斯坦”主要还是依靠对人体不同部分的建模,然后把这些模型连接起来,通过一些处理,能够让最终呈现的三维建模符合现实。在这个模型的基础上,“亚当”则加入了头发和脚的元素,抛弃了“弗兰肯斯坦”的一些特殊处理的地方,从模型的角度来说更加简单,并且达到了更加逼真的程度。
论文的核心方法
首先我们来看一看这个叫“弗兰肯斯坦”的模型。这个模型的思路是尽量把能够对人体各个部分建模的最好的模型先拼接到一起。总体来说每一个部分基本上都由三组参数组成运动参数Motion Parameters、形状参数Shape Parameters和全局翻译参数Global Translation Parameter
对于人的身体部分作者们采用了SMPL模型[1]。这个模型根据人体形状的均值和形状的变化量进行线性的叠加然后经过一个LBS变换来得到对身体部分的最终建模。
对人脸的部分作者们采用了一个叫FaceWarehouse的模型[2]。这个模型是根据人脸形状的均值、形状的变化量,以及动态的变化量来进行线性的叠加。
对于手而言目前并没有模型可以直接用。作者们在这篇论文中提出了自己的模型总的来说就是对手的骨架和关节进行建模然后进行类似于身体的LBS变换。同样也对人体的脚进行了类似的建模。
当我们有了身体、人脸、手和脚的模型以后,下面的工作就是把这些部分衔接上。首先,作者们保留了人体模型,移除这个模型上的人脸、手和脚。然后利用人脸模型、手的模型以及脚的模型相应的全局翻译参数,使得这些部分的坐标能够拼接上。最后,作者们还应用了一个“融合函数”来构建出一个平滑的人体结构。
“弗兰肯斯坦”的模型有一个四项的目标优化函数。这个函数的第一项是拟合“关键点”Key Points让人体的躯干骨架能够拟合上运动轨迹。第二项是拟合“三维点云”3D Point Cloud也就是让人体大部分躯体的实体能够拟合运动轨迹。第三项就是作者们附加的一个“小技巧”Heuristic用来把人体的每个部分连接在一起。这一项解决的就是整个模型设计带来的问题也就是每个部分都是单独的形状参数而并没有完全在模型上连接起来。最后一项是高斯先验概率用来帮助模型找到唯一的解。
在“弗兰肯斯坦”的基础上作者们开发了“亚当”模型。为了构建“亚当”他们捕捉了70个人体的形态数据首先构建这些人体的“弗兰肯斯坦”模型。在这些模型基础之上作者们加入了人体的头发和衣服的形态并且重新定义了整个模型的框架。
和“弗兰肯斯坦”相比“亚当”是对所有的人体部件直接进行建模。这个模型和我们前面描述的某个具体部分的模型其实很类似也是把人体的形态均值、形态的变化值和人脸表现值进行线性叠加然后进行LBS变换。
因为“亚当”在模型上进行了简化和创新,所以在目标优化函数中只有三项变量。而我们刚刚讲过的用于“弗兰肯斯坦”模型的小技巧在“亚当”中就变得不需要了。
实验结果
在实验中作者们使用了140个VGA照相机对三维身体的关键点进行重建用了480个VGA照相机对脚进行重建31个高清照相机用于脸部和手部关键点的重建以及三维点云的构建。
作者们显示了利用“弗兰肯斯坦”和“亚当”这两个模型对人体的三维运动进行建模。总体来说,这两个模型的表现非常相似。“亚当”因为有了头发和衣服的形态,在运动中显得更加真实。只是在有一些情况下,“亚当”构建的腿会显得有一些不协调的瘦,出现这种情况的原因,作者们归结于数据的缺失。
不过,从总体效果上来讲,这篇论文作为第一个对于人体的全身进行三维建模并动态跟踪的工作,应该算是达到了满意的结果。
小结
今天我为你讲了CVPR 2018的最佳学生论文。
一起来回顾下要点:第一,我们详细介绍了这篇文章要解决的问题,就是从整体上对人体的运动进行三维建模;第二,我们简要介绍了文章提出的两个模型,“弗兰肯斯坦”和“亚当”核心内容;第三,我们简单介绍了这篇论文所取得的不错的实验结果。
最后,给你留一个思考题,如果我们需要对“亚当”这个模型进行改进,你认为下一步应该做什么?
参考文献
M. Loper, N. Mahmood, J. Romero, G. Pons-Moll, and M. J. Black. SMPL: A Skinned Multi-Person Linear Model. In TOG, 2015.
C. Cao, Y. Weng, S. Zhou, Y. Tong, and K. Zhou. FaceWareHouse: A 3D Facial Expression Database for Visual Computing. In TVCG, 2014.

View File

@ -0,0 +1,77 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
028 ACL 2018论文精读问答系统场景下如何提出好问题
今年7月15日~20日计算语言学协会年会ACL 201856th Annual Meeting of the Association for Computational Linguistics在澳大利亚的墨尔本举行这是自然语言处理和计算语言学领域的顶级会议。
计算语言学协会ACL最早成立于1962年每年都赞助举行各种学术交流和研讨大会。ACL大会是ACL的旗舰会议可以说这个会议是了解自然语言处理每年发展情况的重量级场所。
会议今年收到了1018篇长论文和526篇短论文的投稿。最终大会接收了256篇长论文以及125篇短论文综合录用率达到24.7%。
今天我们来看这次会议的一篇最佳论文题目是《学习提出好问题使用完美信息的神经期望价值对澄清问题进行排序》Learning to Ask Good Questions: Ranking Clarification Questions using Neural Expected Value of Perfect Information
首先给你简单介绍下论文的作者。
第一作者萨德哈·饶Sudha Rao来自马里兰大学学院市分校University of Maryland, College Park是计算机系的博士生。她已经在ACLEMNLP、NAACL等自然语言处理大会上发表了多篇论文并且在微软研究院实习过。
第二作者是饶的导师哈尔·道姆三世Hal Daume III是马里兰大学学院市分校计算机系的一名教授目前也在纽约的微软研究院工作。他是机器学习和自然语言处理领域的专家在诸多领域都发表过不少研究成果论文引用数达到9千多次。
论文的主要贡献
这篇论文关注的是“问答系统”Question & Answering。问答系统不仅在实用领域受到大量用户的青睐产生了诸如Quora、知乎、Stack Overflow等知名的在线问答服务也在人工智能系统开发领域受到研究者的关注。
我们曾经提到过“图灵测试”,用来衡量一个系统或者说是一个机器人是否具有真正的人工智能,这个测试其实就是建立在人机问答的交互场景下的。因此,建立有效的问答系统一直是人工智能研究,特别是自然语言处理研究的核心课题之一。
这篇论文的作者们认为在问答系统的场景中一个非常重要的手段是针对已经提出的问题进行“澄清式”Clarification提问从而能够引导其他回答者更加有效地进行回答。也就是说作者们研究的主题是如何找到这些具有桥梁作用的“澄清式问题”这是这篇论文的第一个重要贡献。
论文的第二个主要贡献是利用了“决策论”Decision Theoretic框架下的EVPIExpected Value of Perfect Information完美信息的期望价值来衡量一个澄清式问题会对原始的问题增加多少有用的信息。简而言之EVPI就是这篇论文提出来的一个衡量有用信息的测度Measure
论文的第三个贡献是通过Stack Exchange平台Stack Overflow是其一个子站点构造了一个7万7千条含有澄清式问题的数据集。作者们从这个数据集中选取了500个样本进行了实验并且发现提出的模型要明显好于一些之前在问题系统中的类似算法。
论文的核心方法
既然这篇论文的一个核心贡献是提出了“澄清式提问”这么一个新的概念,用于帮助问答系统的开发。那么,究竟什么是“澄清式提问”呢?
实际上在这篇文章里,作者们并没有对“澄清式提问”给出一个清晰的定义,而是仅仅提供了一个实例来解释什么是“澄清式提问”。
例如一个用户在Ask Ubuntu这个子论坛里询问在安装APE程序包时遇到的问题。这个时候如果我们需要问“澄清式问题”究竟什么样的问题可以激发其他人或者提出澄清式问题的人来进一步解答原始的问题呢
我们看下面几个从不同角度提出的问题可以问这个用户使用的Ubuntu系统具体的版本号也可以问用户的WiFi网卡信息还可以问用户是不是在X86体系下运行Ubuntu。
那么,在这一个场景下,后两个问题要么无法为原始的问题提供更多有价值的信息,要么就是彻底的不相关,而第一个问题关于具体的版本号,很明显是用户可以提供的,并且可以帮助回答问题的人来缩小问题的范围。
这也带出了这篇论文的第二个贡献点,如何衡量一个帖子的价值呢?
要回答这个问题,我们需要知道这里有两种帖子是模型需要处理的。第一种帖子集合是候选的澄清式问题集合。第二种帖子集合是候选的最终回答集合。我们最终的目的是得到最佳的最终回答。这里面起到“搭桥”作用的就是澄清式问题。
所以作者们就构造了一个针对每一个最终问题的EVPI值用于衡量这个问题的“期望价值”。为什么是期望价值呢因为这里面有一个不确定的因素那就是根据不同的澄清式问题可能会产生不同的回答。因此作者们在这里使用了概率化的表达。
也就是说EVPI的核心其实就是计算给定当前的原始问题以及某一个澄清式回答的情况下某一个最终回答的概率乘以这个回答所带来的“收益”。当我们针对候选最终回答集合中所有的回答都进行了计算以后然后求平均就得到了我们针对某一个澄清式回答的EVPI。换句话说某一个澄清式回答的EVPI就是其所能产生的所有可能的最终回答的加权平均收益。
从上面这个定义中我们有两点不确定。第一我们并不知道给定当前的原始问题以及某一个澄清式回答的情况下某一个最终回答的条件概率第二我们并不知道问题的收益。因此作者们利用了两个神经网络模型来对这两个未知量进行联合学习Joint Learning。这可以算是本文在建模上的一个创新之处。
具体来说首先作者们利用LSTM来针对原始问题、候选澄清问题、以及最后解答产生相应的表达向量。然后原始问题和某一个候选澄清问题的表达向量通过一个神经网络产生一个综合的表达。最后作者们定义了一个目标函数来针对这些初始的表达向量进行优化。
这个目标是需要我们学习到的答案的表达靠近初始得到的答案的表达,同时,也要靠近最终答案的表达,如果这个最终答案所对应的问题也靠近原来的问题。换句话说,如果两个问题的表达相近,答案的表达也需要相近。
那什么样的问题是相近的问题呢作者们利用了Lucene这个信息检索工具根据一个原始的问题寻找相近的问题。这里作者们并没有真实的标签信息所以利用了一些方法来标注数据从而能够让模型知道两个问题是否相关。
论文的实验结果
作者们利用了Stack Exchange来构建一个分析澄清式问题的数据集。具体的思路是如果原始问题曾经被作者修改过那么后面的某一个帖子中所提出的问题就会被当作是澄清式问题而原始问题就被当作是因为澄清式问题而得以改进的帖子。很明显这是一个非常粗略的数据收集条件。当原始问题被作者修改过以后并且最后因为这个修改得到回复就被认为是一个最终的答案。经过这么一番构建作者们整理了7万7千多条帖子。
作者们利用论文提出的方式和其他的经典模型进行比较。最后的结论是,提出的模型能够更好地找到最佳的澄清式问题,效果要好于仅仅是简单利用神经网络,来匹配原始问题和相应的澄清式问题。
小结
今天我为你讲了ACL 2018的一篇最佳论文。
一起来回顾下要点:第一,这篇论文提出了“澄清式提问”这个概念,来帮助问答系统的开发;第二,文章提出了一系列方法,对澄清式问题进行描述和衡量;第三,文章构建了一个数据集,通过实验论证了所提出方法的有效性。
最后,给你留一个思考题,通过这篇文章关于澄清式问题的介绍,你能否给澄清式问题下一个定义呢?

View File

@ -0,0 +1,74 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
029 ACL 2018论文精读什么是对话中的前提触发如何检测
今天我来和你分享ACL 2018的第二篇最佳论文题目是《让我们“再”次做到检测副词前提触发词的第一种计算方法》Lets do it “again”: A First Computational Approach to Detecting Adverbial Presupposition Triggers
这篇论文的作者都来自加拿大麦吉尔大学McGill University的计算机系。前三位学生作者是这篇论文的共同第一作者对论文的贡献相同。他们的导师张智杰Jackie Chi Kit Cheung助理教授是这篇论文的最后一个作者。张智杰于2014年从多伦多大学博士毕业之前曾两次在微软研究院实习过他长期从事自然语言处理的研究。
论文的主要贡献
这篇论文的背景要从“语用学”Pragmatics说起。语用学是语言学的一个分支学科与符号学理论相互交叉、渗透研究语境对语言含义产生的影响和贡献。语用学包括言语行为理论、对话内涵义、交流中的对话以及从哲学、社会学、语言学以及人类学等角度解析人类语言行为的研究。
语用学分析研究语言行为(如招呼、回答、劝说)的文化准绳和发言规则。不同的文化之间皆有约定俗成、客套的对话,在跨文化交流中,为了避免因为语言规范的差异而在交谈之中产生误解,社会语言学的知识与务实能力是语言学习者所不能忽视的。
在语用学中“前提”Presuppositions是交谈的参与者共同约定的假设和认知而且在谈话中被广泛使用。同时在这篇论文中作者们把提示“前提”的“表达”Expression定义为“前提触发”Presupposition Triggers包括一些动词、副词和其他短语。为了更加清晰地说明这些概念作者们举了这么一个例子。
假设我们现在有两句话:
约翰再次要去那家餐厅John is going to the restaurant *again*)。
约翰已经去过了那家餐厅John has been to the restaurant
第一句话要能够成立必须要建立在第二句话的基础上。特别是“前提触发”词“再”Again的使用是建立在第二句话真实的情况下。换句话说第一句话必须在第二句话的上下文中才能够被理解。值得一提的是即便我们对第一句话进行否定“约翰不打算再去那家餐厅了”John is not going to the restaurant again依然需要第二句话的支持。也就是说“前提触发”词在这里并不受到否定的影响。
这篇论文的核心贡献就是对以副词为主的前提触发词进行检测。这里面包括“再”Again、“也”Also和“还”Still等。再此之前还没有对这方面词汇进行检测的学术研究工作。能够对这类前提触发词进行检测可以应用到文本的归纳总结Summarization和对话系统等场景中。
为了更好地研究这个任务作者们还基于著名的自然语言处理数据Penn Treebank和English Gigaword建立了两个新的数据集从而能够进行触发词的分类检测工作。最后作者们设计了一个基于“关注”Attention机制的时间递归神经网络RNN模型来针对前提触发词进行检测达到了很好的效果。
论文的核心方法
现在,我们来讨论这篇论文的一些细节。
首先我们来看看数据集是如何生成的。数据中的每一个数据点都是一个三元组分别是标签信息正例还是负例文本的单词文本单词所对应的“词类标签”或简称为POS标签例如动词、名词
数据点正例就表明当前数据包含前提触发词反之则是负例。另外因为我们需要检测的是副词性的前提触发词因此我们还需要知道这个词所依靠的动词。作者们把这个词叫作副词的“管理词”Governor
作者们首先针对文档扫描看是否含有前提触发词。当发现有前提触发词的时候提取这个触发词的管理词然后提取管理词前50个单词以及管理词后面到句子结束的所有的单词。这就组成了正例中的单词。当找到了所有的正例之后作者们利用管理词来构建负例。也就是说在文本中寻找哪些句子含有一样的管理词但并不包括后面的前提触发词这样的句子就是负例。
下面我们来看一下作者们提出模型的一些构成。从大的角度来说为了识别前提触发词作者们考虑了一个双向LSTM的基本模型架构在此之上有一个“关注机制”在不同的情况下来选择LSTM的中间状态。
具体来说,整个模型的输入有两部分内容。
第一部分是文本的单词进行了词向量Embedding的转换。我们已经反复看到了这是在自然语言处理场景中利用深度学习模型必不可少的步骤。这样做的好处就是把离散数据转换成了连续的向量数据。
第二部分是输入这些单词相对应的POS标签。和单词不一样的是POS标签依然采用了离散的特性表达。
然后连续的词向量和离散POS标签表达合并在一起成了双向LSTM的输入。这里利用双向LSTM的目的是让模型针对输入信息的顺序进行建模。跟我们刚才提到的例子一样前提触发词和其所依靠的动词在一个句子的段落中很明显是和前后的其他单词有关联的。因此双向LSTM就能够达到对这个结构进行记忆的目的并且提取出有用的中间变量信息。
下面需要做的就是从中间变量信息到最终的分类结果的变换。这里作者们提出了一个叫“加权池化网络”Weighted Pooling Network的概念并且和“关注”机制一起来进行这一步的中间转换。
可以说作者们这一步其实是借助了计算机视觉中的经常使用的卷积神经网络CNN中的池化操作来对文档进行处理。具体来说作者们把所有LSTM产生的中间状态堆积成一个矩阵然后利用同一个矩阵乘以其自身的转置就得到了一个类似于相关矩阵的新矩阵。可以说这个新矩阵是完全抓住了当前句子通过LSTM中间变量转换后所有中间状态的两两关系。
然后,作者们认为最后的分类结构就是从这个矩阵中抽取信息而得到的。至于怎么抽取,那就需要不同的权重。这种根据不同的情况来设置权重的机制就叫作“关注”机制。经过矩阵中信息的抽取,然后再经过全联通层,最终就形成了标准的分类输出。
论文的实验结果
作者们在我们上面提到的两个新数据集上进行了实验并且和一系列的方法进行了比较。其他的方法包括简单的对数几率回归方法Logistic Regression简化了的但是依然利用了双向LSTM结构的模型还有一个利用CNN来进行提取信息的模型。
在两个数据集上论文提出的方法比对数几率回归以及CNN的方法都要好10%20%左右。和简化的LSTM模型相比优势并没有那么大但依然有统计意义上的好效果。
小结
今天我为你讲了ACL 2018的另外一篇最佳论文。
一起来回顾下要点第一这篇论文的背景是语用学核心贡献是对以副词为主的前提触发词进行检测第二论文的核心方法是提出一个双向LSTM的基本模型架构并利用“关注机制”根据不同的情况来设置权重第三论文构建了两个数据集取得了较好的实验结果。
最后给你留一个思考题这篇论文使用了双向LSTM的架构能不能使用单向LSTM呢

View File

@ -0,0 +1,73 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
030 ACL 2018论文精读什么是端到端的语义哈希
今天我们来看今年ACL大会的一篇最佳论文提名题目是《NASH面向生成语义哈希的端到端神经架构》NASH: Toward End-to-End Neural Architecture for Generative Semantic Hashing
先来简单介绍下论文的作者群,我着重介绍三位。
第一作者沈丁涵Dinghan Shen音译是杜克大学计算机科学系的博士生。他已经发表了多篇自然语言处理和机器学习相关的论文并且在NEC实验室和微软研究院都实习过。
论文的共同第一作者苏勤亮Qinliang Su音译目前是中山大学数据科学与计算机学院的副教授。他在香港大学取得博士学位之后曾在杜克大学从事博士后研究工作。
作者中的劳伦斯·卡林Lawrence Carin是杜克大学教授。卡林是机器学习的权威也是沈丁涵的导师。
论文的主要贡献
在很多的应用中我们都需要根据一个已有的文档表达和一个文档库找到最相近的或者说最类似的文档。这经常被叫作“相似查找”Similarity Search或者“最近邻查找”Nearest-Neighbor Search在推荐系统、信息检索、图片检索等领域都有非常广泛的应用。
“语义哈希”Semantic Hashing被认为是解决“相似查找”的一个重要并且行之有效的方法。简单来说“语义哈希”要做的就是把文档表达为离散的也就是二元的向量。这些向量保留了文档在原始空间中的相似关系。因此常常被认为是带有语义的哈希过程这也就是“语义哈希”这个名字的来历。
当我们把文档转换为语义哈希空间之后文档之间相似度的计算就变成了利用“汉明距离”Hamming Distance来计算离散向量之间的距离。在当下的计算机体系架构中上百万文档之间的“汉明距离”都可以在几个毫秒间完成计算。因此我们可以看到“语义哈希”的一个优势就是计算快捷并且保持了原始空间的语义信息。
那么,看似这么有优势的“语义哈希”有没有什么劣势呢?
虽然已经有相当多的研究针对文字数据产生哈希,但是这些现有的方法都有一些明显的问题,其中最紧要的一个问题就是这些方法大多都需要两个阶段。
具体是哪些方法呢?我把这些方法归纳为两种思路。
第一种思路我们首先需要在无监督的条件下学习文档的二元哈希然后我们需要训练L个二元分类器来预测L个二元位的哈希值这个步骤是监督学习过程。
第二种思路,我们首先针对文档学习连续的表达向量,然后在测试阶段再把连续值进行二元离散化。
很明显不管是哪一种思路这种两个步骤的方法都不可避免地会仅仅得到次优的结果。这是因为两个步骤的优化流程是脱节的。而且在从连续的表达向量到二元离散化的过程中往往利用的是经验法则Heuristic因此语义信息可能被丢失。
基于这些问题这篇论文提出了“端到端”End-to-End的“语义哈希”训练过程。作者们认为经过一个阶段就可以得到完整哈希值的研究工作这篇文章是第一个。在此之上作者们利用了最新的NVI框架Neural Variational Inference神经化的变分推断来学习文档的二元编码在无监督和监督环境下都取得了不错的结果。
这篇论文的另一个贡献就是在提出的方法和“比率损失理论”Rate Distortion Theory之间建立了联系。在这个联系的基础上作者们展示了如何在模型的训练过程中“注入”Inject“数据相关的噪音”Data-Dependent Noise来达到更好的效果。
论文的核心方法
作者们首先把从文档生成“语义哈希”看作是一种编码Encode和解码Decode的流程。文档的二元哈希向量则被看成了表达文档的一种隐变量Latent Variable。也就是说作者们认为文档的哈希向量是从文档的特性可以是TF-IDF值产生的一组隐变量这也被认为是一种编码的过程是从文档的特性向量到哈希向量的编码。
在过去的模型中,编码过程是被反复关注的,但是解码过程则很少有模型去直接建模。所谓的解码过程就是从已经产生的哈希向量转换成为文档的特性向量的过程。也就是说,我们希望能够重新从哈希向量中生成原始的数据。
对原始数据和中间隐变量的编码过程统一进行建模,是当前神经网络生成式模型的一种标准方法。在这里,编码和解码都各自有不同的神经网络,用于表达相应的条件概率分布。
具体来说数据的原始信息X首先经过一个多层感知网然后再变换成为二元的中间变量Z。这时候Z其实就是我们需要得到的哈希向量了。只不过在提出的模型中还有第二个部分那就是从Z得到X的一个重现也就是我们刚才提到的利用哈希来重构数据。很明显我们希望重构的X和原始的X之间要非常相似也就是说距离最小。
作者们发现从数据中学习一个二元编码是“信息论”Information Theory中典型的“有损源编码”Lossy Source Coding问题。因此“语义哈希”其实也可以被看作是一个“比率损失平衡”Rate Distortion Tradeoff问题。
什么意思呢?就是说,我们希望用较少的比率来对信息进行编码,同时又希望从编码中重构的数据能够和原始的数据尽量相近。很明显,这两者有一点“鱼与熊掌不可兼得”的意思,也就是这两者需要一个平衡才能达到最优。
把重写模型的目标函数定为“比率损失平衡”,通过这种形式,作者们意识到模型中的从编码到重构数据的条件分布,也就是一个高斯分布中的方差值,其实控制了这个平衡的关系。那么,就需要针对不同的文档对这个方差值进行调整,从而达到最优的编码效果,同时又是比率损失平衡的。作者们并没有采用去优化这个方差值的办法,而是在一个固定的方差值周围加入一些随机噪声,从而在实际实验中收到了不错的效果。
论文的实验结果
作者们利用了三个数据集进行实验所有的数据集都首先转换成为TF-IDF的形式。作者们把提出的方法和其他的五种基本方法进行了比较。
从总体上来说,文章提出的方法在没有随机噪声的情况下,已经比其他五种方法要好得多。加入随机噪声之后,模型就有了更好的表现力。同时,作者还展示了学到的二元哈希值的确能够保持语义信息,相同文本类别的文档,它们的哈希值非常类似,也就是我们之间说过的,他们之间的汉明距离很近。
小结
今天我为你讲了今年ACL的一篇最佳论文提名至此我们关于ACL 2018的分享就告一段落。
一起来回顾下要点:第一,这篇文章针对语义哈希产生过程的劣势,提出了“端到端”的语义哈希训练过程;第二,论文的核心方法是把文档生成语义哈希看作是一种编码和解码的流程,进一步发现“语义哈希”其实也可以被看作是一个“比率损失平衡”问题;第三,论文取得了不错的实验效果。
最后,给你留一个思考题,在现实中利用语义哈希,有没有什么障碍?比如要在推荐系统中做语义哈希,最大的挑战会是什么?

View File

@ -0,0 +1,81 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
032 经典搜索核心算法BM25及其变种内附全年目录
周一我们讲了TF-IDF算法和它的四个变种相对于TF-IDF而言在信息检索和文本挖掘领域BM25算法则更具理论基础而且是工程实践中当仁不让的重要基线Baseline算法 。BM25在20世纪70年代到80年代被提出到目前为止已经过去二三十年了但是这个算法依然在很多信息检索的任务中表现优异是很多工程师首选的算法之一。
今天我就来谈谈BM25算法的历史、算法本身的核心概念以及BM25的一些重要变种帮助你快速掌握这个信息检索和文本挖掘的利器。
BM25的历史
BM25有时候全称是Okapi BM25是由英国一批信息检索领域的计算机科学家开发的排序算法。这里的“BM”是“最佳匹配”Best Match的简称。
BM25背后有两位著名的英国计算机科学家。第一位叫斯蒂芬·罗伯逊Stephen Robertson。斯蒂芬最早从剑桥大学数学系本科毕业然后从城市大学City University获得硕士学位之后从伦敦大学学院University College London获得博士学位。斯蒂芬从1978年到1998年之间在城市大学任教。1998年到2013年间在微软研究院剑桥实验室工作。我们之前提到过美国计算机协会ACM现在每三年颁发一次“杰拉德·索尔顿奖”用于表彰对信息检索技术有突出贡献的研究人员。2000年这个奖项颁给斯蒂芬奖励他在理论方面对信息检索的贡献。BM25可谓斯蒂芬一生中最重要的成果。
另外一位重要的计算机科学家就是英国的卡伦·琼斯Karen Spärck Jones。周一我们在TF-IDF的文章中讲过。卡伦也是剑桥大学博士毕业并且毕生致力于信息检索技术的研究。卡伦的最大贡献是发现IDF以及对TF-IDF的总结。卡伦在1988年获得了第二届“杰拉德·索尔顿奖”。
BM25算法详解
现代BM25算法是用来计算某一个目标文档Document相对于一个查询关键字Query的“相关性”Relevance的流程。通常情况下BM25是“非监督学习”排序算法中的一个典型代表。
顾名思义,这里的“非监督”是指所有的文档相对于某一个查询关键字是否相关,这个信息是算法不知道的。也就是说,算法本身无法简单地从数据中学习到相关性,而是根据某种经验法则来“猜测”相关的文档都有什么特质。
那么BM25是怎么定义的呢我们先来看传统的BM25的定义。一般来说经典的BM25分为三个部分
单词和目标文档的相关性
单词和查询关键词的相关性
单词的权重部分
这三个部分的乘积组成某一个单词的分数。然后,整个文档相对于某个查询关键字的分数,就是所有查询关键字里所有单词分数的总和。
我们先从第一部分说起即单词和目标文档的相关性。这里相关性的基本思想依然是“词频”也就是TF-IDF里面TF的部分。词频就是单词在目标文档中出现的次数。如果出现的次数比较多一般就认为更相关。和TF-IDF不同BM25最大的贡献之一就是挖掘出了词频和相关性之间的关系是非线性的这是一个初看有违常理但细想又很有道理的洞察。
具体来说每一个词对于文档相关性的分数不会超过一个特定的阈值。这个阈值当然是动态的根据文档本身会有调整。这个特征就把BM25里的词频计算和一般的TF区分开了。也就是说词频本身需要“标准化”Normalization要达到的效果是某一个单词对最后分数的贡献不会随着词频的增加而无限增加。
那BM25里词频的标准化是怎么做的呢就是某一个词的词频除以这个词的词频加上一个权重。这个权重包含两个超参数Hyper-parameter这些超参数后期是可以根据情况手动调整的。这个做法在非监督的排序算法中很普遍。同时这个权重还包括两个重要信息第一当前文档的长度第二整个数据集所有文档的平均长度。
这几个因素混合在一起,我们就得到了一个新的词频公式,既保证单词相对于文档的相关度和这个单词的词频呈现某种正向关系,又根据文档的相对长度,也就是原始长度和所有文档长度的一个比值关系,外加一些超参数,对词频进行了限制。
有了单词相对于文档的相关度计算公式作为基础,单词相对于查询关键字的相关度可以说是异曲同工。首先,我们需要计算单词在查询关键字中的词频。然后,对这个词频进行类似的标准化过程。
和文档的标准化过程唯一的区别这里没有采用文档的长度。当然对于查询关键字来说如果需要使用长度也应该是使用查询关键字的长度和平均长度。但是根据BM25经典公式来说这一部分并没有使用长度信息进行重新标准化。
接着我来谈谈最后一个部分,单词权重的细节,通常有两种选择。
第一种选择就是直接采用某种变形的IDF来对单词加权。一般来说IDF就是利用对数函数Log函数对“文档频率”也就是有多少文档包含某个单词信息进行变换。这里回顾一下周一讲的内容IDF是“文档频率”的倒数并且通过对数函数进行转换。如果在这里使用IDF的话那么整个BM25就可以看作是一个某种意义下的TF-IDF只不过TF的部分是一个复杂的基于文档和查询关键字、有两个部分的词频函数。
第二种单词的权重选择叫作“罗伯逊-斯巴克-琼斯”权重Robertson-Spärck-Jones简称RSJ值是由计算机科学家斯蒂芬·罗伯逊和卡伦·琼斯合作发现。我们刚才讲过这两位都是重要的信息检索学术权威。这个权重其实就是一个更加复杂版本的IDF。一个关键的区别是RSJ值需要一个监督信息就是要看文档对于某个查询关键字是否相关而IDF并不需要。
对比以上两种思路在很多情况下利用IDF来直接进行单词权重的版本更加普遍。如果在有监督信息的情况下RSJ值也不失为一个很好的选择。
通过这里简单的介绍我们可以很容易地发现BM25其实是一个经验公式。这里面的每一个成分都是经过很多研究者的迭代而逐步发现的。很多研究在理论上对BM25进行了建模从“概率相关模型”Probabilistic Relevance Model入手推导出BM25其实是对某一类概率相关模型的逼近。对这一部分我在这里就不展开论述了。需要你记住的是BM25虽然是经验公式但是在实际使用中经常表现出惊人的好效果。因此很有必要对这一类文档检索算法有所了解。
BM25算法变种
由于BM25的情况一方面是经验公式另一方面是某种理论模型的逼近这样就出现了各式各样的BM25变种。这里我仅仅介绍一些有代表性的扩展。
一个重要的扩展是BM25F也就是在BM25的基础上再多个“域”Field文档上的计算。这里“域”的概念可以理解成一个文档的多个方面。比如对于很多文档来说文档包括标题、摘要和正文。这些组成部分都可以认为是不同的“域”。那么如何结合不同的“域”让文档的相关性能够统一到一个分数上就是BM25F的核心内容。
具体来说BM25F对于BM25的扩展很直观。那就是每一个单词对于文档的相关性是把各个域当做一个“小文档”的加权平均。也就是说我们先把每个域当做单独的文档计算词频进行标准化。然后集合每个域的值进行加权平均再乘以词的权重我们上面提到了用IDF或者是RSJ值
另外一个重要的扩展就是把BM25和其他文档信息非文字结合起来。这个想法是在“学习排序”Learning To Rank这一思路出现以前的一种普遍的做法往往就是用线性加权的形式直接把各种信息相结合。例如在21世纪初期比较流行的做法是用BM25和PageRank的线性结合来确定网页的相关度。这里面BM25是和某个查询关键字有联系的信息而PageRank则是一个网页的总体权重。
小结
今天我为你讲了文档检索领域或者说搜索领域里最基本的一个技术BM25。我们可以看到BM25由三个核心的概念组成包括词在文档中相关度、词在查询关键字中的相关度以及词的权重。BM25是一个长期积累的经验公式也有很深的理论支持是一个强有力的非监督学习方法的文本排序算法。
一起来回顾下要点第一简要介绍了BM25的历史。第二详细介绍了BM25算法的三个主要组成部分。第三简要地介绍了BM25的一些变种 。
最后给你留一个思考题虽然BM25是非监督的排序方法并且我们提到其中有一些超参数那么是否可以通过机器学习的手段来学习到这些超参数的最佳取值呢

View File

@ -0,0 +1,87 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
033 经典搜索核心算法:语言模型及其变种
在信息检索和文本挖掘领域我们之前已经讲过了TF-IDF算法和BM25算法。TF-IDF因其简单和实用常常成为很多信息检索任务的第一选择BM25则以其坚实的经验公式成了很多工业界实际系统的重要基石。
然而在信息检索研究者的心里一直都在寻找一种既容易解释又能自由扩展并且在实际使用中效果显著的检索模型。这种情况一直到20世纪90年代末、21世纪初才得到了突破一种叫“语言模型”Language Model的新模型得到发展。其后10多年的时间里以语言模型为基础的各类变种可谓层出不穷成了信息检索和搜索领域的重要研究方向。
今天我就来谈谈语言模型的历史,算法细节和语言模型的重要变种,帮助初学者快速掌握这一模型。
语言模型的历史
语言模型在信息检索中的应用开始于1998年的SIGIR大会International ACM SIGIR Conference on Research and Development in Information Retrieval国际信息检索大会。来自马萨诸塞州大学阿姆赫斯特分校UMass Amherst的信息检索学者杰·庞特Jay M. Ponte和布鲁斯·夸夫特W. Bruce Croft发表了第一篇应用语言模型的论文从此开启了一个新的时代。
布鲁斯是信息检索的学术权威。早年他在英国的剑桥大学获得博士学位之后一直在马萨诸塞州大学阿姆赫斯特分校任教。他于2003年获得美国计算机协会ACM颁发的“杰拉德·索尔顿奖”表彰他在信息检索领域所作出的突出贡献。另外布鲁斯也是ACM院士。
从那篇论文发表之后,华人学者翟成祥对于语言模型的贡献也是当仁不让。他的博士论文就是系统性论述语言模型的平滑技术以及各类语言模型的深刻理论内涵。
翟成祥来自中国的南京大学计算机系并于1984年、1987年和1990年分别获得南京大学的学士、硕士和博士学位2002年他从美国卡内基梅隆大学计算机系的语言与信息技术研究所获得另外一个博士学位。
翟成祥曾经获得过2004年的美国国家科学基金会职业生涯奖NSF CAREER Award和2004年ACM SIGIR最佳论文奖。另外2004年翟成祥还获得了著名的美国总统奖PECASEPresidential Early Career Award for Scientists and Engineers
语言模型详解
语言模型的核心思想是希望用概率模型Probabilistic Model来描述查询关键字和目标文档之间的关系。语言模型有很多的类型最简单的、也是最基础的叫做“查询关键字似然检索模型”Query Likelihood Retrieval Model。下面我就来聊一聊这个模型的一些细节。
首先,我来描述什么是语言模型。简单来说,一个语言模型就是一个针对词汇表的概率分布。比如,词汇表总共有一万个英语单词,那么一个语言模型就是定义在这一万个单词上的离散概率分布。拿骰子来做类比,这里的骰子就有一万种可能性。
一旦语言模型的概念形成。“查询关键字似然检索模型”的下一步就是认为查询关键字是从一个语言模型中“抽样”Sample得到的一个样本。什么意思呢 就是说,和我们通常情况下从一个概率分布中抽样相同,“查询关键字似然检索模型”认为查询关键字是从这个语言模型的概率分布中进行采样,从而产生的一个随机过程。这一观点不仅是这个简单语言模型的假设,也是很多语言模型的核心假设。
我们假设这个语言模型也就是这个概率分布的参数已知那么如何来对一个查询关键字打分Scoring就变成了计算在这个概率分布的情况下一组事件也就是这组词出现的联合概率。现实中因为联合概率可能会很小因此很多时候都通过一个对数变换来把概率的乘积变成概率对数的加和。
然而,现实情况是,我们事先并不知道这个语言模型的参数,这个信息一般来说是未知的。
要想确定这个语言模型的参数我们首先要确定语言模型的形态。我刚才说过语言模型本质上就是定义在词汇表上的离散概率分布。那么这里就有几种经典的选择。首先我们可以选择“类别分布”Categorical Distribution函数也就是多项分布Multinomial Distribution去除排列组合信息。这也是最常见的语言模型的实现形式。
在类别分布的假设下,我们认为每一个单词都是从类别分布中采样得到的结果,而单词之间互相独立。那么,定义在一万个单词上的类别分布就有一万个参数。每个参数代表所对应的单词出现的概率,或者说可能性。当然,这个参数是未知的。
除了利用类别分布或者多项分布来对语言模型建模以外其他的离散概率分布也都曾被提出来用作语言模型。比如伯努利分布Bernoulli Distribution或者泊松分布Poisson Distribution。这些不同的假设我今天就不展开讲了。但是在实际应用中其他概率分布假设的语言模型基本上都还属于纯研究状态。
还是回到刚才说的基于类别分布的语言模型。由于参数是未知的,那么问题的核心就变成了如何估计这样的参数,这里就回归到基本的统计参数估计的范畴。
因为类别分布是概率分布在有观测数据的情况下这个的观测数据就是现实中的文档和查询关键字最直接的参数估计算法叫“最大似然估计”Maximum Likelihood Estimation。在这里我不展开这个算法的细节。
最大似然估计的核心思路就是把参数估计问题变换成一个最大化的优化问题,从而通过求解这个优化问题来达到参数估计的目的。在类别分布的假设下,最大似然估计的最优参数解,恰好有解析形式。
也就是说,在有数据的情况下,我们能够得到一个唯一的最优的参数估计。而且这个解非常直观,也就是每个单词出现的可能性,正好等于这个单词在目标文档中出现的次数,除以所有单词在目标文档中出现的次数。换句话说,每个单词的参数正好等于单词出现的频率。
这样的话,每个文档都对应一个类别分布。有多少个文档就有多少个类别分布,而且每个类别分布都可以从自己这个文档中求得所有的参数。
最大似然估计有一个很大的问题,那就是如果某一个单词没有在训练数据中出现过,那么这个单词的参数,根据上面的最优解,就是零。
什么意思呢?也就是说,在最大似然估计的情况下,没有出现过的单词的参数是零,然后模型认为这个词出现的可能性、或者概率就是零。这显然是一个非常悲观的估计。因为你可以认为,不管在任何情况下,就算一个单词没有出现过,但是出现的概率也不应该绝对是零。
那么如何针对这些为“零”的概率估计就成了语言模型研究和实践中的一个重要问题。一个通常的技术叫“平滑”Smoothing。这个技术的基本思想就是给这些因为最大似然估计所产生的零值一些非零的估计值。最简单的一个做法其实也是很有效的一个做法就是通过整个数据集的频率来做平滑。
具体来说,就是对于每一个词,我们计算一个目标文档的频率,同时也计算一个全数据集的平率。然后这个单词的最终估计值,是这两个频率的一个加权平均。这个权重就成了另外一组超参数,可以动态调整。
另外一个常见的平滑策略是借助贝叶斯统计推断Bayesian Inference的方法。也就是说为类别概率分布加上一个先验分布通常是狄利克雷分布Dirichlet Distribution并且计算出某个单词在先验分布和数据都存在情况下的后验概率我这里就不展开这个思路了。
在这里需要注意的是经过研究人员发现语言模型的平滑其实是不可或缺的。一方面是为了解决我们刚才提到的零概率估计问题另一方面经过一个代数变形语言模型的平滑其实可以写成一个类似TF-IDF的形式。
于是研究人员指出这个平滑其实削减了过分流行词汇的概率使最后的估计值并不完全只是由单词的多少而决定。我在之前介绍TF-IDF算法和BM25算法的时候都分别提到了这个观点那就是单词出现的多少和相关性的关系问题。从经验上看这个关系一定是有一个阈值的。
语言模型变种
语言模型有很多类型的变种,我这里简单地提两个比较有代表的方向。
一个方向就是我刚才说的不同类型的平滑策略,比如,结合全数据集平滑和狄利克雷平滑。或者是先把文档分成一些聚类或者不同的类别(例如不同的话题),然后根据不同的类别或者话题进行平滑等等。
另外一个方向其实就是在语言模型本身的的定义上做文章。比如,在查询关键字似然检索模型里,我们假定有一个语言模型,查询关键字是这个模型的一个抽样。乍一看这很有道理,但是仔细一想,这个模型并没有明说目标文档和查询关键字之间的关系。目标文档进入视野完全是为了估计这个语言模型的参数,“相关性”这个概念并没有明确定义。
那么,另外一个主流的语言模型,就是认为有两个模型(分布)。查询关键字从一个分布中产生,目标文档从另外一个分布中产生,而这两个分布的距离,成为了相关性的定义。在这样的结构下,文档和查询关键字形成了一种对称的局面,而相关性也根据距离直接得到定义。
小结
今天我为你讲了文档检索领域或者说搜索技术里一个很有理论深度的技术语言模型。我们可以看到语言模型相对于TF-IDF以及BM25而言其实更加直观更好理解。语言模型也是一个强有力的非监督学习方法的文本排序算法。
一起来回顾下要点:第一,简要介绍了语言模型的历史。第二,详细介绍了简单语言模型,即“查询关键字似然检索模型”的主要组成部分。第三,简要地介绍了语言模型的两个变种方向。
最后,给你留一个思考题,如果根据语言模型,也就是概率分布函数的估计,无法得到我们之前提到的最优解析解的话,我们应该怎么求解语言模型的参数呢?

View File

@ -0,0 +1,71 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
034 机器学习排序算法:单点法排序学习
在专栏里我们已经讲解过最经典的信息检索技术。这些技术为2000年之前的搜索引擎提供了基本的算法支持。不管是TF-IDF、BM25还是语言模型Language Model这些方法和它们的各类变种在很多领域不限文本都还继续发挥着作用。
然而自从机器学习的思想逐渐渗透到信息检索等领域之后一个最直观的想法就是如何用机器学习来提升信息检索的性能水平。这个思想引领了2000年到2010年这个领域的研究产生了各类基于机器学习的排序算法也带来了搜索引擎技术的成熟和发展。
我今天就从最简单也是最实用的一类机器学习排序算法讲起那就是单点法排序学习Pointwise Learning to Rank。这类方法在工业界非常实用得到广泛应用在实际效果中也表现得很强健Robust。同时理解这类模型可以为后面学习复杂的排序算法打下基础。
单点法排序学习的历史
早在1992年德国学者诺伯特·福尔Norbert Fuhr就在一篇论文中最早尝试了用机器学习来做搜索系统参数估计的方法。三年之前他还发表过一篇利用“多项式回归”Polynomial Regression来做类似方法的论文。诺伯特因其在信息检索领域的长期贡献于2012年获得美国计算机协会ACM颁发的“杰拉德·索尔顿奖”。
1992年加州大学伯克利分校的一批学者在SIGIR上发表了一篇论文文中使用了“对数几率”Logistic Regression分类器来对排序算法进行学习。可以说这篇论文是最早利用机器学习思维来解决排序算法学习的尝试。然而由于机器学习和信息检索研究在当时都处于起步阶段这些早期结果并不理想。
2000年之后支持向量机在工业界和学术界逐渐火热随之利用机器学习进行排序算法训练重新进入人们的视野。搜索引擎成了第一次互联网泡沫的重要阵地各类搜索引擎公司都开始投入使用机器学习来提升搜索结果的精度。这股思潮开启了整整十年火热的机器学习排序算法的研究和开发。
单点法排序学习详解
要想理解单点法排序学习,首先要理解一些基本概念。这些基本概念可以帮助我们把一个排序问题转换成一个机器学习的问题设置,特别是监督学习的设置。
我之前介绍的传统搜索排序算法比如TF-IDF、BM25以及语言模型都是无监督学习排序算法的典范也就是算法本身事先并不知道哪些文档对于哪些关键字是“相关”的。这些算法其实就是“猜测”相关性的一个过程。因此传统信息检索发展出一系列理论来知道算法对每一个“关键字和文档对”Query-Document Pair进行打分寄希望这样的分数是反映相关性的。
然而,从现代机器学习的角度看,毫无疑问,这样的排序算法不是最优的,特别是当相关信息存在的时候,是可以直接用这些相关信息来帮助算法提升排序精度的。
要想让训练排序算法成为监督学习,首先来看需要什么样的数据集。我们要建模的对象是针对每一个查询关键字,对所有文档的一个配对。也就是说,每一个训练样本至少都要包含查询关键字和某个文档的信息。这样,针对这个训练样本,就可以利用相关度来定义样本的标签。
在极度简化的情况下如果标签定义为某个文档针对某个关键字是否相关也就是二分标签训练排序算法的问题就转换成了二分分类Binary Classification的问题。这样任何现成的二分分类器几乎都可以在不加更改的情况下直接用于训练排序算法。比如经典的“对数几率”分类器或者支持向量机都是很好的选择。
我们说这样的方法是“单点法排序学习”Pointwise Learning to Rank是因为每一个训练样本都仅仅是某一个查询关键字和某一个文档的配对。它们之间是否相关完全不取决于其他任何文档也不取决于其他关键字。也就是说我们的学习算法是孤立地看待某个文档对于某个关键字是否相关而不是关联地看待问题。显然单点法排序学习是对现实的一个极大简化但是对于训练排序算法来说是一个不错的起点。
知道了如何构建一个训练集以后,我们来看一看测试集,重点来看如何评估排序算法的好坏。测试集里的数据其实和训练集非常类似,也是“查询关键字和文档对”作为一个样本。标签也是这个“配对”的相关度信息。前面说了,如果这是一个二分的相关信息,那么评估排序算法其实也就变成了如何评估二分分类问题。
对二分分类问题来说有两个主要的评价指标第一精度Precision也就是说在所有分类器已经判断是相关的文档中究竟有多少是真正相关的第二召回Recall即所有真正相关的文档究竟有多少被提取了出来。
因为是排序问题和普通二分分类问题不太一样的是这里就有一个Top-K问题。什么意思呢就是说针对某一个查询关键字我们不是对所有的文档进行评估而只针对排序之后的最顶部的K个文档进行评估。
在这样的语境下精度和召回都是定义在这个K的基础上的。要是没有这个K的限制在全部数据情况下精度和召回都退回到了“准确度”这个最基本的分类问题的评估测量情形。
在实际的应用中K的取值往往是很小的比如3、5、10或者25而可能被评分的文档的数量是巨大的理论上来说任何一个文档对于任何一个查询关键字来说都有可能是潜在相关对象。所以在评价排序算法的时候这个K是至关重要的简化问题的方法。
除了精度和召回以外信息检索界还习惯用F1值对排序算法进行评估。简单来说F1值就是精度和召回“和谐平均”Harmonic Mean的取值。也就是说F1结合了精度和召回并且给出了一个唯一的数值来平衡这两个指标。需要指出的是在很多实际情况中精度和召回是类似于“鱼与熊掌不可兼得”的一组指标。所以F1值的出现让平衡这两个有可能产生冲突的指标变得更加方便。
刚才我说的评估主要是基于二分的相关信息来说的。而相关的标签信息其实可以定义为更加丰富的多元相关信息。比如,针对某一个查询关键字,我们不再只关心某个文档是否相关,而是给出一个相关程度的打分,从“最相关”、“相关”、“不能确定”到“不相关”、“最不相关”,一共五级定义。在这种定义下,至少衍生出了另外两个评价排序算法的方法。
我们可以使用多类分类Multi-Class Classification的评价方法也就是把五级相关度当做五种不同的标签来看分类器的分类准确度。当然这样的评价方式对于排序来说是有问题的。因为对于一个实际的数据集来说五种相关类型所对应的数据量是不同的。
一般来说,“最相关”和“相关”的文档数量,不管是针对某个查询关键字还是从总体上来看,都是比较少的,而“不相关”和“最不相关”的文档是大量的。因此,单单看分类准确度,很可能会得出不恰当的结果。
比如说,某个排序算法能够通过分类的手段把大量的“最不相关”和“不相关”的文档分类正确,而可能错失了所有的“最相关”文档。即便从总的分类准确度来说,这样的算法可能还“看得过去”,但实际上这样的算法并没有任何价值。所以,从多类分类的角度来评价排序算法是不完整的。
针对这样的情况研究者们设计出了基于五级定义的排序评价方法NDCGNormalized Discounted Cumulative Gain。在这里针对NDCG我就不展开讨论了你只需要知道NDCG不是一个分类的准确度评价指标而是一个排序的精度指标。
NDCG这个指标的假设是在一个排序结果里相关信息要比不相关信息排得更高而最相关信息需要排在最上面最不相关信息排在最下面。任何排序结果一旦偏离了这样的假设就会受到“扣分”或者说是“惩罚”。
需要特别指出的是我们这里讨论的NDCG仅仅是针对测试集的一个排序评价指标。我们的排序算法依然可以在训练集上从五级相关度上训练多类分类器。仅仅是在测试集上采用了不同的方法来评价我们的多类分类器结果而不是采用传统的分类准确度。从某种意义上来说这里的NDCG其实就起到了“模型选择”Model Selection的作用。
小结
今天我为你讲了单点法排序学习。可以看到,整个问题的设置已经与传统的文字搜索技术有了本质的区别 。
一起来回顾下要点第一单点法排序学习起步于20世纪90年代直到2000年后才出现了更多有显著效果的研究。第二详细介绍了单点法排序学习的问题设置包括训练集、测试集以及测试环境。
最后给你留一个思考题有没有什么方法可以把我们之前讨论的TF-IDF、BM25和语言模型这些传统的排序算法和单点法排序学习结合起来

View File

@ -0,0 +1,70 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
039 查询关键字理解三部曲之扩展
我们在本周的前两篇文章中分别介绍了查询关键字分类Query Classification和查询关键字解析Query Parsing的基本概念和思想。今天我来讲一个稍微有一些不同的查询关键字理解模块查询关键字扩展Query Expansion
查询关键字扩展想要解决的问题和分类以及解析略微不同。其主要目的不仅仅是希望能够对用户输入的关键字进行理解,还希望能够补充用户输入的信息,从而达到丰富查询结果的效果。
查询关键字扩展的概念
为什么要提供查询关键字扩展主要原因还是用户输入的查询关键字信息不足。还记得我们上次提到的“苹果价格”这个例子吗在这个例子中用户到底是希望查询“苹果”作为一种水果的价格还是“苹果”作为手机的价格其实无法真正从这个查询关键字中得出。因此作为搜索引擎如果为用户提供一些“扩展选项”也就是一个被改写Reformulated过的查询关键字会提供更加好的用户体验和更加精准的搜索结果。
查询关键字扩展除了显示出来能够让用户有更好的体验以外还有一个作用是增加文档的“召回”Recall从而为提高搜索结果奠定基础。设想这样一个例子用户搜索“iphone 6 backup”希望了解如何备份iPhone 6的信息。因为苹果手机的绝大多数机型的备份流程都大同小异因此如果把“iphone 6”给扩展到“iphone”其他机型然后看是否有比较好的介绍备份的网页可以显示。
值得注意的是在扩展的过程中也有可能失去“精度”Precision。比如假设苹果对iPhone 7的备份流程做了很大的改进那么其他机型的流程也许就不适用了所以当用户搜索“iphone 7 backup”的时候如果我们扩展到了其他机型那让用户看到的很可能就是不那么相关的信息了。因此对“精度”和“召回”的平衡成了查询关键字扩展的一个重要的权衡点。
查询关键字扩展的另外一个重要应用就是对同义词和缩写的处理。比如唐纳德·特朗普Donald Trump是美国现任总统。那么如果用户在搜索“Donald Trump”、“Trump”、“US President”、“POTUS”这是“President Of The United States”的简称等类似词汇的时候搜索引擎应该提供相似的结果。而从词汇的直接联系上这些词汇在表面形式上可能有很大的差异比如“Trump”和“POTUS”因此需要其他手段学习到这些词语内涵的同义。
查询关键字扩展的技术
知道了查询关键字扩展的含义以后,我们就来看看有哪些技术可以为查询关键字扩展提供支持。
根据上面提供的一些例子,你可以看到,这里的核心就是找到搜索结果意义上的“同义词”。那么,在搜索中,如何挖掘“同义词”呢?
今天我在这里分享两种思路。
第一种思路是根据查询关键字和查询结果之间的自然结合产生的同义效果。这需要对用户的搜索行为数据进行大规模挖掘。这里的基本假设是这样的假设我们有两个搜索关键字A和B。从A的搜索结果中用户可能点击了一些网页从B的结果中用户可能点击了另外的一些网页。如果这些被点击的网页恰好非常类似那么我们就可以认为A和B其实是同义的查询关键字。
更加完整的做法是把查询关键字和网页分别表示成“图”Graph中的两类节点Node。每个关键字节点和多个网页节点建立联系Edge或者边Link象征这些网页对应这个关键字的相关页面。而从每个网页的角度上看多个关键字节点又和同一个网页节点相连表示这些关键字都有可能和某个网页相关。
拿上面提到的特朗普的例子来说美国白宫的首页作为一个节点的话就有可能会和“Trump”、“US President”以及“POTUS”这几个查询关键字相关。因此你可以看到寻找同义词的工作就变成了如何在这个图上进行相似节点特别是相似关键字节点的挖掘工作。
如果把查询关键字的节点放在一边把网页节点放在一边我们就看到了典型的“二分图”Bipartite Graph。二分图的特点是同边的节点之间没有连接比如关键字和关键字之间没有连接而所有的连接都发生在不同边的节点之间关键字和网页之间
二分图的聚类问题Clustering是机器学习界的经典的问题。而利用二分图的聚类问题来做查询关键字的同义词挖掘也是很多研究人员尝试的方向。文末我列了几个参考文献比如参考文献[2]就是利用二分图上的“随机游走”Random Walk以及随机游走所产生的“到达时间”Hitting Time来挖掘出类似的关键字。如果你有兴趣可以查看这篇经典论文。
说了基于用户行为信息和关键字挖掘的思路以后,我们再来看看第二种思路。
第二种思路的核心是从海量的文本信息中分析出词语之间的相关度。这里面需要注意的是这些词语的相关度有可能是语言本身带来的。比如单词“Male”和“Man”。也可能是语境带来的比如谈论手机的网页中对于“iPhone 6”和“iPhone 7”的谈论。
总之这一个思路的想法就是如何为每一个词组都建一个“表达”Representation从而通过这个表达找到同义词。近年来流行的一个做法是为单词找到数值表达也就是通常所说的“嵌入”Embedding。如果两个词在“嵌入空间”Embedding Space通常是“欧式空间”中距离相近那么我们就可以认为这两个词是同义词。
如何为词组产生“嵌入”向量呢这里面也有很多做法。比较通用的有算法Word2Vec参考文献[3]目标是通过一个文档的每一句话中某一个词周围的词来预测这个词出现的概率。可以设想一下在苹果手机的很多帮助文档或者帮助站点中描述如何帮助iPhone 6或者iPhone 7来做数据备份的字句都是相似的甚至可能唯一的区别就是描述机型的名字。
因此在这样的情况下通过文字周围的“上下文信息”Contextual来对单词本身的“嵌入向量”进行学习可以有效地学习到单词的语义。而通过语义我们就能够找到其他的同义词。当然要想真正应用到查询关键字扩展中可能还需要有其他的调试比如文末我列的参考文献[4],就是其中的一种。如果你感兴趣,建议去精读。
最后我需要说明的是,第一种思路需要已经有不少的用户交互数据,而第二种思路可以通过其他的语料(比如维基百科)加以学习,并不需要用户数据。这也是另一个值得参考的信息点。
小结
今天我为你讲了查询关键字理解中的查询关键字扩展问题。你可以看到,查询关键字扩展从技术上的两种流派,一个是通过用户的交互数据来产生一个图,并且利用图挖掘技术来得到查询关键字之间的关系;另外一个就是通过产生词汇的嵌入向量从而得到同义词。
一起来回顾下要点:第一,简要介绍了查询关键字扩展的内涵。由于用户输入的查询关键字信息不足,通过查询关键字扩展可以提供更好的用户体验和更加精准的搜索结果。第二,详细介绍了查询关键字扩展的两个主要技术。
最后,给你留一个思考题,如何来测试查询关键字扩展的优劣呢?
参考文献
Claudio Carpineto and Giovanni Romano. A Survey of Automatic Query Expansion in Information Retrieval. ACM Computing Surveys. 44, 1, Article 1 (January 2012), 50 pages.2012.
Qiaozhu Mei, Dengyong Zhou, and Kenneth Church. Query suggestion using hitting time. Proceedings of the 17th ACM conference on Information and knowledge management (CIKM 08). ACM, New York, NY, USA, 469-478. 2008.
Mikolov, Tomas, Kai Chen, Greg Corrado, and Jeffrey Dean. Efficient estimation of word representations in vector space. arXiv preprint arXiv:1301.3781 (2013).
Diaz, Fernando, Bhaskar Mitra, and Nick Craswell. Query expansion with locally-trained word embeddings. arXiv preprint arXiv:1605.07891 (2016).

View File

@ -0,0 +1,79 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
040 搜索系统评测,有哪些基础指标?
我在之前几周的专栏文章里主要讲解了最经典的信息检索Information Retrieval技术和基于机器学习的排序学习算法Learning to Rank以及如何对查询关键字Query进行理解包括查询关键字分类、查询关键字解析以及查询关键字扩展。这些经典的技术是2000年后开始流行的各类搜索引擎的核心技术。
在进一步介绍更多的搜索引擎技术前我觉得有必要专门抽出一周时间来好好地看一下搜索系统的评测Evaluation以及我们经常使用的各类指标Metric。俗话说得好“如果你不能衡量它你就不能改进它”If You Cant Measure It, You Cant Improve It。意思其实就是说对待一个系统如果我们无法去衡量这个系统的好坏没有相应的评测指标那就很难真正地去琢磨怎么改进这些指标从而达到提升系统的目的。
虽然我们这里是在搜索系统这个重要场景中讨论评测和指标,但实际上我们这周要讨论的很多细节都可以应用到很多类似的场景。比如,我们后面要讨论的推荐系统、广告系统等,在这些场景中几乎就可以无缝地使用这周要讲的很多内容。
线下评测
假设你今天开发了一个新软件,比如说是一个最新的手机软件,你怎么知道你的用户是不是喜欢你的软件呢?你怎么知道你的用户是不是愿意为你的软件掏钱呢?
评测的核心其实就是了解用户的喜好。最直接的方法,当然是直接询问用户来获得反馈。例如你可以对每一个下载了你手机软件的用户强行进行问卷调查,询问他们对待新软件的态度。
然而,我们很快就会发现这样的方法是行不通的。姑且不说用户是否会因为这样强行的方式产生反感,我们是不是能通过这些调查问卷获得用户的真实反馈,这本身就是一个问题。这里面涉及到调查问卷设计的科学性问题。
即便这些调查问卷都能完整准确地反映出用户对手机软件的看法,真正实施起来也会面临种种困难。如果这款手机软件的用户数量有百万甚至千万,那我们就要进行如此大规模的问卷调查,还要处理调查后的数据,显然这样做的工作量非常大。而这些调查问卷是没法反复使用的,因为下一个版本的软件更新后,用户的态度就会发生改变,这样的方式就没法系统地来帮助软件迭代。
那么如何才能形成一组数据来帮助系统反复迭代,并且还能够减少人工成本,这就成了一个核心问题。
在信息检索系统开发的早年研究人员和工程师们就意识到了这个核心问题的重要性。英国人赛利尔·克莱温顿Cyril Cleverdon可以算是最早开发线下测试集的计算机科学家。
赛利尔生于1914年在英国的布里斯托Bristol图书馆工作了很长时间。从1950年开始赛利尔就致力于开发信息检索系统以提高图书馆查询的效率。1953年他尝试建立了一个小型的测试数据集用于检测图书管理员查找文档的快慢。这个工作最早发表于1955年的一篇论文参考文献[1])。
这之后,英美的一些早期信息检索系统的研发都开始顺应这个思路,那就是为了比较多个系统,首先构造一个线下的测试数据集,然后利用这个测试集对现有的系统反复进行改进和提升。如果你想对早期测试集的构造以及信息有所了解,建议阅读文末的参考文献[2]。
那么,当时构造的这些测试数据集有些什么特点呢?
这些测试数据集都会包含一个查询关键字集合。这个集合包含几十到几百不等的查询关键字。一方面,这些关键字的选取大多来自于经验。另一方面,从赛利尔就开始认识到,需要保证有一些信息一定能够通过这些关键字来找到。其实,这里就是在测试我们后面要讲的“召回”。
在有了这些查询关键字以后,这些测试数据集往往有几百到几千不等的文档。这些文档中的某一部分,研究人员在构造数据集的时候就知道了会包含所对应查询关键字需要的信息,也就是我们后面要说的相关文档。
你可以看到几十到几百的查询关键字以及几千个文档很明显不能代表所有可能使用系统的用户的行为。你甚至可以说这都无法代表绝大多数用户的行为。然而这种测试集的好处是查询关键字和文档本身是和要测试的系统无关的。也就是说今天我们要测试系统A还是明天要测试系统B都可以反复利用同样一组测试数据集。这样做的好处相比于我们之前提到的问卷调查是显而易见的。
另外,我需要强调的是,“用户”这个概念在测试数据集中被“抽象”出去了。当我们在讨论文档相对于某个查询关键字的相关度时,我们假定这种相关度是恒定的,是对于所有用户都适用的。因此,究竟是哪位用户在使用这个系统并不重要。只要研发的系统能够在这些“标准化”的查询关键字和文档的集合表现优异,我们就相信这个系统能够满足所有用户的需要。
因为测试数据集并不是用户与产品交互产生的真实回馈结果,所以我们往往又把测试数据集叫作“线下评测数据”。
基于二元相关度的评测指标
从线下收集评测数据以后,我们最容易做到的就是利用“二元相关度”所定义的一系列评测指标来衡量手中系统的好坏。
什么叫“二元相关度”呢?简单来说,就是指针对某一个查询关键字而言,整个测试集里的每一个文档都有一个要么“相关”要么“不相关”的标签。在这样的情况下,不存在百分比的相关度。而每个文档针对不同的关键字,有不同的相关信息。
假定某个系统针对某个关键字,从测试数据集中提取一定量的文档而不是返回所有文档,我们就可以根据这个提取的文档子集来定义一系列的指标。
有两个定义在“二元相关度”上的指标就成了很多其他重要指标的基石。一个叫“精度”Precision也就是说在提取了的文档中究竟有多少是相关的。另一个叫“召回”Recall也就是说 在所有相关的文档中,有多少是提取出来了的。
“精度”和“召回”的相同点在于分子都是“即被提取出来了又相关的文档数目”。这两个指标所不同的则是他们的分母。“精度”的分母是所有提取了的文档数目而“召回”的分母则是所有相关的文档数目。如果我们返回所有的文档“召回”是1“精度”则是相关文档在全集中的比例。因此我们注意到这两个指标其实都假定提取的文档数目相比于全集而言是相对比较小的子集。
很快大家从实践中就体会到“精度”和“召回”就像是“鱼与熊掌不可兼得”。一个系统很难做到“精度”和“召回”都能够达到很高的数值。也就是说我们往往需要在这两个指标之间做一些平衡。于是研究人员开始寻找用一个数字来表达“精度”和“召回”的“平均水平”。来自英国的学者范·李杰斯博格C. J. van Rijsbergen最早在论文中采用了 “调和平均数” Harmonic Mean来计算 “精度”和“召回”的平均(参考文献 [3]。这个方法被后人称为“F值” ,并且一直沿用至今。
这里对“精度”和“召回”还需要注意一点因为这两个指标都是基于“二元相关度”的。因此这两个指标都不是“排序指标”Ranking Metrics。换句话说这两个指标其实并不能真正评价排序系统。
比如我们针对某个关键字提取10个文档如果有3个相关文档被取出来不管是“精度”还是“召回”都无法分辨这三个文档在最后序列中的位置是头三位还是后面的三位很遗憾“精度”和“召回”都无法解决这个问题。“二元相关度”的这个问题也就指引研究人员去开发真正能对排序进行评估的指标。
小结
今天我为你讲了搜索系统的评测和常用指标。这是现代搜索技术中一个非常重要的一个环节,那就是如何评价我们构建的系统。我们详细讲解了线下测试的由来以及这样的测试相比于调查问卷的优势。
一起来回顾下要点:第一,简要介绍了可重复使用的线下测试集的历史,以及这样的测试集都有什么特点与局限。第二,详细介绍了两个非常流行和重要的基于“二元相关度”的评测指标,那就是“精度”和“召回”。
最后,给你留一个思考题,我们讲了排序的好坏不能简单地从“精度”和“召回”的数值看出,那能不能动一些手脚呢?如果我们就依赖“二元相关度”,有没有什么方法来看“排序”的好坏呢?
参考文献
R.G. THORNE, B.Sc., A.F.R.Ae.S. The Efficiency Of Subject Catalogues and The Cost of Information Searches. Journal of Documentation, Vol. 11 Issue: 3, pp.130-148, 1995.
K. SPARCK JONES, C.J. VAN RIJSBERGEN. Information Retrieval Test Collections. Journal of Documentation, Vol. 32 Issue: 1, pp.59-75, 1976.
C.J. VAN RIJSBERGEN. Foundation of Evaluation. Journal of Documentation, Vol. 30 Issue: 4, pp.365-373, 1974.

View File

@ -0,0 +1,76 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
041 搜索系统评测,有哪些高级指标?
周一我们介绍了基于“二元相关”原理的线下评测指标。可以说从1950年开始这种方法就主导了文档检索系统的研发工作。然而“二元相关”原理从根本上不支持排序的评测这就成了开发更加准确排序算法的一道障碍。于是研究人员就开发出了基于“多程度相关”原理的评测标准。今天我就重点来介绍一下这方面的内容。
基于多程度相关原理的评测
从“二元相关”出发自然就是给相关度更加灵活的定义。在一篇发表于NIPS 2007的文章中参考文献[1]雅虎的科学家介绍了雅虎基于五分标准的相关评价体系从最相关到最不相关。而在同一年的SIGIR上谷歌的科学家也发表了一篇文章参考文献[2]),介绍了他们的“多程度”相关打分机制。至此之后,基于“多程度相关”原理的评价标准慢慢被各种搜索系统的研发者们所接受。
在这样的趋势下基于“二元相关”的“精度”Precision和“召回”Recall都变得不适用了。我们需要新的、基于“多程度相关”的评价指标。
芬兰的科学家在2000年的SIGIR上参考文献[3])发表了一种计算相关度评测的方法。这种方法被广泛应用到了“多程度相关”的场景中。那么,芬兰科学家发明的方法是怎样的呢?
这种方法被称作是“折扣化的累积获得”Discounted Cumulative Gain简称“DCG”。 在介绍DCG之前我们首先假定位置1是排位最高的位置也就是顶端的文档而位置数随着排位降低而增高位置10就是第一页最后的文档。
DCG的思想是这样的。
首先,一个排序的整体相关度,是这个排序的各个位置上的相关度的某种加权。这样用一个数字就描述了整个排序。只要排序的结果不同,这个数字就会有所不同。因此,这就避免了“精度”或“召回”对排序不敏感的问题。
其次每个位置上面的“获得”Gain是和这个文档原本定义的相关度相关的但是根据不同的位置要打不同的“折扣”。位置越低也就是位置数越大折扣越大。这就是DCG名字的由来。
在原始的DCG定义中“折扣”是文档的相关度除以位置的对数转换。这样既保证了位置越低位置数大折扣越大还表达了高位置位置数小的差别要大于低位置之间的差别。这是什么意思呢意思就是如果某一个文档从位置1挪到了位置2所受的折扣或者说是损失要大于从位置9挪到了位置10。在这样的定义下DCG鼓励把相关的文档尽可能地排到序列的顶部。
事实上假设我们有5个文档假定他们的相关度分别是1、2、3、4、5分别代表“最不相关”、“不相关”、“中性”、“相关”和“最相关”。那么在DCG的定义下最佳的排序就应该是把这5个文档按照相关度的顺序也就是5、4、3、2、1来排定。任何其他的顺序因为根据位置所定义的“折扣获得”的缘故都会取得相对较小的DCG因此不是最优。DCG比“精度”和“召回”能够更好地表达对排序的评估。
但直接使用DCG也存在一个问题。如果我们有两个查询关键字返回的文档数不一样那么直接比较这两个查询关键字的DCG值是不“公平”的。原因在于DCG的“加和”特性结果肯定是越加越大因此不能直接比较两个不同查询关键字的DCG值。
有没有什么办法呢把DCG加以“归一化”的指标叫做 nDCG Normalized Discounted Cumulative Gain。nDCG的思路是下面这样的。
首先对某一个查询关键字的排序根据相关信息来计算一组“理想排序”所对应的DCG值。理想排序往往就是按照相关信息从大到小排序。然后再按照当前算法排序所产生的DCG值除以理想的DCG值就产生了“归一化”后的DCG也就是我们所说的nDCG值。简单来说nDCG就是把DCG相对于理想状态进行归一化。经过nDCG归一化以后我们就可以比较不同查询关键字之间的数值了。
这里需要说明的是我们上面介绍的是DCG的原始定义。后来微软的学者们在2005年左右发明了另外一个变种的DCG基本原理没有发生变化只是分子分母有一些代数变形。这个新的版本后来在工业界得到了更加广泛的应用。如果你感兴趣可以查看文末的参考文献[4]。
直到今天nDCG以及DCG依然是评价排序算法以及各种排序结果的标准指标。
比较两个不同的排序
不管是我们之前谈到的“精度”和“召回”还是今天介绍的nDCG我们都是使用一个“数”来描述了相对于某个查询关键字一组结果的好坏。当我们有多个查询关键字的时候我们该如何比较两个不同排序的结果呢
这里面的一个问题是相对于两个不同的排序A和B来说可能结果各有千秋也许对于某一个关键字A比B的表现要好但是另外一个关键字B就比A的结果更棒。这怎么办呢
也许你会想到用平均值来描述A和B的表现。这的确是很好的第一步。于是我们就计算A和B两个排序的平均表现。这样对于这两个排序而言我们就有了两个数值来表达这两个排序的好坏。
然而很快我们就会遇到问题。假设A的nDCG平均值是0.781B的nDCG平均值是0.789我们可以下结论认为B是比A更好的排序算法吗
答案当然是不一定。这种情况,我们就需要依赖统计工具“假设检验”来评价两个排序的好坏。
我这里就不去复习假设检验的细节了,简单说一个经常使用的工具。
如果我们比较A和B是在同一组查询关键字上的话那我们常常可以使用“两个样本的配对T检验”Two Sample Paired T-Test。这里所谓的“配对”是指A和B的结果是可以一一比较的。这里的“T检验”其实就是说借助“T分布”或者我们通常所说的“学生分布”来进行假设检验。如果我们是在不同查询关键字集合中进行比较的话还有其他的假设检验工具这里就不展开了。
值得注意的是假设检验本身也不是“万灵药”。第一怎么最有效地在排序结果上进行假设检验还是一个研究话题包括我们刚说的“两个样本的配对T检验”在内的所有方法都不是“金科玉律”。第二依靠假设检验得出来的结论仅仅是统计意义上的“好坏”和这些系统在用户面前的表现可能依然会有很大差距。因此对于假设检验的结果也要带有“批判”的眼光。
小结
今天我为你讲了现代搜索技术中如何评价我们构建的系统,特别是如何评价排序系统。
一起来回顾下要点第一简要讲解了基于“多程度相关”的评价体系包括其由来和DCG以及nDCG的概念。第二详细介绍了如何来比较两个排序的好坏。
最后给你留一个思考题如果我们只有“二元”相关信息能不能用nDCG来评价好坏呢
参考文献
Ben Carterette and Rosie Jones. Evaluating search engines by modeling the relationship between relevance and clicks. Proceedings of the 20th International Conference on Neural Information Processing Systems (NIPS07), J. C. Platt, D. Koller, Y. Singer, and S. T. Roweis (Eds.). Curran Associates Inc., USA, 217-224,2007.
Scott B. Huffman and Michael Hochster. How well does result relevance predict session satisfaction? Proceedings of the 30th annual international ACM SIGIR conference on Research and development in information retrieval (SIGIR 07). ACM, New York, NY, USA, 567-574, 2007.
Kalervo Järvelin and Jaana Kekäläinen. IR evaluation methods for retrieving highly relevant documents. Proceedings of the 23rd annual international ACM SIGIR conference on Research and development in information retrieval (SIGIR 00). ACM, New York, NY, USA, 41-48, 2000.
Chris Burges, Tal Shaked, Erin Renshaw, Ari Lazier, Matt Deeds, Nicole Hamilton, and Greg Hullender. Learning to rank using gradient descent. Proceedings of the 22nd international conference on Machine learning (ICML 05). ACM, New York, NY, USA, 89-96, 2005.

View File

@ -0,0 +1,69 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
042 如何评测搜索系统的在线表现?
我在本周前面的两篇文章中为你讲解了基于“二元相关”和基于“多程度相关”原理的线下评测指标。利用这些指标,研发人员在半个世纪的时间里开发了一代又一代的搜索系统,这些指标和系统也都在不断演化。
虽然我们这周讲过的这些指标都很有指导意义,但大多数指标被提出来的时候都是基于线下的静态数据集,并不是真正去检测用户和系统的互动(虽然后期也有研发人员直接使用这些评测工具用于在线评测,但在使用上就产生了问题)。那有什么样的方法来评测搜索系统的在线表现呢?
为了回答这个问题,我们今天就来探讨一下进行在线评测的几个话题。
在线可控实验
我们先回到整个评测指标的初衷,为什么要进行线下测试呢?
第一个原因是在信息检索系统也就是最早的搜索系统的开发时期还很难做在线可控实验Controlled Experiments研发人员还没有开发出值得依赖的手段来判断用户的行为。因此在那个年代比较可靠的方法就是调查问卷和后来开发出来的线下静态评测。可以说这些手段都是真正了解用户行为的一个“代理”Proxy
要进行评测,不管是线下还是线上,另外一个原因就是我们需要某种手段来分辨两个系统的好坏,从而能够不断地通过这种手段来改进系统,做到数据驱动。
那么能够正确观测两个系统不同的工具就是“在线可控实验”有时候又称作“在线实验”或者也叫作“在线A/B实验”。
在线可控实验其实是建立“因果联系”Causal Relationship的重要工具也可以说是唯一完全可靠的工具。这里面的基础是统计的假设检验。
具体来说就是我们针对访问网站或者应用的人群进行某种划分一般情况下是平均随机划分百分之五十的用户进入划定的一个群组叫作“控制组”Control Bucket而另外百分之五十的用户进入另外一个群组叫作“对照组”Treatment Bucket。“控制组”和“对照组”的唯一区别在于所面对的系统。
假设有一个搜索系统我们想对其中的某个部分进行改进那么我们可以保持住其他的部分让这个希望得到改进的部分成为唯一的“独立变量”Independent Variable也就是在整个实验设置中的变量。这样我们就希望看到能否通过在线实验以及假设检验的工具来认定这个“独立变量”是否会带来系统性能上的提高亦或是降低。
这里面还有一个需要提前确定的那就是需要评测的指标特别是用户指标比如网站的点击率、搜索的数量等等。这些指标我们称之为“依赖变量”Dependent Variable。说白了我们就是希望能够在“独立变量”和“依赖变量”之间通过假设检验建立联系。
虽然在概念上很容易理解在线可控实验,但在实际操作中会面临很多挑战。
虽然在理想状态下,我们可以把用户五五对分,让用户分别进入“控制组”和“对照组”。然而现实中,经过随机算法分流的用户群在这两个群组中很可能并不呈现完全一样的状态。什么意思呢?
举个例子,比如,在“控制组”中,相比于“对照组”而言,可能存在更多的女性用户;或者是在“对照组”中,可能存在更多来自北京的用户。在这样的情况下,“依赖变量”,比如网站点击率,在“控制组”和“对照组”的差别,就很难完全解释为“独立变量”之间的差别。
也就是说,如果“控制组”下的点击率比“对照组”高,是因为我们更改了系统的某部分产生了差别呢,还是因为这多出来的女性用户呢,又或者是因为女性用户和系统的某些部分的交互,产生了一定复杂的综合结果导致的呢?这就比较难说清楚了。对于刚才说的有更多来自北京的用户这个例子也是一样的。
当然,在现实中,如果说我们依然可以比较容易地通过算法来控制一两个额外的变量,使得在“控制组”和“对照组”里面这些变量的分布相当,那么,面对十几种(例如,年龄、性别、地域、收入层级等等)重要变量,要想完全做到两边的分布相当,难度很大。
即便我们能够做到通过随机算法使得已知变量在两个群组中的分布相当,我们依然不能对当前还未知的变量进行如此操作。因此,如何处理因人群特性所带来的对结论的影响是现实中在线实验的难点之一。
在线实验的难点之二是,我们有可能很难做到如设想中的那样,让系统的某个部分成为“控制组”和“对照组”中唯一的“独立变量”,即便是除去了刚才所提到的人群差异。
在现代网站或者应用中,有很多服务、子系统、页面、模块同时在为整个网站服务。而这些服务、子系统、页面和模块,都有不同的前端系统和后端系统,很可能属于不同的产品和工程团队。每个部分都希望能够做自己的可控实验,希望自己改进的部分是唯一变化的“独立变量”。然而,我们从宏观的角度去看,如果每个部分都在做自己的实验,而我们做实验的基本单元依旧是每个用户的话,那这就很难保证用户之间的可比性。
举个例子如果用户U1进入了首页的“控制组”然后访问了搜索页面的“对照组”继而离开了网站。而用户U2直接访问了帮助页面的“对照组”然后访问了搜索页面的“控制组”。那U1和U2两个用户最终产生的点击率的差别就很难从他们访问网站页面的过程中得出结论。即便是在有大量数据的情况下我们也很难真正去平衡用户在所有这些页面的组别之间的关系。
实际上如何能够有效地进行在线实验包括实验设计、实验评测等都是非常前沿的研究课题。每年在KDD、WSDM、ICML等学术会议上都有不少新的研究成果出炉。
利用因果推论对实验结果进行分析
今天的最后我想提一下因果推论Causal Inference。因果推论不是普通的统计教科书内容也不在一般工程类学生接触到的统计内容之内。然而这个领域在最近几年受到了机器学习界越来越多的关注因此了解因果推论对于学习机器学习的前沿知识来说很有必要。
像我们刚才提到的种种实验中产生的用户特征不平均、实验之间可能存在关系等在这些方面我们都可以利用很多因果推论的工具进行分析。另外对于工程产品而言并不是所有的情况都能够通过A/B测试来对一个希望测试的内容、模型或者产品设计在一定时间内找到合理的结果有很多情况下是不能进行测试的。因此在不能进行测试的情况下还能通过数据研究得出期望的结果也就是说我们能否模拟在线实验这就是因果推论的核心价值。
一般而言,在机器学习中需要因果推论的场景也很常见。比如,我们需要用数据来训练新的模型或者算法,这里面的数据采集自目前线上的系统。然而,现在的线上系统是有一定偏差的,那么,这个偏差就会被记录到数据里。在此,因果推论就为机器学习带来了一系列工具,使得在一个有偏差的数据中依然能够无偏差地进行训练以及评测模型和算法。
小结
今天我为你讲了在现代搜索技术中,如何利用在线实验,特别是可控实验来评价我们构建的系统。
一起来回顾下要点:第一,详细介绍了在线实验的一些因素,并分析了在线实验中可能产生的用户不平衡以及实验有相互作用的问题。第二,简短地提及了现在利用因果推论来进行在线实验数据分析以及“偏差”调整的一个思路。
最后给你留一个思考题如何建立在线实验评测结果和线下指标比如nDCG之间的关系呢