1 of K 表記法 (one-hot表現)

1 of K 表記法 (one-hot表現)

1 of K 表記法 (one-hot表現)

1 of K データの作成

 ニューラルネットワークは、バックプロパゲーションという手法を使って出力値を目標変数(正解値)に近づけるように重みを調整します。ネットワークが分類問題を学習する場合、一般に目標変数は 1 of K (one-hot) 形式で表記されます。

 Neural Network 1 of k

 1 of K 表記法は、あるデータが属しているクラス K に等しいインデックスの要素のみを 1 とし、他の要素はすべて 0 とします。たとえば、クラス 2 を 1 of K で表す場合、インデックス 2 の要素を 1 として他の要素は 0 にします。すなわち、 [0 0 1] と表されます(下図参照)。

 one_hot (1 of K)

 練習として、Python で 1 of K データを作ってみましょう。
 2 次元配列 xm に格納された 1 次元配列データが、それぞれ 1, 0, 2, 1, 0 のクラスに属しているとします。NumPy の identity()関数を使うと、クラス番号を要素にもつ配列を 1 of K に変換できます。

# リストM10-B-1

import numpy as np

# データ配列
xm = np.array([[2, 1],
               [1, 0],
               [1, 2],
               [2, 2],
               [3, 0]])

# クラス番号を要素にもつ配列を定義
x_class = np.array([1, 0, 2, 1, 0])

# xを3クラスの1-of-K型配列に変換
x_class = np.identity(3, dtype = "int32")[x_class]

print(x_class)
[[0 1 0]
 [1 0 0]
 [0 0 1]
 [0 1 0]
 [1 0 0]]

 機械学習では「あるクラスに属するデータを抜き出す」というコードをよく書きます。データ配列 x から、クラス 1 に分類されているデータだけを抜き出してみましょう。

# リストM10-B-2

# クラス1のデータを取り出す
x_c1 = xm[x_class[:, 1] == 1]

print(x_c1)
[[2 1]
 [2 2]]

 x_class[:, 1] は x_class からインデックス 1 の列 (左から 2 番目の列) を抜き出します。クラス 1 に属するデータは、この列の要素が 1 となっています。すなわち、x_class[:, 1] == 1 が True と判定されるデータを xm から抜き出せば、クラス 1 に属しているデータ [2 1], [2 2] を取り出すことができます。
 

出力値のクラス分類

 分類問題を扱うニューラルネットワークの出力値 $y_k$ は、入力データがクラス k に分類される確率を表しています。冒頭の図を例にとると、出力値は [0.05, 0,83, 0.12] なので、1 of K 表記法で [0 1 0] となる、すなわちクラス 1 に分類される確率が最も高いことを意味しています(クラス 0 やクラス 2 である可能性も僅かにあります)。そこで、学習を終えた後(交差エントロピー誤差が最小になるように重みパラメータを最適化した後)で出力ベクトルの最大成分を 1 に、他の成分は 0 に置き換えて分類を完了します。

 例として ソフトマックス関数 の戻り値を分類してみます。ソフトマックス関数は戻り値の成分をすべて足し合わせると 1 になるという性質をもっています。Python でソフトマックス関数を定義して、戻り値を確認しておきましょう。

# リストM10-B-3

import numpy as np

# ソフトマックス関数
def softmax(x):
    f = np.exp(x)/np.sum(np.exp(x))
    return f

x = np.array([1, 2, 4])
y = softmax(x)
s = np.sum(y)

print("戻り値 {}".format(y))
print("戻り値の成分の総和 {}".format(s))
戻り値 [0.04201007 0.1141952  0.84379473]
戻り値の成分の総和 1.0

 numpy.argmax() は配列の最大要素のインデックスを返します。
 この関数を使って y のクラスを決定することができます。

# リストM10-B-4

# 配列の最大要素のインデックスを取得
y_class = np.argmax(y)

print("データyのクラス:{}".format(y_class))
データyのクラス:2

 最後に numpy.identity() を使って、1 of K 表記法に変換します。
 numpy.identity() には整数要素の配列しか渡せないので、予め astype()メソッドを使って y_class の dtype を変更しておきます。

# リストM10-B-5

# 配列要素を整数に変換
y_class = y_class.astype("int8")

# 1-of-K表記法に変換
y_class_1ofk = np.identity(3, dtype = "int8")[y_class]

print("データyのクラス:{}".format(y_class_1ofk))
データyのクラス:[0 0 1]

 

2クラス分類マップ

 下図のようなニューラルネットワークを構築して2クラス分類マップを作成してみます。

 Python 1 of k 表記による出力

 中間層にシグモイド関数、出力層にはソフトマックス関数を組み込みます。今回は NumPy のテクニックを駆使して、for文を一切使わずにコードを書いてみます (for文は処理速度が遅いので、可能な限り避けたほうがいいです)。

# リストM10-C-1

import numpy as np
import matplotlib.pyplot as plt

# 恒等関数
def identity(x):
    return x

# シグモイド関数
def sigmoid(x):
    f = 1 / (1 + np.exp(-x))
    return f

# ソフトマックス関数
def softmax(x):
    f = np.exp(x)/np.sum(np.exp(x))
    return f

# ニューラルネットワークの層
def layer(xm, wm, func = identity):
    dummy = np.ones((xm.shape[0], 1))
    xm = np.append(xm, dummy, axis = 1)
    u = np.dot(wm, xm.T).T
    if u.ndim == 1:
        u = u[:, np.newaxis]
    return func(u)

# 中間層の重みを設定
wm1 = np.array([[1.0, 2.5, 0.1],
                [3.0, 2.0, -0.1]])

# 出力層の重みを設定
wm2 = np.array([[-1.0,  1.5, 0.1],
                [ 2.0, -1.0, -0.1]])

# ネットワークへの入力データを設定
x0 = np.linspace(-2, 2, 17)
x1 = np.linspace(-2, 2, 17)
X0, X1 = np.meshgrid(x0, x1)
X0 = X0.reshape(-1)
X1 = X1.reshape(-1)
xm0 = np.vstack([X0, X1]).T

# 出力層への入力行列
xm1 = layer(xm0, wm1, sigmoid)

# ネットワークからの出力行列
xm2 = layer(xm1, wm2, softmax)

# 出力値にクラス番号を割り当てる
xm2 = np.argmax(xm2, axis = 1)

# 1-of-Kに変換
xm2 = np.identity(2, dtype = "int8")[xm2]

# クラス0に属するデータを抽出
xc_0 = xm0[xm2[:, 0] == 1]

# クラス1に属するデータを抽出
xc_1 = xm0[xm2[:, 0] == 0]

# データをプロット
fig = plt.figure(figsize = (5, 5))
ax = fig.add_subplot(111)
ax.set_xlabel("x0", size = 15, labelpad = 10)
ax.set_ylabel("x1", size = 15, labelpad = 10)
ax.set_xlim([-2, 2])
ax.set_ylim([-2, 2])
ax.scatter(xc_0[:, 0], xc_0[:, 1],
           marker = "D", color = "darkorange")
ax.scatter(xc_1[:, 0], xc_1[:, 1],
           marker = "+", color = "darkblue")

 Python class map
 重みパラメータを変更すると色々な模様の図が描かれるので、ぜひ試してみてください。