surolog

AI・機械学習・データ分析 と 本 など

Pythonで情報利得を計算してみる

ジニ係数に引き続き、情報利得の関数も作ってみました。
ジニ係数については以下を参照ください、


[Pythonでジニ係数を計算してみる - surolog

以下の流れでご紹介

  • 情報利得の簡単な説明
  • 情報利得の実装

情報利得って

wikiによれば

カルバック・ライブラー情報量 - Wikipedia


カルバック・ライブラー情報量(カルバック・ライブラーじょうほうりょう、英: Kullback–Leibler divergence、カルバック・ライブラー・ダイバージェンス)とは、確率論と情報理論における2つの確率分布の差異を計る尺度である。情報ダイバージェンス(Information divergence)、情報利得(Information gain)、相対エントロピー(Relative entropy)とも呼ばれる。

http://ja.wikipedia.org/wiki/カルバック・ライブラー情報量

wiki冒頭にあるように、いろいろな呼び方があります。私はたまたま情報利得という呼び方を最初に知ったので、その呼び方に慣れていますが、内容を一番わかりやすく表現しているのは相対エントロピーではないかと思います。

Pythonで実装

まずは単一の状態でのエントロピーを計算する関数を実装します。
こいつをある変数に対して分割前の状態と分割後の各状態で計算して、その差分を見るのがデータ分析での情報利得の扱いですね。

# -*- encoding=utf-8 -*-

import numpy
import pandas
from collections import Counter

def entropy(vec):
    entropys    = list()
    count       = Counter(vec)
    countall    = float(numpy.sum(count.values()))
    for item in count.items():
        counteach       = item[1]
        prob            = counteach/countall
        entropyeach     = -prob * numpy.log(prob)
        # print  "%s : %f" %(item[0],entropyeach)
        entropys.append(entropyeach)
    entropy = numpy.sum(entropys)
    print "%s : %f" % (vec.name, entropy)
    return entropy


続いて、このentropyを呼び出して分割前の目的変数yのエントロピーと、あるカテゴリカル変数xで分割した後のエントロピーを比較します。この差分が正の方向に大きいほど、「xによる分割で各サブセット内のyに秩序が生じた」ことになるので、xは影響力のありそうな特徴量かな、となります。

def informationGain(x,y):
    # x,yともにカテゴリカル変数
    root_ent        = entropy(y)
    grouped         = y.groupby(x)
    leaf_ent_0      = grouped.apply(entropy)
    leaf_weight     = grouped.apply(len) / float(len(y))
    leaf_ent        = leaf_ent_0 * leaf_weight

    ig  = root_ent - leaf_ent.sum()
    print "information gain : %f" %ig
    return ig


ついでにxが連続量の場合についても作ってみました。
厳密にはxについて積分が必要な気がしますが、簡易版として100分割して
離散化しています。

def informationGainNum(x,y, bins=100):
    # xが連続変数、yがカテゴリカル変数
    root_ent        = entropy(y)
    xname = x.name
    yname = y.name

    xy      = pandas.concat([x,y], axis=1)
    xbins = pandas.cut(xy[xname], bins)
    # xbins   = pandas.qcut(xy[xname], 10)

    grouped         = xy[yname].groupby(xbins)
    leaf_ent_0      = grouped.apply(entropy)
    leaf_weight     = grouped.apply(len) / float(len(y))
    leaf_ent        = leaf_ent_0 * leaf_weight

    ig  = root_ent - leaf_ent.sum()
    print "information gain : %f" %ig
    return ig


では実際に使ってみます。

x = numpy.random.choice(["A","B","C"], 100)
x = pandas.Series(x)
y = numpy.random.choice([0,1], 100)
y = pandas.Series(y)
y.name = "testy"

informationGain(x,y)

これを実行すると、以下のようになります。

testy : 0.692947
A : 0.693147
B : 0.690186
C : 0.692627
information gain : 0.001116

せば