Eisen's Blog

© 2024. All rights reserved.

2015 年终总结

2016 March-23

我知道今天是 16 年的 3 月下旬,但是如果按照农历来算的话现在做去年的年终总结也不是很过分吧...不过也真是因为自己很忙很忙,或者是说自己觉得自己很忙很忙才会这个样子。不过 15 年发生了很多很多事情,去过很多很多地方,希望把这些记下来。

15 年的这个时候我在做什么呢?哦,我应该是在实验室里打酱油然后在等台湾通行证的结果了。再往前呢?在小妮妮没有去台湾之前我们在学校里过着好开心的生活,还记得年前去滑雪,我刚刚踩上滑雪板风一吹就倒了。整个过程自己只有一次成功的从山坡上滑下来但偏偏这一次被摄像头记了下来。晚上回来还去吃了一顿烧烤大餐满足的不得了,可是小妮妮居然都忘记了。

后来小妮妮去了台湾,我分别在三月底和答辩后的六月中去了两次台湾。还记得第一次刚刚到桃园机场见到小妮妮的样子,那时候下着点小雨,空气潮湿闷热但是十分干净没有一丝泥泞。排队等机场大巴的时候工作人员给我的行李箱上贴上了国光字样的行李验证码。(现在想想,从北京出发去台北的那一天正是我飞来飞去的一年的起点,我大黄色亮晶晶的行李箱从那天开始渐渐贴上了各种乱七八糟的条形码。)第一次去台湾去了两个地方,台北和垦丁,先到台北以台大为中心在周边瞎逛,感觉台湾的吃的又好吃又便宜,台大的园子也不比北大差,椰林大道显得很是气派。感觉在这里交流几个月也真是令人羡慕。去垦丁恰好赶在了垦丁的春浪之前,所以人还不多,民宿的价格也还比较便宜,不过就是交通不太方便,先坐高铁到了左营然后租车开了一两个小时才来到垦丁。在垦丁住的地方穿过一条马路就是海边,当天晚上跑去海边走了一下没什么感觉就回来了。但第二天早晨爬起来拉开窗帘的时候就惊呆了。那是一个大晴天,基本无风,太阳把海面照的湛蓝湛蓝的,在别的地方没有见过这样的海面哎!然后只有两个人的卡丁车跑道也真是奢侈,如果还能再去垦丁一定要再跑上几把。

垦丁
垦丁

第二次去台湾相对于第一次安排的更周密一些,我们去了北投泡温泉,去了澎湖骑机车看落日钓小管,去了台中蓬甲夜市又回到台北吃吃吃。澎湖的一大特色就是没有人,在只有两个人的公路上任意驰骋在大陆真是不敢想。然后最后去的山水沙滩真的是美到爆,后悔没有住在海边了。最后我们还没有直接回北京而是跑去厦门玩了两天。相比台湾干净的海水,厦门的海实在是没什么可看的,但是相比台湾没有滋味的海鲜,厦门的爆炒花蛤真是好吃爆了,我们甚至留下了和两盘花蛤的合影。

澎湖
澎湖

然后我就毕业了,终于毕业了。毕业搬家的时候那叫一个匆忙,我好像是突然就把东西打包好送去了邮局。我只记得当时老季过来帮我把一堆东西搬到了他那里,第二天就去了杭州,匆匆到记不清当时宿舍里还有几个人。在杭州闲了没多久就回家继续闲着,但是闲了没几天就又回到北京准备入职工作了。刚到北京那几天特别艰难,由于我马上去印度培训,没有租房子,自己在同学宿舍的地板上借住了一宿又自己跑去公司附近的宾馆住了两天,那两天我拉着行李箱跑来跑去真是怀念有宿舍的日子。可没想到在之后的大半年里都一直保持这种状态...

8月 10 号我入职 tw,14 号就和几个新入职的同事一起去了印度参加 twu 的培训。为期 5 周的培训课程相当的密集,也相当的辛苦。9 月下旬回到北京暂住公司公寓直到 10 月底开始了小巨人的培训。在成都为期三周的培训让 twu 显得那么的苍白无力。一周 80 个小时的开发时间完全碾压以前任意时段的工作强度。这里培训也把我带入了一个全新的软件设计领域。从此我更注重时间管理,任务拆分,模型设计和单据追朔,DDD 的书籍至今也不离手,反复的阅读希望可以有更深入的理解。

小巨人培训
小巨人培训

