我在Lucene索引中有一组文本文档。索引中有超过4.000.000个文档。该程序基于用户查询运行搜索并返回与查询相关的N个文档。然后,我们的想法是对N个文档中的术语执行互信息计算,并找到与集合整体密切相关的术语。
基本上,给定查询“计算机”,程序应该返回一个术语列表,如“计算机,网络,程序员,客户端,服务器”等。这应该在理论上有效,但我从来没有得到预期的结果,我不知道我是否只是错误地实现它,或者我的集合中有什么东西使得它效率低下。
这是代码:
public class CoOccurrence {
private Map<String, Double> cooccurrenceMatrix = new HashMap<String, Double>();
private int n;
private List<String> terms = new ArrayList<String>();
public CoOccurrence(List<String> abstracts){
n = abstracts.size();
for(int i = 0; i < abstracts.size(); i++){
String[] line = abstracts.get(i).split(" ");
for(String word: line){
if(!Utils.containsDigits(word)){
if(!terms.contains(word)){
terms.add(word);
}
}
}
}
getMutualInformation(abstracts);
}
private void getMutualInformation(List<String> abstracts){
double n = this.n;
double sum;
for(int f1 = 0; f1 < terms.size()-1; f1++){
sum = 0;
for(int f2 = f1 + 1; f2 < terms.size(); f2++){
Bigram bigram = new Bigram(terms.get(f1), terms.get(f2));
//Fetch number of documents that contains both term.f1 and term.f2
double p_xy = docsContainingXandY(bigram, abstracts) / n;
//Fetch number of documents containing term.f1
double p_x = docsContainingX(bigram, abstracts) / n;
//Fetch number of documents containing term.f2
double p_y = docsContainingY(bigram, abstracts) / n;
sum += (p_xy) * Utils.logWithoutNaN( p_xy / (p_x * p_y) );
}
cooccurrenceMatrix.put(terms.get(f1), sum);
}
}
}
任何人都可以看到我以某种错误的方式进行计算,或者是否存在妨碍预期结果的任何其他错误或误解?
编辑:Bigram类是一个包含两个字符串的简单包装器:String x,String y。
Util类包含许多不同的任务,但logWithoutNaN如下所示:
public static double logWithoutNaN(double value) {
if (value == 0) {
return Math.log(EPSILON);
} else if (value < 0) {
return 0;
}
return Math.log(value);
}
EPSILON = 0.000001
关于输出:搜索“计算机”给出20个应该相关的术语,但是(大多数)不是:“处理称为东南亚地区联邦边缘searcc协会非营利组织输入国家一般意义专业提供协会科学给予“
答案 0 :(得分:0)
有些事情会立即浮现在脑海中。但是,由于我不知道您的数据(有多少文档,有多大等等),因此很难说话。
但是蝙蝠的权利,terms
应该是一个集合,而不是一个列表,那么“包含”(不断扫描列表)应该更有效率。
接下来,虽然分离是一个值得称道的目标,但我将docContaining...
例程合并到一个例程中。你遍历文档列表,我假设他们的整个内容,3次。您(可能)可能会扫描一次并同时获取所有3个值(p_xy
,p_x
,p_y
)。
所以,我只是看到很多重复扫描,或许可以选择更好的算法和数据结构,这可能会产生很大的影响。
附录:淋浴带来的惊人之处。
这里的根本问题是中心的主要双for
循环,您在这些循环中迭代单词对。这就是杀死你的原因。它是n ^ 2。更多的话,更糟糕的表现。
所以真正的目标是尽早消除语言。
首先,你应该对单词运行一个词干分析器。干扰消除了复数和收缩等事物。你还应该消除常见的停用词(“the”,“a”,“then”等)。这两个过程都会减少字数。你可以搜索词干算法,Lucene肯定已经有了这些算法。
接下来,我将为每个文档创建唯一的单词列表。从这些列表中,我将开始删除文档特有的单词。显然,如果一个单词仅存在于单个文档中,则文档集中没有任何关联。
最后,我开始抛弃一些共享的用词。如果您对每个相关性感兴趣,那么您不希望这样做。但大多数人都对“前几名”感兴趣。因此,尽早消除那些明显不会出现在列表顶部的词语应该是直截了当的。例如,在2个文档中仅使用一次的单词。您可以尝试一些启发式方法,例如对所有文档中的单词出现进行平均,并将这些单词放在特定阈值以下。
所有这些目标都是为了减少整体单词列表,因为每个新单词都会对整体性能产生如此巨大的影响。