(1) ライブラリのインポート
import tensorflow as tf
import numpy
numpyも使うので、忘れずにインポートすること。
(2) データの規模とNNの次元設定
SCORE_SIZE = 33
HIDDEN_UNIT_SIZE = 32
TRAIN_DATA_SIZE = 90
SCORE_SIZE: 入力データの次元
HIDDEN_UNIT_SIZE: 隠れ層(中間層)のノード数
TRAIN_DATA_SIZE: 学習データのサンプル数
変数名は、自分で設定してOK!
(3) データの規模とNNの次元設定
raw_input = numpy.loadtxt(open(`input.csv`), delimiter=`,`)
[salary, score] = numpy.hsplit(raw_input, [1])
numpy.loadtxt: numpyライブラリにあるデータロード関数。
delimiterは、データ区切りの指定。
著者は、input.csvにデータ格納した。
http://goo.gl/h5g8cJ
raw_inputは配列型データを格納する。
numpy.hsplit: 垂直方向(列方向)に割線を入れ、水平方向(行方向に)配列を分割する関数
raw_input:分割対象の配列
[1]:分割の境界線。0列目を選択してsalaryに,1列目からをscoreに代入する。したがって、salaryは成分数=人数(=94)のベクトル、scoreは94 x 33の配列にそれぞれなる。
ここで、salaryが教師データ、scoreが入力データになる点に注意する。
http://goo.gl/hoMyGH
(4) 訓練データを整形する(訓練用の選手とテスト用の選手に分ける)
[salary_train, salary_test] = numpy.vsplit(salary, [TRAIN_DATA_SIZE])
[score_train, score_test] = numpy.vsplit(score, [TRAIN_DATA_SIZE])
numpy.vsplit:水平方向にメスを入れて、垂直方向に配列を分ける関数
salary_train: 成分数89のベクトル
salary_test: 成分数5のベクトル
score_train: 89 x 33 の配列
score_test: 5 x 33 の配列
(5) 隠れ層のパラメータ指定と活性化関数の計算を定義する
def inference(score_placeholder):
with tf.name_scope('hidden1') as scope:
hidden1_weight = tf.Variable(tf.truncated_normal([SCORE_SIZE, HIDDEN_UNIT_SIZE], stddev=0.1), name=`hidden1_weight`)
hidden1_bias = tf.Variable(tf.constant(0.1, shape=[HIDDEN_UNIT_SIZE]), name=`hidden1_bias`)
hidden1_output = tf.nn.relu(tf.matmul(score_placeholder, hidden1_weight) + hidden1_bias)
with tf.name_scope('output') as scope:
output_weight = tf.Variable(tf.truncated_normal([HIDDEN_UNIT_SIZE, 1], stddev=0.1), name=`output_weight`)
output_bias = tf.Variable(tf.constant(0.1, shape=[1]), name=`output_bias`)
output = tf.matmul(hidden1_output, output_weight) + output_bias
return tf.nn.l2_normalize(output, 0)
inference: 関数名(任意につけられる)
score_placeholder:下のコードで定義されるデータ。
placeholder型のデータには、学習の元になるデータ/更新データ(つまり、入力データと教師データ)を指定する。TF独特のデータ型である。
TFでは学習の更新の際に、feedという仕組みで元データを取り込む。placeholder型はfeedと連携しており、入力データが関連の計算に反映されるようになる。
http://goo.gl/uhHk3o
with tf.name_scope('hidden1') as scope:
with: pythonの文法。with以下の命令を、文脈外のコマンドに持ち越さないという命令。
tf.name_scope: TFの名前管理関数。この関数下の入れ子の命令に、ひとつの文脈・グループを形成する。
名前の管理をするメリットは、描画である。TensorFlowはTensorBoardにて構築した学習器のモデルを図示することができる。名前を管理しておくと、描画のアウトプットがわかりやすくなる。
ここでは、以下のweight や biasへの代入行為がname_scope関数下にネストされている。
すわなち、これらの命令が'hidden1'という隠れ層のパラメータ計算に関する文脈に入っていることを定義づけている。
http://goo.gl/AYodFB
hidden1_weight = tf.Variable(tf.truncated_normal([SCORE_SIZE, HIDDEN_UNIT_SIZE], stddev=0.1), name=`hidden1_weight`)
hidden1_bias = tf.Variable(tf.constant(0.1, shape=[HIDDEN_UNIT_SIZE]), name=`hidden1_bias`)
hidden1_output = tf.nn.relu(tf.matmul(score_placeholder, hidden1_weight) + hidden1_bias)
入力層→隠れ層の計算である。
tf.Variable: TFの変数(Variable)クラスを適用できる
変数としての生成のほか、様々な機能を有する。
例えば、assignという命令により、変数の値を上書きできる。(詳細は下リンク)
http://goo.gl/nUJafs
tf.truncated_normal:正規分布の乱数を返してくれる
[SCORE_SIZE, HIDDEN_UNIT_SIZE]:欲しい乱数の配列サイズ。
stddev:正規分布の標準偏差を指定する。標準正規分布では、「mean=0.0, stddev=1.0」と指定する。
NNの重みWWの初期値を乱数で生成している。この段階で、このプログラムが事前学習ないNNであると解釈できる。
hidden1_weightは、重みWWの行列と解釈できる。
nameは単純にこの関数の実行に名前をつけている。
http://goo.gl/oZkcvs
tf.constant:定数を生成する関数
0.1:定数0.1を生成する。
shape: 定数を作るサイズ。隠れ層のユニット数(HIDDEN_UNIT_SIZE)だけ作り、hidden1_biasに代入する。
hidden1_biasは隠れ層のバイアス項であり、ここでは初期値を0.1と設定した。
tf.nn.relu:活性化関数のひとつであるReLUを計算する関数
(本稿はコードの解説を目的とするので、ReLUの意味は、別途学んでくださいm(_ _)m)
tf.matmul: 行列(ベクトル)の積(ベクトル同士だったら、内積)を計算する関数
ここでは、行列score_placeholderと行列hidden1_weightの積を計算する。下の定義でわかるが、score_placeholderの列数がSCORE_SIZEと定義されるので、この定義が成立する。
結果的に、outputとして、ユニット数分の計算結果が、ベクトルとして算出される。
with tf.name_scope('output') as scope:
output_weight = tf.Variable(tf.truncated_normal([HIDDEN_UNIT_SIZE, 1], stddev=0.1), name=`output_weight`)
output_bias = tf.Variable(tf.constant(0.1, shape=[1]), name=`output_bias`)
output = tf.matmul(hidden1_output, output_weight) + output_bias
return tf.nn.l2_normalize(output, 0)
隠れ層→出力層の計算である。
outputはスカラーで算出される。つまり、出力層のユニットは1であり、これが選手の年俸(教師データ)との比較対象になる。
このケースでは、出力層に対して活性化関数を恒等写像にして処理している。
tf.nn.l2_normalize:正規化を計算して返す関数
ベクトルであれば、ベクトルの各成分にノルムを除して求める。(ベクトルの大きさが1になる変形のやーつ)
行列、テンソル以降も同様。
スカラーであれば、1が返る。ここでは、元データも正規化しているため、正規化した値同士の比較が必要になる。
https://goo.gl/NEFajc
(6) 誤差関数の定義
def loss(output, salary_placeholder, loss_label_placeholder):
with tf.name_scope('loss') as scope:
loss = tf.nn.l2_loss(output - tf.nn.l2_normalize(salary_placeholder, 0))
tf.scalar_summary(loss_label_placeholder, loss)
return loss
tf.nn.l2_loss: 2乗誤差を計算する関数。
データ型に制限があるので注意する。
主な誤差関数には二乗誤差関数と交差エントロピー誤差関数がある。二乗誤差関数は数値予測タスク、交差エントロピー誤差関数はクラス分類タスクにそれぞれ用いる。
salary_placeholderという指定の仕方は教師データとしての給与データをこの変数に更新ごとに投入するためである。
http://goo.gl/V67M7c
tf.scalar_summary:対象のスカラーに文字列を付けて、値の意味付けを記録する関数。
loss_label_placeholderは、文字列のplaceholderとして、下に定義されている。
http://goo.gl/z7JWNe
(7) SGD(確率的勾配降下法)の適用
def training(loss):
with tf.name_scope('training') as scope:
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
return train_step
tf.train.GradientDescentOptimizer: SGDのアルゴリズムを格納するクラスである。
0.01: 学習係数εを表す
minimize(loss):トリッキーだが、tf.train.GradientDescentOptimizerというクラスに格納されている関数で、対象の変数(ここではloss)を最小化させる
計算過程を格納したオブジェクトなので、微分計算後の更新した重みパラメータが返される。
http://goo.gl/5XENkX
(8) 実行体系の記述
TensorFlowでは、学習実行にまつわるアルゴリズム群を、Graphというクラスに格納している。
計算(session.run)はもちろんのこと、名前の通りグラフ描画(session.graph)に必要な情報も整理してくれる。(便利だねっ(・ω<))
with tf.Graph().as_default():
salary_placeholder = tf.placeholder(`float`, [None, 1], name=`salary_placeholder`)
score_placeholder = tf.placeholder(`float`, [None, SCORE_SIZE], name=`score_placeholder`)
loss_label_placeholder = tf.placeholder(`string`, name=`loss_label_placeholder`)
with tf.Graph().as_default(): :グラフクラスを宣言する文
salary_placeholder:年収の教師データを格納するオブジェクト
[None, 1]:列数は1、行数は任意の数という意味。
score_placeholder:入力データを格納するオブジェクト
loss_label_placeholder = アウトプット時の要約情報に組み込み/反映させるための文字列格納オブジェクト
feed_dict_train={
salary_placeholder: salary_train,
score_placeholder: score_train,
loss_label_placeholder: `loss_train`
}
feed_dict_train:学習更新ごとに食わせる辞書型データの宣言
辞書型データ(feed_dict_train)に格納している。TFが都度データを読み込むためには、feedを噛ませなければならない。
http://goo.gl/00Ikjg
↑辞書型データの復習
keyに仮のTensor、valueに初期値を入力。
学習データに関する辞書セットである。
このタイプの辞書が必要な理由は、下記のsession.runのところで分かる。
feed_dict_test={
salary_placeholder: salary_test,
score_placeholder: score_test,
loss_label_placeholder: `loss_test`
}
feed_dict_test: テストデータに関する辞書データの作成
構成は、上記と同じ。
python
output = inference(score_placeholder)
loss = loss(output, salary_placeholder, loss_label_placeholder)
training_op = training(loss)
output
に、順伝播のNN計算関数inferenceをぶち込む。
loss
に、二乗誤差関数の計算関数lossをぶち込む。
training_op
に、SGDのアルゴリズム実行関数trainingをぶち込む。
python
summary_op = tf.merge_all_summaries()
tf.merge_all_summaries: Summary関数の情報を集約してくれる。
init = tf.initialize_all_variables()
initに、全ての変数を初期化する関数tf.initialize_all_variablesを代入する。
初期化のinit宣言するタイミングが重要だ。必要な変数を全て定義してから宣言しないと、エラーを返す。
best_loss = float(`inf`)
浮動小数点数であるbest_lossを宣言。
下記で、誤差の更新をするところがある。
その値にbest_lossを使うのだが、初期値のlossを必ず入れる必要があるので、初期のbest_lossはどんな数よりも高く(inf;無限大)している。
with tf.Session() as sess:
Sessionは、Graphの中でも中核になるクラスで、実行関係全般の命令が詰まっている。Sessionを宣言しないと、一切のtfオブジェクトがらみ処理が始まらない。
summary_writer = tf.train.SummaryWriter('data', graph_def=sess.graph_def)
tf.train.SummaryWriter: 要約情報をイベントファイルに書き込む関数。
sess.run(init)
sess.run: さらに中核になる関数。
最初の引数に書いてある命令を実行する。
Windowsで言えば、.exeみたいなものだ!
for step in range(10000):
sess.run(training_op, feed_dict=feed_dict_train)
loss_test = sess.run(loss, feed_dict=feed_dict_test)
for step in range(10000): 10000回の繰り返し。「89人分のデータを学習し、5人分のデータをテストする。」これで1回です!
feed_dict=feed_dict_train:session.runの重要なオプション
ここが繰り返し説明してきたfeedシステム。ここで必要なデータを取り込んで、学習更新計算が行われる。
if loss_test < best_loss:
best_loss = loss_test
best_match = sess.run(output, feed_dict=feed_dict_test)
誤差の最小値が更新された時に限り、その誤差値と出力層(推定の年俸)を記録する。
if step % 100 == 0:
summary_str = sess.run(summary_op, feed_dict=feed_dict_test)
summary_str += sess.run(summary_op, feed_dict=feed_dict_train)
summary_writer.add_summary(summary_str, step)
100ステップに1回、要約情報を集めるという命令。
さらに、集めた情報をイベントファイルに書き込む。
print sess.run(tf.nn.l2_normalize(salary_placeholder, 0), feed_dict=feed_dict_test)
print best_match
毎ステップごとの教師データと精度の高い出力層のデータを表示する。
0 件のコメント:
コメントを投稿