MoreLikeThis,相似检索。找出某篇文档的相似文档,常见于“类似新闻”、“相关文章”等,这里完全是基于内容的分析。
1)MoreLikeThis的使用
FSDirectory directory = SimpleFSDirectory.open(new File("d:/nrtTest2")); IndexReader reader = IndexReader.open(directory); IndexSearcher searcher = new IndexSearcher(reader); // MoreLikeThis mlt = new MoreLikeThis(reader); mlt.setFieldNames(new String[] { "ab" }); //用于计算的字段 // int docNum = 1;// TermFreqVector vector = reader.getTermFreqVector(docNum, "ab");// System.out.println(vector.toString()); Query query = mlt.like(docNum);//试图找到与docnum=1相似的documents System.out.println(reader.document(docNum)); System.out.println(query.toString());//查看构造的query,后面的就是常规的lucene的检索过程。 TopDocs topDocs = searcher.search(query, 10); ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (ScoreDoc sdoc : scoreDocs) { Document doc = reader.document(sdoc.doc); System.out.println(doc.get("ti")); // System.out.println(doc.get("ti")); }
2)MoreLikeThis的源码解读
把MLT运行起来很简单,那么我们进一步看看他是怎么实现的。
关键就是 Query query = mlt.like(docNum); 我们就从他下手。
2.1)like
public Query like(int docNum) throws IOException { if (fieldNames == null) { // gather list of valid fields from lucene Collectionfields = ReaderUtil.getIndexedFields(ir); fieldNames = fields.toArray(new String[fields.size()]); } return createQuery(retrieveTerms(docNum)); }
filedNames为参与“more like this”运算的字段,在moreLikeThis对象的setFiledNames方法中进行设置。
2.2)retrieveTerms
public PriorityQueue
2.3)addTermFrequencies
由于TermVector中的term和field没有关系,不管是标题还是正文,只要term内容一样就将其频率累加。addTermFrequencies就做这个事情!
把累加的结果存放到termFreqMap中。
private void addTermFrequencies(MaptermFreqMap, TermFreqVector vector) { String[] terms = vector.getTerms(); int freqs[] = vector.getTermFrequencies(); for (int j = 0; j < terms.length; j++) { String term = terms[j]; if (isNoiseWord(term)) { continue; } // increment frequency Int cnt = termFreqMap.get(term); if (cnt == null) { cnt = new Int(); termFreqMap.put(term, cnt); cnt.x = freqs[j]; } else { cnt.x += freqs[j]; } } }
截止,我们将指定的文档(被匹配文档)按照指定的运算字段,将其term和对应的frequency存放到了map中。在这个过程中,我们看到了一个听起来比较牛逼的操作——去噪!
那么这里怎么判断一个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; }
他判断的标准十分简单,第一:是否是规定的停用词;第二:term长度是否过长或过短,这个范围由minWordLen和maxWordLen控制。
2.4)createQueue
这里的queue应该是一个优先级队列,上一步我们获得了所有<term, frequency>,虽然做了去噪,但是term项目还是太多了,还需要找出相对重要的前N个Term。
private PriorityQueue
在这里,我们对每个term进行了打分排序,主要还是通过tf、idf进行计算。
这里他的意思就是:
1.将指定参与运算字段的term的frequency进行累加;(这里对ti、ab字段的tf进行累加)
2.通过df的比较,选取df大的字段作为最终“运算”的字段,但tf为所有字段的累加值。这和我们看普通检索时的打分算法不太一样,普通检索中tf为当前字段的词频。
至于为什么这么做,还得验证!!!
2.5)createQuery
到此我们将term的打分排序拿到了,分值越大的term更能表述整篇document的主要内容!(有没有想到这就类似主题词!!!)
private Query createQuery(PriorityQueue
这样就根据一篇document和指定字段得到了一个query。这个query作为代表着document的灵魂,将寻找和他类似的documents。
3)实例
被匹配的document为:
Document<stored,indexed,tokenized<an:CN00103249.6>
stored,indexed<ad:20000320> stored,indexed,tokenized,termVector<ab: 本发明涉及一种高级毛料服装洗涤剂。使用该洗涤剂,洗衣服可不用到干洗店,自己在家水洗就行,且洗涤后毛料服装笔挺膨松,抗静电,不缩水,洗涤中不刺激皮肤。此剂主要由去污剂、抗静电剂、防缩剂、表面活性剂与其它助剂配制而成。去污率≥90%,缩水率≤1‰。>
stored,indexed,tokenized,termVector<ti:高级毛料服装洗涤剂>>
计算出来的query为:
ab:毛料 ab:服装 ab:洗涤剂 ab:抗静电 ab:高级 ab:剂 ab:洗涤
计算结果为:
高级毛料服装洗涤剂
抗静电防尘污灭菌广谱洗涤剂及制备方法一种抗紫外线的织物涂层材料服装绿色干洗及服装翻新技术洗碟用柔性含蛋白酶的液体或凝胶洗涤组合物复合洗霉制剂洗衣机一种抗静电合成纤维实验室专用洗涤剂液晶相结构型液体洗涤剂及制造工艺貌似结果不是很相似,那么我们可以试着只用ti做运算,这样从标题看起来比较相似。
还可以对MLT的各项参数进行设置,这里就不在实验了!