工作之后只在北京待过一个月,其他的时间都在出差。从北京到成都,从成都到武汉,从武汉到西安从西安到深圳,从深圳去北京再从北京回到西安。相对来说西安是待的时间最长的地方。每天大部分时间都在写程序,看书,有点闷,但是希望可以闷出成绩,做出好的东西。

航线
航线

流水账就说道这里吧。

15 年 8 月份之前我依然主力做有关数据方面的东西,参见了天池的大数据竞赛,其中的资金流入流出比赛做的还可以。深深感慨到现在的这样的数据平台才是推动生产力发展的关键:以前大家辛辛苦苦优化的结果通过高计算能力的集群轻松的突破了,超大规模的 GBDT 这样基本不可能的事情成为了可能。再看单机上 xgboost 这样的工具也让我可以在任何特征都筛选的时候做到非常高的分值。benchmark 相对于从前真是高出了不少。个人认为数据和机器智能的发展可以在很短的时间内达到一个不错的程度,自己在最近也尽量抽空做点点比赛,但是感觉这样的游击战并不能真正的提升自己这方面的能力,还是需要在接下来的一年更系统的去学习相关的知识。

8月份入职之后似乎进入了一个和数据不沾边的领域。之前想的非常清楚了,之所以来 tw 是因为对自己的系统设计能力不满意。我自己觉得不论是做什么样子的具体的软件和系统,前期的设计和架构能力都是必要的能力。另一方面,生产力是最有说服力的东西,如果我可以把软件做的比比人快很多但是又有很好的设计和架构以支持未来的自由扩展,那具体到做什么样子的东西来说都会游刃有余。半年过去了,我觉得来到这里是非常值得的,在软件设计方面相比从前真的是有了一个不小的提升。一直希望自己可以具备快速的构建一个项目的能力,从设计到编码,从前端到后端都可以用最快的速度完成。现在感觉自己离具备这个能力又进了一步。不过一切都才刚刚开始。也希望自己在今年开始做一些更靠谱的项目。

上班之后也开始更强烈的感受到了生活的压力,上学的时候吃住的花销不大加上自己的实习和 freelancer 的项目还能有个不错的收入,上班之后发现吃住的花销也还真是惊人,最近开始注意记账追朔自己的花销,在财务上开始培养一些好习惯,真正的效果上还是要靠坚持才行。

和小妮妮这一年真是聚少离多,四处奔波的日子也是挺让人烦的。小妮妮毕业之前还可以随我一起去各个城市玩玩,自一月份以后就没有这样子的机会了。希望今年可以好好想想,想想以后的规划安排,改变现在的局面。

最后,博客不能停,多多总结,免得自己忘记了。


CIKM query detection

2014 August-13

自上次做了阿里的比赛之后似乎有点不过瘾,趁巧发现群里在宣传百度与 cikm 合作的这个比赛,于是 又来打酱油。之前的比赛搞的有点混乱,这次想把思路捋顺清楚。就用这个新的 blog 把一些关键的东西 记下来吧。

Task

首先说一下大体的比赛任务:对给出的搜索词 (query) 做分类。百度对一些搜索词做了人工标注,然后给 出了这些数据在 session 中的查询以及点击的页面标题。然后要对另一批标记为 TEST 的数据做标注。 而在 session 中出现的并没有标记的 query 被标记为 UNKNONW。

Baselines

naive baseline 1

大体思路是首先处理已经有标记的数据,取标记数据的 query, title 的前 N 个字符然后标记这个 prefix 的标签为当前的 label。如果出现了同一个 prefix 有多个标签的情况,就把这个 prefix 的标签设置成有最多标记次数的标签。

然后读取 test 数据,同样取 test query 的前 N 个字符字符,然后在之前的 query-label 对中查看这个 prefix 是否存在,如果 存在就设置 prefix 为 label,否则就设置成 OTHER。

这个 baseline 可以得到大概 34% 的结果。

naive baseline 2

如果说上面的那个似乎有点低了,那么这个我刚刚想到的 baseline 就靠谱多了。

仔细观察数据会发现这样子的 session:

