在Java中高效实现互信息

时间:2011-09-29 18:07:15

标签: java optimization machine-learning

我希望使用Java来计算两个功能之间的互信息。

我已经阅读了Calculating Mutual Information For Selecting a Training Set in Java,但这是关于互信息是否适合海报的讨论,只有一些关于实施的轻伪代码。

我目前的代码如下,但我希望有一种优化方法,因为我需要处理大量信息。我知道调用另一种语言/框架可能会提高速度,但是现在我想集中精力在Java中解决这个问题。

任何帮助都非常感激。

public static double calculateNewMutualInformation(double frequencyOfBoth, double frequencyOfLeft,
                                                   double frequencyOfRight, int noOfTransactions) {
    if (frequencyOfBoth == 0 || frequencyOfLeft == 0 || frequencyOfRight == 0)
        return 0;
    // supp = f11
    double supp = frequencyOfBoth / noOfTransactions; // P(x,y)
    double suppLeft = frequencyOfLeft / noOfTransactions; // P(x)
    double suppRight = frequencyOfRight / noOfTransactions; // P(y)
    double f10 = (suppLeft - supp); // P(x) - P(x,y)
    double f00 = (1 - suppRight) - f10; // (1-P(y)) - P(x,y)
    double f01 = (suppRight - supp); // P(y) - P(x,y)

    // -1 * ((P(x) * log(Px)) + ((1 - P(x)) * log(1-p(x)))
    double HX = -1 * ((suppLeft * MathUtils.logWithoutNaN(suppLeft)) + ((1 - suppLeft) * MathUtils.logWithoutNaN(1 - suppLeft)));
    // -1 * ((P(y) * log(Py)) + ((1 - P(y)) * log(1-p(y)))
    double HY = -1 * ((suppRight * MathUtils.logWithoutNaN(suppRight)) + ((1 - suppRight) * MathUtils.logWithoutNaN(1 - suppRight)));

    double one = (supp * MathUtils.logWithoutNaN(supp)); // P(x,y) * log(P(x,y))
    double two = (f10 * MathUtils.logWithoutNaN(f10)); 
    double three = (f01 * MathUtils.logWithoutNaN(f01));
    double four = (f00 * MathUtils.logWithoutNaN(f00));
    double HXY = -1 * (one + two + three + four);
    return (HX + HY - HXY) / (HX == 0 ? MathUtils.EPSILON : HX);
}        

public class MathUtils {
public static final double EPSILON = 0.000001;

public static double logWithoutNaN(double value) {
    if (value == 0) {
        return Math.log(EPSILON);
    } else if (value < 0) {
        return 0;
    }
    return Math.log(value);
}  

2 个答案:

答案 0 :(得分:1)

我不是数学家但是......

这里只有一堆浮点计算。一些数学家可能能够将此减少到更少的计算,尝试Math SE

与此同时,您应该能够static final double使用Math.log(EPSILON)

您的问题可能不是一次调用,而是必须进行此计算的数据量。通过投入更多硬件可以更好地解决这个问题。

答案 1 :(得分:1)

我发现以下内容很快,但我没有将它与您的方法进行比较 - 仅在weka中提供。

它的工作原理是重新安排MI方程,以便最大限度地减少浮点运算的次数:

mutual information equation

我们首先将pcdot定义为样本/事务数量的计数/频率。因此,我们将项目数量定义为n,x出现的次数为| x |,y出现的次数为| y |和它们共同出现的次数| x,y |。然后我们得到,

mi1

现在,我们可以通过翻转内部鸿沟的底部来重新排列,这给了我们(n | x,y |)/(| x || y |)。此外,计算使用N = 1 / n所以我们有一个较少的除法运算。这给了我们:

mi2

这给了我们以下代码:

/***
 * Computes MI between variables t and a. Assumes that a.length == t.length.
 * @param a candidate variable a
 * @param avals number of values a can take (max(a) == avals)
 * @param t target variable
 * @param tvals number of values a can take (max(t) == tvals)
 * @return 
 */
static double computeMI(int[] a, int avals, int[] t, int tvals) {
    double numinst = a.length;
    double oneovernuminst = 1/numinst;
    double sum = 0;

    // longs are required here because of big multiples in calculation
    long[][] crosscounts = new long[avals][tvals];
    long[] tcounts = new long[tvals];
    long[] acounts = new long[avals];
    // Compute counts for the two variables
    for (int i=0;i<a.length;i++) {
        int av = a[i];
        int tv = t[i];
        acounts[av]++;
        tcounts[tv]++;
        crosscounts[av][tv]++;
    }

    for (int tv=0;tv<tvals;tv++) {
        for (int av=0;av<avals;av++) {
            if (crosscounts[av][tv] != 0) {
                // Main fraction: (n|x,y|)/(|x||y|)
                double sumtmp = (numinst*crosscounts[av][tv])/(acounts[av]*tcounts[tv]);
                // Log bit (|x,y|/n) and update product
                sum += oneovernuminst*crosscounts[av][tv]*Math.log(sumtmp)*log2;
            }
        }

    }

    return sum;
}

该代码假定a和t的值不是稀疏的(即min(t)= 0且tvals = max(t)),因为它是有效的。否则(如注释)将创建大型和不必要的数组。

我相信这种方法可以在同时计算多个变量之间的MI时进一步提高(计数操作可以浓缩 - 尤其是目标的计数)。我使用的实现是与WEKA接口的实现。

最后,即使从总结中取出日志也可能更有效。但我不确定日志或电源是否会在循环内进行更多计算。这可以通过以下方式完成:

  1. 应用* log(b)= log(a ^ b)
  2. 使用log(a)+ log(b)= log(ab)
  3. 将日志移至摘要外部

    并给出:

    mi2