深層学習でCIFAR10分類(一般物体認識)

CIFAR10とういうデータを使って一般物の認識をしてみます。

まずは必要なモジュールをインポートします。

1
2
3
4
5
6
7
8
9
10
import numpy as np
import matplotlib.pyplot as plt

import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
import chainer
import chainer.datasets as ds
import chainer.dataset.convert as con
from chainer import Variable, Chain, config, cuda

共通関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# -------------- #
# 共通関数の定義 #
# -------------- #
# データ振り分け関数
def data_divide(Dtrain, D, xdata, tdata, shuffle='on'):
if shuffle == 'on':
index = np.random.permutation(range(D))
elif shuffle == 'off':
index = np.arange(D)
else:
print('error')
xtrain = xdata[index[0:Dtrain],:]
ttrain = tdata[index[0:Dtrain]]
xtest = xdata[index[Dtrain:D],:]
ttest = tdata[index[Dtrain:D]]
return xtrain, xtest, ttrain, ttest

# グラフ表示関数
def show_graph(result1, result2, title, xlabel, ylabel, ymin=0.0, ymax=1.0):
# 学習記録の表示(誤差)
Tall = len(result1)
plt.figure(figsize=(12, 8))
plt.plot(range(Tall), result1, label='train')
plt.plot(range(Tall), result2, label='test')
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.xlim([0, Tall])
plt.ylim(ymin, ymax)
plt.legend()
plt.show()

# 学習用関数
def learning_classification(model,optNN,data,result,T=10):
for time in range(T):
optNN.target.cleargrads()
ytrain = model(data[0])
loss_train = F.softmax_cross_entropy(ytrain,data[2])
acc_train = F.accuracy(ytrain,data[2])
loss_train.backward()
optNN.update()

with chainer.using_config('train', False), chainer.using_config('enable_backprop', False):
ytest = model(data[1])
loss_test = F.softmax_cross_entropy(ytest,data[3])
acc_test = F.accuracy(ytest,data[3])
result[0].append(cuda.to_cpu(loss_train.data))
result[1].append(cuda.to_cpu(loss_test.data))
result[2].append(cuda.to_cpu(acc_train.data))
result[3].append(cuda.to_cpu(acc_test.data))

一般物体認識のデータセットを読み込みます。

1
2
3
4
5
6
7
# 一般物体認識のデータセットの読み込み
train, test = ds.get_cifar10(ndim=3)
xtrain, ttrain = con.concat_examples(train)
xtest, ttest = con.concat_examples(test)

# データサイズの確認
Dtrain, ch, Ny, Nx = xtrain.shape

どんなデータがあるかいくつか画像を出力します。

1
2
3
4
5
6
7
8
9
# 試しに画像を出力
plt.imshow(xtrain[0,:,:,:].transpose(1, 2, 0))
plt.show()

plt.imshow(xtrain[500,:,:,:].transpose(1, 2, 0))
plt.show()

plt.imshow(xtrain[1200,:,:,:].transpose(1, 2, 0))
plt.show()

結果

画像識別の問題に有効な畳み込みニューラルネットワークを設定します。

1
2
3
4
5
6
7
8
9
# 2層畳み込みニューラルネットワークの設定
C = ttrain.max() + 1
H1 = 10

layers = {}
layers['conv1'] = L.Convolution2D(ch, H1, ksize=3, stride=1, pad=1)
layers['bnorm1'] = L.BatchRenormalization(H1)
layers['l1'] = L.Linear(None, C)
NN = Chain(**layers)

プーリング層を導入した関数を定義します。

1
2
3
4
5
6
7
8
# プーリング層の導入
def model(x):
h = NN.conv1(x)
h = F.relu(h)
h = NN.bnorm1(h)
h = F.max_pooling_2d(h, ksize=3, stride=2, pad=1)
y = NN.l1(h)
return y

GPUを設定します。

1
2
3
4
# GPUの設定
gpu_device = 0
cuda.get_device(gpu_device).use()
NN.to_gpu(gpu_device)

最適化手法を設定します。

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

学習の経過を保存する変数を宣言します。

1
2
3
4
5
6
7
# 学習データ保存エリア
train_loss = []
train_acc = []
test_loss = []
test_acc = []

result = [train_loss, test_loss, train_acc, test_acc]

一度に全部実行するとメモリオーバーになってしまうのでデータセットを分割して実行します。
CIFARのデータは50,000個の訓練データがあるので5,000個ずつを10回に分けて実行します。

1
2
3
4
5
6
# シリアルイテレータの呼び出し
from chainer.iterators import SerialIterator as siter

# データセット振り分ける
batch_size = 5000
train_iter = siter(train, batch_size)

いつくかのバッチと呼ばれる小さな塊に分けて学習を進める方法をバッチ学習といいます。
特にランダムにデータを選んでニューラルネットワークの最適化を進める方法を確率勾配法といいます。
確率勾配法による最適化を行います。

1
2
3
4
5
6
7
# 確率勾配法による最適化
nepoch = 10
while train_iter.epoch < nepoch:
batch = train_iter.next()
xtrain, ttrain = con.concat_examples(batch)
data = cuda.to_gpu([xtrain, xtest, ttrain, ttest])
learning_classification(model, optNN, data, result, 1)

結果を表示します。

1
2
3
show_graph(result[0], result[1], 'loss function', 'step', 'loss function', 0.0, 4.0)

show_graph(result[2], result[3], 'accuracy', 'step', 'accuracy')

誤差

正解率

正解率は6割に届かない程度となりました。
ここから正解率を上げるために4層のニューラルネットワークにしていきます。

畳み込みニューラルネットワークのクラスを定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 畳み込みニューラルネットワークのクラス作成
class CNN(Chain):
def __init__(self, ch_in,ch_out):
layers = {}
layers['conv1'] = L.Convolution2D(ch_in,ch_out,ksize=5,stride=2,pad=2)
layers['bnorm1'] = L.BatchNormalization(ch_out)
super().__init__(**layers)

def __call__(self,x):
h = self.conv1(x)
h = self.bnorm1(h)
h = F.relu(h)

return h

多段階の畳み込みニューラルネットワークと関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 多段階の畳み込みニューラルネットワーク
C = ttrain.max() + 1
H1 = 16
H2 = 32
H3 = 64

