ニューラルネットワークでデータ分類

ニューラルネットワークでデータの分類をしてみます。
まずは、chainerの宣言をします。

1
2
3
4
import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
from chainer import Variable, Chain, config

次に乱数を生成します。100行2列のデータとなります。

1
2
3
4
5
6
import numpy as np
import matplotlib.pyplot as plt

D = 100
N = 2
xdata = np.random.randn(D * N).reshape(D, N).astype(np.float32)

次にデータを分割する関数とその関数で分類したデータを準備します。

1
2
3
4
def f(x):
return x * x

tdata = (xdata[:,1] > f(xdata[:,0])).astype(np.int32)

1層のニューラルネットワーク作成します。
2種類のデータが入力され、2種類のデータ出力される単純なニューラルネットワークとなります。

1
2
3
# 1層のニューラルネットワーク作成
C = 2 # 結果が2種類あるという意味
NN = Chain(l1=L.Linear(N, C))

このニューラルネットワークを使った関数を定義します。

1
2
3
def model(x):
y = NN.l1(x)
return y

上記で定義した関数でデータを分類します。

1
2
ydata = model(xdata)
ydata

乱数で生成したデータ(一部略)
正解率を検証します。

1
2
3
# 精度の検証
acc = F.accuracy(ydata, tdata)
acc

最初の正解率
結果は0.21(21%)ととても低いですが、まったく学習をしていない状態なので気にする必要はありません。

ニューラルネットワークを最適化する手法を設定します。

1
2
3
# 最適化手法の設定
optNN = Opt.SGD()
optNN.setup(NN)

結果を確認するために、誤差と正解率を記録するエリアを定義します。

1
2
3
# 学習の記録用
loss_series = []
acc_series = []

いよいよ学習してみます。処理に関してはソースコメントを参照してください。

1
2
3
4
5
6
7
8
9
10
11
12
T = 5000
for time in range(T):
config.train = True # 学習モードにする
optNN.target.zerograds() # 初期化(全ての勾配を0にする)
ydata = model(xdata) # 誤差を計算する
loss = F.softmax_cross_entropy(ydata, tdata) # 成績チェック
acc= F.accuracy(ydata, tdata) # 誤差逆伝搬
loss.backward() # ニューラルネットワークを調整
optNN.update()
# 結果の記録
loss_series.append(loss.data)
acc_series.append(acc.data)

誤差がどのように変化しているかをグラフ化します。

1
2
3
4
5
6
7
8
9
10
# 学習記録の表示(誤差)
Tall = len(loss_series)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), loss_series)
plt.title('loss function in training')
plt.xlabel('step')
plt.ylabel('loss function')
plt.xlim([0, Tall])
plt.ylim(0, 1)
plt.show()

誤差(1層のニューラルネットワーク)
0.3付近までは順調に誤差が減っていますがそこからは改善していないようです。
次に、正解率の遷移をグラフ化します。

1
2
3
4
5
6
7
8
9
10
# 学習記録の表示(正解率)
Tall = len(acc_series)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), acc_series)
plt.title('accuracy in training')
plt.xlabel('step')
plt.ylabel('accuracy')
plt.xlim([0, Tall])
plt.ylim(0, 1)
plt.show()

正解率(1層のニューラルネットワーク)
0.85(85%)付近までは上昇しますが、そこが限界のようです。

さらなる精度情報のために2層のニューラルネットワークにしてみます。
下記では、入力が2種類、中間層が4層、出力が2種類となるニューラルネットワークを定義しています。

1
2
3
# 2層のニューラルネットワークにする
C = 2
NN = Chain(l1=L.Linear(N, 4), l2=L.Linear(4 , C))

2層のニューラルネットワークの関数化ですが2つの線形変換の間に非線形変換をいれるとよいとのことです。
シグモイド関数をはさんでみました。

1
2
3
4
5
6
# 2層のニューラルネットワークの関数化
def model(x):
h = NN.l1(x) # 線形変換
h = F.sigmoid(h) # 非線形変換(シグモイド関数)
y = NN.l2(h) # 変形変換
return y

あらたにニューラルネットワークを定義したので、最適化手法を再設定します。
学習記録もいったん初期化し、学習を2万回に増やして実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 最適化手法の設定(再設定)
optNN = Opt.SGD()
optNN.setup(NN)

# 学習の記録用(初期化)
loss_series = []
acc_series = []

# 学習
T = 20000
for time in range(T):
config.train = True
optNN.target.zerograds()
ydata = model(xdata)
loss = F.softmax_cross_entropy(ydata, tdata)
acc= F.accuracy(ydata, tdata)
loss.backward()
optNN.update()
# 結果の記録
loss_series.append(loss.data)
acc_series.append(acc.data)

結果は下記のとおりです。
誤差(2万回)
正解率(2万回)

誤差の結果をみるとまだまだ改善するような感じなのでもう2万回 追加で学習をしてみます。
再実行するのは、ループの[学習]のところと[学習記録の表示(誤差)][学習記録の表示(正解率)]です。

誤差(4万回)

正解率(4万回)

だんだん結果がよくなっているので、もう2万回追加で実行します。

誤差(6万回)

正解率(6万回)

ほぼ100%の正解率にすることができました。
誤差が改善する見込みがある場合は、学習回数を単純に増やしていくといいみたいです。

(Google Colaboratoryで動作確認しています。)