CLASS=TEST	0417191 0750813 0396059	-
CLASS=LOTTERY	0417191 0750813 0396059 0040630 0030792 0079323 0387331	-
CLASS=LOTTERY	0417191 0750813 0396059 0040630 0030792 0079323 0387331	0557667 0208773 0631387 0949083 0040630 0030792 0750813 0396059 0490159 0293898 0514247 0754212 0208773 0631387 0949083 0040630 0030792 0750813 0396059 0297573 0040630 0030792 0750813 0396059 0293898 0998010 0359806 0040630 0030792 0580929
CLASS=LOTTERY	0417191 0750813 0396059 0040630 0030792 0079323 0387331	0557667 0208773 0631387 0949083 0040630 0030792 0750813 0396059 0490159 0293898 0514247 0754212 0208773 0631387 0949083 0040630 0030792 0750813 0396059 0297573 0040630 0030792 0750813 0396059 0293898 0998010 0359806 0040630 0030792 0580929
CLASS=LOTTERY	0417191 0750813 0396059 0040630 0030792 0079323 0387331	0949083 0750813 0396059 0040630 0030792 0079323 0387331 1116997 0040630 0030792 0079323 0387331 0750813 0396059 0729226 1081042 0754212 0631387 0976685 0694263
CLASS=LOTTERY	0417191 0750813 0396059 0040630 0030792 0079323 0387331	0949083 0750813 0396059 0438139 0396059 1081042 0729226 0784491 0208773 0631387 0949083 0750813 0396059 0438139 0396059 1081042 0729226 0846596 0750813 0396059 1081042 0729226 0846596 0438139 0396059 1081042 0729226 0297573 0781505 0878719

标记为 TEST 的与有标记的数据出现在了同一个 session 之中,然后我觉得在同一个 session 中的 query 的动机应该是极其类似的。 那么我就做一个非常粗略的判定:同一个 session 中的 label 应该是一样的。那么,我们就可以把既有 TEST 标签又有标定的数据的 session 中的 query 全部设置为这个已标定的标签。然后我发现在 session 中出现 label 的情况还是挺多的,经过以上规则的标定,我可以找出 21000+ 个这样的 test query 的标签, 然后把剩下的 query 标记为出现最多的 VIDEO。这样的线上提交结果达到了 67%。


以上是一些比较基本的提交,为的是大概了解数据的情况以及提交数据的样子。后面就是一些模型的尝试了。

Ngram

做文本的分类,肯定会想到用 bag of word 抽取特征,然后用一些模型去做分类了。我一开始也是这个思路,迅速的用 sklearn 的 TfidfVectorizer 对标记了的 query + title 做了 BOW 然后用 NaiveBayes 去预测结果。 用一元文法、二元文法、三元文法做了尝试之后发现三元的效果比较好,就用三元的提交了一个结果,线上的结果是 80% 了呢,感觉还算靠谱。然后又尝试了 LogisticRegression 以及 LinearSVC 的模型,结果也有所提升。

然后...似乎就不知道要做什么了...跑去群里和别人讨论了一下,发现如果仅仅使用 query 而不用 title 的数据反而会有所提升。于是我也用了这个方法,最后达到目前的最高分 84.61% ...

Word

目前的数据是加了密的,每个 string 对应一个中文的汉字或者是一个英文的词,那么前面的 Ngram 还是比较粗糙的,看了之前百度的比赛,获胜选手都是要首先做分词来着。 虽然这里加了密,但是依然可以尝试一些无词典的分词方式进行分词的。我这里参见了 matrix67 的一篇旧文,首先从目前的数据生成出一个词典来,然后再用 mmseg 做分词。 我本来以为这是一个很不错的主意,但是在实现的过程过程中发现这个生成词典的计算量非常的大...程序中有一部分是计算一个词的子词构成这个词的概率,内存和 cpu 都有点吃不消,在尝试计算前三千万数据失败之后我只好使用了计算前一千万数据的结果做为词典。然后把分词之后的 bag of word 与之前的 3ngram bow 组合计算了一个新的 svm 的模型,结果...结果降了 T_T。这尼玛是为什么。难道分词太差劲了么?而且,事实上,我自己本地测试的数据是提升了几个百分点的,虽然提升的不多,但是总不至于降低吧... 我甚至有点怀疑,目前的线上测试集因为是全体测试数据的一个子集,所以还是不能太信它的,过拟合了的话就没有意义了。

不过真的是郁闷了...

然后就开始考虑各种情况了,比如那么多的 UNKNOWN 数据都没有使用啊,要怎么使用才对吧?但是要怎么使用呢,因为同一个 session 下的数据标定为同一个 label 的结果只有 67% 啊, 也就是说我不能随便判断这个 UNKNOWN 真实的标签是什么才对呢...所以说,可以考虑 如何对 test 与 labeled 在同一个 session 的情况下做标定 着手,为对 UNKNOWN 数据的标定提供参考。

