Linux性能分析工具简介

这半年一直在研究pLink 2的加速策略,了解了相关的性能分析工具,现记录如下。 对软件进行加速的技术路线一般为:首先对代码进行性能分析( performance analysis,也称为 profiling),然后针对性能瓶颈提出优化方案,最后在大数据集上评测加速效果。所以进行性能优化的前提就是准确地测量代码中各个模块的时间消耗。听起来很简单,不就是测量每行代码的运行时间吗,直接用time_t t=clock();不就好了,但是事情并没有那么简单。如果只进行粗粒度的性能分析,比如测量几个大的模块的运行时间,clock()还比较准确,但是如果测量的是运算量比较小的函数调用,而且有大量的小函数调用,clock()就不太准确了。 比如下面的一段代码,我最开始的性能分析方法是在fun1()~fun3()前后添加time_t t=clock(),然后作差求和的。但是3个fun()加起来的时间居然不等于整个while循环的时间,有将近50%的时间不翼而飞了! 1 2 3 4 5 6 7 8 9 10 11 while (true) { if (fun1()) { for (int i = 0; i < k; ++k) { if (flag1) { fun2(); } } } else { fun3(); } } 一种可能是while循环以及内部的for循环本身占用了较多的时间,但是好像不太可能呀。还有一种可能是clock()测量有误,time_t只能精确到秒,clock_t只能精确到毫秒,如果一次fun*()的时间太短,导致一次测量几乎为0,那么多次的while和for循环调用累加的时间也几乎为0,导致实际测量到的fun*()时间远小于真实时间。所以自己用代码进行性能分析可能会有较大的误差,最好借助已有的性能分析工具。 ...

February 7, 2017 · 1 min

2016年终总结

