Python(基本的なこと009: mnist_交差エントロピー_サイコロの目)

■Tensorflowのサンプルとしてmnistを実行してみるの続き
mnistのサンプルの内容の理解にあたって、いくつかよく分からない言葉が出てきた。前回のソフトマックス関数に続き、交差エントロピーについて調べてみる。

交差エントロピーは、下のような関数。
ここでxとyは確率分布。

xとyの確率分布が同じになるとき、交差エントロピーHが最も小さくなるらしい。 機械学習では、yに真の確率、xに推定の確率を入れ、最もHが小さくなる部分を探すということをやるので、yの値を固定して、xを動かしてx-Hのグラフを作ってみた。コードは下の通り。

import math
import numpy as np
import matplotlib.pyplot as mp

num_list = []
result = []
for num in range(1, 100):
    x = np.array([num / 100, 1 - (num / 100)])
    #y = np.array([0.01, 0.99])
    #y = np.array([0.50, 0.50])
    y = np.array([0.75, 0.25])
    num_list.append(num)
    result.append((-y[0] * math.log(x[0])) + (-y[1] * math.log(x[1])))
    print(x, ":",result[num-1])

mp.plot(num_list, result, color="red")
mp.grid()
mp.show()

ここで、yは[1/100, 99/100], [1/2, 1/2], [3/4, 1/4]の確率を持つものとし、1-99までの数字に応じて、xを[1/100, 99/100], [2/100, 98/100] ... [99/100, 1/100]となるようしてHの値を求めていった。上から y = [1/100, 99/100], y = [1/2, 1/2], y = [3/4, 1/4]。 それぞれxとyが同じになるとき、Hが最も小さくなる。

y = [1/100, 99/100]、横x、縦H
y = [1/2, 1/2] 、横x、縦H
y = [3/4, 1/4] 、横x、縦H

これで交差エントロピーの関数の動きのイメージはついた。

次に、交差エントロピーの値について少し見てみる。
上の y = [1/2, 1/2] では、最も小さなHは0.6931だった。この時は要素の数が2(1/2が2つ)だけど、これが増えた場合Hの値はどうなるか。
同じ確率の要素を増やした場合で、最もHが小さくなる(xとyが同じ)ときの値を下のコードで求めた。

x = np.array([1 / 4, 1 / 4, 1 / 4, 1 / 4])
y = np.array([1 / 4, 1 / 4, 1 / 4, 1 / 4])

print("4:", (-y[0] * math.log(x[0])) + (-y[1] * math.log(x[1])) + (-y[2] * math.log(x[2]))+ (-y[3] * math.log(x[3])))

x = np.array([1 / 5, 1 / 5, 1 / 5, 1 / 5, 1 / 5])
y = np.array([1 / 5, 1 / 5, 1 / 5, 1 / 5, 1 / 5])

print("5:", (-y[0] * math.log(x[0])) + (-y[1] * math.log(x[1])) + (-y[2] * math.log(x[2]))+ (-y[3] * math.log(x[3]))+ (-y[4] * math.log(x[4])))

x = np.array([1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6])
y = np.array([1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6])

print("6:",(-y[0] * math.log(x[0])) + (-y[1] * math.log(x[1])) + (-y[2] * math.log(x[2]))+ (-y[3] * math.log(x[3]))+ (-y[4] * math.log(x[4]))+ (-y[5] * math.log(x[5])))

結果は、下の通り。要素の数が増えるに従い大きくなった。xとyの確率分布を同じにしても、要素の数が増えるだけでHの値は大きくなってしまうので、別の交差エントロピーと値を比較することには意味がないのではと思う。
4つ: 1.3862943611198906
5つ: 1.6094379124341005
6つ: 1.7917594692280547

最後に、サイコロを投げて出た目から確率分布を出して、真の確率([出た目1, 2, 3, 4, 5, 6] = [1/6, 1/6, 1/6, 1/6, 1/6, 1/6])との交差エントロピーを求めてみた。
randomのライブラリを使って、1-6までの数値をランダムに得る。投げる回数はDICE_COUNT_1TRIALで指定し、それをTRIAL_COUNTの数だけ繰り返す。下のコードでは、36000回ふった数から、出た目(1-6)の確率分布をだし、10トライアル分のデータにする(6列×10行のnumpyデータ)。
なお、tensorflowライブラリのtf.reduce_sum, tf.reduce_meanを使用して、numpyデータをまとめていく。これはmnistのサンプルを動かす部分と同じコード。ここでは、tf.reduce_sumで出た目の y * log(x)を合計し、tf.reduce_meanで10トライアル分のデータの平均を出している(計算するにつれ、6列×10行のデータが、10行のデータ、1つの値のデータと次元が減る)。

import tensorflow as tf
import numpy as np
import random

i = 0
cntList = []
DICE_COUNT_1TRIAL = 36000
TRIAL_COUNT = 10
cnt = [0, 0, 0, 0, 0, 0]

for i in range(0, TRIAL_COUNT):
    cnt = [0, 0, 0, 0, 0, 0]
    for var in range(0, DICE_COUNT_1TRIAL):
        num = random.randint(1, 6)

        if num == 1:
            cnt[0] = cnt[0] + 1
        elif num == 2:
            cnt[1] = cnt[1] + 1
        elif num == 3:
            cnt[2] = cnt[2] + 1
        elif num == 4:
            cnt[3] = cnt[3] + 1
        elif num == 5:
            cnt[4] = cnt[4] + 1
        elif num == 6:
            cnt[5] = cnt[5] + 1

    for var in range(0, 6):
        if cnt[var] == 0:
            cnt[var] = 0.000000000000000000001

    cntList.append(cnt)

print(cntList)
cntNp = np.array(cntList, dtype='float32')
print(cntNp / DICE_COUNT_1TRIAL)

y = np.array([1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6], dtype='float32')

elementwise_product = y * tf.log(cntNp / DICE_COUNT_1TRIAL)
xentropy = -tf.reduce_sum(elementwise_product, reduction_indices=1)
loss = tf.reduce_mean(xentropy)

sess = tf.Session()
result = sess.run(elementwise_product)
print(result)
result_xent = sess.run(xentropy)
print(result_xent)
result_loss = sess.run(loss)
print("Dice throw in a trial:", DICE_COUNT_1TRIAL, "  Trial number:", TRIAL_COUNT, "  cross-entropy:", result_loss)

結果は下の通り。

投げる回数(DICE_COUNT_1TRIAL) トライアル(TRIAL_COUNT)交差エントロピー
360101.7999942
3600101.7925708
36000101.7918148
360000101.7917680
3600000101.7917604
36010001.7986851
3600010001.7918298

上のコードで真の確率と推定の確率が同じときは1.7917594と分かったので、この値が最も小さくなる。投げる回数を増やすほど、この値に近づく。直感的には当然のことだけど、交差エントロピーから見ても回数を増やした方が1/6の確率に近づくことが分かる。トライアルの数は平均なので、数を増やすほど安定した値となる。信頼度がますといえばいいのかな。ここでは1000トライアルに増やす意味をあまり感じないけど。

交差エントロピーはひとまずこんなところで。