④線形分類問題(多クラス分類)
第四問
- 設問1.ガウシアンノイズを付加した入力x1、x2が(x1, x2) = (5, 5), (0, -5), (-5, 5)となるデータセットをワン・ホット表現を用いて作成せよ
- 設問2.活性化関数にソフトマックス、損失関数にクロスエントロピーを用いて出力y1、y2と損失量Eを求めよ
- 設問3.勾配降下法でパラメータを400回更新し、その出力をデータセットと同じグラフ上にプロットせよ
- 設問4.ガウシアンノイズを付加した入力x1、x2が(x1, x2) = (0, 0), (5, 5), (5, -5), (-5, -5), (-5, 5)となるデータセットを、勾配降下法で分類した結果と同一のグラフにプロットせよ
- まとめ
前回はワン・ホット表現を用いて2クラス分類にトライしました。今回は更に多クラス(3クラス以上)の分類に取り組んでみたいと思います・
設問1.ガウシアンノイズを付加した入力x1、x2が(x1, x2) = (5, 5), (0, -5), (-5, 5)となるデータセットをワン・ホット表現を用いて作成せよ
クラス分類の入力が3つになりました。とは言え、前回の2クラスから3クラスになっただけなので、データグループの数を増やせばいいだけです。
まずはライブラリのインポート。
import numpy as np import matplotlib.pyplot as plt
続いてデータセットを作成します。
#dataset num_of_sam = 40 std_dv = 1.8 group1 = np.array([5,5])+np.random.randn(num_of_sam, 2)*std_dv group2 = np.array([0,-5])+np.random.randn(num_of_sam, 2)*std_dv group3 = np.array([-5,5])+np.random.randn(num_of_sam, 2)*std_dv X = np.vstack((group1, group2, group3))
group3が増えていますが、今まで通りですね。では正解ラベルを書いていきます。
t_group1 = np.tile([0,0,1],(num_of_sam,1)) t_group2 = np.tile([0,1,0],(num_of_sam,1)) t_group3 = np.tile([1,0,0],(num_of_sam,1)) T = np.vstack((t_group1, t_group2, t_group3))
こちらも前回からgroup3が増えているだけですね。最後にXもTも結合するのを忘れないように注意です。
ではグラフ化してみましょう。
plt.scatter(group1[:,0],group1[:,1],marker='o',color='blue') plt.scatter(group2[:,0],group2[:,1],marker='o',color='red') plt.scatter(group3[:,0],group3[:,1],marker='o',color='green') plt.show()
確かに3クラスになっていますね、これでこの設問は終了です。
設問2.活性化関数にソフトマックス、損失関数にクロスエントロピーを用いて出力y1、y2と損失量Eを求めよ
こちらも前回と流れは同じですが、データセットと出力の数が変わるので確認しておきます。
線の数がだいぶ多くなってきました。
入力と出力の数は同じなので、基本的な骨格はこれまでと変わりません。なので、変更するのはパラメータW、Bの行列の型だけです。まずはソフトマックスとクロスエントロピーをPythonの関数に落としておきます。
#function def softmax(x): return np.exp(x) / np.sum(np.exp(x), axis=1, keepdims=True) def loss(y, t): return -np.sum(np.multiply(t, np.log(y)) + np.multiply((1 - t), np.log(1 - y)))
次にパラメータWとBの設定です。
#initial setting W = np.random.randn(2,3) B = np.random.randn(1,3)
ニューロンの数が2→3となっているのでパラメータの型も変更しています。
では、準備が整ったので実際に計算してみます。
Y = softmax(np.dot(X, W)+B) E = loss(Y, T)
ここは前回と同じですね。設問2は終了です。
設問3.勾配降下法でパラメータを400回更新し、その出力をデータセットと同じグラフ上にプロットせよ
パラメータの更新式ですが、行列の型が変わっているだけで式は同じです。
ではコードに落とします。学習率は0.003とします。
#initial setting W = np.random.randn(2,3) B = np.random.randn(1,3) learning_rate = 0.003 E_save = [] #iteration num_of_itr = 400 for i in range(num_of_itr): #forward propagation Y = softmax(np.dot(X, W)+B) E = loss(Y, T) E_save = np.append(E_save, E) #back propagation dW = X.T.dot(Y-T) dB = np.sum(Y-T, axis=0, keepdims=True) #update W = W - learning_rate*dW B = B - learning_rate*dB
ではグラフに描画します。グリッドを切ってそれぞれの出力を結合します。詳しくは前回の問題参照で。
#plot grid_range = 10 resolution = 50 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 = softmax(np.dot(X_grid, W)+B) Y_predict = np.around(Y_grid) out_connect = np.hstack((X_grid,Y_predict)) green_group = out_connect[out_connect[:,2]==1] red_group = out_connect[out_connect[:,3]==1] blue_group = out_connect[out_connect[:,4]==1]
グリッドが切れたので実際にグラフ化します。
#plot_dataset plt.scatter(group1[:,0],group1[:,1],marker='o',color='blue') plt.scatter(group2[:,0],group2[:,1],marker='o',color='red') plt.scatter(group3[:,0],group3[:,1],marker='o',color='green') #plot_dataset plt.scatter(green_group[:,0],green_group[:,1],marker='o',alpha=0.3,color='green') plt.scatter(red_group[:,0],red_group[:,1],marker='o',alpha=0.3,color='red') plt.scatter(blue_group[:,0],blue_group[:,1],marker='o',alpha=0.3,color='blue') plt.show()
groupが3つになっていますが、書いている内容は同じですね。
綺麗に分類できました、これで設問3は終了です。
[note] なぜか色の付かない欠損点ができるんですよね、出力ラベルが全部ゼロになってるのかな。
設問4.ガウシアンノイズを付加した入力x1、x2が(x1, x2) = (0, 0), (5, 5), (5, -5), (-5, -5), (-5, 5)となるデータセットを、勾配降下法で分類した結果と同一のグラフにプロットせよ
3クラス分類は2クラス分類からグループを増やしただけで実行することができたので、最後の設問として一気に5クラス分類にトライしてみたいと思います。
まずはデータセットの作成です。
import numpy as np import matplotlib.pyplot as plt #dataset num_of_sam = 40 std_dv = 1.8 group1 = np.array([0,0])+np.random.randn(num_of_sam, 2)*std_dv group2 = np.array([5,5])+np.random.randn(num_of_sam, 2)*std_dv group3 = np.array([5,-5])+np.random.randn(num_of_sam, 2)*std_dv group4 = np.array([-5,-5])+np.random.randn(num_of_sam, 2)*std_dv group5 = np.array([-5,5])+np.random.randn(num_of_sam, 2)*std_dv X = np.vstack((group1, group2, group3, group4, group5)) t_group1 = np.tile([0,0,0,0,1],(num_of_sam,1)) t_group2 = np.tile([0,0,0,1,0],(num_of_sam,1)) t_group3 = np.tile([0,0,1,0,0],(num_of_sam,1)) t_group4 = np.tile([0,1,0,0,0],(num_of_sam,1)) t_group5 = np.tile([1,0,0,0,0],(num_of_sam,1)) T = np.vstack((t_group1, t_group2, t_group3, t_group4, t_group5))
コピペコピペ&コピペです。さすがに5グループ分も同じ記述が続くとブサイクに見えるので、この書き方は微妙です。もっとうまい記述はあると思いますが、とりあえず動きが見たいので今回コーディングの改良はスルーで。
グラフ化します。
plt.scatter(group1[:,0],group1[:,1], marker='o',color='yellow') plt.scatter(group2[:,0],group2[:,1], marker='o',color='black') plt.scatter(group3[:,0],group3[:,1], marker='o',color='red') plt.scatter(group4[:,0],group4[:,1], marker='o',color='green') plt.scatter(group5[:,0],group5[:,1], marker='o',color='blue') plt.show()
いい感じですね。
引き続きコピペを勧めます。活性化関数と損失関数は今まで通り「ソフトマックス」と「クロスエントロピー」です。
#function def softmax(x): return np.exp(x) / np.sum(np.exp(x), axis=1, keepdims=True) def loss(y, t): return -np.sum(np.multiply(t, np.log(y)) + np.multiply((1 - t), np.log(1 - y)))
続いてイテレーション計算です。基本的に設定はすべて同じですが、パラメータWとBはニューロンの数に合わせて変更が必要です。
#initial setting W = np.random.randn(2,5) B = np.random.randn(1,5) learning_rate = 0.003 E_save = [] #iteration num_of_itr = 400 for i in range(num_of_itr): #forward propagation Y = softmax(np.dot(X, W)+B) E = loss(Y, T) E_save = np.append(E_save, E) #back propagation dW = X.T.dot(Y-T) dB = np.sum(Y-T, axis=0, keepdims=True) #update W = W - learning_rate*dW B = B - learning_rate*dB
爆速で進んでいますが、ほぼ上記のコピペです。
さて、結果をグラフ化します。まずはグリッド切りから。
grid_range = 10 resolution = 50 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 = softmax(np.dot(X_grid, W)+B) Y_predict = np.around(Y_grid) out_connect = np.hstack((X_grid,Y_predict)) blue_group = out_connect[out_connect[:,2]==1] green_group = out_connect[out_connect[:,3]==1] red_group = out_connect[out_connect[:,4]==1] black_group = out_connect[out_connect[:,5]==1] yellow_group = out_connect[out_connect[:,6]==1]
実際にグラフ化します。
#plot_dataset plt.scatter(group1[:,0],group1[:,1],marker='o',color='yellow') plt.scatter(group2[:,0],group2[:,1],marker='o',color='black') plt.scatter(group3[:,0],group3[:,1],marker='o',color='red') plt.scatter(group4[:,0],group4[:,1],marker='o',color='green') plt.scatter(group5[:,0],group5[:,1],marker='o',color='blue') #plot_output plt.scatter(blue_group[:,0],blue_group[:,1],marker='o',alpha=0.3,color='blue') plt.scatter(red_group[:,0],red_group[:,1],marker='o',alpha=0.3,color='red') plt.scatter(green_group[:,0],green_group[:,1],marker='o',alpha=0.3,color='green') plt.scatter(black_group[:,0],black_group[:,1],marker='o',alpha=0.3,color='black') plt.scatter(yellow_group[:,0],yellow_group[:,1],marker='o',alpha=0.3,color='yellow') plt.show()
ほんと5グループも並ぶとコードはブサイクですね。まあ気にせず今回は力技でいきましょう。
うまく分類できました。線形分類ですが5クラスちゃんと分けられるんですね。へーすごい。
まとめ
今回も学習が進む過程をGIFでアニメ化します、2本立てです。
さてさて、直線を使ってクラスを分類する「線形分類」を長々と扱ってきました。次回からは直線では分類できないようなデータセット(ドーナツ型とか)を分類できるような非線形問題にトライしていきたいと思います。これまではニューラルネットワークにとって比較的問簡単な問題だったので、パラメータの更新には勾配降下法しか使わなくてもそれなりに結果を出すことができました。しかし、そろそろ問題も複雑になってくるので、他の更新方法だとどのような違いが見られるのかも扱っていければと思います。
全体コード
- 3クラス分類
- 5クラス分類
次の問題
①非形分類問題(2クラス分類) - 社畜エンジニア発掘戦線