NN = Chain(cnn1 = L.Convolution2D(ch,H1,ksize=1,stride=1,pad=0),
cnn2 = L.Convolution2D(H1,H1,ksize=3,stride=1,pad=0),
cnn3 = L.Convolution2D(H1,H1,ksize=5,stride=1,pad=0),
cnn4 = L.Convolution2D(H1,H2,ksize=1,stride=1,pad=0),
cnn5 = L.Convolution2D(H2,H2,ksize=3,stride=1,pad=0),
cnn6 = L.Convolution2D(H2,H2,ksize=5,stride=1,pad=0),
cnn7 = L.Convolution2D(H2,H3,ksize=1,stride=1,pad=0),
cnn8 = L.Convolution2D(H3,H3,ksize=3,stride=1,pad=0),
cnn9 = L.Convolution2D(H3,C,ksize=5,stride=1,pad=3),
bnorm1 = L.BatchNormalization(H1),
bnorm2 = L.BatchNormalization(H1),
bnorm3 = L.BatchNormalization(H1),
bnorm4 = L.BatchNormalization(H2),
bnorm5 = L.BatchNormalization(H2),
bnorm6 = L.BatchNormalization(H2),
bnorm7 = L.BatchNormalization(H3),
bnorm8 = L.BatchNormalization(H3))

def model(x):
h = NN.cnn1(x)
h = F.relu(h)
h = NN.bnorm1(h)
h = NN.cnn2(h)
h = F.relu(h)
h = NN.bnorm2(h)
h = NN.cnn3(h)
h = F.relu(h)
h = NN.bnorm3(h)
h = F.max_pooling_2d(h, 2)
h = NN.cnn4(h)
h = F.relu(h)
h = NN.bnorm4(h)
h = NN.cnn5(h)
h = F.relu(h)
h = NN.bnorm5(h)
h = NN.cnn6(h)
h = F.relu(h)
h = NN.bnorm6(h)
h = F.max_pooling_2d(h, 2)
h = NN.cnn7(h)
h = F.relu(h)
h = NN.bnorm7(h)
h = NN.cnn8(h)
h = F.relu(h)
h = NN.bnorm8(h)
h = NN.cnn9(h)
y = F.mean(h,axis=(2,3))

return y

GPUの設定を行います。

1
2
3
4
# GPUの設定
gpu_device = 0
cuda.get_device(gpu_device).use()
NN.to_gpu()

結果を一旦初期化します。

1
2
3
4
5
train_loss = []
train_acc = []
test_loss = []
test_acc = []
result = [train_loss,train_acc,test_loss,test_acc]

最適化手法にAdamを設定します。

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

関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def shift_labeled(labeled_data):
data, label = labeled_data

ch, Ny, Nx = data.shape
z_h = Ny * (2.0 * np.random.rand(1) - 1.0) * 0.3
z_v = Nx * (2.0 * np.random.rand(1) - 1.0) * 0.3
data = np.roll(data, int(z_h), axis=1)
data = np.roll(data, int(z_v), axis=2)

return data, label

def flip_labeled(labeled_data):
data, label = labeled_data
z = np.random.randint(2)
if z == 1:
data = data[:,::-1,:]
z = np.random.randint(2)
if z == 1:
data = data[:,:,::-1]

z = np.random.randint(2)
if z == 1:
data = data.transpose(0, 2, 1)
return data, label

def check_network(x, link):
print('input:', x.shape)
h = link(x)
print('output:', h.shape)

確率勾配法で最適化し、結果をグラフ化します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from tqdm import tqdm

nepoch = 100
batch_size = 128
train_iter = siter(train, batch_size)
with tqdm(total = nepoch) as pbar:
while train_iter.epoch < nepoch:
pbar.update(train_iter.is_new_epoch)
batch = train_iter.next()
batch = ds.TransformDataset(batch, flip_labeled)
xtrain,ttrain = con.concat_examples(batch)
data = cuda.to_gpu([xtrain,xtest,ttrain,ttest])
learning_classification(model,optNN,data,result,1)
if train_iter.is_new_epoch == 1:
show_graph(result[0],result[1],'loss function in training','step','loss function',0.0,4.0)
show_graph(result[2],result[3],'Accuracy in training and test','step','accuracy')
print(result[3][len(result[3])-1])

2、3時間実行しているのですが、進捗が48%でまだ時間がかかるので一旦ここまでの結果を確認しておきます。
誤差
正解率

なんとか6割の正解率を超えてきているでしょうか。ただここまで時間がかかるのと正解率が微妙にしか上昇していないのでなんらかの改善の余地はあると思います。

5時間20分かかってようやく終了しました。
誤差
正解率

最終的な正解率は73%ですか。。。まー実用性があるといってもいいような気がします。

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

ニューラルネットワークで株価予測

ニューラルネットワークで株価予測をしてみます。
まずは必要なモジュールをインポートします。

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

import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
from chainer import Variable, Chain, config

データ振り分け処理、グラフ表示、回帰分析用の関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# -------------- #
# 共通関数の定義 #
# -------------- #
# データ振り分け関数
def data_divide(Dtrain, D, xdata, tdata, shuffle='on'):
if shuffle == 'on':
index = np.random.permutation(range(D))
elif shuffle == 'off':
index = np.arange(D)
else:
print('error')
xtrain = xdata[index[0:Dtrain],:]
ttrain = tdata[index[0:Dtrain]]
xtest = xdata[index[Dtrain:D],:]
ttest = tdata[index[Dtrain:D]]
return xtrain, xtest, ttrain, ttest

# グラフ表示関数
def show_graph(result1, result2, title, xlabel, ylabel, ymin=0.0, ymax=1.0):
# 学習記録の表示(誤差)
Tall = len(result1)
plt.figure(figsize=(12, 8))
plt.plot(range(Tall), result1, label='train')
plt.plot(range(Tall), result2, label='test')
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.xlim([0, Tall])
plt.ylim(ymin, ymax)
plt.legend()
plt.show()

# 回帰関数の定義
def learning_regression(model, optNN, data, T=10):
train_loss = []
test_loss = []
for time in range(T):
config.train = True
optNN.target.cleargrads() # zerogradsより記憶容量の確保にいい
ytrain = model(data[0])
loss_train = F.mean_squared_error(ytrain, data[2])
loss_train.backward()
optNN.update()

config.train = False
ytest = model(data[1])
loss_test = F.mean_squared_error(ytest, data[3])

train_loss.append(loss_train.data)
test_loss.append(loss_test.data)
return train_loss, test_loss

株価予測の前に適当な関数で変換したデータを回帰分析してみます。
データを作成します。

1
2
3
4
# 時系列データ作成
M = 100
time_data = np.linspace(0.0, 10.0, M)
value_data = np.sin(time_data) + 2.0 * np.sin(2.0 * time_data)

直前2回分データを入力データとして、入力データとラベルデータを作成します。

1
2
3
4
5
6
7
8
9
10
11
12
# 直前2回分のデータを入力にする
N = 2
xdata = []
tdata = []
for k in range(N, M):
xdata.append(value_data[k - N:k])
tdata.append(value_data[k])
xdata = np.array(xdata).astype(np.float32)
tdata = np.array(tdata).reshape(M - N, 1).astype(np.float32)

# データの形を確認
D, N = xdata.shape