2017农历新年的钟声都已经敲响了,我这2016的年终总结才开始动笔。 2016年,经历了很多,也成长了很多,遇到了很多曾经以为只会出现在电视剧中的场景,令人开始怀疑这个世界。前几天在朋友圈看到一个同学发的状态,觉得很适合作为这篇年终总结的开端。(同学你要是觉得被侵权了,告诉我,我立马删掉:-)) 每个家庭的故事都会是一部长篇史诗。曾经总以为很多情节只会出现在电视剧中,现实的生活很是平淡无味,没有任何波澜,偶尔甚至还会抱怨一下自己不是故事的女主角,其不知现实的生活相比于电视剧,往往是有过之而无不及。 今天哥哥来电话了,从“天津”,一个美丽的谎言,一直在继续,还会坚持很久… 奶奶很开心… ——by angel 关于学习和科研。上半年在雁栖湖完成了研一的下半个学期,完完全全的结束了自己的学生时代。下半年开始进入实验室,直面惨淡的科研。原本以为会由组里的大师兄超哥指导我,没想到中秋前3天,直接接到H Boss的指令,要在中秋前完成一项我从来没做过的评测。不知道怎样设计实验,不知道怎样计算评价指标,甚至连需要评测的软件都不熟。不过好在加班加点完成了。 凌乱的工位 从9月到10月中旬,一直在各种数据集上做各种对比评测,基本上一周做完一个评测数据集,完成一份报告,直接提交给H Boss,下一周在做另一个评测的同时,要根据导师的反馈建议修改完上一个评测报告。这一个多月的时间,共完成了5份报告共计14个版本,真的是要吐了。 后来发现pLink要比对手慢,于是就尝试各种加速策略。开始从外围查找原因,尝试了各种策略,虽然多多少少能加速,但是效果都不完美,有可能对精度有影响。直到12月份,借助谷歌的开源性能分析工具gperftools才找到了软件的性能瓶颈,开始优化并取得了好几倍的加速效果。12月份对软件本身的性能优化大概是我这半年做的唯一一个和计算机本身有点关系的工作吧,可能也是为数不多的我比较享受的一件事。 整个这半年的工作,都是H Boss御驾亲征,亲自指导,当然这样有利有弊。好处是能直接和H Boss对话,机会难得呀,H那种严于律己、追求完美的品质,H的为人处世、口才都是非常值得我学习的。坦白说,虽然我只是无名小卒一个,但从小到大,真正让我从心里佩服的人没几个,H Boss绝对算是其中之一。当然不便之处也非常明显,首先会感觉特别累和压抑,除了每周一次的面谈,每天的邮件,还经常在晚上11点多收到老师的工作微信。所以这半年确实不轻松,工作日晚上基本都在加班,而且加班到晚上11点也是常事,周末也至少工作一天。几乎没有时间运动,身体素质应该下降了不少。与人的沟通也非常少,好几个师兄师姐都问我为啥看我整天都在工位上坐着,好像从来没有动过。夸张点说就是每天晚上下班要走的时候,才发现自己这一天还没有说话。 当然半年高强度的工作,也有不少的收获,基本摸清了pLink代码的来龙去脉,也加速了两三倍,自己的表现也稍微得到了老师和同学的肯定。不过我自己还是不太满意的,加速并没有达到理想的效果。 关于亲情。随着我们两兄弟的大学毕业,家里的情况也在稍稍好转,但是只能算是曲折前进吧。以前小的时候,都是爸妈两个人闹,现在哥哥出场了,真是可笑。谈了快两年的女朋友,女方父母又是要查户口本,又是催着要付定金,说什么不给定金就要拉回老家相亲。这TM比电视剧还荒唐,真把自己当商品了,是不是给的钱多就跟谁呀,混蛋。哥哥也不是个省油的灯,分手之后没过多久说什么被公司派去新加坡学习了,去了之后,连个固定的联系方式都没有,三天两头失联,都老大不小的人了,还让父母担心。工作了两年,一分钱都没攒到,连大学的助学贷款都要我这个还在上学的弟弟替他还。女朋友分手也就算了,没赚到钱也不要紧,关键是你不能让家人这样担心你呀,你定期给家人打个电话,说说你到底在哪里干什么,既然到了新加坡,发几张国外的照片回来分享一下,不可以吗?已经两年过年没回家了,而且两年除夕居然连个电话都没有,这不是不孝是什么,混蛋。 今年妈妈也终于愿意外出挣钱了,虽然不多,但是起码在和爸爸一起努力。家里装修好了一层房子,但是也就是把墙什么的弄好了,家具还没制备。本来想着过年回家给家里买个小米电视,但是爸妈死活说不要买,现在买了也就过年看几天,不划算。后来只好作罢。放假给老爸买的红米手机,终于在除夕这一天拿到手了。给爸妈包的红包,也在按计划逐年的递增着。 自己有时候也埋怨家里,为什么家境这么的不堪,为什么父母没有达到我理想的高度,为什么哥哥这么不争气,为什么没有人关心我。但是埋怨有用吗,肯定是没用的,还是要看各自的造化。 关于爱情。我还是太幼稚,看看我的家境吧,有哪个女生愿意摊上我家这些破事呢,我就不应该奢望有什么爱情。不过今年上半年,爱情确实来过,抛开所有的一切,纯粹的校园爱情。可是10月份的一件事,彻底打醒了我,爱情没有那么简单,需要考虑的问题太多了。关于那段时间的记忆,写过很多文字,也流过很多泪。回顾整个下半年,欣欣和我的状态都不太好,除了那件事的原因,和工作变化也有很大的关系。我们都从雁栖湖到中关村,需要经历一个由学生到科研工作者的角色转变,面对科研的未知,都显得有些手足无措。科研的不顺,生活的压力以及家里的一些烦心事,一股脑的涌向了我们,矛盾也时有发生。经历过不少的磕磕绊绊,总算顺利度过了2016年,没有了热恋时期的疯狂,生活终要归于平淡。正所谓陪伴是最长情的告白,爱情的意义是否就在于两个人一起经历,一起成长呢。让我们共同守护。 关于友情。是的,我还欠很多人一顿饭。很多同学,如果长时间不见面,恐怕真的要忘掉了。 关于个人提高。上半年忙于课程学习,下半年忙于科研,花在个人提高上的时间真的是太少了。不过感谢有欣欣一直做我的榜样,我现在的小目标就是希望比欣欣看更多的书、刷更多的题。 另外下半年回到市区之后,也去了一些之前没去过的地方,比如:清华艺术博物馆、繁星戏剧村、中国美术馆、三联书店、香山等地。其中前两个地方都是和欣欣一起去的,感觉超棒~第一次看达芬奇的特展,开始了解这样一个天才,后来还看过他的传记;第一次去剧院看话剧,感觉和看电影完全不一样,小剧场的效果也是棒棒的。2016年12月31日也是一个值得纪念的日子: 2017跨年活动~ 今年借着CNCP2016会议的机会,去了一次大连,见识了一下海滨城市的风貌,后来还跑去渤海学游泳,海水很脏,而且咸得发苦。希望今后每年都去一个除了上班地点和家里之外的第三个城市。 大连滨海国家地质公园 大连滨海国家地质公园 最后看看年初计划的完成情况: 完成国科大下学期的课程任务:完成 接手pLink软件:完成 刷完LeetCode所有题目:进度147/461,没有完成 读10本书:目前读了《数学之美》、《大话设计模式》、《我不知道该说什么,关于死亡还是爱情》、《男人来自火星、女人来自金星,卷I》、《只有医生知道,卷I》、《文学的种子》、《讲理》、《暗时间》、《达·芬奇传:放飞的心灵》、《人间失格》,刚好10本,圆满完成任务:-) 去电影院看10场电影:目前看了《美人鱼》、《北京遇上西雅图之不二情书》、《忍者神龟2:破影而出》、《七月与安生》、《湄公河行动》、《比利·林恩的中场战事》、《你的名字》、《血战钢锯岭》,只有8场,其中7场是和欣欣一起看的~ 改正坐姿:有一段时间刻意改正了,但是这东西貌似改不过来? 除了LeetCode完成度太差之外,其他计划完成度还是蛮高的。下面定一下2017年的年度计划: 发表pLink 2文章 至少完成毕业工作的80% 刷完LeetCode所有简单题和中等题,找工作之前最好刷完两遍 找到一个满意的工作 读10本书 去电影院看10场电影 看一场话剧(音乐会、歌剧等都可以) 学会游泳 去第三个城市 简短总结一下我的2016:完成了由上课到科研的转变;开始有能力感恩家人;遇到了欣欣,由一个人变成了两个人;第一次去剧场看话剧。展望2017,找工作和准备毕业迫在眉睫,注定又是繁忙的一年! 最后用汪老师的年终总结PPT封面的一句话来结束吧:

January 28, 2017 · 1 min

电动汽车加电站

