babytree

搜索引擎是一种通过一个只能容纳几十个汉字的文本框建立起来的一个人与包含数十块硬盘的若干台服务器之间的交流。我们的站内搜索,每天要接纳妈妈们的各种问题,然后细心的把最好的答案告诉给她们。一个妈妈想知道,在BabyTree上有多少上海的父母,于是他在搜索框里输入“上海”两个字,我们的搜索引擎会把所有所在地点为上海的父母、他们的宝宝树、以及他们最近的活动做成一个简单而又细致的列表呈现给这个妈妈;另一个妈妈想知道,什么时候可以开始给宝宝喂辅食,于是她在搜索框里输入“什么时候喂辅食”,我们的搜索引擎则会告诉她与这句话有关的所有讨论过的话题,以及有关的小圈子。作为负责BabyTree站内搜索的工程师,我的愿望是我提供的程序能够耐心的接受妈妈们的每一个请求,并快速、准确而又温和的将答案告诉给她们。

 

机器并不像人脑那样灵活,但它的优点是可以以人脑无法比拟的速度进行逻辑运算。搜索引擎无非是拿到一个词,去一大堆文章的数据里面寻找那些包含这个词的文章。它需要一个很好的机制来保证速度,以致于当文章越来越多时,速度不会变得很慢。搜索引擎并不是把每篇文章拿出来一一比较,而是建立一个所谓的“反索引”。既是把这些文章里每一个词都拿出来,做成一个列表,每一个词后面对应着所有包含这个词的文章的编号,以及它在每篇文章里出现的次数和位置。每次新加一篇文章,都要进行一次“索引”操作。也就是把一篇文章分解成词的操作,这种操作被称为“分词”。

 

对于拉丁语言来说,几乎不存在分词的问题,因为只需要通过空格就能判断词与词之间的分隔。但是中国日本和韩国等语言就不是这么简单,词与词之间无间隙的连接促使我们必须解析出正确的语义才能得到正确的分词。这需要告诉程序一个解析的规则,这个规则越复杂,解析的越准确,同时,这种解析运算所花费的时间也会越多,这个规则就是分词算法。目前的中文分词算法有很多种,你在google上搜索“chinese word segmentation”可以查阅到很多相关的文献。不过无论哪种分词算法都需要以词典为基础,对一个整句匹配出若干个候选方案,因为多数时候分词会发生歧义。比如,“宝宝树今天上线了一款新产品”。经过字典匹配这一环节,得到两个候选方案:

 

1、宝宝树 | 今天 | 上线 | 了 | 一款 | 新产品

2、宝宝树 | 今 | 天上 | 线 | 了 | 一款 | 新产品

 

很明显,第一个方案是正确的,但是计算机这个时候并不知道我们的逻辑,所以我们在编写算法的时候需要把这些逻辑抽象成代码告诉给它,例如基于词性的判断,以及基于上下文统计的判断等,这也是分词算法设计的一个难点。前面说到了,无论哪种算法,都是以词典为基础。字典的选择是很关键的,字典的结构更影响了分词算法的运算时间。今天我先给大家介绍一个简单的词典结构。基于汉语的特殊性,二字词在词典中占了70%以上,我采用的字典结构为“双字哈希”结构:

 

这是一个三层的索引结构,

第一层(首字哈希索引)是一个HashMap,由一个字C1,映射到所有以C1开头的词的第二字的索引。

第二层(次字哈希索引)也是一个HashMap,由一个字C2,对应的是C1C2是否为词以及以C1C2开头的词的剩余部分所组成的ArrayList。

经过理论和实践证实,这种结构的字典在检索时拥有很理想的速度。

 

前面说到对于拉丁语言,按空格就可判断词之间的边界。但事实上我们也需要做一点工作。以英语为例,比如一篇文章里有这样一句话:MeeSea is a good boy。如果搜索meetsea那这篇文章并不会出现在结果中,因为大写字母和小写字母储存在硬盘上是不同的Unicode编码,同样在进行字符之间的比较时,计算机也会认为他们是不同的字。所以通常的做法是在建立索引和搜索时,把英文都转换为小写字母的形式,这是一种通用的形式。同样,对于中文来说也有一个繁简之分,幸运的是,繁体对应简体是N->1的关系,因此对照着一张2000多字的繁简对应表,我轻松的解决了繁体中文搜索的问题。

 

搜索一个词的时候,经常会搜索到很多篇文章。经过统计,90%的人在浏览搜索结果时不会去翻页。因此,如何优化第一页上显示的10篇文章成为又一个问题,也就是说,我们要把最相关的文章列在最前面。这里,前人给出了经典的TF-IDF公式。TF(Term Frequency)就是指搜索词在这篇文章里出现的次数。显然,这个次数越多文章就越相关。当用户搜索一个短语时,经过前面所说的分词过程,短语会被分解为几个词。如“北京的妈妈”,会被分解为“北京”、“的”、“妈妈”,任何一篇文章中“的”字都会出现很多,而整个短语中重要的部分应该是“北京”和“妈妈”,IDF(C)=log(N/docNum(C))(Inverse Document Frequency,N为文章总数,docNum(C)为包含C的文章数)定义了词的重要性。易知,包含词C的文章越多,C这个词越不重要。所以对于搜索出来的每一篇文章,我们把短语切词后的每一个词的在文章里出现在次数(TF)乘上一个权重(IDF)再相加,就得到了这篇文章的相似度(Score)。

 

仅仅这样算是不够的,基于经典的TF-IDF公式之上,我们必须做出一些改进。在我们搜索“北京的妈妈”时。显然,“北京”、“的”、“妈妈”这三个词连在一起的时候,是最匹配的情况。然而TF-IDF公式并不能判断词之间的距离。因此,我们必须在由TF-IDF得出的Score之上附加一个基于距离的参数,也就是在搜索结果中的每一篇文章里,把“北京|的|妈妈”摆放成文章里这3个词的相对位置所需要的最少的移动次数。我们把这个次数设为D(Distance),这个基于距离的参数以1/(D+1)给出。即D越小,这个值越大,这样的文章相关度也越高,当D=0时,也就是文章里包含与搜索的短语一模一样的短语,这样的文章显然排名应该靠前一些。事实上我们只会把D小于一定值以内的文章的Score附加上1/(D+1),那些“距离太大”的文章,我们希望它们排在后面。

 

影响文章排名,还需要更多的参数,在我们的网站上,一篇文章(也就是一个话题)包含标题和内容,这两部分都是我们要搜索的地方(Field)。然而他们的重要性(Field Boost)并不一样,在标题中出现关键词的文章要比在内容中出现的文章更“好”。因此我们必须定义Field之间的比重,使得在搜索话题时,在标题中匹配的文章排得相对靠前;在搜索小圈子时,在小圈子关键字中匹配,要比在内容(小圈子介绍、相册、话题)中匹配的小圈子相关度更高。

 

此外,即使一篇文章通过上面的算法算出的相关度很高,也不一定会是一篇好文章。在相关度之外,还应该有一些数值会影响文章的排序,比如用户对文章的评分,多个用户给予很高评价的文章应该要排名更靠前。同时,文章的及时性也会是一个重要指标,尤其是新闻类的文章,需要有一个发表时间与当前时间的差有关的参数来影响文章的排名。然而,我对这部分理论还未成熟,但我从未停止过思考,我们每一天都比昨天做得更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值