wand(weak and)算法基本思路

  一般搜索的query比较短,但如果query比较长,如是一段文本,需要搜索相似的文本,这时候一般就需要wand算法,该算法在广告系统中有比较成熟的应

该,主要是adsense场景,需要搜索一个页面内容的相似广告。

   Wand方法简单来说,一般我们在计算文本相关性的时候,会通过倒排索引的方式进行查询,通过倒排索引已经要比全量遍历节约大量时间,但是有时候仍

然很慢。

   原因是很多时候我们其实只是想要top n个结果,一些结果明显较差的也进行了复杂的相关性计算,而weak-and算法通过计算每个词的贡献上限来估计文档

的相关性上限,从而建立一个阈值对倒排中的结果进行减枝,从而得到提速的效果。

   wand算法首先要估计每个词对相关性贡献的上限,最简单的相关性就是TF*IDF,一般query中词的TF均为1,IDF是固定的,因此就是估计一个词在文档中的

词频TF上限,一般TF需要归一化,即除以文档所有词的个数,因此,就是要估算一个词在文档中所能占到的最大比例,这个线下计算即可。

   知道了一个词的相关性上界值,就可以知道一个query和一个文档的相关性上限值,显然就是他们共同的词的相关性上限值的和。

   这样对于一个query,获得其所有词的相关性贡献上限,然后对一个文档,看其和query中都出现的词,然后求这些词的贡献和即可,然后和一个预设值比

较,如果超过预设值,则进入下一步的计算,否则则丢弃。

  如果按照这样的方法计算n个最相似文档,就要取出所有的文档,每个文档作预计算,比较threshold,然后决定是否在top-n之列。这样计算当然可行,但

是还是可以优化的。

wand(weak and)算法原理演示

代码实现了主要的算法逻辑以验证算法的有效性,供大家参考,该实现优化了原始算法的一些逻辑尽量减少了无谓的循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
www.169it.com
#!/usr/bin/python
#wangben updated 20130108
class WAND:
    '''implement wand algorithm'''
    def __init__(self, InvertIndex, last_docid):
        self.invert_index = InvertIndex #InvertIndex: term -> docid1, docid2, docid3 ...
        self.current_doc = 0
        self.current_invert_index = {}
        self.query_terms = []
        self.threshold = 2
        self.sort_terms = []
        self.LastID = 2000000000 #big num
        self.debug_count = 0
        self.last_docid = last_docid
    def __InitQuery(self, query_terms):
        '''check terms len > 0'''
        self.current_doc = -1
        self.current_invert_index.clear()
        self.query_terms = query_terms
        self.sort_terms[:] = []
        self.debug_count = 0
        for term in query_terms:
            #initial start pos from the first position of term's invert_index
            self.current_invert_index[term] = [ self.invert_index[term][0], 0 ] #[ docid, index ]
                                                                                    
    def __SortTerms(self):
        if len(self.sort_terms) == 0:
            for term in self.query_terms:
                if term in self.current_invert_index:
                    doc_id = self.current_invert_index[term][0]
                    self.sort_terms.append([ int(doc_id), term ])
        self.sort_terms.sort()
                                                                                            
    def __PickTerm(self, pivot_index):
        return 0
    def __FindPivotTerm(self):
        score = 0
        for i in range(0, len(self.sort_terms)):
            score += 1
            if score >= self.threshold:
                return [ self.sort_terms[i][1], i]
        return [ None, len(self.sort_terms) ]
    def __IteratorInvertIndex(self, change_term, docid, pos):
        '''move to doc id > docid'''
        doc_list = self.invert_index[change_term]
        i = 0
        for i in range(pos, len(doc_list)):
            if doc_list[i] >= docid:
                pos = i
                docid = doc_list[i]
                break
        return [ docid, pos ]
    def __AdvanceTerm(self, change_index, docid ):
        change_term = self.sort_terms[change_index][1]
        pos = self.current_invert_index[change_term][1]
        (new_doc, new_pos) = \
            self.__IteratorInvertIndex(change_term, docid, pos)
                                                                                        
        self.current_invert_index[change_term] = \
            [ new_doc , new_pos ]
        self.sort_terms[change_index][0] = new_doc
                                                                                        
                                                                                        
    def __Next(self):
        if self.last_docid == self.current_doc:
            return None
                                                                                            
        while True:
            self.debug_count += 1
            #sort terms by doc id
            self.__SortTerms()
                                                                                            
            #find pivot term > threshold
            (pivot_term, pivot_index) = self.__FindPivotTerm()
            if pivot_term == None:
                #no more candidate
                return None
                                                                                            
            #debug_info:
            for i in range(0, pivot_index + 1):
                print self.sort_terms[i][0],self.sort_terms[i][1],"|",
            print ""
                                                                                                
            pivot_doc_id = self.current_invert_index[pivot_term][0]
            if pivot_doc_id == self.LastID: #!!
                return None
            if pivot_doc_id <= self.current_doc:
                change_index = self.__PickTerm(pivot_index)
                self.__AdvanceTerm( change_index, self.current_doc + 1 )
            else:
                first_docid = self.sort_terms[0][0]
                if pivot_doc_id == first_docid:
                    self.current_doc = pivot_doc_id
                    return self.current_doc
                else:
                    #pick all preceding term
                    for i in range(0, pivot_index):
                        change_index = i
                        self.__AdvanceTerm( change_index, pivot_doc_id )
                                                                                    
    def DoQuery(self, query_terms):
        self.__InitQuery(query_terms)
                                                                                        
        while True:
            candidate_docid = self.__Next()
            if candidate_docid == None:
                break
            print "candidate_docid:",candidate_docid
            #insert candidate_docid to heap
            #update threshold
        print "debug_count:",self.debug_count
                                                                                        