今天中午和超哥在食堂吃饭的时候聊起了电动汽车加电站的问题,很有意思。 超哥现在开的是一辆电动汽车,他说目前也挺满意的,只要在北京市内开,几乎没问题,充电桩到处都有,马力也足,开起来没有任何噪声。唯一的问题是需要每天充电,去到稍微远一点的京郊可能会电量不足。 然后就讨论到目前电动汽车的瓶颈,主要还是在电池上,一个是续航时间短,另一个是充电时间慢。 然后我就想现在的加油车为什么没有上面的两个问题呢,第一个问题,如果加的油少的话,是不是也会出现续航时间短的问题呢,所以把电动汽车的电池做大一点,密度高一点是不是就可以了呢,虽然技术上可能会有难度,但是我觉得并没有第二个问题严重,所以我觉得电池续航短不是太大的问题。。。 对于第二个问题,在加油站给汽车加油只需要几分钟的时间,但是电动车在充电桩充电可能需要几十分钟甚至一个多小时,所以充电时间慢确实是一个很严重的问题。我当时就说一辆越野车如果要跑沙漠的话,会在车上预存好几桶油备用,类似的,电动车能不能在车上备上几个电池,没电了就换呢,就像手机备用电池一样。然后超哥进一步说不如干脆在现有加油站的基础上,建一个加电站,每辆电动车进站之后,卸下车上的电池,换上提前充满电的电池,整个过程和加油完全一样,还比加油干净。 我突然觉得,哇塞,这个idea不错呀,统一所有电动车的电池,电池没电之后,到站换电池,一个电池就像一个小型的集装箱(或者docker)一样,被卸下来,然后插上满电电池,so easy~但是为啥没公司这么做呢。此时旁边坐着的一位老师也加入了对话,他说电池寿命有长有短,如果自己刚买的新车,没电了拿去换了一个旧电池,肯定不爽;再说了,要统一全国的电池标准,几乎是不可能的,在全国建这样的加电站,没有哪个公司能承担得起这样的成本,最终羊毛出在羊身上,电动车的价格肯定会上涨的…. 这位老师说的都对,但是我依然觉得这个idea是可行的,关键是要看有关部门有没有这个魄力来做这件事。比如国家或行业层面可以强制统一电池的标准,XX汽车协会规定今后的汽车电池必须做成0.5m*0.5m*0.5m的方块,正负极距离5cm,便于拆卸等;同时要求电动汽车的电池安放位置必须在汽车的后右侧等一些列规定。即使国家层面没人愿意做这件事,哪家有魄力的电动汽车公司,是不是可以尝试一下呢,比如特斯拉,统一旗下所有电车的电池规格,并在全球建造加电站,统一更换特斯拉的电池。如果特斯拉这样做了,我相信买特斯拉的车主是愿意承担一部分费用的,毕竟这样的加电站最终还是方便了自己。至于说新车换到旧电池,其实大可不必担心,说不定你的旧车会换到别人的新电池呢,而且特斯拉可以建立一个标准,只有电池能量转换效率大于80%的电池才能进入加电站循环,这样保证了每个人换到的电池续航有保障,至于新旧,我才不管呢,反正下一次加电又要换了。 我个人还是挺喜欢电动汽车的,节能、环保、静音,还看起来酷酷的:-)好希望这个idea能在未来实现呀~ 知乎:电动汽车为什么不统一电池,充电站更换相同档次满电电池?

January 3, 2017 · 1 min

最怕空气突然安静

