从Lucene到Elasticsearch:全文检索实战
上QQ阅读APP看书,第一时间看更新

1.6 向量空间模型

向量空间模型(Vector Space Model, VSM)在上世纪70年代由信息检索领域奠基人Salton教授提出,并成功地应用于著名的SMART文本检索系统。把对文本内容的处理简化为向量空间中的向量运算,它以空间上的相似度表达语义的相似度,直观易懂。当文档被表示为文档空间的向量,就可以通过计算向量之间的相似性来度量文档间的相似性。向量空间模型的数学理论基础是余弦相似性理论,下面我们先从数学推导上认识余弦相似性理论。

在同一个N维空间中存在两个非零向量A和B:

向量A记作A=( x1, x2, x3, x4...…xn-2, xn)

向量B记作B=(y1, y2, y3, y4...... yn-1, yn)

向量A和B的夹角为θ,那么由夹角公式可得:

夹角余弦值cosθ是与向量的长度无关的,仅仅与向量的指向方向相关。当A向量和B向量方向完全相同时,那么两个向量之间的夹角为0, cosθ = 1;当A向量和B向量方向完全相反时,那么两个向量之间的夹角为180度,cosθ = -1;当A向量和B向量互相垂直时,即夹角为90度,cosθ = 0。0度角的余弦值是1,而其他任何角度的余弦值都不大于1,并且其最小值是-1,从而通过两个向量之间的角度的余弦值确定两个向量是否大致指向相同的方向。用向量夹角余弦值来衡量相似度:Sim(A, B) = cosθ

余弦相似度通常用于正空间,因此给出的值为0到1之间。如图1-3所示,假设有两个文档doc1和doc2, doc1和doc2在向量空间中分别用向量d1和向量d2来表示。

图1-3 文档向量和查询向量

关键词查询向量为q, d1q的夹角为α, d2q的夹角为θ。通过计算cos(d1, q)得出查询向量和doc1之间的相似性:

计算cos(d2, q)得出查询向量和doc2之间的相似性:

比较cosα和cosθ的大小可以得出文档1和文档2哪个和查询关键词相关度更大。余弦相似性理论除了应用在信息检索模型中以外,在文本挖掘领域可用于文件比较,在数据挖掘领域中可以用来度量集群内部的凝聚力,在推荐系统中可以用来比较用户偏好的相似性。相对于标准布尔模型,向量空间模型具有如下优点:

● 基于线性代数的简单模型。

● 词组的权重不是二元的。

● 文档和查询之间的相似度取值是连续的。

● 允许根据文档间可能的相关性来进行排序。

● 允许局部匹配。

向量空间模型的Java实现计算见代码清单1-2。在Vsm类中实现了一个静态的方法用于计算两个向量的夹角,传入参数为两个Map,在main函数中给出了测试案例。

代码清单1-2

        import java.util.HashMap;
        import java.util.HashSet;
        import java.util.Map;
        import java.util.Set;
        public class Vsm {
            public static double calCosSim(Map<String, Double> v1,
              Map<String, Double> v2){
              double sclar = 0.0, norm1=0.0, norm2=0.0, similarity=0.0;
              Set<String> v1Keys = v1.keySet();
              Set<String> v2Keys = v2.keySet();
              Set<String> both= new HashSet<>();
              both.addAll(v1Keys);
              both.retainAll(v2Keys);
              System.out.println(both);
              for(String str1 : both){
                  sclar += v1.get(str1)* v2.get(str1);
              }
              for(String str1:v1.keySet()){
                  norm1+=Math.pow(v1.get(str1),2);
              }
              for(String str2:v2.keySet()){
                  norm2+=Math.pow(v2.get(str2),2);
              }
              similarity=sclar/Math.sqrt(norm1*norm2);
              System.out.println("sclar:"+sclar);
              System.out.println("norm1:"+norm1);
              System.out.println("norm2:"+norm2);
              System.out.println("similarity:"+similarity);
              return similarity;
            }
            public static void main(String[] args){
              Map<String, Double> m1 = new HashMap<>();
              m1.put("Hello", 1.0);
              m1.put("css", 2.0);
              m1.put("Lucene", 3.0);

              Map<String, Double> m2 = new HashMap<>();
              m2.put("Hello", 1.0);
              m2.put("Word", 2.0);
              m2.put("Hadoop", 3.0);
              m2.put("java", 4.0);
              m2.put("html", 1.0);
              m2.put("css", 2.0);
              calCosSim(m1, m2);
          }
        }

事实上,Lucene中的评分机制更加复杂,糅合了索引期和查询期用户赋予的权重等多种因素。实际的评分公式如下:

● coord(q, q):评分因子,其值为出现查询词项占文档的百分比,一个文档中出现查询词项的个数越高,文档匹配程度越高。

● querynorm(q):查询的标准因子,目的是使不同的查询之间可比较,所有的排序文档都会乘以这个因子,因此不会影响文档的排序。

● tf:文档频率。

● idf:逆文档频率。

● t.getboost():查询时期的附加权重。

● norm(t, d):索引时期的权重和长度因子。

整体而言,上述公式仍然是基于tf-idf和向量空间模型的相似度计算。但是向量空间模型仍然存在一些缺点,比如长文档的tf一般会越高,呈正相关性,对短文档不公平,而且查询词之间并不是完全独立的。基于此,排序模型也在不断改进和完善之中。