Python中的连续互信息

18 投票
1 回答
6812 浏览
提问于 2025-04-17 07:34

[前言] (如果你只想要问题,可以跳过这一部分):

我现在在研究如何使用香农-韦弗互信息标准化冗余来测量离散和连续特征值之间的信息遮蔽程度,这些特征值是按特征组织的。通过这种方法,我的目标是构建一个算法,这个算法看起来和ID3非常相似,但它不使用香农熵,而是寻求(作为循环约束)最大化或最小化单个特征与一组特征之间的共享信息。如果新特征能够增加或减少互信息,才会将其添加到特征集合中。这样一来,ID3的决策算法就转移到了配对空间,并将一个集成方法与之结合,带来了这两种方法的预期时间和空间复杂度。

[/前言]


接下来是问题:我正在尝试在Python中使用SciPy实现一个连续的积分器。因为我在比较离散和连续变量,所以我目前对每对特征的比较策略如下:

  • 离散特征与离散特征:使用离散形式的互信息。这会导致概率的双重求和,我的代码对此处理得很好。

  • 其他所有情况(离散与连续、反向情况,以及连续与连续):使用连续形式,利用高斯估计器来平滑概率密度函数

我可以对后者的情况进行某种离散化处理,但由于我的输入数据集本身并不是线性的,这可能会变得复杂而不必要。


以下是关键代码:

import math
import numpy
import scipy
from scipy.stats import gaussian_kde
from scipy.integrate import dblquad

# Constants
MIN_DOUBLE = 4.9406564584124654e-324 
                    # The minimum size of a Float64; used here to prevent the
                    #  logarithmic function from hitting its undefined region
                    #  at its asymptote of 0.
INF = float('inf')  # The floating-point representation for "infinity"

# x and y are previously defined as collections of 
# floating point values with the same length

# Kernel estimation
gkde_x = gaussian_kde(x)
gkde_y = gaussian_kde(y)

if len(binned_x) != len(binned_y) and len(binned_x) != len(x):
    x.append(x[0])
    y.append(y[0])

gkde_xy = gaussian_kde([x,y])
mutual_info = lambda a,b: gkde_xy([a,b]) * \
           math.log((gkde_xy([a,b]) / (gkde_x(a) * gkde_y(b))) + MIN_DOUBLE)

# Compute MI(X,Y)
(minfo_xy, err_xy) = \
    dblquad(mutual_info, -INF, INF, lambda a: 0, lambda a: INF)

print 'minfo_xy = ', minfo_xy

请注意,故意对一个点进行过度计数是为了防止SciPy的gaussian_kde类出现奇异性。当x和y的大小相互接近无穷大时,这种影响变得微不足道。


我现在遇到的问题是尝试在SciPy中对多重积分进行高斯核密度估计。我一直在尝试使用SciPy的dblquad来进行积分,但在这种情况下,我收到了大量的以下消息。

当我设置numpy.seterr ( all='ignore' )时:

警告:检测到舍入误差的发生,这阻止了请求的容忍度的实现。该错误可能被低估。

而当我将其设置为'call'并使用错误处理程序时:

浮点错误(下溢),标志4

浮点错误(无效值),标志8

这很容易理解发生了什么,对吧?嗯,差不多:IEEE 754-2008和SciPy只告诉我发生了什么,并没有告诉我为什么如何解决这个问题


总结一下:minfo_xy通常会变成nan; 它的采样不足以防止在进行Float64数学运算时信息丢失或无效。

在使用SciPy时,有没有什么通用的解决方法?

更好的是:如果有一个强大且现成的Python连续互信息实现,接口可以接受两个浮点值集合或合并的对集合,那将完全解决这个问题。如果你知道有这样的实现,请分享链接。

提前谢谢你。


编辑:这解决了上面示例中的nan传播问题:

mutual_info = lambda a,b: gkde_xy([a,b]) * \
    math.log((gkde_xy([a,b]) / ((gkde_x(a) * gkde_y(b)) + MIN_DOUBLE)) \
        + MIN_DOUBLE)

然而,舍入修正的问题仍然存在,关于更强大实现的请求也依然存在。任何在这方面的帮助都将不胜感激。

1 个回答

4

在尝试一些比较激进的解决方案,比如重新定义问题或者使用不同的集成工具之前,先看看这个方法是否有效。把 INF=float('INF') 替换成 INF=1E12 或者其他一些大数字——这样可能会消除因为对输入变量进行简单算术运算而产生的 NaN 结果。

这个方法不一定有效,但有时候在进行大规模的算法重写或者更换工具之前,尝试一个简单的修复是有帮助的。

撰写回答