最怕空气突然安静 最怕朋友突然的关心 最怕回忆突然翻滚绞痛着不平息 最怕突然听到你的消息 想念如果会有声音 不愿那是悲伤的哭泣 事到如今终於让自已属於我自已 只剩眼泪还骗不过自己 突然好想你你会在哪里 过的快乐或委屈 突然好想你突然锋利的回忆 突然模糊的眼睛 我们像一首最美丽的歌曲 变成两部悲伤的电影 为什麽你带我走过最难忘的旅行 然後留下最痛的纪念品 我们那麽甜那麽美那麽相信 那麽疯那麽热烈的曾经 为何我们还是要奔向 各自的幸福和遗憾中老去 突然好想你你会在哪里 过的快乐或委屈 突然好想你突然锋利的回忆 突然模糊的眼睛 最怕空气突然安静 最怕朋友突然的关心 最怕回忆突然翻滚绞痛着不平息 最怕突然听到你的消息 最怕此生已经决定自己过 没有你却又突然听到你的消息 就好像是突然之间,整个世界都失去了你的声音,以前每天都会收到你的喜怒哀乐、衣食住行、午安晚安,突然之间,空气都安静了,没有了你的消息。翻遍你的空间、朋友圈、微博、博客,都没有消息,不知道你在干什么,好伤心。 想你,想知道你在哪里,想知道你在干什么,想要发消息给你,又害怕不能收到你的回信,然后郁闷一整天。想要引起你的注意,绞尽脑汁故意发一些不着边际的微博,等了一整天,没有收到你的点赞或评论。 约你出来吃饭,你一句简单得不能再简单的“可以”,冷冰冰。见到你,裹着厚厚的棉衣,戴着帽子和手套,没有一丝的眼神交流,两个人就这样默默的吃着不知道什么味道的饭菜。 想要打破这宁静的空气,之前想到无数要和你说的人和事,现在却一句话也说不出。两个最熟悉的人,突然之间,像多年未见的朋友,因完全不同的人生轨迹而没有任何共同语言,成了最熟悉的陌生人。 你说最近科研压力很大,周围的同学又是发论文,又是发专利,你却一无所有。挑战赛答辩和开题答辩在即,手足无措。 你说未来太渺茫,没钱买房买车,就算月入两万,要在北京买房也得攒20年。即使买了房,在北京还要买车还要摇号,看病也很贵,还要担心孩子上学各种问题。 我向来是个不会安慰别人的人,但是我尽力想要告诉你,你已经很优秀了,从小到大的尖子生,多才多艺,研一在雁栖湖那么多高手,你照样轻松拿下第一。你的编程能力也远超你们实验室的人,只不过你目前处于一种有力没处使的状态,你的导师让你干一些杂活,如果你的导师也让专心指导你的挑战赛,你现在肯定也写论文了。 我尽力安慰你,希望你对对未来乐观一点。面包会有的,不要太在意这些东西,生活快乐最重要,不要太在意别人的看法,自己纵向比较有进步就行了。突然觉得自己词穷,完全不会安慰一个人。 世界上比你悲惨的人多得太多了,为什么不想想自己有的,至少你有一个健康的身体。 是的,我猜到了,你不理我和我们上周的体检结果有关。我有病,是的,我有病,而且是不可能治好的病,是随时都可能发病的病。我知道这对于你来说很为难,你有矛盾,我理解,只是我希望你能把你的所有顾虑都告诉我,至少让我和你一起分担,如果你说因为我的病,因为我那卑微的背景,想要离开我,我无话可说,这也无可厚非,我接受。 刚开始交往时,我没有告诉你,是我的错。上周的检查结果至少说明你是健康的,我很庆幸,没有伤害到你。如果因为我而让你不开心,让你纠结,请一定让我知道,我会默默走开,我是一个讲理的人。希望你永远健康快乐。 你说这东西还有窗口期,我认,再过几个月,我们再去检查一次,我默默祈祷你是健康的。我知道你和我一样,对一件微小的事也要纠结半天,我不想让你那样难过。 我原本以为我是一个耐得住安静和寂寞的人,我一度还觉得你像一个叽叽喳喳的小鸟不停的在我耳边发出噪声。直到有一天,我发现你的声音不见了,我开始慌了,不安、烦躁、忧郁充斥我的大脑,想要马上见到你问个一清二楚。也许这就是日久生情,这就是感情吧。 熟悉了你随性而又纠结的脾气;习惯了每次和你一起吃饭时纠结菜里到底有没有混猪肉;习惯了每次吃饭时要双份碗筷和三份汤的感觉;渐渐喜欢上了和你一起漫无目的的逛街试衣服的感觉;习惯了买两本一样的书,然后每次你看得都比我快,逼迫我不得不也快快的看;喜欢和你一起看电影出去玩的感觉。 也不知从什么时候开始,微信里的情侣表情都积了厚厚的一层灰;好久好久都没有掰你了,你的手指应该纤细如初了吧。带上耳机,听了一整晚的音乐,已经很久很久没有听歌了。 以前常常对电视剧里的爱情嗤之以鼻,完全不能理解他们为什么要为爱情哭得死去活来,现在,我大概理解了。 你说每个正常人遇到我这种情况,都会矛盾,会需要思考、抉择和权衡,是的,爱情完全不是小说里的义无反顾,说到底是各种利益的权衡。 10月真是一个让人忧伤的月份,亲人的离世,自己也前前后后进了三次医院,揭开了深藏心底N年的伤疤,曾经一直陪伴在身边的人也想要离开。科研上的压力就更不说了,老板一直不停的催促,每天活得像个陀螺,没有方向的转,没有一丝的停顿。 这病态的社会。 亲爱的,我愿意等你,我尊重你的决定,如果你离开,我会祝福你,如果你留下,我希望你不是在怜悯我。

October 28, 2016 · 1 min

C++基本数据类型备忘

今天阅读《C++ Primer, 5e》的第二章,介绍C++的基本内置类型,觉得有一些平时工作容易出错的知识点,现摘录如下: 1 unsigned char c = -1; // 假设char占8比特,c的值为255 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如,8比特大小的unsigned char可以表示0至255区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋给8比特大小的unsigned char所得的结果是255。 1 signed char c2 = 256; // 假设char占8比特,c2的值是未定义的 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。 1 2 3 4 unsigned u = 10; int i = -42; std::cout << i + i << std::endl; // 输出-84 std::cout << u + i << std::endl; // 如果int占32位,输出4294967264 在第一个输出表达式里,两个(负)整数相加并得到了期望的结果。在第二个输出表达式里,相加前首先把整数-42转换成无符号数。把负数转换成无符号数类似于直接给无符号数赋一个负数,结果等于这个负数加上无符号数的模。unsigned (int)的取值范围是0~\(2^{32}-1\),所以总数有\(2^{32}\)个数,-42%\(2^{32}\)=-42+\(2^{32}\),u+i=10+(-42+\(2^{32}\))=4294967264。 1 2 3 unsigned u1 = 42, u2 = 10; std::cout << u1 – u2 << std::endl; // 正确:输出32 std::cout << u2 – u1 << std::endl; // 正确:不过,结果是取模后的值 当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负值。 ...

September 24, 2016 · 1 min

随机矩阵及其特征值