4層のニューラルネットワークを作成し、関数化します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 4層ニューラルネットワークを作成し、関数化
C = 1
H1 = 5
H2 = 5
H3 = 5
layers = {}
layers['l1'] = L.Linear(N, H1)
layers['l2'] = L.Linear(H1, H2)
layers['l3'] = L.Linear(H2, H3)
layers['l4'] = L.Linear(H3, C)
layers['bnorm1'] = L.BatchNormalization(H1)
layers['bnorm2'] = L.BatchNormalization(H2)
layers['bnorm3'] = L.BatchNormalization(H3)
NN = Chain(**layers)

def model(x):
h = NN.l1(x)
h = F.relu(h)
h = NN.bnorm1(h)

h = NN.l2(h)
h = F.relu(h)
h = NN.bnorm2(h)

h = NN.l3(h)
h = F.relu(h)
h = NN.bnorm3(h)

y = NN.l4(h)
return y

最適化手法を設定します。

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

入力データとラベルデータをそれぞれ訓練データとテストデータに振り分けます。
ここでは先頭3分の1のデータを訓練データとします。

1
2
3
4
5
6
7
8
# データ分割(3分の1を訓練データ、3分の2をテストデータとする)
Dtrain = D // 3

xtrain, xtest, ttrain, ttest = data_divide(Dtrain, D, xdata, tdata, 'off')

# print(xtrain.shape, xtest.shape, ttrain.shape, ttest.shape)
data = [xtrain, xtest, ttrain, ttest]
# result = [train_loss, test_loss]

回帰分析を行います。200回学習します。

1
train_loss, test_loss = learning_regression(model, optNN, data, 200)

学習結果と予測結果をグラフ表示します。

1
2
3
4
5
6
7
ytrain = model(xtrain).data
ytest = model(xtest).data
plt.figure(figsize=(12, 8))
plt.plot(time_data[0:Dtrain], ytrain, marker='x', linestyle='None') # 学習結果
plt.plot(time_data[Dtrain:D], ytest, marker='o', linestyle='None') # 予測結果
plt.plot(time_data[0:D - N], value_data[N:D])
plt.show()

予測結果
予測がうまくいってない箇所もありますが、十分な結果がでているようです。
(実行するごとに微妙に結果が変わります。ニューラルネットワークの個性ということでしょうか。)

次に予測用の株価データを準備します。
(株価の読み込み期間を変えるニューラルネットワークの学習処理でエラーになることがあります。
異常値エラーとのことですが、理由がよくわからないので今回は何回か試して問題のなかった[2005/01/01-2007/12/31]を分析期間としました。)

1
2
3
4
5
6
7
# 株価読み込み用モジュール
import pandas_datareader.data as web
import datetime as dt

start = dt.date(2005, 1, 1)
end = dt.date(2007, 12, 31)
web_data = web.DataReader('AMZN', 'yahoo', start, end)

読み込んだ株価データをグラフ化します。

1
2
3
plt.figure(figsize=(12, 8))
plt.plot(web_data['Close'])
plt.show()

株価データ

株価データを入力データとラベルデータに振り分けます。
今回は直前5回分のデータを入力データとします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
value_data = web_data['Close']
Total = len(value_data)

N = 5 # 直前何回分を入力データとするか
xdata = [] # 入力データ
tdata = [] # ラベルデータ
for k in range(N, Total):
xdata.append(value_data[k - N:k])
tdata.append(value_data[k])

xdata = np.array(xdata).astype(np.float32)
tdata = np.array(tdata).reshape(len(tdata), 1).astype(np.float32)

# 入力データの形を確認
D, N = xdata.shape

4層ニューラルネットワークを作成し、関数化します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 4層ニューラルネットワークを作成し、関数化
C = 1
H1 = 5
H2 = 5
H3 = 5
layers = {}
layers['l1'] = L.Linear(N, H1)
layers['l2'] = L.Linear(H1, H2)
layers['l3'] = L.Linear(H2, H3)
layers['l4'] = L.Linear(H3, C)
layers['bnorm1'] = L.BatchNormalization(H1)
layers['bnorm2'] = L.BatchNormalization(H2)
layers['bnorm3'] = L.BatchNormalization(H3)
NN = Chain(**layers)

def model(x):
h = NN.l1(x)
h = F.relu(h)
h = NN.bnorm1(h)

h = NN.l2(h)
h = F.relu(h)
h = NN.bnorm2(h)

h = NN.l3(h)
h = F.relu(h)
h = NN.bnorm3(h)

y = NN.l4(h)
return y

最適化手法を設定し、訓練データとテストデータに振り分けます。
訓練データとテストデータは半分ずつに分けます。

1
2
3
4
5
6
7
8
9
10
# 最適化手法の設定
optNN = Opt.MomentumSGD()
optNN.setup(NN)

# データ分割
Dtrain = D // 2

xtrain, xtest, ttrain, ttest = data_divide(Dtrain, D, xdata, tdata, 'off')

data = [xtrain, xtest, ttrain, ttest]

回帰分析を行います。今回は学習回数を1000回に設定しました。

1
train_loss, test_loss = learning_regression(model, optNN, data, 1000)

誤差と予測結果を表示します。

1
2
3
4
5
6
7
8
9
10
11
# 誤差の表示
show_graph(train_loss, test_loss, 'loss function', 'step', 'loss function', ymin=0.0, ymax=10.0)

ytrain = model(xtrain).data
ytest = model(xtest).data
time_data = np.arange(Total - N)
plt.figure(figsize=(12, 8))
plt.plot(time_data[0:Dtrain], ytrain, marker='x', linestyle='None') # 学習結果
plt.plot(time_data[Dtrain:Total], ytest, marker='o', linestyle='None') # 予測結果
plt.plot(time_data[0:Total - N], value_data[N:Total])
plt.show()

誤差
株価予測結果
いまいちな結果です。やはり株価を予測するのは無理なのでしょうか。

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

非線形関数一覧

ニューラルネットワークを構築する際に、どの非線形関数を選択するかは大事なポイントとなります。
そこでどんな非線形関数があるかそれぞれグラフ化して確認します。

まずは必要なモジュールのインストールとx軸用に等間隔の数値を用意します。

1
2
3
4
5
6
7
8
# モジュールのインポート
import chainer.functions as F
import numpy as np
import matplotlib.pyplot as plt

# 等間隔の数値を用意する(x軸用)
D = 100
ndata = np.linspace(-5.0, 5.0, D)

非線形関数をグラフ化するコードは下記の通りです。
1行目のF.absoluteの箇所をそれぞれの非線形関数に置き換えて実行していきます。