然后就是 title 的数据如何利用?看起来 title 会引入很多的噪声,那么 title 数据可不可以做一些额外的处理来降噪后加以利用呢?

Add title and query together

之前就考虑过说把 title 考虑进去,却导致了结果的下降,这次在考虑 word 的同时,把 title 添加了进去。而且,取得是 title 的前 N 个字符,这样就可以过滤掉后面那些噪音了。 当然,在生成测试集的标签的时候同样是把 test data 的 title 考虑在内去处理。然后目前的得分是 87.39%


2014-10-01 结束后的总结

这...都说懒是没得救的,我差不多了。这 blog 上次写还是 8 月份。

今天按理说是比赛结束了吧?可是我发现怎么还有人在提交到 leaderboard 呢...百度的同学们放假了是吧?既然知道要放假你们为毛要把结束时间设置到 10 月 1 号呢。

好了,不废话了。说一说我自己的最好成绩是怎么做的吧...其实我也是小白,半路出家,找个类库瞎试,有什么不对的地方请指出。

我这个比赛主要就是在使用 scikit-learn 做训练的基础库。抽特征,训模型都是靠的它。后面说到的一些内容我尽量不涉及具体的库,不过还是要宣传一下,我感觉它还是很不错的呢。

数据预处理

原始数据是按照 session 组织的,既然我们要做的是 query detection,我首先是把相同 query 的数据集合在一起。大概的形式如下

label   query   text
1       121     121 123 124 ...

其中 text 也要包含 query 的内容,然后 text 中没有重复的 query 以及 title。并且剔除了 CLASS=UNKNOWN 的数据。

按照这样处理后,训练数据就只有 79M 了。

特征

  1. 3gram
  2. 做了分词之后的 2gram 分词见上文
  3. prefix 比如 text 为 "1 2 3 4 5" 那么,生成的 prefix 就是 "1", "1 2", "1 2 3"...
  4. postfix 比如 text 为 "1 2 3 4 5" 那么,生成的 postfix 就是 "5", "4 5", "3 4 5"...

模型

LinearSVC 或者说是 liblinear

然后,没了...就是这样,LinearSVC 没怎么调参,随便改个参数都没有默认的好...最后 leaderboard 上 89.91

总结

  1. sklearn 是个好东西,里面的 model 算是挺全的,然后 gridsearch 做调参很爽
  2. 我没有用其他的工具,这个其实并不好,yr_SYSU 推荐了 vowpal wabbit 以后有机会去试试
  3. unknown 根本没有使用,本来我是有尝试的,比如对有 label 的临近的 unknown 标定为相同的 label 但是这样反而导致结果的下降
  4. 尝试了词向量,也是下降了,悲剧的
  5. 分词其实提升也很有限...就一点点,看群里说分词是必须的,可我其实觉得这加过密的数据做分词都不知道效果,有的时候加了都不如不加吧
  6. 没做模型融合

python package and namespace

2013 October-07

python namespace and package

最近使用了一下 python,用到了 package 去组织文件。但是用的很恼火。今天好好的查了一下相关的内容,算是搞的差不多了。

首先,python 的 package 通常是在文件 __name__ 不是 __main__ 的时候有效的。如果一个文件要以 main 直接执行,那么它就应该以绝对路径的方式引入。

├── __init__.py
├── main.py
├── importer
│   ├── __init__.py
│   ├── db_migrator.py
├── spider
│   ├── __init__.py
│   ├── fetcher.py
└── util
    ├── __init__.py
    ├── other.py
    └── tool.py

如上的结构

main.py:

import spider.fetcher

if __name__ == '__main__':
  spider.fetcher.fetch()

spider.fetcher.py:

import importer.db_migrator


def fetch():
  """docstring for fetch"""
  print 'fetch'

如果执行 main.pyspider/fetcher.py 永远以类库的形式被引用,那么这样的写法是没有问题的。

但是,如果 spider/fetcher.py 包含 __main__ 部分的话,例如

import importer.db_migrator

def fetch():
  """docstring for fetch"""
  print 'fetch'

if __name__ == '__main__':
  fetch()

直接执行 python spider/fetcher.py 是会报错的。应该为其提供绝对路径的 path 才可以。

import sys
import os

PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(PROJECT_ROOT)

import importer.db_migrator

def fetch():
  """docstring for fetch"""
  print 'fetch'

if __name__ == '__main__':
  fetch()

增加的内容就是强制将项目的路径添加到寻找 package 的路径列表里,使得在运行时可以找到需要的包。