随机矩阵是这样一类方阵,其元素为非负实数,且行和或列和为1。如果行和为1,则称为行随机矩阵;如果列和为1,则称为列随机矩阵;如果行和和列和都为1,则称为双随机矩阵。 前面我们介绍的谷歌矩阵和HMM中的转移矩阵都属于随机矩阵,所以随机矩阵也称为概率矩阵、转移矩阵、或马尔可夫矩阵。 随机矩阵有一个性质,就是其所有特征值的绝对值小于等于1,且其最大特征值为1。下面通过两种方法证明这个结论。 首先,随机矩阵A肯定有特征值1,即 $$\begin{equation}A\vec 1=1\times\vec 1\end{equation}$$其中的单位向量\(\vec 1=(\frac{1}{n},…,\frac{1}{n})^T\),因为A的行和为1,所以上述等式成立。即1是A的特征值。 反证法 假设存在大于1的特征值\(\lambda\),则有\(A\vec x=\lambda\vec x\)。令\(x_k\)是\(\vec x\)中最大的元素。又因为A的元素非负,且行和为1,所以\(\lambda\vec x\)中的每个元素都是\(\vec x\)中元素的凸组合,所以\(\lambda\vec x\)中的每个元素都小于等于\(x_k\)。 $$\begin{equation}a_{i1}x_1+a_{i2}x_2+…+a_{in}x_n=\lambda x_i\leq x_k\end{equation}$$但是如果\(\lambda>1\),则\(\lambda x_k>x_k\),和(2)式矛盾,所以\(\lambda\leq 1\)。又因为(1)式,所以A的最大特征值为1。 常规证法 设对称随机矩阵A的特征值\(\lambda\)对应的特征向量为\(x\)(为了简便,以下省略向量符号),则有\(Ax=\lambda x\),即\(x^TAx=\lambda x^Tx\),欲证明\(|\lambda|\leq 1\),只需证明 $$\begin{equation}\lambda=\frac{< x, Ax >}{< x, x >}\leq 1\end{equation}$$根据定义有: $$\begin{equation}< x, Ax >=\sum_{i=1}^na_{ii}x_i^2+2\sum_{i < j, i\sim j}a_{ij}x_ix_j\end{equation}$$对于\(i < j, i\sim j\),有: $$\begin{equation}a_{ij}(x_i-x_j)^2=a_{ij}x_i^2-2a_{ij}x_ix_j+a_{ij}x_j^2\end{equation}$$两边求和并移项得到: $$ \begin{equation} \begin{array} \displaystyle{2\sum_{i < j}}a_{ij}x_ix_j & = & \displaystyle{\sum_{i < j}a_{ij}x_i^2+\sum_{i < j}a_{ij}x_j^2-\sum_{i < j}a_{ij}(x_i-x_j)^2}\\ & = & \displaystyle{\sum_{i < j}a_{ij}x_i^2+\sum_{i < j}a_{ji}x_j^2-\sum_{i < j}a_{ij}(x_i-x_j)^2}\\ & = & \displaystyle{\sum_{i < j}a_{ij}x_i^2+\sum_{i > j}a_{ij}x_i^2-\sum_{i < j}a_{ij}(x_i-x_j)^2}\\ & = & \displaystyle{\sum_i(\sum_{j\neq i}a_{ij}x_i^2)-\sum_{i < j}a_{ij}(x_i-x_j)^2}\\ & = & \displaystyle{\sum_i(x_i^2(1-a_{ii}))-\sum_{i < j}a_{ij}(x_i-x_j)^2} \end{array} \end{equation} $$第2、3个等号都是因为A是对称矩阵,所以可以把\(a_{ij}\)替换为\(a_{ji}\),然后互换\(i,j\)下标。最后一个等号是因为A的行和为1。 ...

August 23, 2016 · 1 min

马尔可夫聚类算法

马尔可夫聚类算法(The Markov Cluster Algorithm, MCL)是一种快速可扩展的基于图的聚类算法。它的基本思想为:在一个稀疏图G中,如果某个区域A是稠密的(是一个聚类),则在A中随机游走k步,还在A内的概率很大,也就是说,A内的k步路径(k-length path)很多。所以我们可以在图中随机游走k步,如果某个区域连通的概率很大,则该区域是一个聚类。随机游走的下一步只和当前所处节点有关,也就是说这是一个马尔可夫的随机游走过程。 我们用一个例子来演示马尔可夫聚类算法的过程。 上图是一个很小的网络,我们用肉眼大概能看出有三个聚类,分别是左边的{1,6,7,10},中间的{2,3,5}和右边的{4,8,9,11,12}。我们用MCL看看结果如何。 为了随机游走,我们常用邻接矩阵来表示图,如果i,j有边,则N[i][j]=1,否则N[i][j]=0。又随机游走可能有自回路,所以加上单位矩阵I,得到矩阵N+I。 MCL有两个关键的步骤,分别是Expansion和Inflation。 Expansion就是不断对矩阵进行幂次运算,相当于随机游走。假设随机游走了2步,则得到如下图的关联矩阵\((N+I)^2\),第1行第10列为4,说明1到10的2-length path有4条:1→6→10,1→7→10,1→1→10,1→10→10。随机游走k步之后,\((N+I)^k[i][j]\)越大,说明\(i\)和\(j\)之间的连通性越强。 $$\begin{equation}Expand(M)=M^k\end{equation}$$ Inflation是为了增强更强的连接,减弱更弱的连接,只有这样才能得到边界比较明确的聚类。MCL的做法是对元素做幂次运算,然后按列归一化,公式为: $$\begin{equation}(\Gamma_rM)_{pq}=\frac{(M_{pq})^r}{\sum_{i=1}^k(M_{iq})^r}\end{equation}$$参数经验值是\(k=r=2\)。不断做Expansion和Inflation操作,直到算法收敛,得到若干个聚类。中间过程请点此查看,下图为最终结果。 从图中可以看出,和1有边的只剩下6,7,10了,所以得到聚类{1,6,7,10},同理能得到聚类{2,3,5}和{4,8,9,11,12} ,和我们肉眼得到的结果是一致的。 MCL算法的原理很简单,得到的聚类效果也不错。下面总结一下MCL的算法过程: 给定无向图G,Expansion和Inflation的参数\(k\)和\(r\) 生成G的邻接矩阵\(N\) 添加自回路,得到矩阵\(N+I\) 循环对\(N+I\)做Expansion和Inflation操作,即计算公式(1)和(2),直到收敛 根据最终得到的矩阵,进行划分聚类 此算法是我在上《生物信息学中的算法设计》课上是学到的,当时觉得这个算法真是神奇,如此简单,但又如此有效,实在高明。查阅文献得知,此为Stijn van Dongen的博士论文,本博客的图片均来自其博士论文,想深入了解图聚类算法,请下载他的论文。

