array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 [ lucene扩展 ] MoreLikeThis 相似检索 - 爱码网

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
      Collection<String> fields = 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<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);//取出term向量
      
      // 如果当前字段没有存储termVector,那么需要重新计算。其实这里就是分词,并计算term词频的过程,注意他默认使用的是StandardAnalyzer分词器!!!
      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 {//如果之前保存了termVector那么就方便多了。
        addTermFrequencies(termFreqMap, vector);
      }
      
    }

 

2.3)addTermFrequencies

由于TermVector中的term和field没有关系,不管是标题还是正文,只要term内容一样就将其频率累加。addTermFrequencies就做这个事情!

把累加的结果存放到termFreqMap中。

private void addTermFrequencies(Map<String,Int> termFreqMap,
      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<Object[]> createQueue(Map<String,Int> words)
      throws IOException {
    // 获取当前index的文档总数。
    int numDocs = ir.numDocs();
    FreqQ res = new FreqQ(words.size()); // 按照term的得分进行存放
    
    Iterator<String> it = words.keySet().iterator();
    while (it.hasNext()) { // 对所有term进行遍历
      String word = it.next();
      
      int tf = words.get(word).x; // 对应term的tf
      if (minTermFreq > 0 && tf < minTermFreq) {
        continue; // 和去噪类似,tf太小的term直接过掉。
      }
      
      // 对于同一个term,找到df最大的那个字段,存放到topField。
      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;
      }
      //df太小的term也要直接过掉
      if (minDocFreq > 0 && docFreq < minDocFreq) {
        continue; // filter out words that don't occur in enough docs
      }
      //df太大的term也要直接过掉
      if (docFreq > maxDocFreq) {
        continue; // filter out words that occur in too many docs
      }
      //df==0的term也要直接过掉,怎么会有df的term???这里说是index文件的问题
      if (docFreq == 0) {
        continue; // index update problem?
      }
      //经典的idf、tf又来了
      float idf = similarity.idf(docFreq, numDocs);
      float score = tf * idf;
      
      //将结果存放到优先队列中。
      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、idf进行计算。

这里他的意思就是:

1.将指定参与运算字段的term的frequency进行累加;(这里对ti、ab字段的tf进行累加)

2.通过df的比较,选取df大的字段作为最终“运算”的字段,但tf为所有字段的累加值。这和我们看普通检索时的打分算法不太一样,普通检索中tf为当前字段的词频。

至于为什么这么做,还得验证!!!

 

2.5)createQuery

到此我们将term的打分排序拿到了,分值越大的term更能表述整篇document的主要内容!(有没有想到这就类似主题词!!!

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]));
      //这里还可以对termquery进行boost的设置。默认为false
      if (boost) {
        if (qterms == 0) {
          bestScore = ((Float) ar[2]).floatValue();
        }
        float myScore = ((Float) ar[2]).floatValue();
        
        tq.setBoost(boostFactor * myScore / bestScore);
      }
      //构建boolean query,should关联。
      try {
        query.add(tq, BooleanClause.Occur.SHOULD);
      } catch (BooleanQuery.TooManyClauses ignore) {
        break;
      }
      
      qterms++;
      if (maxQueryTerms > 0 && qterms >= maxQueryTerms) {//限定参与运算的term的数量
        break;
      }
    }
    
    return query;
  }

这样就根据一篇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的各项参数进行设置,这里就不在实验了!

 

相关文章:

  • 2021-11-11
  • 2018-03-31
  • 2021-11-27
  • 2021-08-01
  • 2020-10-23
  • 2021-11-16
  • 2019-01-26
  • 2018-10-31
猜你喜欢
  • 2020-02-24
  • 2021-11-29
  • 2020-02-25
  • 2021-11-16
  • 2021-11-28
  • 2019-07-11
  • 2018-07-14
  • 2017-11-27
相关资源
相似解决方案