if __name__ == "__main__":
    testIndex = {}
    testIndex["t1"] = [ 0, 1, 2, 3, 6 , 2000000000]
    testIndex["t2"] = [ 3, 4, 5, 6, 2000000000 ]
    testIndex["t3"] = [ 2, 5, 2000000000 ]
    testIndex["t4"] = [ 4, 6, 2000000000 ]
    w = WAND(testIndex, 6)
    w.DoQuery(["t1", "t2", "t3", "t4"])

输出结果中会展示next中循环的次数,以及最后被选为candidate的docid。  这里省略了建立堆的过程,使用了一个默认阈值2作为doc的删选条件,候选doc和query doc采用重复词的个数计算UB,这里只是一个算法演示,实际使用的时候需要根据自己的相关性公式进行调整

本文来源:广告系统中weak-and算法原理及编码验证

广告系统中weak-and算法原理及编码验证的更多相关文章

  1. 【转】单片机系统中数字滤波的算法【C程序整理】

    随机误差是有随机干搅引起的,其特点是在相同条件下测量同一个量时,其大小和符号做无规则变化而无法预测,但多次测量结果符合统计规律.为克服随机干搅引入的误差,硬件上可采用滤波技术,软件上可以采用软件算法实 ...

  2. Android中图像变换Matrix的原理、代码验证和应用(二)

    第二部分 代码验证 在第一部分中讲到的各种图像变换的验证代码如下,一共列出了10种情况.如果要验证其中的某一种情况,只需将相应的代码反注释即可.试验中用到的图片: 其尺寸为162 x 251. 每种变 ...

  3. 机器学习中K-means聚类算法原理及C语言实现

    本人以前主要focus在传统音频的软件开发,接触到的算法主要是音频信号处理相关的,如各种编解码算法和回声消除算法等.最近切到语音识别上,接触到的算法就变成了各种机器学习算法,如GMM等.K-means ...

  4. Android中图像变换Matrix的原理、代码验证和应用(一)

    第一部分 Matrix的数学原理 在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类.Android中的Matrix是一个3 x 3的矩阵,其内容如下: Matri ...

  5. Android中图像变换Matrix的原理、代码验证和应用(三)

    第三部分 应用 在这一部分,我们会将前面两部分所了解到的内容和Android手势结合起来,利用各种不同的手势对图像进行平移.缩放和旋转,前面两项都是在实践中经常需要用到的功能,后一项据说苹果也是最近才 ...

  6. 合约广告系统-Hadoop

    Hadoop Hadoop 概况 Hadoop 由 Apache Software Foundation 公司于 2005 年秋天作为Lucene的子项目 Nutch的一部分正式引入.它受到最先由 G ...

  7. ML学习分享系列(2)_计算广告小窥[中]

    原作:面包包包包包包 改动:寒小阳 && 龙心尘 时间:2016年2月 出处:http://blog.csdn.net/Breada/article/details/50697030 ...

  8. 广告行业中常说的 CPC,CPM,CPD,CPT,CPA,CPS 等词的意思是什么?

    广告投放流程主要分为展示和转化,CPC/CPM/CPD/CPT/CPA/CPS等代表的是不同的结算模式 展示端的结算方式有: CPM(Cost Per Mille) 每千人成本:只要向足够量级的用户展 ...

  9. [Spring cloud 一步步实现广告系统] 13. 索引服务编码实现

    上一节我们分析了广告索引的维护有2种,全量索引加载和增量索引维护.因为广告检索是广告系统中最为重要的环节,大家一定要认真理解我们索引设计的思路,接下来我们来编码实现索引维护功能. 我们来定义一个接口, ...