August 22, 2016 · 1 min

隐马尔可夫模型及其应用(2)学习问题&识别问题

上一回介绍了HMM的解码问题,今天我们介绍HMM的学习问题和识别问题,先来看学习问题。 正如上一回结束时所说,HMM的学习问题是:仅已知观测序列\(\vec y\),要估计出模型参数组\(\vec\lambda=(\mu,A,B)\),其中\(\mu\)为初始概率分布向量,\(A\)为转移概率矩阵,\(B\)为发射概率矩阵。 算法设计 求解HMM的参数学习问题,就是求解如下的最优化问题: $$\begin{equation} P(\vec Y = \vec y|\hat \lambda)=\max\limits_{\vec \lambda} P(\vec Y = \vec y|\vec \lambda)\end{equation}$$也就是找一个参数\(\vec \lambda\),使得模型在该参数下最有可能产生当前的观测\(\vec y\)。如果使用极大似然法求解,对于似然函数\(P(\vec Y=\vec y|\vec \lambda)=\sum\limits_{i_1,…,i_T}\mu_{i_1}b_{i_1y_1}a_{i_1i_2}…a_{i_{T-1}i_T}b_{i_Ty_T}\)而言,这个最大值问题的计算量过大,在实际中是不可能被采用的。为此,人们构造了一个递推算法,使其能相当合理地给出模型参数\(\vec \lambda\)的粗略估计。其核心思想是:并不要求备选\(\vec\lambda\)使得\(P(\vec Y=\vec y|\vec \lambda)\)达到最大或局部极大,而只要求使\(P(\vec Y=\vec y|\vec \lambda)\)相当大,从而使计算变为实际可能。 EM算法 为此,我们定义一个描述模型“趋势”的量\(Q(\vec\lambda^*|\vec\lambda)\)代替似然函数\(P(\vec Y=\vec y|\vec\lambda)\),其定义为: $$\begin{equation} Q(\vec\lambda^*|\vec\lambda)=\sum\limits_{\vec x}P(\vec x,\vec y|\vec\lambda)\ln P(\vec x,\vec y|\vec\lambda^*)\end{equation}$$利用在\(0 < x < 1\)时,不等式\(\ln x\leq x-1\)成立,可以证明: $$\begin{equation} Q(\vec\lambda^*|\vec\lambda)-Q(\vec\lambda|\vec\lambda)\leq P(\vec Y=\vec y|\vec\lambda^*)-P(\vec Y=\vec y|\vec\lambda)\end{equation}$$由此可见,对于固定的\(\vec\lambda\),只要\(Q(\vec\lambda^*|\vec\lambda)>Q(\vec\lambda|\vec\lambda)\),就有\(P(\vec Y=\vec y|\vec\lambda^*)>P(\vec Y=\vec y|\vec\lambda)\)。于是想把模型\(\vec\lambda_m\)修改为更好的模型\(\vec\lambda_{m+1}\),只需找\(\vec\lambda_{m+1}\)使得: $$\begin{equation}Q(\vec\lambda_{m+1}|\vec\lambda_m)=\sup_{\vec\lambda}Q(\vec\lambda|\vec\lambda_m)\end{equation}$$即只要把\(Q(\vec\lambda|\vec\lambda_m)\)关于\(\vec\lambda\)的最大值处取成\(\vec\lambda_{m+1}\),就有\(P(\vec Y=\vec y|\vec\lambda_{m+1})>P(\vec Y=\vec y|\vec\lambda_m)\)。 这样得到的模型序列\(\{\vec\lambda_m\}\)能保证\(P(\vec Y=\vec y|\vec\lambda_m)\)关于\(m\)是严格递增的,虽然在这里还不能在理论上证明\(P(\vec Y=\vec y|\vec\lambda_m)\)收敛到\(\max_{\vec\lambda}P(\vec Y=\vec y|\vec\lambda)\),但是当\(m\)充分大时,\(\vec\lambda_m\)也还能提供在实际中较为满意的粗略近似。 ...

August 21, 2016 · 1 min

隐马尔可夫模型及其应用(1)简介&解码问题

