2017/04/11
0:00

TensorFlowによるニューラルネットワーク(誤差値の推移と過学習)



みなさんこんにちは、人工知能ラボの助手です。

今日は、前回の記事でお話しした、ニューラルネットワーク
のプログラムを使って、実際にデータを用いて学習の様子を
見ていきましょう。



今回使用するデータですが、エクセルを使って生成した、
次のようなデータです。

■x(説明変数)
x1からx5までの5変数。
すべて0-1の乱数(一様分布)で生成。

■y(目的変数・教師データ)
3x1+3x2+x3+0.5x4+0.5x5 に0-3の
乱数(一様分布)を加えたもので生成。


データ数は300件です。

本当は実世界に存在するなんらかのデータを使えたら
面白かったのですが…
今回は学習の様子を見ていくのが目的ですので、
それはまた今度に機会にしましょう。

それでは、もう一度コードを見てみましょう。
今回のデータに合わせたり、結果を見たりするために
前回お見せしたものから若干の追加や変更を加えています。

#coding:utf-8

import tensorflow as tf
import numpy as np
import math


#隠れ層のノード数
node = 10
#学習回数
training = 20000
#学習率
ln_r = 0.001
#何件をトレーニングデータに使用するか
train_n = 250

data = np.loadtxt('./blog_neuraldata.txt', delimiter=',')

data_x, data_y = np.hsplit(data,[5])

#column = 説明変数の数 と data_n = 全データ数 を取得
data_n, column = data_x.shape

train_x, test_x = np.vsplit(data_x, [train_n])
train_y, test_y = np.vsplit(data_y, [train_n])

#入力データ
x = tf.placeholder("float",shape=[None,column])
y_ = tf.placeholder("float",shape=[None,1])

#入力層から中間層への重みとバイアス
w1 = tf.Variable(tf.truncated_normal([column,node],stddev=0.1))
b1 = tf.Variable(tf.constant(1.0,shape=[node]))

#中間層の計算
h1 = tf.nn.sigmoid(tf.matmul(x,w1) + b1)

#中間層から出力層への重みとバイアス
w2 = tf.Variable(tf.truncated_normal([node,1],stddev=0.1))
b2 = tf.Variable(tf.constant(1.0,shape=[1]))

#出力層の計算
y = tf.nn.relu(tf.matmul(h1,w2) + b2)

#2乗誤差を計算
loss = tf.reduce_sum(tf.square(y - y_))

#学習ステップ
train_step = tf.train.AdamOptimizer(ln_r).minimize(loss)

init = tf.initialize_all_variables()

#セッション作成と変数初期化
sess = tf.Session()
sess.run(init)

f = open('./lossdata.csv', 'w')

#学習
for step in xrange(training):

    sess.run(train_step, feed_dict={x:train_x, y_:train_y})

    #学習100回ごとに、トレーニングデータ、テストデータの1件ごとの誤差を表示
    if (step+1) % 100 == 0:
        print math.sqrt(sess.run(loss, feed_dict={x:train_x, y_:train_y})/train_n), \
            math.sqrt(sess.run(loss, feed_dict={x:test_x, y_:test_y})/(data_n - train_n))

        f.write(str(math.sqrt(sess.run(loss, feed_dict={x:train_x, y_:train_y})/train_n)))
        f.write(',')
        f.write(str(math.sqrt(sess.run(loss, feed_dict={x:test_x, y_:test_y})/(data_n - train_n)))
        f.write('\n')

f.close()


まず、データを同フォルダにあるテキストファイルから
ロードして、そのうえで説明変数と目的変数に分け、さらに
学習用のトレーニングデータと検証用のテストデータに
分けています。

今回は全データ300件中の250件をトレーニングデータ
としました。

そして、実は記事冒頭の画像が、今回の結果をまとめた
グラフになっています。
このグラフを見てみると、学習回数が増えるにつれて誤差値が
減っていくのがよくわかりますね。

ここで、ひとつ気になるところがあると思います。
学習回数が12,000回を超えたあたりから、トレーニングデータの
誤差値は減少し続けるものの、テストデータの誤差値が
増加し始めていますね。

これが前に少しお話しした、過学習(オーバーフィッティング)
という現象です。トレーニングデータにフィットするようにひたすら
パラメータを調整していくことにより、未知のデータ(テストデータ)
に対応する汎化能力が落ちて行ってしまっています

もちろん、機械学習の結果としては、トレーニングデータに過度に
フィットしたものよりも、未知のデータに対応できる汎用性のあるものの
ほうが良いです。

よって、今回得られた結果から学習回数を決定するとしたら、
過学習が起こり始める前の12,000回程度が良いでしょう。
(ただし、学習結果を判断する他の指標も考えた場合は、
その限りではありません)

実は、今回学習の様子を見てもらったもっとも大きな
目的は、この過学習をみていただくためでした。
(前にちゃんと説明をしていなかったので)

ちなみに、この過学習を防ぐための手法としては、
効きすぎている重みにぺナルティを与える正則化や、
学習時に一定割合のニューロンを取り除くドロップアウト
などがあります。

これらの、手法についてもまたいずれお話したいと思っています。

それでは、今回はここまでにしましょう。

お疲れ様でした。