求海量文本中两两相似文本的快速算法

来源:互联网 发布:淘宝苹果数据线 编辑:程序博客网 时间:2024/06/06 00:49

问题描述

这个问题的输入是一个文本语料库,输出是语料库中相似度大于某个阈值的所有文本对,阈值可以是0.9或者0.8.

       举个例子,比如我的输入是下面的语料库,我的阈值是0.6,我的相似度度量用的是文本中以字为单位的jaccard相似度

1. 福禄很可爱

2. 福禄真可爱

3. 福禄是可爱

4. 阿里巴巴牛逼

5. 阿里巴巴很牛逼

    我的输出是:

文档ID文档ID相似度

   1  2   0.66

   1   3   0.66

   4   5   0.8

暴力求解

看我上面的例子你可能觉得这很简单吗?大不了我用暴力计算,来一个二重循环,把语料库整个遍历一遍,不就算出来了吗?如果语料库中的文本数少,这么搞当然可以。

假设语料库中的文本数是N,这么算的复杂度是O(n^2),对海量文本来说,这简直是灾难,此路不通;


map-reduce思路

大数据时代,我们其实不缺计算资源,暴力求解的思路可以有map reduce的方法来求解

图1就是这个问题的一个map reduce解法的例子 ,其中RID代表文档号,a列是原始文本


图1 MAP reduce解法示例

 算法步骤:
1. MAP阶段:
以文本中的单个token(可以是字符,词,或者Ngram)为key,以文本id和文本内容为value去map,这样
包含相同token的文本会被map到相同的节点,换句话说,可能相似的文本会被map到相同的节点,或者说相似度大于0的文本会被map到相同的节点。
        2. reducer阶段:
暴力计算,两两计算单个recuer节点上的候选文本,输出相似度大于阈值的文本对。

然而,这个方法容易产生数据倾斜,某些高频的token对应的节点上分配的候选文本非常多,还是逃脱不了O(n^2)的魔咒

让我们想想MAP reduce解法有哪些可以优化的地方?
看下面两个文本,‘A’, 'A,B,C,D' ,jaccard('A','ABCD') 必然小于等于 0.25,一个长度为1的文本,和一个长度为4的文本,它们的jaccard相似度最大也就是0.25.

让我们看jaccard相似度的一些性质,看下面的这些符号
x,y为两个文本
J(x,y)为x,y的jaccard相似度
H(x,y)为x,y中不同的token数
O(x,y)为x,y重合的token数
|x|为x的token数,|y|为y的token数
p-prefix表示一个文本的长度为p的前缀,'abcd'的长度为1,2,3,4的前缀分别为'a','ab','abc','abcd'
下面的不等式成立:
证明:
     如 J(x,y) >=t ,
简单推论,下面这几个不等式也很容易证明


由(3)我们很容易得到之前提到的一个过滤原则

长度过滤原则:若相似度阈值为t,对于文本x,y,若|y|<t*|x|,则可以不用计算x,y的相似度,因为J(x,y) < t。

Map-Reduce解法中的map过程,其实就是一个建立倒排索引的过程,我们真的有必要对所有token都建立倒排索引吗?

还记得‘抽屉原理’吗?

若x,y是两个按照token排序(比如按照字母表顺序)的集合,O(x,y)>=a,则必然有x排在前面的|x|-a+1个token

和y的排在前面|y|-a+1个token中至少有一个重合。

如何证明这个呢?反证法呀

假设结论不成立,则必然推出结论O(x,y) < a (这里不解释,自己去想吧),得出矛盾

如果文本已经按照token排序,那么我们在建倒排索引的时候,其实就没有必要把一个文本中所有的token都建索引,

只需对前面|x|-a+1个建立索引即可。我们就得到第二个过滤原则,即前缀过滤原则



前缀过滤原则:对于已经按照token排序的文本x,y,若O(x,y)>=a,则x,y的长度为|x|-a+1的前缀和长度为|y|-a+1的前缀

至少有一个token重合.

按照前缀过滤原则,我们在MAP阶段(倒排索引建立阶段),可以不必对所有的token建立索引。

回头看(1),和(3)式,前缀过滤原则最终可以转换成对于一个按token排序的文本x,只需索引长度为

的前缀中的token。还是来看个例子吧

对于下面的一组语料,我们只需索引标红的token

w =[C, D, F ]
z =[A, B, E, F,H ]
y =[ A, B,C, D, E]
x =[ B,C, D, E, F ]

我们给出一个完整的算法,PPjoin


这就是PPjoin算法,虽然PPjoin还有一些细节可以优化,但这里不再赘述。

我使用PPjoin算法对几百万个文本,阈值设为0.9最终1个小时左右跑出了所有相似度大于0.9的文本。当然,我是

在阿里的ODPS里面跑的,若是你用的其他的平台,这个时间仅供参考。


资源

源代码 https://code.google.com/p/ppjoinplus/

PPjoin 论文 http://www.cse.unsw.edu.au/~weiw/files/TODS-PPJoin-Final.pdf


声明:本文是我参考PPjoin论文写的读书笔记,文正的图片和算法步骤都是出自论文,本文是博主原创,转载请注明出处

0 0
原创粉丝点击