隐马尔可夫模型(Hidden Markov Model, HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔可夫过程。其难点是从可观察的参数中确定该过程的隐含参数。然后利用这些参数来作进一步的分析,例如模式识别。 先举一个简单的例子以直观地理解HMM的实质——韦小宝的骰子。 假设韦小宝有两个骰子,一个正常的骰子A,A以1/6的概率均等的出现每个点;一个不正常的骰子B,B出现5,6点数的概率为0.3,出现其他点数的概率为0.1。显然投掷B更容易出现大的点数。每次试验第一次投掷时,韦小宝会以0.4的概率出千(即投掷B)。但是在一次试验中,韦小宝不太可能一直出千,所以骰子会在A、B之间转换,比如这次投了B,下次可能会以0.1的概率投A。A、B之间的转移概率如下图。 某一次试验,我们观察到韦小宝掷出的骰子序列为\(O=(1,3,4,5,5,6,6,3,2,6)\),请问韦小宝什么时候出千了。这个问题就可以通过HMM求解。 HMM有2个状态: 观测状态。我们观察到的骰子序列称为观测状态\(\mathbf{Y}=\{y_1,y_2,…,y_T\}\) 隐状态。隐含在每个观测状态里面的是隐状态\(\mathbf{X}=\{x_1,x_2,…,x_T\}\) T是时间,也可以认为是观测的次数。HMM有3个参数: 初始分布\(\mathbf{\mu}=(\mu_i)\),\(\mu_i=Pr(x_1=i)\),即第一次观测时,每个隐状态出现的概率 转移概率矩阵\(A=(a_{ij})\),\(a_{ij}=Pr(x_{t+1}=j|x_t=i)\),即t时刻的隐状态为i,t+1时刻转移到隐状态j的概率 发射概率矩阵\(B=(b_{il})\),\(b_{il}=Pr(y_t=l|x_t=i)\),即t时候隐状态为i的情况下,观测到状态为l的概率 参数\(\mathbf{\lambda=\{\mu,A,B\}}\)称为HMM的模型参数。具体到上面的例子,我们有初始分布和转移概率为: 发射概率为: 观测状态为\(\mathbf{Y}=(1,3,4,5,5,6,6,3,2,6)\),问题就是求解出隐状态\(\mathbf{X}\),此问题被称为HMM的解码问题,可以由著名的维特比算法(Viterbi algorithm)解决。 解码问题是要求出使得观测状态\(Y\)出现概率最大的隐状态\(X\),假设有N个隐状态(本例为2),共有T个时刻(本例为10),则每个时刻有N个取值可能,则共有\(N^T\)条可能的隐状态链(本例为\(2^{10}\))。我们需要求出每一条隐状态链下T个发射概率的乘积,然后取最大值,这是指数时间复杂度的(\(O(N^T)\))。 但是Viterbi算法是一个动态规划算法,只需多项式时间即可解决该问题。该算法的原理很好理解,假设我们求得到\(s_{i2}\)的最大概率路径为下图中的红线\(s_{11}\rightarrow s_{22}\rightarrow … s_{i2}\),则在求经过\(s_{i2}\)到\(s_{(i+1)1}\)的最大概率路径时,不需要再测试\(s_{13}\rightarrow s_{21}\rightarrow s_{i2}\rightarrow s_{(i+1)1}\)这条路径(下图蓝线),因为显然已经知道红线概率大于蓝线概率了。图中还有很多类似蓝线的路径都可以不用计算了,大大提高了求解速度。 因为计算第\(i+1\)时刻的累积概率只和第\(i\)时刻的概率有关,每次至多计算\(N*N\)个概率乘积(可以从\(i\)时刻的\(N\)个状态到达\(i+1\)时刻的某个状态,\(i+1\)时刻共有\(N\)个状态),最多计算T次(共T个时刻),所以时间复杂度降到了\(O(N^2T)\)。 下面我们形式化的描述Viterbi算法。 假设\(\delta_t(i)\)为\(t\)时刻取到隐状态\(i\),且1~t的观测状态都符合观测值\(Y\)的各个路径的最大概率,即 $$ \begin{equation}\delta_t(i)=\underset{i_1,…,i_{t-1}}{\max}Pr(X_t=i,X_{t-1}=i_{t-1},…,X_1=i_1,Y_t=y_t,…,Y_1=y_1|\mathbf{\lambda})\end{equation} $$联系上图,可认为\(\delta_t(i)\)为红线。则递推公式为: $$ \begin{equation}\delta_{t+1}(i)=b_{iy_{t+1}}\underset{j}{\max}(\delta_t(j)a_{ji})\end{equation} $$由\(j\)到\(i\)的转移概率,再乘上\(i\)发射\(y_{t+1}\)的概率。 在初始时刻\(t=1\),有: $$ \begin{equation}\delta_1(i)=\mu_ib_{iy_1}\end{equation} $$最后的全局最大概率为\(\underset{j}{\max}\delta_T(j)\)。为了得到完整路径,我们保留每一隐状态取得最大概率时的上一隐状态,即: $$ \begin{equation}\psi_{t+1}(i)=j^*\end{equation} $$其中\(j^*\)要满足 $$ \begin{equation}\delta_{t+1}(i)=b_{iy_{t+1}}\delta_t(j^*)a_{j^*i}\end{equation} $$最后使用如下回溯法得到所有最佳隐状态: $$\begin{equation}X_T=i^*\in\{i:\delta_T(i)=\underset{j}{\max}\delta_T(j)\}\end{equation}$$$$\begin{equation}X_t=\psi_{t+1}(X_{t+1})\end{equation}$$下面我们利用Viterbi算法来求解韦小宝的骰子这个例子。 \(t=1\)时,\(y_1=1\),有\(\delta_1(A)=0.6*1/6=0.1\),\(\delta_1(B)=0.4*0.1=0.04\)。 \(t=2\)时,\(y_2=3\),有: 隐状态为A:a)A->A有\(\delta_2(A)=(1/6)*0.1*0.8=1.33*10^{-2}\);b)B->A有\(\delta_2(A)=(1/6)*0.04*0.1=6.6*10^{-4}\)。所以A->A,\(\psi_2(A)=A\)。 隐状态为B:a)A->B有\(\delta_2(B)=0.1*0.1*0.2=2*10^{-3}\);b)B->B有\(\delta_2(B)=0.1*0.04*0.9=3.6*10^{-3}\)。所以B->B,\(\psi_2(B)=B\)。 如此计算下去,可以得到如下表: \(t=10\)时最大概率为\(\delta_{10}(B)\),经过回溯得到最佳隐状态为: 所以HMM很神奇吧,可以抓住韦小宝从第5次开始就一直在出千,而且出千之后,掷出的点数大部分为5和6。 Viterbi算法还可用于解决语音识别或者拼音输入法。我们知道中文的一个拼音可以对应多个汉字,连续的一段拼音就能组成成千上万种可能的句子,哪一个句子才是最佳候选呢?我们可以把每个拼音当成观测状态,同音的汉字当成可能的隐状态。通过背景语料库统计得到每个汉字出现在词首的概率、汉字之间的转移概率和汉字与拼音之间的发射概率,这样我们就能得到模型参数,然后利用Viterbi算法求解出一个最佳的隐状态序列,这样就能完成一个简易的拼音输入法。 HMM在实际中主要有3个方面的应用,分别是: 从一段观测序列\(\mathbf{Y}\)及已知模型\(\mathbf{\lambda=(\mu,A,B)}\)出发,估计出隐状态\(\mathbf{X}\)的最佳值,称为解码问题,这是状态估计问题。这篇博客讨论的就是这个问题。 从一段观测序列\(\mathbf{Y}\)出发,估计模型参数组\(\mathbf{\lambda=(\mu,A,B)}\),称为学习问题,就是参数估计问题。 对于一个特定的观测链\(\mathbf{Y}\),已知它可能是由已经学习好的若干模型之一所得的观测,要决定此观测究竟是得自其中哪一个模型,这称为识别问题,就是分类问题。 关于HMM的学习问题和识别问题,请听下回分解。