1
2
3
ydata = F.absolute(ndata).data   # 非線形関数を変えながら実行する。
plt.plot(ndata, ydata)
plt.show()
非線形関数一覧
F.absolute F.add F.arccos
F.arcsin F.arctan F.broadcast
F.ceil F.clipped_relu F.cos
F.cosh F.cumprod F.cumsum
F.digamma F.dropout F.elu
F.erf F.erfc F.erfcinv
F.erfcx F.erfinv F.exp
F.expm1 F.fix F.flatten
F.flipud F.floor F.hard_sigmoid
F.identity F.leaky_relu F.lgamma
F.log F.log10 F.log1p
F.log2 F.log_ndtr F.ndtr
F.ndtri F.relu F.rrelu
F.rsqrt F.selu F.sigmoid
F.sign F.sin F.sinh
F.softplus F.sqrt F.square
F.squeeze F.tan F.tanh
F.transpose

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

ニューラルネットワークで回帰分析

ニューラルネットワークで回帰分析をします。
まずは必要なモジュールのインポートと共通関数の定義をします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# モジュールのインポート
import numpy as np
import matplotlib.pyplot as plt

import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
from chainer import Variable, Chain, config, cuda

# -------------- #
# 共通関数の定義 #
# -------------- #
# データ振り分け関数
def data_divide(Dtrain, D, xdata, tdata):
index = np.random.permutation(range(D))
xtrain = xdata[index[0:Dtrain],:]
ttrain = tdata[index[0:Dtrain]]
xtest = xdata[index[Dtrain:D],:]
ttest = tdata[index[Dtrain:D]]
return xtrain, xtest, ttrain, ttest

# グラフ表示関数
def show_graph(result1, result2, title, xlabel, ylabel, ymin=0.0, ymax=1.0):
# 学習記録の表示(誤差)
Tall = len(result1)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), result1, label='train')
plt.plot(range(Tall), result2, label='test')
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.xlim([0, Tall])
plt.ylim(ymin, ymax)
plt.legend()
plt.show()

# 回帰関数の定義
def learning_regression(model, optNN, data, T=10):
train_loss = []
test_loss = []
for time in range(T):
config.train = True
optNN.target.cleargrads() # zerogradsより記憶容量の確保にいい
ytrain = model(data[0])
loss_train = F.mean_squared_error(ytrain, data[2])
loss_train.backward()
optNN.update()

config.train = False
ytest = model(data[1])
loss_test = F.mean_squared_error(ytest, data[3])

train_loss.append(loss_train.data)
test_loss.append(loss_test.data)
return train_loss, test_loss

データを準備します。
x軸を-5から5までの等間隔100個分準備し、その後適当な関数でy軸に変換します。

1
2
3
4
5
6
7
8
# 等間隔の数値を用意する(x軸用)
D = 100
ndata = np.linspace(-5.0, 5.0, D)

# 関数でデータ作成
N = 1
xdata = ndata.reshape(D, N).astype(np.float32) # 2次元配列に変換(100行1列)
tdata = (np.sin(ndata) + np.sin(2.0 * ndata)).reshape(D, N).astype(np.float32)

作成したデータを視覚化します。

1
2
3
# 作成したデータをグラフ表示
plt.plot(xdata, tdata)
plt.show()

結果

ニューラルネットワークを作成し関数化します。
非線形関数はreluを使用します。

1
2
3
4
5
6
7
8
9
10
11
# 回帰のニューラルネットワークを作成し、関数化
C = 1 # 出力数
H = 20 # 中間層の数
NN = Chain(l1=L.Linear(N, H), l2=L.Linear(H, C), bnorm1=L.BatchNormalization(H))

def model(x):
h = NN.l1(x)
h = F.relu(h)
h = NN.bnorm1(h)
y = NN.l2(h)
return y

参考までに学習前のニューラルネットワークの状態を確認します。
まずは重さです。

1
2
# ニューラルネットワークの状態調査
print(NN.l1.W.data) # 最初はでたらめな重さ

結果
初期値としてでたらめな重さが設定されています。

次に勾配を表示してみます。

1
2
# ニューラルネットワークの勾配表示
print(NN.l1.W.grad) # 全て未計算(Not a Number)

結果
こちらはnan(Not a number)が設定されています。

最適化手法を設定し学習を行います。

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

# 学習結果記録用
train_loss = []
test_loss = []

Dtrain = D // 2 # 訓練データ数
xtrain, xtest, ttrain, ttest = data_divide(Dtrain, D, xdata, tdata) # データ分割
data = [xtrain, xtest, ttrain, ttest]
result = [train_loss, test_loss]

train_loss, test_loss = learning_regression(model, optNN, data, 1000)

show_graph(train_loss, test_loss, 'loss function', 'step', 'loss function', 0.0, 3.0)

学習結果の誤差は下記のようになります。
結果

次第に減少してはいますが0.2から0.3付近で改善しなくなっています。

回帰の結果を比較します。

1
2
3
4
5
6
7
8
# 回帰の結果を比較する
config.train = False
ytrain = model(xtrain).data
ytest = model(xtest).data
plt.plot(xtrain, ytrain, marker='x', linestyle='None')
plt.plot(xtest, ytest, marker='o', linestyle='None')
plt.plot(xdata, tdata)
plt.show()

結果
結果はぜんぜん合っていないようです。なんらかの改善策をとる必要がありそうです。

非線形関数調整してみます。
今回使ったreluの形は下記のようになっており、今回学習するデータには合わないようです。

1
2
3
4
# F.reluの形を調べる
ydata = F.relu(xdata).data
plt.plot(xdata, ydata)
plt.show()

結果

別の非線形関数sigmoidの形を調べると、reluよりは今回学習するデータに近いようにみえます。

1
2
3
4
# F.sigmoidの形を調べる
ydata = F.sigmoid(xdata).data
plt.plot(xdata, ydata)
plt.show()

結果

非線形関数をreluからsigmoidに変更し、同じように学習・結果表示を行います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 回帰のニューラルネットワークを作成し、関数化
C = 1 # 出力数
H = 20 # 中間層の数
NN = Chain(l1=L.Linear(N, H), l2=L.Linear(H, C), bnorm1=L.BatchNormalization(H))

def model(x):
h = NN.l1(x)
h = F.sigmoid(h) # シグモイドに変更
h = NN.bnorm1(h)
h = NN.l2(h)
return h

# 最適化手法の設定
optNN = Opt.MomentumSGD()
optNN.setup(NN)

# 学習結果記録用
train_loss = []
test_loss = []

Dtrain = D // 2 # 訓練データ数
xtrain, xtest, ttrain, ttest = data_divide(Dtrain, D, xdata, tdata)
data = [xtrain, xtest, ttrain, ttest]
result = [train_loss, test_loss]

train_loss, test_loss = learning_regression(model, optNN, data, 1000)

show_graph(train_loss, test_loss, 'loss function', 'step', 'loss function', 0.0, 3.0)

