覚え書きブログ

Pythonによる機械学習3(ニューラルネット 3/3)

<< Pythonによる機械学習3(ニューラルネット 2/3)

【Pythonによる機械学習3(ニューラルネット 3/3)の目次】

演習4(難しいデータに対するニューラルネットワーク)

以下のsigmoidメソッドのシグモイド関数の計算を完成させましょう。

	#------------------------------------
	# 6) シグモイドの計算
	# x: 入力データ(入力ベクトルの次元数×データ数のnumpy.array)
	def sigmoid(self,x):
		sigmoid = x		#【シグモイド関数の計算】
		return sigmoid
	#------------------------------------

次に、以下のupdateメソッドを、自分のコードにコピーし、ニューラルネットワークを完成させましょう。

	#------------------------------------
	# 3) 最急降下法によるパラメータの更新
	# alpha: 学習率(スカラー)
	# printLoss: 評価値の表示指示(真偽値)
	def update(self, alpha=0.1,printEval=True):

		# 次のバッチ
		x, t = self.nextBatch(self.batchSize)
		
		# データ数
		dNum = x.shape[1]
		
		# 中間層の計算
		h = self.hidden(x)
		
		# 事後確率の予測と真値の差分
		predict = self.predict(x,h)
		predict_error =  predict - t
		
		# 入力層に値「1」のノードを追加
		x_with_one = np.append(x, np.ones([1,x.shape[1]]),axis=0)
		
		# W1の更新
		hidden_error = np.matmul(self.W2,predict_error)
		self.W1 -= alpha * np.matmul(x_with_one,(hidden_error[:-1] * h * (1-h)).T)

		# 中間層に値「1」のノードを追加
		h_with_one = np.append(h, np.ones([1,h.shape[1]]),axis=0)

		# W2の更新
		self.W2 -= alpha * np.matmul(predict_error, h_with_one.T).T

		# 交差エントロピーとAccuracyを標準出力
		if printEval:
			# 交差エントロピーの記録
			self.losses = np.append(self.losses, self.loss(self.x[:,self.validInd],self.t[:,self.validInd]))

			# 正解率エントロピーの記録
			self.accuracies = np.append(self.accuracies, self.accuracy(self.x[:,self.validInd],self.t[:,self.validInd]))
		
			print("loss:{0:02.3f}, accuracy:{1:02.3f}".format(self.losses[-1],self.accuracies[-1]))
	#------------------------------------

そして、難しい場合のデータに対してニューラルネットワークを実行し、以下の内容をmoodleにて提出してください。
1)学習後の交差エントロピー損失と正解率の値
2)学習後の各カテゴリの事後確率の画像
3)ロジスティック回帰と比べ精度が改善した原因の考察

ニューラルネットワークの実行例

完成させたニューラルネットワークを、中間層の数(hDim)を20に設定して実行すると以下のような出力が得られます。

Training ite:1 loss:1.662, accuracy:0.233
Training ite:2 loss:1.524, accuracy:0.233
Training ite:3 loss:1.404, accuracy:0.233
Training ite:4 loss:1.302, accuracy:0.233
Training ite:5 loss:1.217, accuracy:0.200
...
Training ite:996 loss:0.121, accuracy:1.000
Training ite:997 loss:0.121, accuracy:1.000
Training ite:998 loss:0.121, accuracy:1.000
Training ite:999 loss:0.121, accuracy:1.000
Training ite:1000 loss:0.121, accuracy:1.000
Test loss:0.18161537447140794, accuracy:0.9266666666666666

線形モデルのロジスティックモデルでは分離できなかったカテゴリ1と3が、ニューラルネットワークでは以下のように分離できるようになっているのがわかります。


これは、ノード数20の中間層の導入により、入力層と中間層間のパラメータW^1に基づき直線を20本引き、さらに、中間層と出力層間のパラメータW^2により、各カテゴリごとに20本の直線を組み合わせることにより、複雑な境界線を引くことができるようになったことを意味します。

中間層のノード数と交差エントロピー損失の関係

ロジスティック回帰および中間層のノード数が1,5,10,50,100,200,300,500,800と異なるニューラルネットワークを同じデータで学習し、評価データに対する交差エントロピー損失をプロットして比較してみます。

ニューラルネットワークの中間層のノード数が1のときは、損失はロジスティック回帰と同じくらいのですが、ノード数を増やすことにより損失が一気に下がる(改善する)ことがわかります。しかしながら、ノード数が多い場合(例えば、500を超えた場合)、今度は逆に損失が上がる(悪化する)ことがわかります。これは過学習オーバーフィッティングと呼ばれる現象です。モデルの表現能力が高くなりすぎることにより、学習データのノイズに過度に適合してしまい、運用時のデータ(今回は評価データ)に対して十分に性能を発揮できない問題です。

以下は、異なる中間層のノード数を用いて学習したニューラルネットワークの正解率のプロットです。

交差エントロピーと同様に、中間層のノード数が1から増えるにつれて、正解率が上がっていきます。しかしながら、交差エントロピーほど顕著な差はでませんが、中間層のノード数が500を超えると、逆に正解率が徐々に下がる(悪化する)のがわかります。

以下に、中間層のノード数が800と50のときのカテゴリ1の事後確率を描画してみます。
【中間層のノード数が300(学習データと一緒にプロット)】

【中間層のノード数が50(学習データと一緒にプロット)】

中間層のノード数が800のときは、50のときと比べて境界線の形状が複雑になっていることがわかります。座標(x1,x2)=(1,-2)付近にあるカテゴリ1のノイズ(オレンジの〇が3つ)に対して、ノード数20のときは、顕著に反応せず滑らかな境界線が獲得されているのに対し、ノード数800のときは、過度に反応し、カテゴリ1の境界が大きく突き出し、下まで伸びているのがわかります。次に、中間層のノード数が800のと50ときのカテゴリ1の事後確率を、評価データと一緒に描画してみます。

【中間層のノード数が300(評価データと一緒にプロット)】

【中間層のノード数が50(評価データと一緒にプロット)】

中間ノード数が800のとき、座標(x1,x2)=(1,-2)付近のカテゴリ1の境界の突き出しにより、カテゴリ3のデータ(△)をカテゴリ1(〇)と誤分類していることがわかります。このように、学習データに過度に適合すると、評価データにおいて誤分類を起こしやすくなることがわかります。

宿題

1) 中間層のノード数が1,5,10,50,100,200,300,500,800と異なるニューラルネットワークを学習し、評価データ(myData.xTest,myData.tTest)に対する交差エントロピー損失および正解率をプロット(横軸:ノード数、縦軸:損失または正解率)するPythonスクリプトcompareClassifiers.pyを作りましょう。

なお、学習および評価データは、以下のスクリプトで生成したものを使ってください。

# 人工データの生成(難しい場合+ノイズあり)
myData = data.artificial(300,500,mean1=[1,2],mean2=[-2,-1],mean3=[4,-2],mean3multi=[-2,4], cov=[[1,0],[0,1]],noiseMean=[1,-2])

作成したスクリプトおよび出力したグラフ画像を、Moodleにて提出してください。