增量熵计算

时间:2013-06-14 08:52:44

标签: c++ algorithm decision-tree entropy

std::vector<int> counts为正整数的向量,让N:=counts[0]+...+counts[counts.length()-1]为向量分量的总和。设置pi:=counts[i]/N,我使用经典公式H=p0*log2(p0)+...+pn*log2(pn)计算熵。

counts向量正在改变---计数递增---每200次改变我重新计算熵。快速google和stackoverflow搜索后,我找不到任何增量熵计算方法。所以问题是:是否存在增量方法like the ones for variance,用于熵计算?

编辑:这个问题的动机是使用这些公式来增加VFDT中的增量信息增益估计 - 就像学习者一样。

已解决:请参阅this mathoverflow post

2 个答案:

答案 0 :(得分:2)

我导出了熵和基尼指数的更新公式和算法,并做了注释available on arXiv。 (该备注的工作版本可用here。)另请参阅this mathoverflow answer。

为了方便起见,我提供了简单的Python代码,演示了派生公式:

from math import log
from random import randint

# maps x to -x*log2(x) for x>0, and to 0 otherwise 
h = lambda p: -p*log(p, 2) if p > 0 else 0

# update entropy if new example x comes in 
def update(H, S, x):
    new_S = S+x
    return 1.0*H*S/new_S+h(1.0*x/new_S)+h(1.0*S/new_S)

# entropy of union of two samples with entropies H1 and H2
def update(H1, S1, H2, S2):
    S = S1+S2
    return 1.0*H1*S1/S+h(1.0*S1/S)+1.0*H2*S2/S+h(1.0*S2/S)

# compute entropy(L) using only `update' function 
def test(L):
    S = 0.0 # sum of the sample elements
    H = 0.0 # sample entropy 
    for x in L:
        H = update(H, S, x)
        S = S+x
    return H

# compute entropy using the classic equation 
def entropy(L):
    n = 1.0*sum(L)
    return sum([h(x/n) for x in L])

# entry point 
if __name__ == "__main__":
    L = [randint(1,100) for k in range(100)]
    M = [randint(100,1000) for k in range(100)]

    L_ent = entropy(L)
    L_sum = sum(L)

    M_ent = entropy(M)
    M_sum = sum(M)

    T = L+M

    print "Full = ", entropy(T)
    print "Update = ", update(L_ent, L_sum, M_ent, M_sum)

答案 1 :(得分:0)

您可以通过重新计算计数并使用一些简单的数学标识来简化熵公式来重新计算熵

K = count.size();
N = count[0] + ... + count[K - 1];
H = count[0]/N * log2(count[0]/N) + ... + count[K - 1]/N * log2(count[K - 1]/N)
  = F * h
h = (count[0] * log2(count[0]) + ... + count[K - 1] * log2(count[K - 1]))
F = -1/(N * log2(N)) 

由于log2(a / b) == log2(a) - log2(b)

而成立

现在给出一个旧的向量count到目前为止,以及另一个新的200个观察向量batch,你可以在C ++ 11中做到

void update_H(double& H, std::vector<int>& count, int& N, std::vector<int> const& batch)
{
    N += batch.size();
    auto F = -1/(N * log2(N));
    for (auto b: batch)
       ++count[b];
    H = F * std::accumulate(count.begin(), count.end(), 0.0, [](int elem) { 
        return elem * log2(elem);
    });
}

我假设您已将观察结果编码为int。如果您有某种符号,则需要一个符号表std::map<Symbol, int>,并在更新batch之前对count中的每个符号进行查找。

这似乎是为一般更新编写一些代码的最快方法。如果您知道在每个批次中只有少数计数实际发生变化,您可以像@migdal那样进行并跟踪变化的计数,减去它们对熵的旧贡献并添加新的贡献。