結果
誤差に関しては2つの非線形関数でそれほど結果が変わりませんでした。

回帰の結果を確認してみます。

1
2
3
4
5
6
7
8
# 回帰の結果を比較する
config.train = False
ytrain = model(xtrain).data
ytest = model(xtest).data
plt.plot(xtrain, ytrain, marker='x', linestyle='None')
plt.plot(xtest, ytest, marker='o', linestyle='None')
plt.plot(xdata, tdata)
plt.show()

結果
reluよりはsigmoidを使った方がだいぶましになったように思われます。

ただまだまだ納得いく結果ではありません。
ほかの非線形関数を調べたところsin関数というのがありました。

1
2
3
4
# F.sinの形を調べる
ydata = F.sin(xdata).data
plt.plot(xdata, ydata)
plt.show()

結果
この関数はかなり今回回帰分析を行いたいグラフと形が似ているようです。

非線形関数をsinに変更してまたまた回帰分析を行います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 回帰のニューラルネットワークを作成し、関数化
C = 1 # 出力数
H = 20 # 中間層の数
NN = Chain(l1=L.Linear(N, H), l2=L.Linear(H, C), bnorm1=L.BatchNormalization(H))

def model(x):
h = NN.l1(x)
h = F.sin(h) # サインに変更
h = NN.bnorm1(h)
h = NN.l2(h)
return h

# 最適化手法の設定
optNN = Opt.MomentumSGD()
optNN.setup(NN)

# 学習結果記録用
train_loss = []
test_loss = []

Dtrain = D // 2 # 訓練データ数
xtrain, xtest, ttrain, ttest = data_divide(Dtrain, D, xdata, tdata)
data = [xtrain, xtest, ttrain, ttest]
result = [train_loss, test_loss]

train_loss, test_loss = learning_regression(model, optNN, data, 1000)

show_graph(train_loss, test_loss, 'loss function', 'step', 'loss function', 0.0, 3.0)

結果
誤差は十分に下がることが確認できました。

回帰の結果も確認してみます。

1
2
3
4
5
6
7
8
# 回帰の結果を比較する
config.train = False
ytrain = model(xtrain).data
ytest = model(xtest).data
plt.plot(xtrain, ytrain, marker='x', linestyle='None')
plt.plot(xtest, ytest, marker='o', linestyle='None')
plt.plot(xdata, tdata)
plt.show()

結果
完璧な結果がでました。
ただ今回はグラフの見た目でそれに近い非線形関数を使うという少しずるい手順を踏んでしましました。

relu関数のまま結果を改善することはできないのでしょうか。
試しにニューラルネットワークの階層を増やしてみました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 4層のニューラルネットワークを作成し関数化
C = 1 # 出力の種類
H1 = 5
H2 = 5
H3 = 5
layers = {}
layers['l1'] = L.Linear(N, H1)
layers['l2'] = L.Linear(H1, H2)
layers['l3'] = L.Linear(H2, H3)
layers['l4'] = L.Linear(H3, C)
layers['bnorm1'] = L.BatchNormalization(H1)
layers['bnorm2'] = L.BatchNormalization(H2)
layers['bnorm3'] = L.BatchNormalization(H3)
NN = Chain(**layers)

def model(x):
h = NN.l1(x)
h = F.relu(h)
h = NN.bnorm1(h)
h = NN.l2(h)
h = F.relu(h)
h = NN.bnorm2(h)
h = NN.l3(h)
h = F.relu(h)
h = NN.bnorm3(h)
y = NN.l4(h)
return y

# 最適化手法の設定
optNN = Opt.MomentumSGD()
optNN.setup(NN)

# 学習結果記録用
train_loss = []
test_loss = []

Dtrain = D // 2 # 訓練データ数
xtrain, xtest, ttrain, ttest = data_divide(Dtrain, D, xdata, tdata)
data = [xtrain, xtest, ttrain, ttest]
result = [train_loss, test_loss]

train_loss, test_loss = learning_regression(model, optNN, data, 1000)

show_graph(train_loss, test_loss, 'loss function', 'step', 'loss function', 0.0, 3.0)

# 回帰の結果を比較する
config.train = False
ytrain = model(xtrain).data
ytest = model(xtest).data
plt.plot(xtrain, ytrain, marker='x', linestyle='None')
plt.plot(xtest, ytest, marker='o', linestyle='None')
plt.plot(xdata, tdata)
plt.show()

結果は以下の通りです。
結果
誤差は十分に下がっています。

結果
回帰の結果もなかなか良いものがでました。回帰分析の結果を改善するためには非線形関数を変更する方法とは別に、ニューラルネットワークの階層を深くするのも有効な手段だと言えそうです。

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

GPUを使ってFashion-MNIST

Google ColaboratoryではGPUを簡単に使うことができます。
メニューから[ランタイム]->[ランタイムのタイプを変更]で表示されるダイアログから[ハードウェアアクセラレータ]でGPUを選択するだけです。

またソースもGPU用にいくつか変更しなくてはなりません。
コメントに # GPU対応対応 を追加しておきましたので参考にしてください。

まずは必要なモジュールをインポートし、共通関数(グラフ表示、学習)を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# モジュールのインポート
import numpy as np
import matplotlib.pyplot as plt

import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
from chainer import Variable, Chain, config, cuda

# -------------- #
# 共通関数の定義 #
# -------------- #
# グラフ表示関数
def show_graph(result1, result2, title, xlabel, ylabel, ymin=0.0, ymax=1.0):
# 学習記録の表示(誤差)
Tall = len(result1)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), result1, label='train')
plt.plot(range(Tall), result2, label='test')
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.xlim([0, Tall])
plt.ylim(ymin, ymax)
plt.legend()
plt.show()

# 学習用関数
def learning(model, optNN, data, T=10):
# 学習記録用エリア(返値)
train_loss = []
train_acc = []
test_loss = []
test_acc = []

data = cuda.to_gpu(data, gpu_device) # GPU対応
for time in range(T):
# 学習
config.train = True
optNN.target.cleargrads() # zerogradsより記憶容量の確保にいい
ytrain = model(data[0])
loss_train = F.softmax_cross_entropy(ytrain, data[2])
acc_train = F.accuracy(ytrain, data[2])
loss_train.backward()
optNN.update()

# テスト(検証)
config.train = False
ytest = model(data[1])
loss_test = F.softmax_cross_entropy(ytest, data[3])
acc_test = F.accuracy(ytest, data[3])

# 結果の記録
train_loss.append(cuda.to_cpu(loss_train.data)) # GPU対応
train_acc.append(cuda.to_cpu(acc_train.data)) # GPU対応
test_loss.append(cuda.to_cpu(loss_test.data)) # GPU対応
test_acc.append(cuda.to_cpu(acc_test.data)) # GPU対応
return train_loss, test_loss, train_acc, test_acc

