Python中互信息的实现
我在使用Python的机器学习库中的互信息函数时遇到了一些问题,特别是这个函数:
sklearn.metrics.mutual_info_score(labels_true, labels_pred, contingency=None)
(http://scikit-learn.org/stable/modules/generated/sklearn.metrics.mutual_info_score.html)
我正在尝试实现我在斯坦福NLP教程网站上找到的一个例子:
这个网站在这里:http://nlp.stanford.edu/IR-book/html/htmledition/mutual-information-1.html#mifeatsel2
问题是我总是得到不同的结果,但还没搞清楚原因。
我理解互信息和特征选择的概念,但我就是不明白它在Python中是怎么实现的。我做的是根据NLP网站的例子,给mutual_info_score方法提供两个数组,但它输出的结果却不一样。还有一个有趣的事实是,不管你怎么改变这些数组中的数字,结果往往都是一样的。我是不是应该使用Python特定的其他数据结构,或者说这个问题的根源是什么?如果有人以前成功使用过这个函数,对我来说会非常有帮助,谢谢你的时间。
2 个回答
下面的代码应该能给出一个结果: 0.00011053558610110256
c=np.concatenate([np.ones(49), np.zeros(27652), np.ones(141), np.zeros(774106) ])
t=np.concatenate([np.ones(49), np.ones(27652), np.zeros(141), np.zeros(774106)])
computeMI(c,t)
我今天遇到了同样的问题。经过几次尝试,我发现真正的原因是:如果你严格按照自然语言处理(NLP)的教程,你会使用log2,但实际上,sklearn.metrics.mutual_info_score使用的是自然对数(以e为底,欧拉数)。我在sklearn的文档中没有找到这个细节……
我通过以下方式验证了这一点:
import numpy as np
def computeMI(x, y):
sum_mi = 0.0
x_value_list = np.unique(x)
y_value_list = np.unique(y)
Px = np.array([ len(x[x==xval])/float(len(x)) for xval in x_value_list ]) #P(x)
Py = np.array([ len(y[y==yval])/float(len(y)) for yval in y_value_list ]) #P(y)
for i in xrange(len(x_value_list)):
if Px[i] ==0.:
continue
sy = y[x == x_value_list[i]]
if len(sy)== 0:
continue
pxy = np.array([len(sy[sy==yval])/float(len(y)) for yval in y_value_list]) #p(x,y)
t = pxy[Py>0.]/Py[Py>0.] /Px[i] # log(P(x,y)/( P(x)*P(y))
sum_mi += sum(pxy[t>0]*np.log2( t[t>0]) ) # sum ( P(x,y)* log(P(x,y)/( P(x)*P(y)) )
return sum_mi
如果你把这个np.log2
改成np.log
,我觉得结果会和sklearn的一样。唯一的区别是,当这个方法返回0时,sklearn会返回一个非常接近0的数字。(当然,如果你不在乎对数的底数,直接用sklearn就好,我的这段代码只是演示,效果不太好……)
顺便说一下,1)sklearn.metrics.mutual_info_score
可以接受列表和np.array;2)sklearn.metrics.cluster.entropy
也使用对数,而不是log2。
补充一下,关于“相同的结果”,我不太确定你具体指的是什么。一般来说,向量中的值并不是最重要的,重要的是值的“分布”。你关注的是P(X=x)、P(Y=y)和P(X=x,Y=y),而不是具体的值x和y。