②線形分類問題(2値分類:バイアス付加)
第二問
- 設問1.ガウシアンノイズを付加した入力x1、x2、正解ラベルtが(x1, x2, t) = (2.5, 2.5, 0), (7.5, 7.5, 1)となるデータセットを作成せよ
- 設問2.パラメータにバイアスを付加し、活性化関数にシグモイド関数、損失関数にクロスエントロピーを用いて出力と損失量を求めよ。
- 設問3.勾配降下法を用いてパラメータを更新し、イテレーションを500回まわした後のパラメータWとbを求めよ
- 設問4.更新後のパラメータを用いた出力をデータセットと同じグラフ上にプロットせよ
- まとめ
今回も2値分類問題にトライします。前回と異なり、パラメータにバイアスを追加します。バイアスを追加することで原点の束縛から開放されてより自由に2値のクラスを分類します。
設問1.ガウシアンノイズを付加した入力x1、x2、正解ラベルtが(x1, x2, t) = (2.5, 2.5, 0), (7.5, 7.5, 1)となるデータセットを作成せよ
データセットのコードは前回と全く同じですが座標を変えました。この座標であれば原点で束縛されている状態(バイアスなし)では正しく分類できません。とりあえずコードに落としましょう。
まずはライブラリのインポート、matplotlibはカラーマップ(cm)も使用します。
import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cm
次にデータセットを作成します。
#dataset num_of_sam = 40 std_dv = 1.8 group1 = np.array([2.5,2.5])+np.random.randn(num_of_sam, 2)*std_dv group2 = np.array([7.5,7.5])+np.random.randn(num_of_sam, 2)*std_dv X = np.vstack((group1, group2)) t_group1 = np.zeros((num_of_sam, 1)) t_group2 = np.ones((num_of_sam, 1)) t = np.vstack((t_group1, t_group2))
グラフ化してみます。
plt.scatter(X[:,0], X[:,1], vmin=0, vmax=1, c=t[:,0], cmap=cm.bwr, marker='o', s=50) Plt.show()
確かに原点に固定されていれば正しく分類できない配置です。
設問2.パラメータにバイアスを付加し、活性化関数にシグモイド関数、損失関数にクロスエントロピーを用いて出力と損失量を求めよ。
まずはニューラルネットワークを設計します。基本的に前回と同じですが、バイアスを付加するのでこんな感じ。
次に、出力と損失量の計算に使用するシグモイド関数とクロスエントロピーをPythonで先に定義しておきます。
#function def sigmoid(x): return 1 / (1 + np.exp(-x)) def loss(y, t): return -np.sum(np.multiply(t, np.log(y)) + np.multiply((1 - t), np.log(1 - y)))
これで、それぞれの関数が簡単に使えるようになりました。
次にパラメータの設定です。今回は重みとバイアスを定義しなければいけません。
#initial setting W = np.random.randn(2,1) b = np.random.randn(1,1)
今回バイアスはスカラー値なのでわざわざ行列で書かなくてもいいんですが、今後の多変数への拡張性を込めて行列で書いておきます。
では、出力と損失量を計算します。シグモイドの中身にバイアスが追加されているところに注意です。
y = sigmoid(np.dot(X, W)+b) E = loss(y, t)
以上で設問2は終了です。
設問3.勾配降下法を用いてパラメータを更新し、イテレーションを500回まわした後のパラメータWとbを求めよ
いつも通り、損失関数をパラメータで微分し、降下法によるパラメータの更新を行います。重みWとバイアスbの微分が必要です。
重みWについては前回計算しているので結果だけ。
バイアスについて計算します。
微分式の要素をひとつずつ計算します。
この微分式より、バイアスのパラメータの更新式は下記のようになります。
これで各パラメータの更新式が求められたので、コードに落とします。今回の学習率は0.003としましょう。
#initial setting W = np.random.randn(2,1) b = np.random.randn(1,1) learning_rate = 0.003 E_save = [] #iteration num_of_itr = 500 for i in range(num_of_itr): #forward propagation y = sigmoid(np.dot(X, W)+b) E = loss(y, t) E_save = np.append(E_save, E) #back propagation dW = np.sum(X*(y-t),axis=0) db = np.sum(y-t) #update W = W - learning_rate*np.reshape(dW,(2,1)) b = b - learning_rate*db
いつも通りの流れなので問題ないですね。これで設問3が終了です。
設問4.更新後のパラメータを用いた出力をデータセットと同じグラフ上にプロットせよ
出力のグラフ描画は前回と全く同じなのでガバっとコピペします。
#plot_grid grid_range = 10 resolution = 60 x1_grid = x2_grid = np.linspace(-grid_range, grid_range, resolution) xx, yy = np.meshgrid(x1_grid, x2_grid) X_grid = np.c_[xx.ravel(), yy.ravel()] Y_grid = sigmoid(np.dot(X_grid, W)+b) Y_predict = np.around(Y_grid) #plot_output plt.scatter(X_grid[:,0], X_grid[:,1], vmin=0, vmax=1, c=Y_predict[:,0], cmap=cm.bwr, marker='o', s=50,alpha=0.2) plt.scatter(X[:,0], X[:,1], vmin=0, vmax=1, c=t[:,0], cmap=cm.bwr, marker='o', s=50) plt.show()
確かに、バイアスを付加したことによって境界線が原点を離れてオフセットし、いい感じに2クラスを分類してくれています。
※損失量のグラフ化は特に確認したいこともないので設問にしていません。コードには入れているのでまた興味があれば確認して下さい。
まとめ
学習が進む過程をGIFでアニメ化します。
境界線は縦横無尽に動き回っていますね。
今回は2値分類のバイアス問題にトライしました。しかし、分類は0と1の2値で行われるため、このままの設定では2値以上の分類ができません。「2とか3とか使えばええやん」と思うんですが、クロスエントロピーの性質上、0と1での表現でなければ分類は難しいのが現実です。そこで、次回は0と1のみでいくつもの正解ラベルを表現する「ワン・ホット」というテクニックを使って同じ問題を解いてみたいと思います。
全体コード
github.com
次の問題
③線形分類問題(2クラス分類:ワン・ホット エンコーダ) - 社畜エンジニア発掘戦線