GPU利用の設定を行い、ニューラルネットワークの作成と関数化を行います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# GPUの利用
gpu_device = 0 # GPU対応
cuda.get_device(gpu_device).use() # GPU対応
NN.to_gpu() # GPU対応

# ニューラルネットワークの作成
C = ttrain.max() + 1
NN = Chain(l1=L.Linear(N, 400).to_gpu() , l2=L.Linear(400, C).to_gpu(), # GPU対応
bnorm1=L.BatchRenormalization(400).to_gpu()) # GPU対応

def model(x):
h = NN.l1(x)
h = F.relu(h)
h = NN.bnorm1(h) # バッチの規格化(途中結果の整理)
h = NN.l2(h)
return h

ファッションMNISTのデータセットを読み込み訓練データとテストデータに振り分けます。

1
2
3
4
# fashion_mnistのデータセット読込んでデータ分割
train, test = ds.get_fashion_mnist()
xtrain, ttrain = con.concat_examples(train) # 訓練データ
xtest, ttest = con.concat_examples(test) # テスト(検証)データ

読み込んだデータを2つほど視覚化してみます。

1
2
3
4
5
6
7
# データの視覚化
Dtrain, N = xtrain.shape
print(Dtrain, N)
plt.imshow(xtrain[0,:].reshape(28, 28))
plt.show()
plt.imshow(xtrain[10,:].reshape(28, 28))
plt.show()

結果

最適化手法を設定し、学習させてから、誤差と正解率のグラフを表示します。

1
2
3
4
5
6
7
8
9
10
# 最適化手法の設定
optNN = Opt.MomentumSGD()
optNN.setup(NN)

# 学習を実行
train_loss, test_loss, train_acc, test_acc = learning(model, optNN, [xtrain, xtest, ttrain, ttest], 100)

# 学習結果をグラフ表示
show_graph(train_loss, test_loss, 'loss function', 'step', 'lossfunction', 0.0, 4.0)
show_graph(train_acc, test_acc, 'accuracy', 'step', 'accuracy')

誤差
正解率

正解率は8割程度となりました。悪くない数字かもしれませんがまだまだ改善の余地はありそうです。

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

バッチ規格化とバッチ再規格化の比較

ニューラルネットワークが複雑になり学習がうまくいかない場合にはバッチ規格化を行うといいです。
バッチ規格化を使うと途中の結果を整理しながら学習をすることができます。
またバッチ再規格化というものがあり、バッチ規格よりもよい性能を示すとのことなのでこの2つを比較してみます。

まずchainerよりMNISTデータを読み込みます。

1
2
3
4
import chainer.datasets as ds

# MNISTデータの読み込み
train, test = ds.get_mnist()

結果

次に読み込んだデータを訓練データとテストデータに分けます。
chainerにはconverくらすにconcat_examplesというメソッドがあり容易にデータの振り分けを行うことができます。

1
2
3
4
5
6
7
8
# 入力データとラベルデータの設定
import chainer.dataset.convert as con
# 訓練データとテストデータ分ける
xtrain, ttrain = con.concat_examples(train)
xtest, ttest = con.concat_examples(test)

print(xtrain.shape, ttrain.shape)
print(xtest.shape, ttest.shape)

結果

振り分けたデータの1つをmatplotlibを使って表示してみます。

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt

# データの形とMNIST originalの1列を表示する
Dtrain, N = xtrain.shape
plt.imshow(xtrain[0,:].reshape(28, 28))
plt.show()

print('ラベル(答え)=>', ttrain[0])

結果

本題のバッチ規格化を設定します。
まずニューラルネットワークを作成する際にbnorm1=L.BatchNormalization(400)(9行目)を引数に渡し、またニューラルネットワークの関数でNN.bnorm1関数(15行目)をはさみます。
バッチ再規格をする場合は、BatchNormalizationの代わりにBatchRenormalization(10行目)を指定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
from chainer import Variable, Chain, config

# バッチ規格化(途中の結果を整理整頓しながら学習する)
C = ttrain.max() + 1
NN = Chain(l1=L.Linear(N, 400), l2=L.Linear(400, C),
bnorm1=L.BatchNormalization(400))
# bnorm1=L.BatchRenormalization(400)) #バッチの再規格化(Chainer version4以降)

def model(x):
h = NN.l1(x)
h = F.relu(h)
h = NN.bnorm1(h) # バッチの規格化(途中結果の整理)
h = NN.l2(h)
return h

次に学習用関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 学習用関数
def learning(model, optNN, data, T=50):
# 学習記録用エリア(返値)
train_loss = []
train_acc = []
test_loss = []
test_acc = []

for time in range(T):
# 学習
config.train = True
optNN.target.zerograds()
ytrain = model(data[0])
loss_train = F.softmax_cross_entropy(ytrain, data[2])
acc_train = F.accuracy(ytrain, data[2])
loss_train.backward()
optNN.update()

# テスト(検証)
config.train = False
ytest = model(data[1])
loss_test = F.softmax_cross_entropy(ytest, data[3])
acc_test = F.accuracy(ytest, data[3])

# 結果の記録
train_loss.append(loss_train.data)
train_acc.append(acc_train.data)
test_loss.append(loss_test.data)
test_acc.append(acc_test.data)
return train_loss, test_loss, train_acc, test_acc

グラフ表示用関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# グラフ表示関数
def show_graph(result1, result2, title, xlabel, ylabel, ymin=0.0, ymax=1.0):
# 学習記録の表示(誤差)
Tall = len(result1)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), result1, label='train')
plt.plot(range(Tall), result2, label='test')
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.xlim([0, Tall])
plt.ylim(ymin, ymax)
plt.legend()
plt.show()

最適化手法を設定し、学習を実行したのち結果をグラフ表示します。

1
2
3
4
5
6
7
8
9
10
# 最適化手法の設定
optNN = Opt.MomentumSGD()
optNN.setup(NN)

# 学習を実行
train_loss, test_loss, train_acc, test_acc = learning(model, optNN, [xtrain, xtest, ttrain, ttest])

# 学習結果をグラフ表示
show_graph(train_loss, test_loss, 'loss function', 'step', 'lossfunction', 0.0, 4.0)
show_graph(train_acc, test_acc, 'accuracy', 'step', 'accuracy')

バッチ規格化とバッチ再規格化をそれぞれ実行した結果は下記の通りです。

バッチ規格化 バッチ再規格化
誤差 誤差
正解率 正解率

ほぼ同じ結果で明確な違いがないような気がします。。。

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

ニューラルネットワークで画像認識

ニューラルネットワークで手書き文字の認識をします。有名なMNIST(エムニスト)です。
まず必要なモジュールをインポートします。

