first commit
This commit is contained in:
@ -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)。我们应该在第一次创造上多下功夫,统一集体想象,让目标更明确。
|
||||
|
||||
“以终为始”的思维可以帮助我们更好地规划我们手头任务,也可以帮助我们发现过程中的问题。
|
||||
|
||||
如果今天的内容你只能记住一件事,那请记住:遇到事情,倒着想。
|
||||
|
||||
最后,我想请你思考一下,在实际的工作或生活中,你有运用“以终为始”的思维方式吗?帮助你解决过哪些问题?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,140 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
03 DoD的价值:你完成了工作,为什么他们还不满意?
|
||||
你好,我是郑晔。
|
||||
|
||||
在开始今天的讨论之前,我们先来看一个小故事。小李是一个程序员,有一天,项目经理老张来到他身边,和他商量一个功能特性的进度:
|
||||
|
||||
|
||||
老张:这有一个任务需要完成,你看一下。-
|
||||
小李:这个不难,两天就能做完,两天以后就能上线。
|
||||
|
||||
|
||||
两天以后,老张又来到小李的身边验收工作:
|
||||
|
||||
|
||||
老张:怎么样,做完了吗?今天能上线吗?-
|
||||
小李:我的代码写完了。-
|
||||
老张:测试人员测过了吗?-
|
||||
小李:还没有。-
|
||||
老张:那今天能测完吗?-
|
||||
小李:那我就不知道了。-
|
||||
老张:什么?我可是答应了业务的人,今天一定要上线的!
|
||||
|
||||
|
||||
很明显,老张有些愤怒,而小李也有些委屈。于是,老张、小李和测试人员一起度过了一个不眠之夜。
|
||||
|
||||
听完这个故事,你有什么感想呢?先不急,我们继续看后面的故事。
|
||||
|
||||
又过了几天,老张又来找小李,给小李安排一个很简单的功能。在小李看来,一天就能搞定,而按照老张给出的时间表,小李有两天时间处理这个功能。小李心中暗喜:看来我可以“偷得浮生一日闲”了。
|
||||
|
||||
两天以后,老张又来检查工作。
|
||||
|
||||
|
||||
老张:这个功能开发完了吗?-
|
||||
小李:写完了,你看我给你演示一下。
|
||||
|
||||
|
||||
小李熟练地演示了这个新写好的功能,这次老张很满意:
|
||||
|
||||
|
||||
老张:做得不错。单元测试都写了吧?-
|
||||
小李:啊?还要写单元测试吗?-
|
||||
老张:要不为啥给你两天的时间?
|
||||
|
||||
|
||||
怎么会这样?小李心里很委屈,自己明明已经很好地完成了工作,老张是不是故意在找自己的麻烦呢?
|
||||
|
||||
好,故事讲完了。是不是有些似曾相识的感觉呢?为什么小李辛辛苦苦地工作,老张却总能挑出毛病来呢?老张是不是来挑刺的呢?其实,老张才没那么闲,小李的委屈主要是因为他和老张对于“完成”有着不一样的理解。换句话说,他们之间存在一个理解的鸿沟。
|
||||
|
||||
理解的鸿沟
|
||||
|
||||
在这个模块里,我们讨论的主题是“以终为始”。那我们第一个问题就是,“终”到底是什么?在前面这个例子里,“终”就是“完成”,可是,小李认为他的活已经做完了,老张却认为他没做完。
|
||||
|
||||
怎么会这样?二人之所以有分歧,归根结底,就在于二人对“完成”的定义理解的不同。
|
||||
|
||||
在第一个故事里,作为项目经理,老张认为“完成”应该是“上线运行”,而程序员小李则认为“完成”是“功能代码编写完毕”。这中间存在的理解偏差,包括了测试人员的测试工作,可能还包括了运维人员的上线工作。
|
||||
|
||||
在第二个故事里,老张给了小李两天时间。小李认为这两天都是编写功能代码的,而老张想的是,小李应该自己写好功能代码和单元测试,可能还包括了功能测试,这中间的差异是测试代码的工作量。
|
||||
|
||||
因为双方的理解不一致,所以无论怎样努力,小李都不可能达成项目经理老张的要求,正所谓“南辕北辙”。
|
||||
|
||||
那该怎么办呢?小李会说,我又不是老张肚子里的蛔虫,怎么才能和他达成一致呢?答案很简单,既然双方的理解有差异,那就把这个差异弥合上,后面的问题便也不是问题了。
|
||||
|
||||
弥合差异的方式有很多,有一个最佳实践,它的名字叫 DoD(Definition 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 的概念以后,你是不是有了一些新的想法呢?欢迎在留言区留言。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
|
||||
|
||||
|
||||
|
@ -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 …”这样的需求描述方式。
|
||||
|
||||
在我看来,无论采用哪种需求描述方式,这部分也都是能说清楚的。那我们要从用户故事中学到什么呢?我认为就是用户故事的关键点:验收标准,它可以清晰地定义出需求边界。
|
||||
|
||||
验收标准非常重要的一环是异常流程的描述。大部分程序员都擅长解决正常流程,而异常流程则是最容易忽略的,也是产生扯皮的关键环节。既然容易扯皮,我们就在一开始把它定义清楚。怎么才算做完需求呢?验收标准说了算。
|
||||
|
||||
采用用户故事之后,我经常在写完了主要流程之后,再去看一下验收标准,为自己的开发查缺补漏。因为我知道,那是标准,达不成就不算任务完成。
|
||||
|
||||
当我们说自己开发完成,可以交给测试人员测试时,我们需要照着验收标准给测试人员演示一遍,证明我们的系统确实能够跑通。这之后,测试人员才会把系统接手过去,做更系统的测试。
|
||||
|
||||
验收标准给出了这个需求最基本的测试用例,它保证了开发人员完成需求最基本的质量。如果你了解 BDD(Behavior-Driven Development,也就是“行为驱动开发”),就可以按照验收标准中给出的内容编写验收测试用例了。
|
||||
|
||||
在实际工作中,许多产品经理把需求交给开发人员之前,很多细节是没想清楚的,那种功能列表式的需求常常只包含了正常路径,那些缺失的细节就是在后续的过程中,由开发人员补全的。用户故事就是一种固定的格式,让他们把这些应该想清楚的问题想清楚。
|
||||
|
||||
如果你的团队采用用户故事的格式进行需求描述固然好,如果不能,在功能列表中,补充验收标准也会极大程度地改善双方协作的效率。
|
||||
|
||||
你的角色
|
||||
|
||||
或许你会有这样的疑问,如果产品经理通过用户故事的方式,将需求实现细节都描绘得清清楚楚,那我们程序员的发挥空间在哪里?请注意,验收标准所给出实现细节应该是业务上的,程序员在这种问题上思考才是真正意义上的浪费时间,我们的发挥空间应该是在技术实现上。
|
||||
|
||||
然而,在现实情况中,很多团队做不到这种程度。
|
||||
|
||||
你会发现,我们在开发中之所以会“丢三落四”,很重要的一个原因是,在开发一个功能特性的时候,因为一些环节的缺失,我们不得已扮演了很多的角色,其中之一就是产品经理。你是一个专业的程序员,但大多数情况下,你却只是一个业余的产品经理,“丢三落四”就在所难免了。
|
||||
|
||||
或许你会说,我在一个小公司工作,公司没那么多人,没有专门的产品经理,只有我们几个“全世界都缺”的程序员,需求都是老板扔给我们的,谁来帮我们写验收标准呢?
|
||||
|
||||
没办法,答案只能是你自己。虽然你名义上是程序员,但当拿到一个需求的时候,你要做的事不是立即动手写代码,而是扮演产品经理的角色,分析需求,圈定任务范围。相信我,事前分析绝对比你拿一个写好的系统给老板,而他却告诉你这不是他想要的,好太多了。
|
||||
|
||||
另外我想提醒你注意的是,扮演不同角色的时候,我们的思考模式是不同的。还是以开发用户名密码登录为例,你想到的可能是:输入正确的用户名和密码可以正常登录,输入错误的用户名和密码不能登录,并且给出提示。
|
||||
|
||||
如果你只扮演开发人员的角色,想到这些就算不错了。但如果你扮演的是产品经理的角色,会从产品的角度进行思考,也就会看到不同的内容,比如:
|
||||
|
||||
|
||||
登录是否需要验证码
|
||||
是否需要第三方登录
|
||||
用户名和密码的长度在系统内是否有限制
|
||||
密码是否需要满足一定的规则
|
||||
……
|
||||
|
||||
|
||||
我知道,如果让你来填写,这个列表会更长。可能这并不是我们都需要完成的功能,但站在分析的角度,这都是我们要考虑的问题,一个登录功能,绝不仅仅是用户名和密码校验那么简单的。我们能想到这些,仅仅是因为我们正在扮演一个不同的角色。
|
||||
|
||||
所以,如果你要兼顾开发人员和产品经理两个角色,建议你先扮演好产品经理的角色,多花点时间把验收标准制定好,再回到开发人员的角色上去写代码。毕竟,最好维护的代码是没有写出来的代码。
|
||||
|
||||
总结时刻
|
||||
|
||||
需求,是软件开发中的一个关键环节,一旦需求理解出现问题,势必会造成大量的浪费。传统的功能列表只是简单罗列了要实现的功能,丢失了大量的上下文,会导致团队成员对于需求“只见树木不见森林”。
|
||||
|
||||
而在比较大的团队中,更是会将一个功能分拆到多个小团队中,每个人看到的只是功能碎片。于是,后来产生了其他的需求描述方式,比如用例和用户故事。
|
||||
|
||||
在实际的开发过程中,大量的分歧来自于对“需求完成”的定义。当我们把“以终为始”的原则应用在需求领域中,就会注意到,用户故事有一个非常重要的组成部分是验收标准。
|
||||
|
||||
验收标准不仅仅描述出了正常流程,也会关注到异常流程的处理,它也是我们验收测试用例的起点。一旦事先定义好验收标准,大量的扯皮工作就随之烟消云散了。
|
||||
|
||||
理解了验收标准的作用,即便我们不使用用户故事来定义需求,依然可以把用户故事中的关键点应用到自己的实践中,在功能列表的每个功能定义中,增加验收标准。
|
||||
|
||||
如果今天的内容你只能记住一件事,那请记住:在做任何需求或任务之前,先定好验收标准。
|
||||
|
||||
最后,我想请你回想一下,在实际工作中,你是如何澄清你的需求,或者因为需求不清晰给你造成了哪些困扰?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,110 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
09 你的工作可以用数字衡量吗?
|
||||
你好,我是郑晔。
|
||||
|
||||
今天的分享从日常工作开始。请你回想一下,你每天到岗之后做的第一件事是什么呢?然后你来猜猜我的答案是什么?你可能猜不到,我每天到公司之后,第一件正事是看数字。
|
||||
|
||||
我现在服务于一家做数字资产的公司,我们提供的是一个24小时运行的服务。从加入这家公司的第一天开始,公司的人就给我不断灌输一个重要理念——看数字。在我座位的正前方,摆着一个巨大的显示器,上面展示着各种不断变换的曲线、柱状图和数字,这些数字反映的是各种系统运行的指标。
|
||||
|
||||
我们就是每天看着这些指标,来发掘一些线上系统问题的,一旦某些指标出现自己不能理解的异常,就要着手调查。
|
||||
|
||||
你或许会纳闷,我们不是在探讨“以终为始”吗?怎么变成了一个关于监控的讨论呢?别急,我们确实还在讨论“以终为始”,因为数字是诠释“终”的最好方式。
|
||||
|
||||
我们前面讨论了各种“终”,但通常靠语言定义的“终”,或多或少都会存在一些模糊的地方,也就是容易产生误解的地方。而数字却是一个明明白白的“终”。比如,测试覆盖率要求100%,即便你做到了99.9%,不达标就是不达标,没什么好说的,说破天也是不达标。
|
||||
|
||||
再比如,之前内容我们讲到精益创业时,提到了一个重要的反馈循环:开发(build)-测量(measure)-认知(learn)。你会发现,在这个循环中,开发(build)是可控的,认知(learn)必须是得到反馈之后才能有的。所以,这里面最需要我们回答的问题是测量(measure)。而这里的测量,最好的检验标准当然就是数字。
|
||||
|
||||
或许你会说,数字我们都很熟,还用讲吗?不过在我看来,你还真的未必习惯于使用数字。
|
||||
|
||||
熟悉而陌生的数字
|
||||
|
||||
从进化的角度来看,人们做事更多是依赖于直觉的。数字,是人类在非洲大草原上奔跑了许久之后才创造出来的东西。著名科普著作《从一到无穷大》的开篇有这么一个故事:
|
||||
|
||||
|
||||
两个匈牙利贵族决定做一次数数的游戏,看谁说出的数字大。-
|
||||
一个贵族说:“好,那你先说吧!”-
|
||||
另一个绞尽脑汁想了好几分钟,说了一个数字:“3”。-
|
||||
现在轮到第一个贵族苦思冥想了,他想了一刻钟,然后说:“好吧,你赢啦!”
|
||||
|
||||
|
||||
这个故事听起来有些荒诞,但一些非洲探险家证实,在某些原始部族里,不存在比3大的数词。如果问他们有几个孩子,而这个数字大于3的话,他就会回答“许多个”。
|
||||
|
||||
虽然我们中华民族是一个重视教育的民族,现在也都承认数学是一门重要的基础知识。但我们还是习惯性地观其大略,因为在日常生活领域里,除了买东西发工资,需要对数字斤斤计较的场合并不多。
|
||||
|
||||
历史的车轮在不停地滚滚向前,当今社会所面临的复杂度已经远远超过凭直觉就能把事情做好的程度。
|
||||
|
||||
一些人说,自己靠直觉就能把事情做好,其实这是一种误解,因为那种所谓的直觉,通常是一种洞见(Insight),洞见很大程度上依赖于一个人在一个领域长期的沉淀和积累,而这其实是某种意义上的大数据。
|
||||
|
||||
我们都在说,人类马上就要进入智能时代了。之所以这么说,主要是现在人工智能技术不断地向前发展着。而人工智能作为一门在50年代就已经问世的技术,直到最近几年才得到大踏步的前进,主要归功于基础设施的发展。
|
||||
|
||||
在人工智能领域,基于统计的方法早就在学术界提了出来,但由于当时的技术条件所限,人们的数据采集和存储能力都有限,当时的“大”数据和今天的大数据完全不是一个量级的概念。
|
||||
|
||||
直到进入到互联网时代,随着处理数据量的增加,基础设施在不断地拓展,进而促使人们采集更多的数据,这个正向反馈也造就了今天的大数据。
|
||||
|
||||
原本因为缺乏足够数据支撑,难以施展拳脚的 AI 算法,在今天一下子有了足够的表演空间,从一个边缘角色成为了舞台中心的主角。
|
||||
|
||||
今天谈到人工智能,人们主要会谈三件事:算法、算力和数据。算法几乎是行业共有的,而算力在云计算普及的今天也不再是稀缺资源,所以,数据几乎成了兵家必争之“物”。于是,我们看到的现象是各个公司都在努力地搜集各种数据,让数据成为自己的竞争力。所以,在大方向上,数据采集是一个行业共识。
|
||||
|
||||
但是,作为这个世界上最了解数据价值的一批人,我们程序员只是在努力地把数据用于不断改善别人的生活,而对于自己日常工作的改善,则思考得少之又少。
|
||||
|
||||
我们更习惯的讨论方式依然是靠直觉。比如:增加了这个特性可能会让用户增长,做了这个调整应该会让系统的压力变小。
|
||||
|
||||
在一些简单的情形下,或者说大家信息对称、知识背景相差无几的情况下,这样的讨论是很容易得到认同的。而当事情复杂到一定程度时,简单地靠感觉是很难让人相信的。
|
||||
|
||||
所以,在我们的工作中,经常会发生的一个现象是,一个人说,我觉得这个有作用,另一个人说,我觉得那个没有。几个“觉得”下来,双方就开始进入了隔空对话的环节,谁也无法说服谁。
|
||||
|
||||
如果换成用数字的方式进行讨论,效果就会更好。有一次,为了改善用户体验,我们准备进行一次主页改版。产品团队希望在主页上加上大量的内容,而开发团队则认为太多的内容会导致主页加载变慢,进而造成用户体验下降。
|
||||
|
||||
正当这个对话即将进入“空对空”的讨论之时,我们找到了一个测量指标:主页加载速度。只要保证主页加载速度,产品团队就可以按照自己的理解来做调整。于是,一个即将不可挽回的讨论,变成了在一定约束条件下的讨论,双方谁也不再思维发散,讨论就能继续推进了。
|
||||
|
||||
如果你认同了数据本身的价值,那么再结合“以终为始”的理念,我们就应该在着手做一件事之前,先来想怎么去测量。无论是在讨论产品特性,还是功能开发,“信口雌黄”是容易的,落到数字上,人们就会多想一下,这是对彼此的约束。
|
||||
|
||||
从数字出发
|
||||
|
||||
前面的内容我们都是在说应该重视测量指标,重视数字。接下来,我就分享下几个我在实际工作中运用数字的案例,让你看看习惯用数字去思考问题之后,会拓宽哪些思考的维度。
|
||||
|
||||
首先是基于数字进行技术决策。有一次,我们打算做一个技术改进,给系统增加一些缓存,减轻数据库的压力。大家一起设计了两个技术方案。如果查询是特定的,我们就准备简单地在某些方法上加上缓存;如果查询是五花八门的,就准备用一个中间件,使用它的查询方案。
|
||||
|
||||
系统现在的情况到底是什么样的呢?我们发现并不能立刻回答这个问题。于是,大家决定在系统中增加一些统计指标,让数据给我们答案。然后根据数据反映出的情况,进行具体的决策。
|
||||
|
||||
其次是一个准备上线的案例。当时,我们是要做一个影响力比较大的系统升级,因为这是一个系统的关键模块,上下游系统都会受到影响。
|
||||
|
||||
谁也不能确定哪个模块会在上线过程中出问题。于是,设计了一个全新的数据面板,将相关的几个模块的核心指标都摆在上面。而我们要做的就是在上线的同时,观察这些指标的变化。
|
||||
|
||||
所幸的是,这次上线影响不大,几个指标一路平稳,而大家的信心就源自这些提前准备好的指标。
|
||||
|
||||
再次,看一个从数字中发现问题的例子。由于各种历史原因,我们的重点指标面板上,会有几个指标表示的是类似的东西。
|
||||
|
||||
比如,某个模块的处理能力,一个指标是站在这个模块内部度量的,而另一个指标则是由这个模块上下游系统度量的。在大多数情况下,它们的表现是一致的。结果有一天两者突然出现了很大的差异,内部度量表现依然良好,而外部度量则出现了很大的延迟。
|
||||
|
||||
于是,我们开始追问为什么。一路追寻下来,我们发现,是这个模块内部需要定期将内部状态持久化下来,而在那个时间段内,这个模块就会停止从上游读取数据。所以,在内部看一切正常,而外部看则延迟较大。随后,我们找到了方案,解决了这一问题。
|
||||
|
||||
最后再说一个行业中的例子,据我所知,行业里的某些公司已经开始做所谓的 AIOps,也就是通过人工智能的方式,从数据中,发现更多运维的问题。无论哪种做法,都是为了从数字中发现问题,让系统更稳定。
|
||||
|
||||
我的一个同事有个观点非常值得玩味,他说,从数字上看,好的系统应该是“死水一潭”。
|
||||
|
||||
我是赞同这个观点的,因为出现波动尤其是大幅度波动,又不能给出一个合理解释的话,就说明系统存在着隐患。而让系统稳定,正是我们工作的一个重要组成部分。
|
||||
|
||||
回到这一讲的开头,我说每天工作中的一个重要组成部分就是看数字,其实就是在尝试着从数字的趋势中发现问题,如今团队已经习惯了“给个数字看看”这样的沟通方式,内部扯皮的机会也相应地减少了一些。
|
||||
|
||||
总结时刻
|
||||
|
||||
随着智能时代的来临,人类社会开始逐渐认识到数据的重要性。但我们这群 IT 人在通过数据为其他人服务的同时,却很少把数字化的思维带到自己的工作范围内。这也是工作中很多“空对空”对话的根源所在。
|
||||
|
||||
结合着“以终为始”的思考,如果我们可以在一开始,就设计好测量工作有效性的指标,那么就可以更有目的性地去工作了。
|
||||
|
||||
而如果我们习惯了用数字去思考,就可以在很多方面让数字帮助我们。我举了几个小例子,比如:基于数据进行技术决策、预先设定系统指标,以及发现系统中的问题等等。希望你也可以把数字思维带到你的日常工作中。
|
||||
|
||||
如果今天的内容你只记住一件事,那请记住:问一下自己,我的工作是不是可以用数字衡量。
|
||||
|
||||
最后,我想请你分享一下,你的工作中,有哪些应用数字解决问题的场景呢?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
|
||||
|
||||
|
||||
|
@ -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 product,MVP)来确定的,这个最小可行产品又是由我们在这个迭代里要验证的内容决定的。一环扣一环,我们就得到了迭代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清单,它会包含哪些内容呢?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
|
||||
|
||||
|
||||
|
@ -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;根据页面原型编写页面样式;根据交互效果编写页面逻辑等几个步骤。
|
||||
|
||||
不同的可执行定义差别在于,你是否能清楚地知道这个问题该如何解决。
|
||||
|
||||
对于马斯克来说,他的解决方案可能是成立一个公司,找到这方面的专家帮助他实现。对你的日常工作来说,你要清楚具体每一步要做的事情,如果不能,说明任务还需要进一步分解。
|
||||
|
||||
比如,你要把一个信息存起来,假设你们用的是关系型数据库,对大多数人来说,这个任务分解就到了可执行的程度。但如果你的项目选用了一个新型的数据库,比如图数据库,你的任务分解里可能要包含学习这个数据库的模型,然后还要根据模型设计存储方案。
|
||||
|
||||
不过,在实际工作中,大多数人都高估了自己可执行粒度,低估任务分解的程度。换句话说,如果你没做过任务分解的练习,你分解出来的大部分任务,粒度都会偏大。
|
||||
|
||||
只有能把任务拆分得非常小,你才能对自己的执行能力有一个更清楚地认识,真正的高手都是有很强的分解能力。这个差别就相当于,同样观察一个物品,你用的是眼睛,而高手用的是显微镜。在你看来,高手全是微操作。关于这个话题,后面我们再来细聊。
|
||||
|
||||
一旦任务分解得很小,调整也会变得很容易。很多人都在说计划赶不上变化,而真正的原因就是计划的粒度太大,没法调整。
|
||||
|
||||
从当年的瀑布模型到今天的迭代模型,实际上,就是缩减一次交付的粒度。几周调整一次计划,也就不存在“计划赶不上变化”的情况了,因为我的计划也一直在变。
|
||||
|
||||
如今软件行业都在提倡拥抱变化,而任务分解是我们拥抱变化的前提。
|
||||
|
||||
总结时刻
|
||||
|
||||
我们从外星人探索和马斯克的火星探索入手,介绍了任务分解在人类社会诸多方面的应用,引出了分而治之这个人类面对复杂问题的基本解决方案。接着,我给你讲了这一思想在软件开发领域中的一个常见应用,分而治之的算法。
|
||||
|
||||
虽然我们很熟悉这一思想,但在日常工作中,我们却没有很好地应用它,这也使得大多数人的工作有很大改进空间。运用这一思想的难点在于,给出一个可执行的分解。
|
||||
|
||||
一方面,对复杂工作而言,给出一个分解是巨大的挑战;另一方面,面对日常工作,人们更容易忽略的是,分解的任务要可执行。每个人对可执行的理解不同,只要你清楚地知道接下来的工作该怎么做,任务分解就可以告一段落。
|
||||
|
||||
大多数人对于可执行的粒度认识是不足的,低估了任务分解的程度,做到好的分解你需要达到“微操作”的程度。有了分解得很小的任务,我们就可以很容易完成一个开发循环,也就让计划调整成为了可能。软件行业在倡导拥抱变化,而任务分解是拥抱变化的前提。
|
||||
|
||||
如果今天的内容你只记住一件事,那么请记住:动手做一个工作之前,请先对它进行任务分解。
|
||||
|
||||
最后,我想请你回想一下,你在实际工作中,有哪些依靠任务分解的方式解决的问题呢?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
|
||||
|
||||
|
||||
|
96
专栏/300分钟吃透分布式缓存-完/18Redis协议的请求和响应有哪些“套路”可循?.md
Normal file
96
专栏/300分钟吃透分布式缓存-完/18Redis协议的请求和响应有哪些“套路”可循?.md
Normal 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 等,可以在线修改/获取配置。
|
||||
调试统计协议,如 slowlog,monitor,info 等。
|
||||
其他内部命令,如 migrate,dump,restore 等。
|
||||
|
||||
|
||||
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 的对比、选择及改进。
|
||||
|
||||
你可以参考这个思维导图,对这些知识点进行回顾和梳理。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
53
专栏/300分钟吃透分布式缓存-完/19Redis系统架构中各个处理模块是干什么的?.md
Normal file
53
专栏/300分钟吃透分布式缓存-完/19Redis系统架构中各个处理模块是干什么的?.md
Normal 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 集群可以通过数据迁移,来进行在线扩容。
|
||||
|
||||
|
||||
|
||||
|
116
专栏/300分钟吃透分布式缓存-完/20Redis如何处理文件事件和时间事件?.md
Normal file
116
专栏/300分钟吃透分布式缓存-完/20Redis如何处理文件事件和时间事件?.md
Normal 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 个属性组成。
|
||||
|
||||
|
||||
事件 ID:Redis 为时间事件创建全局唯一 ID,该 ID 按从小到大的顺序进行递增。
|
||||
执行时间 when_sec 和 when_ms:精确到毫秒,记录该事件的到达可执行时间。
|
||||
时间事件处理器 timeProc:在时间事件到达时,Redis 会调用相应的 timeProc 处理事件。
|
||||
关联数据 clientData:在调用 timeProc 时,需要使用该关联数据作为参数。
|
||||
链表指针 prev 和 next:它用来将时间事件维护为双向链表,便于插入及查找所要执行的时间事件。
|
||||
|
||||
|
||||
时间事件的处理是在事件循环中的 aeProcessEvents 中进行。执行过程是:
|
||||
|
||||
|
||||
首先遍历所有的时间事件。
|
||||
比较事件的时间和当前时间,找出可执行的时间事件。
|
||||
然后执行时间事件的 timeProc 函数。
|
||||
执行完毕后,对于周期性时间,设置时间新的执行时间;对于单次性时间,设置事件的 ID为 -1,后续在事件循环中,下一次执行 aeProcessEvents 的时候从链表中删除。
|
||||
|
||||
|
||||
|
||||
|
||||
|
31
专栏/300分钟吃透分布式缓存-完/21Redis读取请求数据后,如何进行协议解析和处理.md
Normal file
31
专栏/300分钟吃透分布式缓存-完/21Redis读取请求数据后,如何进行协议解析和处理.md
Normal 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。同时更新相关的统计数值。
|
||||
|
||||
|
||||
|
||||
|
0
专栏/300分钟吃透分布式缓存-完/22怎么认识和应用Redis内部数据结构?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/22怎么认识和应用Redis内部数据结构?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/23Redis是如何淘汰key的?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/23Redis是如何淘汰key的?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/24Redis崩溃后,如何进行数据恢复的?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/24Redis崩溃后,如何进行数据恢复的?.md
Normal file
37
专栏/300分钟吃透分布式缓存-完/25Redis是如何处理容易超时的系统调用的?.md
Normal file
37
专栏/300分钟吃透分布式缓存-完/25Redis是如何处理容易超时的系统调用的?.md
Normal 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 任务后,则再次进入阻塞,等待下一轮唤醒。
|
||||
|
||||
|
||||
|
||||
|
43
专栏/300分钟吃透分布式缓存-完/26如何大幅成倍提升Redis处理性能?.md
Normal file
43
专栏/300分钟吃透分布式缓存-完/26如何大幅成倍提升Redis处理性能?.md
Normal 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 读写和执行,互不干扰、等待与竞争,才能真正高效地利用服务器多核心,达到性能数量级的提升。
|
||||
|
||||
|
||||
|
||||
|
53
专栏/300分钟吃透分布式缓存-完/27Redis是如何进行主从复制的?.md
Normal file
53
专栏/300分钟吃透分布式缓存-完/27Redis是如何进行主从复制的?.md
Normal 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。通过多个 slave,Redis 的节点数据就可以实现多副本保存,任何一个节点异常都不会导致数据丢失,同时多 slave 可以 N 倍提升读性能。master 只写不读,这样整个 master-slave 组合,读写能力都可以得到大幅提升。
|
||||
|
||||
master 在分发写请求时,同时会将写指令复制一份存入复制积压缓冲,这样当 slave 短时间断开重连时,只要 slave 的复制位置点仍然在复制积压缓冲,则可以从之前的复制位置点之后继续进行复制,提升复制效率。
|
||||
|
||||
|
||||
|
||||
主库 master 和从库 slave 之间通过复制 id 进行匹配,避免 slave 挂到错误的 master。Redis 的复制分为全量同步和增量同步。Redis 在进行全量同步时,master 会将内存数据通过 bgsave 落地到 rdb,同时,将构建 内存快照期间 的写指令,存放到复制缓冲中,当 rdb 快照构建完毕后,master 将 rdb 和复制缓冲队列中的数据全部发送给 slave,slave 完全重新创建一份数据。这个过程,对 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 当前保存了复制 id,replid 和 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 中。
|
||||
|
||||
相比之前的 sync,psync2 优化很明显。在短时间断开连接、slave 重启、切主等多种场景,只要延迟不太久,复制偏移仍然在复制积压缓冲,均可进行增量同步。master 不用构建并发送巨大的 rdb,可以大大减轻 master 的负荷和网络带宽的开销。同时,slave 可以通过轻量的增量复制,实现数据同步,快速恢复服务,减少系统抖动。
|
||||
|
||||
但是,psync 依然严重依赖于复制缓冲积压,太大会占用过多内存,太小会导致频繁的全量复制。而且,由于内存限制,即便设置相对较大的复制缓冲区,在 slave 断开连接较久时,仍然很容易被复制缓冲积压冲刷,从而导致全量复制。
|
||||
|
||||
|
||||
|
||||
|
135
专栏/300分钟吃透分布式缓存-完/28如何构建一个高性能、易扩展的Redis集群?.md
Normal file
135
专栏/300分钟吃透分布式缓存-完/28如何构建一个高性能、易扩展的Redis集群?.md
Normal 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 下有多个 slave,Client 需要在多个 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 端。
|
||||
|
||||
|
||||
|
||||
如果一个请求包含多个 key,Proxy 需要将请求的多个 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 的节点,以及数据访问的路由表。除了 Zookeeper,Codis 也支持 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 特别大的业务场景。
|
||||
|
||||
|
||||
|
||||
|
0
专栏/300分钟吃透分布式缓存-完/29从容应对亿级QPS访问,Redis还缺少什么?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/29从容应对亿级QPS访问,Redis还缺少什么?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/30面对海量数据,为什么无法设计出完美的分布式缓存体系?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/30面对海量数据,为什么无法设计出完美的分布式缓存体系?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/32一个典型的分布式缓存系统是什么样的?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/32一个典型的分布式缓存系统是什么样的?.md
Normal file
67
专栏/300分钟吃透分布式缓存-完/33如何为秒杀系统设计缓存体系?.md
Normal file
67
专栏/300分钟吃透分布式缓存-完/33如何为秒杀系统设计缓存体系?.md
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
33 如何为秒杀系统设计缓存体系?
|
||||
本课时我们具体讲解如何为秒杀系统设计缓存体系。
|
||||
|
||||
秒杀系统分析
|
||||
|
||||
互联网电商为了吸引人气,经常会对一些商品进行低价秒杀售卖活动。比如几年前小米的不定期新品发售,又如当前每年定期举行双11、双12中的特价商品售卖。秒杀售卖时,大量消费者蜂拥而至,给电商带来了极大的人气,也给电商背后的服务系统带来了超高的并发访问负荷。
|
||||
|
||||
在不同电商、不同的秒杀活动,秒杀系统售卖的商品、销售策略大不相同,但秒杀背后的秒杀系统却有很大的相似性,基本都有以下这些共同特点。
|
||||
|
||||
首先,秒杀业务简单,每个秒杀活动售卖的商品是事先定义好的,这些商品有明确的类型和数量,卖完即止。
|
||||
|
||||
其次,秒杀活动定时上架,而且会提供一个秒杀入口,消费者可以在活动开始后,通过这个入口进行抢购秒杀活动。
|
||||
|
||||
再次,秒杀活动由于商品售价低廉,广泛宣传,购买者远大于商品数,开始售卖后,会被快速抢购一空。
|
||||
|
||||
最后,由于秒杀活动的参与者众多,远超日常访客数量,大量消费者涌入秒杀系统,还不停的刷新访问,短时间内给系统带来超高的并发流量,直到活动结束,流量消失。
|
||||
|
||||
分析了秒杀系统的特点,很容易发现,秒杀系统实际就是一个有计划的低价售卖活动,活动期间会带来 N 倍爆发性增长的瞬时流量,活动后,流量会快速消失。因此,秒杀活动会给后端服务带来如下的技术挑战。
|
||||
|
||||
首先,秒杀活动持续时间短,但访问冲击量大,秒杀系统需要能够应对这种爆发性的类似攻击的访问模型。
|
||||
|
||||
其次,业务的请求量远远大于售卖量,大部分是最终无法购买成功的请求,秒杀系统需要提前规划好处理策略;
|
||||
|
||||
而且,由于业务前端访问量巨大,系统对后端数据的访问量也会短时间爆增,需要对数据存储资源进行良好设计。
|
||||
|
||||
另外,秒杀活动虽然持续时间短,但活动期间会给整个业务系统带来超大负荷,业务系统需要制定各种策略,避免系统过载而宕机。
|
||||
|
||||
最后,由于售卖活动商品价格低廉,存在套利空间,各种非法作弊手段层出,需要提前规划预防策略。
|
||||
|
||||
秒杀系统设计
|
||||
|
||||
在设计秒杀系统时,有两个设计原则。
|
||||
|
||||
首先,要尽力将请求拦截在系统上游,层层设阻拦截,过滤掉无效或超量的请求。因为访问量远远大于商品数量,所有的请求打到后端服务的最后一步,其实并没有必要,反而会严重拖慢真正能成交的请求,降低用户体验。
|
||||
|
||||
其次,要充分利用缓存,提升系统的性能和可用性。
|
||||
|
||||
|
||||
|
||||
秒杀系统专为秒杀活动服务,售卖商品确定,因此可以在设计秒杀商品页面时,将商品信息提前设计为静态信息,将静态的商品信息以及常规的 CSS、JS、宣传图片等静态资源,一起独立存放到 CDN 节点,加速访问,且降低系统访问压力。
|
||||
|
||||
在访问前端也可以制定种种限制策略,比如活动没开始时,抢购按钮置灰,避免抢先访问,用户抢购一次后,也将按钮置灰,让用户排队等待,避免反复刷新。
|
||||
|
||||
用户所有的请求进入秒杀系统前,通过负载均衡策略均匀分发到不同 Web 服务器,避免节点过载。在 Web 服务器中,首先进行各种服务预处理,检查用户的访问权限,识别并发刷订单的行为。同时在真正服务前,也要进行服务前置检查,避免超售发生。如果发现售出数量已经达到秒杀数量,则直接返回结束。
|
||||
|
||||
秒杀系统在处理抢购业务逻辑时,除了对用户进行权限校验,还需要访问商品服务,对库存进行修改,访问订单服务进行订单创建,最后再进行支付、物流等后续服务。这些依赖服务,可以专门为秒杀业务设计排队策略,或者额外部署实例,对秒杀系统进行专门服务,避免影响其他常规业务系统。
|
||||
|
||||
|
||||
|
||||
在秒杀系统设计中,最重要的是在系统开发之初就进行有效分拆。首先分拆秒杀活动页面的内容,将静态内容分拆到 CDN,动态内容才通过接口访问。其次,要将秒杀业务系统和其他业务系统进行功能分拆,尽量将秒杀系统及依赖服务独立分拆部署,避免影响其他核心业务系统。
|
||||
|
||||
由于秒杀的参与者远大于商品数,为了提高抢购的概率,时常会出现一些利用脚本和僵尸账户并发频繁调用接口进行强刷的行为,秒杀系统需要构建访问记录缓存,记录访问 IP、用户的访问行为,发现异常访问,提前进行阻断及返回。同时还需要构建用户缓存,并针对历史数据分析,提前缓存僵尸强刷专业户,方便在秒杀期间对其进行策略限制。这些访问记录、用户数据,通过缓存进行存储,可以加速访问,另外,对用户数据还进行缓存预热,避免活动期间大量穿透。
|
||||
|
||||
在业务请求处理时,所有操作尽可能由缓存交互完成。由于秒杀商品较少,相关信息全部加载到内存,把缓存暂时当作存储用,并不会带来过大成本负担。
|
||||
|
||||
为秒杀商品构建商品信息缓存,并对全部目标商品进行预热加载。同时对秒杀商品构建独立的库存缓存,加速库存检测。这样通过秒杀商品列表缓存,进行快速商品信息查询,通过库存缓存,可以快速确定秒杀活动进程,方便高效成交或无可售商品后的快速检测及返回。在用户抢购到商品后,要进行库存事务变更,进行库存、订单、支付等相关的构建和修改,这些操作可以尽量由系统只与缓存组件交互完成初步处理。后续落地等操作,必须要入DB库的操作,可以先利用消息队列机,记录成交事件信息,然后再逐步分批执行,避免对 DB 造成过大压力。
|
||||
|
||||
总之,在秒杀系统中,除了常规的分拆访问内容和服务,最重要的是尽量将所有数据访问进行缓存化,尽量减少 DB 的访问,在大幅提升系统性能的同时,提升用户体验。
|
||||
|
||||
|
||||
|
||||
|
79
专栏/300分钟吃透分布式缓存-完/34如何为海量计数场景设计缓存体系?.md
Normal file
79
专栏/300分钟吃透分布式缓存-完/34如何为海量计数场景设计缓存体系?.md
Normal 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,穿透到 DB,DB 的读也会成为瓶颈。因为 DB 能支撑的 TPS 不过 3000~6000 之间,远远无法满足高并发计数访问场景的需要。
|
||||
|
||||
采用 Redis 全量存储方案,通过分片和主从复制,读写性能不会成为主要问题,但容量成本却会带来巨大开销。
|
||||
|
||||
因为,一方面 Redis 作为通用型存储来存储计数,内存存储效率低。以存储一个 key 为 long 型 id、value 为 4 字节的计数为例,Redis 至少需要 65 个字节左右,不同版本略有差异。但这个计数理论只需要占用 12 个字节即可。内存有效负荷只有 12⁄65=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 是微博 id,value 是自定义的多个计数。
|
||||
|
||||
微博的 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 倍。
|
||||
|
||||
|
||||
|
||||
|
0
专栏/300分钟吃透分布式缓存-完/35如何为社交feed场景设计缓存体系?.md
Normal file
0
专栏/300分钟吃透分布式缓存-完/35如何为社交feed场景设计缓存体系?.md
Normal file
0
专栏/AI技术内参/000开篇词你的360度人工智能信息助理.md
Normal file
0
专栏/AI技术内参/000开篇词你的360度人工智能信息助理.md
Normal file
0
专栏/AI技术内参/001聊聊2017年KDD大会的时间检验奖.md
Normal file
0
专栏/AI技术内参/001聊聊2017年KDD大会的时间检验奖.md
Normal file
81
专栏/AI技术内参/002精读2017年KDD最佳研究论文.md
Normal file
81
专栏/AI技术内参/002精读2017年KDD最佳研究论文.md
Normal 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 Frequency–Inverse Document Frequency,词频-逆向文件频率),并且取TF-IDF值最高的一些单词相对应的嵌入向量,加权平均以后,就得到了相应的唯一的目的向量或者是机制向量。作者们发现这种利用TF-IDF值加权的方法可以更加有效地表达文本的各种重要信息。注意,这个步骤是依赖于文档标签的,也就是说,我们只能对训练数据进行这样的构造。
|
||||
|
||||
到目前为止,我们描述了如何从文本到达文本对应的目的向量和机制向量的步骤。那么,如何基于这样的数据以及向量,来对未知的文档进行提取目的向量和机制向量呢?文章采用了深度模型RNN(Recurrent Neural Network,循环神经网络),具体说来是双向的RNN并且有一个GRU(Gated Recurrent Unit,门控循环单元)。
|
||||
|
||||
这里我就不复述细节了,总体的思路就是,根据文档的嵌入向量信息,我们希望得到一组文档的隐含表达(中间参数),然后可以从这个隐含表达来预测目的向量和机制向量。注意,因为需要预测两组目标,目的向量和机制向量,因此,这里至少需要分别有两组参数。
|
||||
|
||||
除了预测文档的目的向量和机制向量以外,作者们还提出了一个用少数关键词来解释这两组变量的机制。具体说来,就是设立一个新的学习目标函数,希望通过少数关键词所对应的嵌入向量来重构目的向量或者机制向量,让得到的误差最小。
|
||||
|
||||
方法的实验效果
|
||||
|
||||
作者们使用了Quirky数据集,通过亚马逊土耳其机器人标注了八千多组产品信息。首先,检测了利用学习到的目的向量和机制向量,是否能够比较容易地从海量数据中提取相应的类比信息。这里,作者们直接利用把目的向量和机制向量拼接的方式来表达问题。答案是,效果非常显著。在前1%到25%的提取结果中,精度(Precision)和召回(Recall)都比之前的标准文本处理方法,比如LDA、TF-IDF、全局的嵌入向量要好10%到20%,可以说这是非常有效果的。
|
||||
|
||||
作者们还测试了,通过提出的方法,是否能够为用户推荐比较好的类比场景。这里,文章又找了38个亚马逊土耳其机器人的虚拟工人来为12个产品思路打分。在不知道推荐方法的情况下,虚拟工人认为这篇文章提出的方法能够推荐更有新意、在深层次上更加类似的场景。这也部分解决了我们前面说到的文章希望解决的问题。
|
||||
|
||||
小结
|
||||
|
||||
今天我为你讲了KDD 2017年的年度最佳研究论文,这篇论文提出了一种自动的方法来挖掘类比信息,为快速创新铺平道路。一起来回顾下要点:第一,我简单地介绍了这篇文章的作者群信息,帮你了解到这篇文章是机器学习和人机交互研究的一个结合。第二,我详细地介绍了这篇文章想要解决的问题以及贡献。第三,我简要地介绍了文章所提出方法的核心内容。
|
||||
|
||||
最后,给你留一个思考题,这篇文章提出的是使用标注信息来获取目的向量和机制向量,我们有没有办法能够不使用标注信息,采用完全无监督的方式呢?
|
||||
|
||||
拓展阅读:Accelerating Innovation Through Analogy Mining
|
||||
|
||||
|
||||
|
||||
|
78
专栏/AI技术内参/003精读2017年KDD最佳应用数据科学论文.md
Normal file
78
专栏/AI技术内参/003精读2017年KDD最佳应用数据科学论文.md
Normal 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
|
||||
|
||||
|
||||
|
||||
|
0
专栏/AI技术内参/004精读2017年EMNLP最佳长论文之一.md
Normal file
0
专栏/AI技术内参/004精读2017年EMNLP最佳长论文之一.md
Normal file
0
专栏/AI技术内参/005精读2017年EMNLP最佳长论文之二.md
Normal file
0
专栏/AI技术内参/005精读2017年EMNLP最佳长论文之二.md
Normal file
0
专栏/AI技术内参/006精读2017年EMNLP最佳短论文.md
Normal file
0
专栏/AI技术内参/006精读2017年EMNLP最佳短论文.md
Normal file
80
专栏/AI技术内参/007精读2017年ICCV最佳研究论文.md
Normal file
80
专栏/AI技术内参/007精读2017年ICCV最佳研究论文.md
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
007 精读2017年ICCV最佳研究论文
|
||||
ICCV(International 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),研究了一种改进的ReLU(Rectified 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
75
专栏/AI技术内参/008精读2017年ICCV最佳学生论文.md
Normal file
75
专栏/AI技术内参/008精读2017年ICCV最佳学生论文.md
Normal 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来从原始的输入图像中抽取基本的图像特性。
|
||||
|
||||
第二,文章采用了一种叫FPN(Feature Pyramid Net)的网络架构来对图像的不同分辨率或者不同大小的情况进行特性抽取。
|
||||
|
||||
第三,和Faster R-CNN相似的,RetinaNet也是用了Anchor的思想,也就是说从小的一个移动窗口中去寻找一个比较大的矩形框的可能性。
|
||||
|
||||
最后,RetinaNet把从FPN抽取出来的特性用于两个平行的网络结构,一个用于物体分类,一个用于矩形框的定位。这一点很类似两个阶段模型的做法。
|
||||
|
||||
方法的实验效果
|
||||
|
||||
作者们使用RetinaNet在目前流行的图像物体检测任务数据集COCO上做了检测。首先,RetinaNet的“平均精度” (Average Precision)要好于之前的所有一个阶段模型,初步验证了提出的目标函数和网络架构的优越性。并且,在实验中,作者们分别使用了不同的“焦点损失”指数参数来展示这个参数对于结果的重要性。同时,作者们还展示了,RetinaNet能够比Faster R-CNN这种经典的两阶段模型,以及一些变种在实验结果上至少持平甚至要更好。
|
||||
|
||||
小结
|
||||
|
||||
今天我为你讲了2017年ICCV的最佳学生论文,这篇文章介绍了目前在图像物体识别中的最新目标函数“焦点损失”的大概内容。
|
||||
|
||||
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息。第二,我们分析了这篇文章要解决的问题和主要贡献 。第三,我们详细介绍了文章提出方法的核心内容 。
|
||||
|
||||
最后,给你留一个思考题,除了这篇文章介绍的更改目标函数的方法,针对不平衡的数据集,你觉得还有哪些通常使用的方法?
|
||||
|
||||
|
||||
|
||||
|
69
专栏/AI技术内参/009如何将深度强化学习应用到视觉问答系统?.md
Normal file
69
专栏/AI技术内参/009如何将深度强化学习应用到视觉问答系统?.md
Normal 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机器人得到一张图片I,Q机器人一开始得到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的一篇有意思的文章。这篇文章介绍了如何利用深度强化学习来搭建一个模型去理解两个机器人的对话并能够理解图像信息。
|
||||
|
||||
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息。第二,我们详细介绍了这篇文章要解决的问题以及贡献 。第三,我们重点介绍了的文章提出方法核心内容 。
|
||||
|
||||
最后,给你留一个思考题,你认为把强化学习用在这样的对话场景中,难点是什么?
|
||||
|
||||
|
||||
|
||||
|
63
专栏/AI技术内参/010精读2017年NIPS最佳研究论文之一:如何解决非凸优化问题?.md
Normal file
63
专栏/AI技术内参/010精读2017年NIPS最佳研究论文之一:如何解决非凸优化问题?.md
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
010 精读2017年NIPS最佳研究论文之一:如何解决非凸优化问题?
|
||||
机器学习与人工智能领域的顶级会议NIPS(Conference 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年的最佳研究论文之一,文章非常理论化。文章的一个核心观点是希望能够通过对损失函数的均值和方差同时建模从而达到让目标函数健壮化的目的。
|
||||
|
||||
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息。第二,我们详细介绍了这篇文章要解决的问题以及贡献 。第三,我们简要地介绍的文章提出方法的核心内容 。
|
||||
|
||||
最后,给你留一个思考题,要想控制目标函数的预测结果的方差,除了本文提出的把均值和方差都设计到目标函数里,还有没有别的方法?
|
||||
|
||||
|
||||
|
||||
|
0
专栏/AI技术内参/012精读2017年NIPS最佳研究论文之三:如何解决非完美信息博弈问题?.md
Normal file
0
专栏/AI技术内参/012精读2017年NIPS最佳研究论文之三:如何解决非完美信息博弈问题?.md
Normal file
0
专栏/AI技术内参/013WSDM2018论文精读:看谷歌团队如何做位置偏差估计.md
Normal file
0
专栏/AI技术内参/013WSDM2018论文精读:看谷歌团队如何做位置偏差估计.md
Normal file
0
专栏/AI技术内参/014WSDM2018论文精读:看京东团队如何挖掘商品的替代信息和互补信息.md
Normal file
0
专栏/AI技术内参/014WSDM2018论文精读:看京东团队如何挖掘商品的替代信息和互补信息.md
Normal file
57
专栏/AI技术内参/015WSDM2018论文精读:深度学习模型中如何使用上下文信息?.md
Normal file
57
专栏/AI技术内参/015WSDM2018论文精读:深度学习模型中如何使用上下文信息?.md
Normal 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的一篇来自谷歌团队的文章,这篇文章介绍了在传统推荐系统的模型中(比如矩阵分解等)都有的交叉特性如何应用在深度学习中。
|
||||
|
||||
一起来回顾下要点:第一,我们简要介绍了这篇文章的作者群信息;第二,我们详细介绍了这篇文章要解决的问题以及贡献 ;第三,我们分析了文章提出方法的核心内容以及实验结果。
|
||||
|
||||
最后,给你留一个思考题,深度学习模型在默认状态下并不能很好地抓住交叉特性,这是深度模型的问题吗?
|
||||
|
||||
|
||||
|
||||
|
69
专栏/AI技术内参/016TheWeb2018论文精读:如何对商品的图片美感进行建模?.md
Normal file
69
专栏/AI技术内参/016TheWeb2018论文精读:如何对商品的图片美感进行建模?.md
Normal 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)等指标。
|
||||
|
||||
从实验效果来看,这篇文章提出的模型要明显好于矩阵分解以及只有基本图像信息的算法,表明针对产品的图像美感进行建模是有价值的。并且,作者们提出的新的张量分解方法也被证明是切实有效的。
|
||||
|
||||
小结
|
||||
|
||||
今天我为你讲了今年万维网大会的一篇优秀论文。文章介绍了如何对商品的图片美感进行建模,以及如何把提取到的信息融入到一个基于张量分解的推荐系统中。
|
||||
|
||||
一起来回顾下要点:第一,我们详细介绍了这篇文章要解决的问题以及贡献;第二,我们简要地介绍了文章提出方法的核心内容;第三,我们简单分享了一下模型的实验成果。
|
||||
|
||||
最后,给你留一个思考题,有没有在没有标签情况下对图片的美感进行建模的呢?
|
||||
|
||||
|
||||
|
||||
|
0
专栏/AI技术内参/017TheWeb2018论文精读:如何改进经典的推荐算法BPR?.md
Normal file
0
专栏/AI技术内参/017TheWeb2018论文精读:如何改进经典的推荐算法BPR?.md
Normal file
0
专栏/AI技术内参/018TheWeb2018论文精读:如何从文本中提取高元关系?.md
Normal file
0
专栏/AI技术内参/018TheWeb2018论文精读:如何从文本中提取高元关系?.md
Normal file
0
专栏/AI技术内参/019SIGIR2018论文精读:偏差和流行度之间的关系.md
Normal file
0
专栏/AI技术内参/019SIGIR2018论文精读:偏差和流行度之间的关系.md
Normal file
67
专栏/AI技术内参/020SIGIR2018论文精读:如何利用对抗学习来增强排序模型的普适性?.md
Normal file
67
专栏/AI技术内参/020SIGIR2018论文精读:如何利用对抗学习来增强排序模型的普适性?.md
Normal 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上的最佳短论文。
|
||||
|
||||
一起来回顾下要点:第一,这篇论文主要涉及了两个概念,分别是跨领域信息检索和对抗学习,我们详细介绍了这篇文章的主要贡献,就是利用对抗学习的理念来增强所学排序模型的普适性;第二,我们简要地介绍了文章提出的方法核心内容,训练两套模型,利用对抗学习来分别产生相关的和不相关的文档;第三,我们简单介绍了论文的实验结果,进行对抗训练会有更好的效果。
|
||||
|
||||
最后,给你留一个思考题,除了使用对抗训练,你还能想到什么方法,能够比较好地学习到不属于某一个特定领域信息的排序模型?
|
||||
|
||||
|
||||
|
||||
|
67
专栏/AI技术内参/021SIGIR2018论文精读:如何对搜索页面上的点击行为进行序列建模?.md
Normal file
67
专栏/AI技术内参/021SIGIR2018论文精读:如何对搜索页面上的点击行为进行序列建模?.md
Normal 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的一个篇精彩论文。
|
||||
|
||||
一起来回顾下要点:第一,我们详细介绍了这篇文章要解决的问题以及贡献,主要是对用户在搜索页面上的点击行为进行序列建模;第二,我们简要介绍了文章提出方法的核心内容,主要是编码器和解码器两个模块;第三,我们简单介绍了论文的实验结果。
|
||||
|
||||
最后,给你留一个思考题,如果针对多个连续的查询关键词的点击行为进行建模,你能否用这篇论文提出的思路来扩展模型呢?
|
||||
|
||||
|
||||
|
||||
|
71
专栏/AI技术内参/022CVPR2018论文精读:如何研究计算机视觉任务之间的关系?.md
Normal file
71
专栏/AI技术内参/022CVPR2018论文精读:如何研究计算机视觉任务之间的关系?.md
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
022 CVPR 2018论文精读:如何研究计算机视觉任务之间的关系?
|
||||
今年6月18 日~22日,计算机视觉和模式识别大会CVPR(Conference 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的最佳论文。
|
||||
|
||||
一起来回顾下要点:第一,我们详细介绍了这篇文章要解决的问题以及贡献,论文研究了计算机视觉任务之间的关系,并且提出了一个计算框架,能够起到迁移学习的作用;第二,我们简要介绍了文章提出的核心方法,主要有四个组成部分;第三,我们简单介绍了论文的实验结果。
|
||||
|
||||
最后,给你留一个思考题,当前挖掘的关系主要是任务的两两关系,能否有一个方法挖掘任务的高维度关系,比如三个任务之间的关系?
|
||||
|
||||
|
||||
|
||||
|
79
专栏/AI技术内参/023CVPR2018论文精读:如何从整体上对人体进行三维建模?.md
Normal file
79
专栏/AI技术内参/023CVPR2018论文精读:如何从整体上对人体进行三维建模?.md
Normal 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
0
专栏/AI技术内参/024CVPR2018论文精读:如何解决排序学习计算复杂度高这个问题?.md
Normal file
0
专栏/AI技术内参/024CVPR2018论文精读:如何解决排序学习计算复杂度高这个问题?.md
Normal file
0
专栏/AI技术内参/025ICML2018论文精读:模型经得起对抗样本的攻击?这或许只是个错觉.md
Normal file
0
专栏/AI技术内参/025ICML2018论文精读:模型经得起对抗样本的攻击?这或许只是个错觉.md
Normal file
0
专栏/AI技术内参/026ICML2018论文精读:聊一聊机器学习算法的公平性问题.md
Normal file
0
专栏/AI技术内参/026ICML2018论文精读:聊一聊机器学习算法的公平性问题.md
Normal file
0
专栏/AI技术内参/027ICML2018论文精读:优化目标函数的时候,有可能放大了不公平?.md
Normal file
0
专栏/AI技术内参/027ICML2018论文精读:优化目标函数的时候,有可能放大了不公平?.md
Normal file
77
专栏/AI技术内参/028ACL2018论文精读:问答系统场景下,如何提出好问题?.md
Normal file
77
专栏/AI技术内参/028ACL2018论文精读:问答系统场景下,如何提出好问题?.md
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
028 ACL 2018论文精读:问答系统场景下,如何提出好问题?
|
||||
今年7月15日~20日,计算语言学协会年会ACL 2018(56th 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),是计算机系的博士生。她已经在ACL,EMNLP、NAACL等自然语言处理大会上发表了多篇论文,并且在微软研究院实习过。
|
||||
|
||||
第二作者是饶的导师哈尔·道姆三世(Hal Daume III),是马里兰大学学院市分校计算机系的一名教授,目前也在纽约的微软研究院工作。他是机器学习和自然语言处理领域的专家,在诸多领域都发表过不少研究成果,论文引用数达到9千多次。
|
||||
|
||||
论文的主要贡献
|
||||
|
||||
这篇论文关注的是“问答系统”(Question & Answering)。问答系统不仅在实用领域受到大量用户的青睐,产生了诸如Quora、知乎、Stack Overflow等知名的在线问答服务,也在人工智能系统开发领域受到研究者的关注。
|
||||
|
||||
我们曾经提到过“图灵测试”,用来衡量一个系统或者说是一个机器人是否具有真正的人工智能,这个测试其实就是建立在人机问答的交互场景下的。因此,建立有效的问答系统一直是人工智能研究,特别是自然语言处理研究的核心课题之一。
|
||||
|
||||
这篇论文的作者们认为,在问答系统的场景中,一个非常重要的手段是针对已经提出的问题进行“澄清式”(Clarification)提问,从而能够引导其他回答者更加有效地进行回答。也就是说,作者们研究的主题是,如何找到这些具有桥梁作用的“澄清式问题”,这是这篇论文的第一个重要贡献。
|
||||
|
||||
论文的第二个主要贡献是利用了“决策论”(Decision Theoretic)框架下的EVPI(Expected 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的一篇最佳论文。
|
||||
|
||||
一起来回顾下要点:第一,这篇论文提出了“澄清式提问”这个概念,来帮助问答系统的开发;第二,文章提出了一系列方法,对澄清式问题进行描述和衡量;第三,文章构建了一个数据集,通过实验论证了所提出方法的有效性。
|
||||
|
||||
最后,给你留一个思考题,通过这篇文章关于澄清式问题的介绍,你能否给澄清式问题下一个定义呢?
|
||||
|
||||
|
||||
|
||||
|
74
专栏/AI技术内参/029ACL2018论文精读:什么是对话中的前提触发?如何检测?.md
Normal file
74
专栏/AI技术内参/029ACL2018论文精读:什么是对话中的前提触发?如何检测?.md
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
029 ACL 2018论文精读:什么是对话中的前提触发?如何检测?
|
||||
今天,我来和你分享ACL 2018的第二篇最佳论文,题目是《让我们“再”次做到:检测副词前提触发词的第一种计算方法》(Let’s 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呢?
|
||||
|
||||
|
||||
|
||||
|
73
专栏/AI技术内参/030ACL2018论文精读:什么是端到端的语义哈希?.md
Normal file
73
专栏/AI技术内参/030ACL2018论文精读:什么是端到端的语义哈希?.md
Normal 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的分享就告一段落。
|
||||
|
||||
一起来回顾下要点:第一,这篇文章针对语义哈希产生过程的劣势,提出了“端到端”的语义哈希训练过程;第二,论文的核心方法是把文档生成语义哈希看作是一种编码和解码的流程,进一步发现“语义哈希”其实也可以被看作是一个“比率损失平衡”问题;第三,论文取得了不错的实验效果。
|
||||
|
||||
最后,给你留一个思考题,在现实中利用语义哈希,有没有什么障碍?比如要在推荐系统中做语义哈希,最大的挑战会是什么?
|
||||
|
||||
|
||||
|
||||
|
0
专栏/AI技术内参/030复盘7一起来读人工智能国际顶级会议论文.md
Normal file
0
专栏/AI技术内参/030复盘7一起来读人工智能国际顶级会议论文.md
Normal file
0
专栏/AI技术内参/031经典搜索核心算法:TF-IDF及其变种.md
Normal file
0
专栏/AI技术内参/031经典搜索核心算法:TF-IDF及其变种.md
Normal file
81
专栏/AI技术内参/032经典搜索核心算法:BM25及其变种(内附全年目录).md
Normal file
81
专栏/AI技术内参/032经典搜索核心算法:BM25及其变种(内附全年目录).md
Normal 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是非监督的排序方法,并且我们提到其中有一些超参数,那么是否可以通过机器学习的手段来学习到这些超参数的最佳取值呢?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
87
专栏/AI技术内参/033经典搜索核心算法:语言模型及其变种.md
Normal file
87
专栏/AI技术内参/033经典搜索核心算法:语言模型及其变种.md
Normal 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年翟成祥还获得了著名的美国总统奖(PECASE,Presidential 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而言,其实更加直观,更好理解。语言模型也是一个强有力的非监督学习方法的文本排序算法。
|
||||
|
||||
一起来回顾下要点:第一,简要介绍了语言模型的历史。第二,详细介绍了简单语言模型,即“查询关键字似然检索模型”的主要组成部分。第三,简要地介绍了语言模型的两个变种方向。
|
||||
|
||||
最后,给你留一个思考题,如果根据语言模型,也就是概率分布函数的估计,无法得到我们之前提到的最优解析解的话,我们应该怎么求解语言模型的参数呢?
|
||||
|
||||
|
||||
|
||||
|
71
专栏/AI技术内参/034机器学习排序算法:单点法排序学习.md
Normal file
71
专栏/AI技术内参/034机器学习排序算法:单点法排序学习.md
Normal 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)的评价方法,也就是把五级相关度当做五种不同的标签,来看分类器的分类准确度。当然,这样的评价方式对于排序来说是有问题的。因为,对于一个实际的数据集来说,五种相关类型所对应的数据量是不同的。
|
||||
|
||||
一般来说,“最相关”和“相关”的文档数量,不管是针对某个查询关键字还是从总体上来看,都是比较少的,而“不相关”和“最不相关”的文档是大量的。因此,单单看分类准确度,很可能会得出不恰当的结果。
|
||||
|
||||
比如说,某个排序算法能够通过分类的手段把大量的“最不相关”和“不相关”的文档分类正确,而可能错失了所有的“最相关”文档。即便从总的分类准确度来说,这样的算法可能还“看得过去”,但实际上这样的算法并没有任何价值。所以,从多类分类的角度来评价排序算法是不完整的。
|
||||
|
||||
针对这样的情况,研究者们设计出了基于五级定义的排序评价方法:NDCG(Normalized Discounted Cumulative Gain)。在这里针对NDCG我就不展开讨论了,你只需要知道NDCG不是一个分类的准确度评价指标,而是一个排序的精度指标。
|
||||
|
||||
NDCG这个指标的假设是,在一个排序结果里,相关信息要比不相关信息排得更高,而最相关信息需要排在最上面,最不相关信息排在最下面。任何排序结果一旦偏离了这样的假设,就会受到“扣分”或者说是“惩罚”。
|
||||
|
||||
需要特别指出的是,我们这里讨论的NDCG仅仅是针对测试集的一个排序评价指标。我们的排序算法依然可以在训练集上从五级相关度上训练多类分类器。仅仅是在测试集上,采用了不同的方法来评价我们的多类分类器结果,而不是采用传统的分类准确度。从某种意义上来说,这里的NDCG其实就起到了“模型选择”(Model Selection)的作用。
|
||||
|
||||
小结
|
||||
|
||||
今天我为你讲了单点法排序学习。可以看到,整个问题的设置已经与传统的文字搜索技术有了本质的区别 。
|
||||
|
||||
一起来回顾下要点:第一,单点法排序学习起步于20世纪90年代,直到2000年后才出现了更多有显著效果的研究。第二,详细介绍了单点法排序学习的问题设置,包括训练集、测试集以及测试环境。
|
||||
|
||||
最后,给你留一个思考题,有没有什么方法可以把我们之前讨论的TF-IDF、BM25和语言模型,这些传统的排序算法和单点法排序学习结合起来?
|
||||
|
||||
|
||||
|
||||
|
0
专栏/AI技术内参/035机器学习排序算法:配对法排序学习.md
Normal file
0
专栏/AI技术内参/035机器学习排序算法:配对法排序学习.md
Normal file
0
专栏/AI技术内参/036机器学习排序算法:列表法排序学习.md
Normal file
0
专栏/AI技术内参/036机器学习排序算法:列表法排序学习.md
Normal file
0
专栏/AI技术内参/037查询关键字理解三部曲之分类.md
Normal file
0
专栏/AI技术内参/037查询关键字理解三部曲之分类.md
Normal file
0
专栏/AI技术内参/038查询关键字理解三部曲之解析.md
Normal file
0
专栏/AI技术内参/038查询关键字理解三部曲之解析.md
Normal file
70
专栏/AI技术内参/039查询关键字理解三部曲之扩展.md
Normal file
70
专栏/AI技术内参/039查询关键字理解三部曲之扩展.md
Normal 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).
|
||||
|
||||
|
||||
|
||||
|
||||
|
79
专栏/AI技术内参/040搜索系统评测,有哪些基础指标?.md
Normal file
79
专栏/AI技术内参/040搜索系统评测,有哪些基础指标?.md
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
|
||||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||||
|
||||
|
||||
040 搜索系统评测,有哪些基础指标?
|
||||
我在之前几周的专栏文章里主要讲解了最经典的信息检索(Information Retrieval)技术和基于机器学习的排序学习算法(Learning to Rank),以及如何对查询关键字(Query)进行理解,包括查询关键字分类、查询关键字解析以及查询关键字扩展。这些经典的技术是2000年后开始流行的各类搜索引擎的核心技术。
|
||||
|
||||
在进一步介绍更多的搜索引擎技术前,我觉得有必要专门抽出一周时间,来好好地看一下搜索系统的评测(Evaluation)以及我们经常使用的各类指标(Metric)。俗话说得好,“如果你不能衡量它,你就不能改进它”(If You Can’t Measure It, You Can’t 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
76
专栏/AI技术内参/041搜索系统评测,有哪些高级指标?.md
Normal file
76
专栏/AI技术内参/041搜索系统评测,有哪些高级指标?.md
Normal 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.781,B的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 (NIPS’07), 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
69
专栏/AI技术内参/042如何评测搜索系统的在线表现?.md
Normal file
69
专栏/AI技术内参/042如何评测搜索系统的在线表现?.md
Normal 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之间的关系呢?
|
||||
|
||||
|
||||
|
||||
|
0
专栏/AI技术内参/043文档理解第一步:文档分类.md
Normal file
0
专栏/AI技术内参/043文档理解第一步:文档分类.md
Normal file
0
专栏/AI技术内参/044文档理解的关键步骤:文档聚类.md
Normal file
0
专栏/AI技术内参/044文档理解的关键步骤:文档聚类.md
Normal file
0
专栏/AI技术内参/045文档理解的重要特例:多模文档建模.md
Normal file
0
专栏/AI技术内参/045文档理解的重要特例:多模文档建模.md
Normal file
Reference in New Issue
Block a user