一:相似度算法
lucene采用的是基于VSM(向量空间模型)的相似度算法,查询向量(query vector)与搜索出来的文档向量(document vector) 形成N个夹角,计算q 和d 之间的夹角,最小的 就是相
似度最高的。
二:lucene的打分公式
看下lucene的打分公式:
表 | 评分公式中的因子 |
评分因子 | 描 述 |
tf(t in d) | 项频率因子——文档(d)中出现项(t)的频率 |
idf(t) | 项在倒排文档中出现的频率:它被用来衡量项的“唯一”性.出现频率较高的term具有较低的idf,出现较少的term具有较高的idf |
boost(t.field in d) | 域和文档的加权,在索引期间设置.你可以用该方法 对某个域或文档进行静态单独加权 |
lengthNorm(t.field in d) | 域的归一化(Normalization)值,表示域中包含的项数量.该值在索引期间计算,并保存在索引norm中.对于该因子,更短的域(或更少的语汇单元)能获得更大的加权 |
coord(q,d) | 协调因子(Coordination factor),基于文档中包含查询的项个数.该因子会对包含更多搜索项的文档进行类似AND的加权 |
queryNorm(q) | 每个査询的归一化值,指毎个查询项权重的平方和 |
在Lucene中score简单说是由tf * idf * boost * norm 计算得出的。
代码实现,必要时可以重写代码。在Lucene的DefaultSimilarity类
1:协调coord(q,d)
<span style="font-size:14px;"> @Override
public float coord(int overlap, int maxOverlap) {
return overlap / (float)maxOverlap;
}</span>
overlap: 文档中命中检索的个数
maxOverlap: 检索条件的个数
比如检索"english book", 现在有一个文档是"this is an chinese book"。
那么,这个搜索对应这个文档的overlap为1(因为匹配了book),而maxOverlap为2(因为检索条件有两个book和english)。最后得到的这个搜索对应这个文档的coord值为0.5。
2:queryNorm(q) 查询规范 :由idf、权重影响
这个因素对所有文档都是一样的值,所以它不影响排序结果。但是如果每次查询的权重不一样,那么该值就会变动
比如如果我们希望所有文档的评分大一点,那么我们就需要设置这个值。
<span style="font-size:14px;"> /** Implemented as <code>1/sqrt(sumOfSquaredWeights)</code>. */
@Override
public float queryNorm(float sumOfSquaredWeights) {
return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));
}</span>
3:tf 单个文档词频
函数图:
只在商品名称中出现了1次。。他的tf =1
Math.sqrt 取平方根 Math.sqrt(9)=3.0如果搜索“笔记本” ,在分类和商品名称中都出现了 他的tf 就是1.414 (2次的平方根)
只在商品名称中出现了1次。。他的tf =1
<span style="font-size:14px;"> @Override
public float tf(float freq) {
return (float)Math.sqrt(freq);
}</span>
4:idf(t) 一个term在多少个文档中出现过
numDocs 总文档数 docFreq 在多少文档中出现过
如果搜索“笔记本” ,总文档有1000个 ,,出现100次。。那么idf=2
如果出现10次 那么其idf =3
idf 越高、在总文档数中 该词出现的频率越低
<span style="font-size:14px;">public float idf(long docFreq, long numDocs) {
return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);
}</span>
5:t.getBoost() 权重
6:norm(t,d) 字段标准化
对term和文档的字段长度进行标准化计算 ,如果字段的长度越长,那么该值越低。
<span style="font-size:14px;"> @Override
public float lengthNorm(FieldInvertState state) {
final int numTerms;
if (discountOverlaps)
numTerms = state.getLength() - state.getNumOverlap();
else
numTerms = state.getLength();
return state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));
}</span>
三:总匹配分值的计算
最终匹配分值score = 查询语句在N个域中的得分之和 * coord的值
每个域中的得分 = queryWeight * fieldWeight
queryWeight = idf * queryNorm * boost
fieldWeight=idf * fieldNorm * tf
假设在两个域A,B中查询,那么总结公式 :
score = sum( A(queryWeight=idf*queryNorm*boost + fieldWeight=idf*fieldNorm*tf) * B(queryWeight=idf*queryNorm*boost +fieldWeight=idf*fieldNorm*tf))* 1/2