1
2
3
4
# モジュール読み込み
import numpy as np
import matplotlib.pyplot as plt
import sklearn.datasets as ds

MNISTデータを読み込み、確認のために画像として表示してみます。

1
2
3
4
5
6
7
8
9
10
11
# MNISTデータの読み込み
MNIST = ds.load_digits()
xdata = MNIST.data.astype(np.float32)
tdata = MNIST.target.astype(np.int32)

# 配列の形を確認
D, N = xdata.shape

# 画像データの表示
plt.imshow(xdata[0,:].reshape(8, 8)) # 1列に並んだデータを8行8列に変換
plt.show()

結果

データ分割関数を定義し、実行します。今回、訓練データと学習データはちょうど半分ずつにしています。

1
2
3
4
5
6
7
8
9
10
11
# データ分割関数
def data_divide(Dtrain, D, xdata, tdata):
index = np.random.permutation(range(D))
xtrain = xdata[index[0:Dtrain],:]
ttrain = tdata[index[0:Dtrain]]
xtest = xdata[index[Dtrain:D],:]
ttest = tdata[index[Dtrain:D]]
return xtrain, xtest, ttrain, ttest

Dtrain = D // 2
xtrain, xtest, ttrain, ttest = data_divide(Dtrain, D, xdata, tdata)

chainerの宣言をします。

1
2
3
4
5
# chainerの宣言
import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
from chainer import Variable, Chain, config

ニューラルネットワークを作成し、ニューラルネットワークの関数を定義します。
また誤差と正解率の遷移を記録する変数を用意します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2層のニューラルネットワークを作成
C = tdata.max() + 1
NN = Chain(l1=L.Linear(N, 20), l2=L.Linear(20, C))

# 2層ニューラルネットワークの関数化
def model(x):
h = NN.l1(x)
h = F.relu(h)
y = NN.l2(h)
return y

# 最適化手法の設定
optNN = Opt.MomentumSGD()
optNN.setup(NN)

# 学習記録用エリア
train_loss = []
train_acc = []
test_loss = []
test_acc = []

最適化を行います。今回は200回学習を行います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 最適化
T = 200
for time in range(T):
# 学習
config.train = True
optNN.target.zerograds()
ytrain = model(xtrain)
loss_train = F.softmax_cross_entropy(ytrain, ttrain)
acc_train = F.accuracy(ytrain, ttrain)
loss_train.backward()
optNN.update()

# テスト(検証)
config.train = False
ytest = model(xtest)
loss_test = F.softmax_cross_entropy(ytest, ttest)
acc_test = F.accuracy(ytest, ttest)

# 結果の記録
train_loss.append(loss_train.data)
test_loss.append(loss_test.data)
train_acc.append(acc_train.data)
test_acc.append(acc_test.data)

グラフ表示用の関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# グラフ表示関数
def show_graph(result1, result2, title, xlabel, ylabel, ymin=0.0, ymax=1.0):
# 学習記録の表示(誤差)
Tall = len(result1)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), result1, label='train')
plt.plot(range(Tall), result2, label='test')
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.xlim([0, Tall])
plt.ylim(ymin, ymax)
plt.legend()
plt.show()

誤差と正解率の遷移をグラフ表示します。

1
2
3
# 誤差と正解率をグラフ表示
show_graph(train_loss, test_loss, 'loss function', 'step', 'loss_function', 0.0, 4.0)
show_graph(train_acc, test_acc, 'accuracy', 'step', 'accuracy')

結果

順調に誤差が減少し、正解率が上昇していることが見てとれます。
ただ正解率が9割そこそこなのが少々不満です。
非線形関数や最適化手法を変えて改善する余地はありそうです。

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

ニューラルネットワークの最適化方法を比較

いくつかある最適化方法のうち下記の3つを比較します。

  • SGD
  • MomentumSGD
  • Adam

まずは必要なモジュールをインポートします。

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

import numpy as np
import matplotlib.pyplot as plt
import sklearn.datasets as ds

アヤメデータを読み込み、訓練データと学習データに分けます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# アヤメデータを読み込む
Iris = ds.load_iris()
xdata = Iris.data.astype(np.float32)
tdata = Iris.target.astype(np.int32)

D, N = xdata.shape # D=150, N=4

# 訓練データとテストデータに分ける
Dtrain = D // 2
index = np.random.permutation(range(D))
xtrain = xdata[index[0:Dtrain],:]
ttrain = tdata[index[0:Dtrain]]
xtest = xdata[index[Dtrain:D],:]
ttest = tdata[index[Dtrain:D]]

グラフ表示関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# グラフ表示関数
def show_graph():
# 学習記録の表示(誤差)
Tall = len(train_loss)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), train_loss, label='train_loss')
plt.plot(range(Tall), test_loss, label='test_loss')
plt.title('loss function in training and test')
plt.xlabel('step')
plt.ylabel('loss function')
plt.xlim([0, Tall])
plt.ylim(0, 4)
plt.legend()
plt.show()

# 学習記録の表示(正解率)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), train_acc, label='train_acc')
plt.plot(range(Tall), test_acc, label='test_acc')
plt.title('accuracy in training and test')
plt.xlabel('step')
plt.ylabel('accuracy')
plt.xlim([0, Tall])
plt.ylim(0, 1.0)
plt.legend()
plt.show()

学習用関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 学習用関数
def training(T):
for time in range(T):
config.train = True
optNN.target.zerograds()
ytrain = model(xtrain)
loss_train = F.softmax_cross_entropy(ytrain, ttrain)
acc_train = F.accuracy(ytrain, ttrain)
loss_train.backward()
optNN.update()

config.train = False
ytest = model(xtest)
loss_test = F.softmax_cross_entropy(ytest, ttest)
acc_test = F.accuracy(ytest, ttest)

train_loss.append(loss_train.data)
train_acc.append(acc_train.data)
test_loss.append(loss_test.data)
test_acc.append(acc_test.data)

最後に最適化方法を変えながら学習・結果表示を行います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 最適化方法を変えながら学習・結果表示を行う
for optNN in [Opt.SGD(), Opt.MomentumSGD(), Opt.Adam()]:
# 3層のニューラルネットワークを作成
C = np.max(tdata) + 1
NN = Chain(l1=L.Linear(N, 3), l2=L.Linear(3, 3), l3=L.Linear(3, C))

# 3層ニューラルネットワークの関数化
def model(x):
h = NN.l1(x)
h = F.sigmoid(h)
#h = F.relu(h)
h = NN.l2(h)
h = F.sigmoid(h)
#h = F.relu(h)
y = NN.l3(h)
return y

# 最適化手法を設定
# optNN = Opt.SGD()
# optNN = Opt.MomentumSGD()
# optNN = Opt.Adam()
optNN.setup(NN)

# 学習の記録用
train_loss = []
train_acc = []
test_loss = []
test_acc = []

