转载:solr MoreLikeThis的原理分析
转载地址:http://blog.sina.com.cn/s/blog_5ddc071f0101muos.html
在solr中有两种方式实现MoreLikeThis:MoreLikeThisHandler和在SearchHandler中的MoreLikeThisComponent。
两种方式大同小异:
一是:将MoreLikeThis作为一个单独的Handler来处理,体现主体地位。
二是:将MoreLikeThis作为一个组件放到SearchHandler中,为Search加入了MLT的功能,是一种辅助功能。
这里我们借助方法一,来简单阐述MLT的实现步骤。
步骤1:
MLT是根据一篇文档(document)的相关字段进行“相似匹配”,例如:
这里我们提供的检索式为:q=id:82790,因此其只有唯一一个检索结果。
MLT第一步工作就是根据我们提供的检索式获取文档(document)。
步骤2:
MLT可以看成是一种特殊的检索,只是他的检索式是根据我们提供的一篇文档(document)生成的。
因此关键是怎么生成这个检索式!!!
MoreLikeThis.java
public Query like(int docNum) throws IOException {
if
(fieldNames == null) {
// gather list of valid fields from lucene
Collection fields = ir
.getFieldNames(IndexReader.FieldOption.INDEXED);
fieldNames = fields.toArray(new String[fields.size()]);
}
return
createQuery(retrieveTerms(docNum));
}
在创建这个“神奇”的query之前,我们先要获得相关的原始term(retrieveTerms)。
public PriorityQueue<Object[]>
retrieveTerms(int docNum) throws IOException {
Map<String,Int> termFreqMap = new
HashMap<String,Int>();
for (int i =
0; i < fieldNames.length; i++) {
String fieldName = fieldNames[i];
TermFreqVector vector = ir.getTermFreqVector(docNum,
fieldName);
// field does not store term vector info
if (vector == null) {
Document d = ir.document(docNum);
String text[] = d.getValues(fieldName);
if (text != null) {
for (int j = 0; j < text.length; j++) {
addTermFrequencies(new StringReader(text[j]), termFreqMap,
fieldName);
}
}
} else {
addTermFrequencies(termFreqMap, vector);
}
}<br> return
createQueue(termFreqMap);<br>}
首先获取每一个字段的TermFreqVector,然后将其添加到TermFrequencies中,该过程是计算TF的过程,结果存放在map<String,Int>中,key为term,value为该term出现的次数(termFrequencies)。
在该过程中需要降噪,及去掉一些无关紧要的term,其判断方式如下:
private boolean isNoiseWord(String term) {
int len =
term.length();
if
(minWordLen > 0 &&
len < minWordLen) {
return true;
}
if
(maxWordLen > 0 &&
len > maxWordLen) {
return true;
}
if
(stopWords != null &&
stopWords.contains(term)) {
return true;
}
return
false;
}
主要两个依据:
1.term长度必须在minWordLen和maxWordLen范围内;
2.term不应出现在stopWords内。
我们再回到retrieveTerms方法中,他返回的是一个PriorityQueue<Object[]>,因此我们还要将之前创建的map<String,Int>(tf)进行一定的处理(重要)。
“Find words for a more-like-this query
former.”
“Create a PriorityQueue from a word->tf
map.”
private PriorityQueue<Object[]>
createQueue(Map<String,Int>
words)
throws IOException {
// have collected all words in
doc and their freqs
int numDocs =
ir.numDocs();
FreqQ res = new
FreqQ(words.size()); // will order words by score
Iterator<String> it =
words.keySet().iterator();
while (it.hasNext()) { // for
every word
String word = it.next();
int tf = words.get(word).x; // term freq in the source doc
if (minTermFreq > 0
&& tf < minTermFreq)
{
continue; // filter out words that don't occur enough times in
the
// source
}
// go through all the fields and find the largest document
frequency
String topField = fieldNames[0];
int docFreq = 0;
for (int i = 0; i < fieldNames.length; i++) {
int freq = ir.docFreq(new Term(fieldNames[i], word));
topField = (freq > docFreq) ? fieldNames[i] :
topField;
docFreq = (freq > docFreq) ? freq : docFreq;
}
if (minDocFreq > 0
&& docFreq <
minDocFreq) {
continue; // filter out words that don't occur in enough docs
}
if (docFreq > maxDocFreq) {
continue; // filter out words that occur in too many docs
}
if (docFreq == 0) {
continue; // index update problem?
}
float idf = similarity.idf(docFreq, numDocs);
float score = tf * idf;
// only really need 1st 3 entries, other ones are for
troubleshooting
res.insertWithOverflow(new Object[] {word, // the word
topField, // the top field
Float.valueOf(score), // overall score
Float.valueOf(idf), // idf
Integer.valueOf(docFreq), // freq in all docs
Integer.valueOf(tf)});
}
return res;
}
该方法我们遍历所有的term,并取出其tf以及在所有指定字段(例如:mlt.fl=ti,ab,mcn)中最大的df。根据df和当前索引文档数计算idf,然后计算该term的score=tf*idf。
创建好PriorityQueue后,我们就可以将他转变成之前提到的那个“神奇”的query了。
“Create the More like query from a
PriorityQueue”
private Query
createQuery(PriorityQueue<Object[]>
q) {
BooleanQuery
query = new BooleanQuery();
Object
cur;
int qterms =
0;
float
bestScore = 0;
while (((cur
= q.pop()) != null)) {
Object[] ar = (Object[]) cur;
TermQuery tq = new TermQuery(new Term((String) ar[1], (String)
ar[0]));
if (boost) {
if (qterms == 0) {
bestScore = ((Float) ar[2]).floatValue();
}
float myScore = ((Float) ar[2]).floatValue();
tq.setBoost(boostFactor * myScore / bestScore);
}
try {
query.add(tq, BooleanClause.Occur.SHOULD);
} catch (BooleanQuery.TooManyClauses ignore) {
break;
}
qterms++;
if (maxQueryTerms > 0
&& qterms >=
maxQueryTerms) {
break;
}
}
return
query;
}
构建一个BooleanQuery,按照score从大到小取出一定数量的term(maxQueryTerm)进行组建:
query.add(tq, BooleanClause.Occur.SHOULD);
这里简单理解就是——取出文档中(相关字段)最重要(tf*idf)的前N个term,组建一个BooleanQuery(Should关联)。
步骤3:
用第二步创建的query进行一次检索,取出得分最高的N篇文档即可。
原理分析:
(1)在MLT中主要是tf、idf,根据score(tf*idf)获取对分类最重要的term,并构建目标Query。
MLT可以理解为:找出给定文档同一类的其他文档。
在一份给定的文件里,词频(term
frequency,TF)指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数(term
count)的归一化,以防止它偏向长的文件。(同一个词语在长文件里可能会比短文件有更高的词数,而不管该词语重要与否。)对于在某一特定文件里的词语 ti 来说,它的重要性可表示为:
以上式子中 ni,j 是该词在文件dj中的出现次数,而分母则是在文件dj中所有字词的出现次数之和。
逆向文件频率(inverse document
frequency,IDF)是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到:
其中
- |D|:语料库中的文件总数
- :包含词语ti的文件数目(即的文件数目)如果该词语不在语料库中,就会导致被除数为零,因此一般情况下使用
然后
某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
(2)根据提供的Query,利用lucene的打分算法,找到相似文档。
Lucene 将信息检索中的Boolean model (BM)和Vector Space
Model (VSM)联合起来,实现了自己的评分机制。
具体内容参见:
那么有哪些环节可以提高相似检索精度呢?
1.降噪环节需要强化,目前solr中是基于term长度和停用此表联合过滤。
例如将term的最小长度限定成2,即单个字不能作为计算的term,例如:
ab:扩印 ab:胶卷 ab:印机 ab:彩色 ab:传动轴 ab:两根 ab:垫板 ab:手轮 ab:齿轮 ab:从动
ab:传动 ab:设置 ab:自动 ab:电动机 mcn:g03b27/46 ab:电动 ab:上片 ab:上手 ab:支撑
ab:精确度 ab:动机 ab:压片 ab:以及 ab:机构 ab:下压
2.提高分词器的精度,并且对于行业性的业务最好提供行业性的词库,并且进行人工维护。
3.调整、改进相似度算法。
简单的我们试试将term的数量(构建目标query的term数量)进行控制,设置成10。例如:
ab:扩印 ab:胶卷 ab:印机 ab:彩色 ab:传动轴 ab:两根 ab:垫板 ab:手轮 ab:齿轮
ab:从动
以上实例只是一个简单说明,更多调整(挑战)还需要在实践中具体分析。
转载:solr MoreLikeThis的原理分析的更多相关文章
- [转载]PDO防注入原理分析以及使用PDO的注意事项
本文全部内容转载自月影无痕的博客http://zhangxugg-163-com.iteye.com/blog/1835721#bc2346092,感谢作者的分享 合理正确使用PDO,可以基本上防止S ...
- 转载:AbstractQueuedSynchronizer的介绍和原理分析
简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础.使用的方法是继承,子类通过 ...
- (转载)Java NIO:NIO原理分析(二)
NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如果我们使用 ...
- 【转载】Select函数实现原理分析
Select函数实现原理分析 <原文> select需要驱动程序的支持,驱动程序实现fops内的poll函数.select通过每个设备文件对应的poll函数提供的信息判断当前是否有资源可用 ...
- Solr缓存原理分析及配置优化
一.缓存原理 缓存,带来急速性能体验! Solr提供了一系列的内置缓存来优化查询性能.Solr的缓存原理主要涉及以下4个方面: 1.缓存大小及缓存置换法 从缓存大小的角度来看,不能将缓存设置的太大,否 ...
- Java NIO使用及原理分析(1-4)(转)
转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...
- [转]Handler MessageQueue Looper消息循环原理分析
Handler MessageQueue Looper消息循环原理分析 Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...
- [原创]Android Studio的Instant Run(即时安装)原理分析和源码浅析
Android Studio升级到2.0之后,新增了Instant Run功能,该功能可以热替换apk中的部分代码,大幅提高测试安装的效率. 但是,由于我的项目中自定义了一些ClassLoader,当 ...
- seo伪原创技术原理分析,php实现伪原创示例
seo伪原创技术原理分析,php实现伪原创示例 现在seo伪原创一般采用分词引擎以及动态同义词库,模拟百度(baidu),谷歌(google)等中文切词进行伪原创,生成后的伪原创文章更准确更贴近百度和 ...
随机推荐
- HDU 1242 Rescue (BFS(广度优先搜索))
Rescue Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submis ...
- 计算直线的交点数(hdu1466简单的dp)
题意:平面上有n条直线,且无三线共点,问这些直线能有多少种不同交点数.比如,如果n=2,则可能的交点数量为0(平行)或者1(不平行). 思路:动态规划,想办法记忆化搜索,当前状态和之前状态结合起来 d ...
- ssh 框架整合试例 (spring+struts2+hibernate)
1.首先用Eclipse创建一个web项目(Eclipse EE 版) new->Other-> 输入web 然后选择Dynamic Web Project->next-> 输 ...
- 数据库连接池 c3p0 demo 代码和分析
import java.sql.Connection; import java.sql.SQLException; import java.beans.PropertyVetoException; i ...
- Redis总结(五)缓存雪崩和缓存穿透等问题
前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhong/category/771056.html .今 ...
- SQL SERVER 2005如何建立自动备份的维护计划
SQL Server 2005中可以使用维护计划来为数据库自动备份,减少数据库管理员的工作负担.其使用方法如下: (1)启动[sql server Management Studio],在[对象资源管 ...
- MySQL事务处理和锁机制
事务处理和并发性 1.1 基础知识和相关概念 1 )全部的表类型都可以使用锁,但是只有 InnoDB 和 BDB 才有内置的事务功能. 2 )使用 begin 开始事务,使用 commit 结束事务, ...
- 新浪微博SSO登陆机制
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- CMSIS Example - Signal
/*---------------------------------------------------------------------------- * RL-ARM - RTX *----- ...
- Java面试葵花宝典
面向对象的特征有哪些方面 1. 抽象:抽象就是忽略一个主题中与当前目标2. 无关的那些方面,3. 以便更充分地注意与当前目标4. 有关的方面.抽象并不5. 打算了解全部问题,而6. 只是选择其中的一 ...