随机推荐

  1. 【原】Spark Rpc通信源码分析

    Spark 1.6+推出了以RPCEnv.RPCEndpoint.RPCEndpointRef为核心的新型架构下的RPC通信方式.其具体实现有Akka和Netty两种方式,Akka是基于Scala的A ...

  2. vm虚拟机上安装apache+php+ftp+mysql

    我在vm虚拟机上想安装 winxp和linux,然后在linux机上装apache+php+ftp+mysql,以下为我的按装过程:  1:连通虚拟机:两个虚拟机都选Host-Onl,查看主机Virt ...

  3. 【Java基础】Java多线程小结

    在说多线程之前,首先要清楚为啥要提出多线程,这就要明白线程和进程间的区别了. 线程和进程间的区别 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单 ...

  4. java中服务器启动时,执行定时任务

    package com.ripsoft.util; import java.util.Calendar; import java.util.Timer; import javax.servlet.Se ...

  5. IOS GCD 使用 (二)

     上一节,主要介绍了GCD的基本的概念,这节将用代码深入详细介绍GCD的使用. 一  使用介绍    GCD的使用主要分为三步:创建代码块;选择或创建合适的分发队列;(同步.异步方式)向分发队列提交任 ...

  6. 四、XML映射配置文件

    MyBatis的XML配置文件包含了影响MyBatis行为甚深的设置和属性信息.XML文档的高层级结构如下: ----configuration配置 --------properties属性 ---- ...

  7. 避免在C#中使用析构函数Finalizer

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:避免在C#中使用析构函数Finalizer.

  8. Unrecognized Windows Sockets error: 0: JVM_Bind 异常解决办法

    java.net.SocketException: Unrecognized Windows Sockets error: 0: JVM_Bind 此异常的原因是服务器端口被占用 所以解决办法是: 一 ...

  9. iOS开发多线程篇---atomic nonatomic区别

    摘要 atomic和nonatomic区别用来决定编译器生成的getter和setter是否为原子操 作.atomic提供多线程安全,是描述该变量是否支持多线程的同步访问,如果选择了atomic 那么 ...

  10. 文件和目录之mkdir和rmdir函数

    用mkdir函数创建目录,用rmdir函数删除目录. #include <sys/stat.h> int mkdir( const char *pathname, mode_t mode ...