training(3000) # 引数は学習回数

show_graph() # グラフ表示

実行結果は比較しやすいように表形式にまとめました。

SGD MomentumSGD Adam
初歩的な最適化 勢いをつけた最適化 適応的モーメント勾配法
誤差 誤差 誤差
正解率 正解率 正解率

(適応的勾配法は、勾配の大きさが小さくなってくると勾配に従って調整する大きさを多くします。)

非線形関数にReLUを使用すると実行結果が安定しなかった(すぐ最適化されたりまったくされなかったり・・・)のでシグモイドを使用しています。

今回のケースではAdamが一番はやく正解率が安定しています。
最適化方法によってだいぶ結果が変わるので、データやケースによって最適化方法をいろいろ試すしかなさそうです。。。

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

ニューラルネットワークでアヤメ分類

アヤメデータの分類をニューラルネットワークで行います。
まずは必要なモジュールをインポートします。

1
2
3
import numpy as np
import matplotlib.pyplot as plt
import sklearn.datasets as ds

アヤメデータを読み込みます。

1
2
3
4
5
6
# アヤメデータを読み込む
Iris = ds.load_iris()
xdata = Iris.data.astype(np.float32) # アヤメデータの特徴量
tdata = Iris.target.astype(np.int32) # アヤメの種類(答え)

D, N = xdata.shape # D=150, N=4 # 4種類の特徴量が150データ分

読み込んだデータを訓練データとテストデータに分けます。半分ずつにします。

1
2
3
4
5
6
7
# 訓練データとテストデータに分ける
Dtrain = D // 2 # 75
index = np.random.permutation(range(D)) # ランダムなインデックスを作成
xtrain = xdata[index[0:Dtrain],:] # 訓練データの入力
ttrain = tdata[index[0:Dtrain]] # 訓練データのラベル(正解)
xtest = xdata[index[Dtrain:D],:] # テストデータの入力
ttest = tdata[index[Dtrain:D]] # テストデータのラベル(正解)

Chainerを使えるように宣言します。

1
2
3
4
5
# Chainerの宣言
import chainer.optimizers as Opt
import chainer.functions as F
import chainer.links as L
from chainer import Variable, Chain, config

3層のニューラルネットワークを作成します。

1
2
3
# 3層のニューラルネットワークを作成
C = np.max(tdata) + 1
NN = Chain(l1=L.Linear(N, 3), l2=L.Linear(3, 3), l3=L.Linear(3, C))

3層ニューラルネットワークを関数化します。

①ニューラルネットワークの関数化
1
2
3
4
5
6
7
8
# 3層ニューラルネットワークの関数化
def model(x):
h = NN.l1(x)
h = F.sigmoid(h)
h = NN.l2(h)
h = F.sigmoid(h)
y = NN.l3(h)
return y

最適化手法を設定します。

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

学習結果を記録する変数を定義します。

1
2
3
4
5
# 学習の記録用
train_loss = []
train_acc = []
test_loss = []
test_acc = []

学習処理を実装します。学習回数は1000回です。

③学習処理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
T = 1000
for time in range(T):
config.train = True
optNN.target.zerograds()
ytrain = model(xtrain)
loss_train = F.softmax_cross_entropy(ytrain, ttrain)
acc_train = F.accuracy(ytrain, ttrain)
loss_train.backward()
optNN.update()

config.train = False
ytest = model(xtest)
loss_test = F.softmax_cross_entropy(ytest, ttest)
acc_test = F.accuracy(ytest, ttest)

train_loss.append(loss_train.data)
train_acc.append(acc_train.data)
test_loss.append(loss_test.data)
test_acc.append(acc_test.data)

結果の誤差をグラフ表示します。

④誤差のグラフ表示
1
2
3
4
5
6
7
8
9
10
11
12
# 学習記録の表示(誤差)
Tall = len(train_loss)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), train_loss, label='train_loss')
plt.plot(range(Tall), test_loss, label='test_loss')
plt.title('loss function in training and test')
plt.xlabel('step')
plt.ylabel('loss function')
plt.xlim([0, Tall])
plt.ylim(0, 4)
plt.legend()
plt.show()

誤差
ほとんど変わっていないようにみえます。
正解率もグラフ化します。

⑤正解率のグラフ表示
1
2
3
4
5
6
7
8
9
10
11
# 学習記録の表示(正解率)
plt.figure(figsize=(8, 6))
plt.plot(range(Tall), train_acc, label='train_acc')
plt.plot(range(Tall), test_acc, label='test_acc')
plt.title('accuracy in training and test')
plt.xlabel('step')
plt.ylabel('accuracy')
plt.xlim([0, Tall])
plt.ylim(0, 1.0)
plt.legend()
plt.show()

正解率
ぜんぜん正解率があがりません。あきらめずにもう10,000回実行してみます。
先ほどの1,000回と合わせてトータルで11,000回実行することになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
T = 10000   # 学習回数を10倍に変更
for time in range(T):
config.train = True
optNN.target.zerograds()
ytrain = model(xtrain)
loss_train = F.softmax_cross_entropy(ytrain, ttrain)
acc_train = F.accuracy(ytrain, ttrain)
loss_train.backward()
optNN.update()

config.train = False
ytest = model(xtest)
loss_test = F.softmax_cross_entropy(ytest, ttest)
acc_test = F.accuracy(ytest, ttest)

train_loss.append(loss_train.data)
train_acc.append(acc_train.data)
test_loss.append(loss_test.data)
test_acc.append(acc_test.data)

④誤差のグラフ表示と⑤正解率のグラフ表示を再び実行します。
誤差
誤差は緩やかながらだんだん減少しているようです。
正解率
正解率は2段階にわけて正解率がぐんとあがることがあり、最終的にはほぼ100%の正解率になってます。あきらめずに続けるって大切ですね。

しかしもっと効率よく学習する方法があります。
それは非線形変換をシグモイドからReLUに変更することです。複雑なニューラルネットワークを利用するときはReLUの方がよいとのことです。
①ニューラルネットワークの関数化を下記のように変更します。

①ニューラルネットワークの関数化を変更
1
2
3
4
5
6
7
8
# 非線形変換をシグモイドからReLUに変更
def model(x):
h = NN.l1(x)
h = F.relu(h)
h = NN.l2(h)
h = F.relu(h)
y = NN.l3(h)
return y

ニューラルネットワークをもう一度生成し、最適化手法を再設定、学習の記録用データをリセットしたあとに、③学習処理(1,000回)と④誤差のグラフ表示、⑤正解率のグラフ表示を実行してみます。

誤差

正解率
確かにシグモイドよりReLUを使った方が早く誤差、正解率ともに改善していることが分かります。

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

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

ニューラルネットワークでデータの分類をしてみます。
まずは、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で動作確認しています。)