相互信息的实施

时间:2014-02-06 15:59:00

标签: java information-retrieval

我在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协会非营利组织输入国家一般意义专业提供协会科学给予“

1 个答案:

答案 0 :(得分:0)

有些事情会立即浮现在脑海中。但是,由于我不知道您的数据(有多少文档,有多大等等),因此很难说话。

但是蝙蝠的权利,terms应该是一个集合,而不是一个列表,那么“包含”(不断扫描列表)应该更有效率。

接下来,虽然分离是一个值得称道的目标,但我将docContaining...例程合并到一个例程中。你遍历文档列表,我假设他们的整个内容,3次。您(可能)可能会扫描一次并同时获取所有3个值(p_xyp_xp_y)。

所以,我只是看到很多重复扫描,或许可以选择更好的算法和数据结构,这可能会产生很大的影响。

附录:淋浴带来的惊人之处。

这里的根本问题是中心的主要双for循环,您在这些循环中迭代单词对。这就是杀死你的原因。它是n ^ 2。更多的话,更糟糕的表现。

所以真正的目标是尽早消除语言。

首先,你应该对单词运行一个词干分析器。干扰消除了复数和收缩等事物。你还应该消除常见的停用词(“the”,“a”,“then”等)。这两个过程都会减少字数。你可以搜索词干算法,Lucene肯定已经有了这些算法。

接下来,我将为每个文档创建唯一的单词列表。从这些列表中,我将开始删除文档特有的单词。显然,如果一个单词仅存在于单个文档中,则文档集中没有任何关联。

最后,我开始抛弃一些共享的用词。如果您对每个相关性感兴趣,那么您不希望这样做。但大多数人都对“前几名”感兴趣。因此,尽早消除那些明显不会出现在列表顶部的词语应该是直截了当的。例如,在2个文档中仅使用一次的单词。您可以尝试一些启发式方法,例如对所有文档中的单词出现进行平均,并将这些单词放在特定阈值以下。

所有这些目标都是为了减少整体单词列表,因为每个新单词都会对整体性能产生如此巨大的影响。