August 20, 2016 · 1 min

2016年中总结

半年的时光又过去了,圆满结束了一年的集中教学任务,离开了美丽的雁栖湖,回到闹市中关村。 这半年基本上延续了研一上学期的高强度学习,四门硬课。《高级算法》这门课由四位大师级的老师授课,内容囊括了近似算法、计算复杂性、随机算法、局部搜索、全息规约等,完全是神一样的课。最后复习的时候,大家都生不如死啊,不过经过一个月的挑灯夜战,我还是取得了97分的好成绩,值了。 《大数据系统与大规模数据分析》这门课的老师是一个年轻的海归,要求很严格,有专门的算法检查平时作业是否抄袭,真的有好几个同学因为抄袭而得0分。这门课的大作业是在GraphLite上实现SVD,我带领队员经过一个月的努力比较圆满的完成了大作业,感谢组里的编程大神。 《机器学习方法与应用》是面向电子学院的课程,讲得太简单,考试基本是概念题,不建议选修。 《生物信息学中的算法设计》这门课其实应该叫统计机器学习在生物信息领域的应用,讲的内容比《机器学习方法与应用》的内容更深更广。不过内容太多也难以消化,好好做大作业应该会有不少收获。 集中教学一年,研一上的GPA是87分,研一下的GPA是89.3分,平均是88.1分。 除了完成若干个课程大作业,这学期还完成了两个组内大作业,分别是倒排索引和蛋白质搜索引擎,也多谢XN和我一起查Bug、对答案。(天啊,我半年是做了多少个大作业啊…) 这半年每周二回所和师姐交接任务,真是要感谢天真呆萌的JL师姐,当初保研的时候就被师姐的热情所感染,现在又有幸接替师姐的接力棒,好幸运。 要说上半年最大的收获,应该是收获了一枚女朋友吧~没错,就是我这篇博客里提到的欣欣~真的没想到这么聊得来,一起吃饭、看电影、聊代码、骑行、游山玩水。这半年拍的照片,比我前22年拍的照片还多。和她在一起很开心,不过有时候也会很累,身体累(羸弱),有时候也心累,毕竟课程压力和组内压力摆在那里,白天去玩了,晚上还是要加班补回来的。有时候冷落了她,也会感到愧疚不安,特别是我在复习《高级算法》期间,两人都很少见面,那一次是真的惹欣欣生气了:-( 总结一下在雁栖湖一年的收获,大致有如下图的四个方面: 看看年初计划的完成情况: 完成国科大下学期的课程任务:完成 接手pLink软件:完成 刷完LeetCode所有题目:上半年基本没刷题,下半年一定完成 读10本书:目前读了《数学之美》、《大话设计模式》、《我不知道该说什么,关于死亡还是爱情》、《男人来自火星、女人来自金星,卷I》,还差6本,下半年加油! 去电影院看10场电影:目前看了《美人鱼》、《北京遇上西雅图之不二情书》、《忍者神龟2:破影而出》,还差好多… 改正坐姿:有一段时间刻意改正了,但是这东西貌似改不过来? 下半年就进入实验室,开始科研实战了,做交联的师兄师姐都毕业了,留下我一个人,感觉好艰难,希望我能顺利进入角色,协助师兄把文章发了,维护好pLink2的软件,并且开发集群版。

August 20, 2016 · 1 min