ディープラーニング（TensorFlow）を使用した株価予想 ～その2～

[前回](http://qiita.com/yai/items/5ec5bbfbcc53c9770c40)の続き。
ディープラーニングのフレームワークであるTensorFlowを使用して株価を予想するぞ～、というお話です。ちなみに前回は完全に失敗でした。
[前回](http://qiita.com/yai/items/5ec5bbfbcc53c9770c40)のコメントで、[tawago](http://qiita.com/tawago)さんから[「Googleが同じようなことしている」](https://cloud.google.com/solutions/machine-learning-with-financial-time-series-data)という情報をいただいたので、そちらをコピ・・・インスパイアしてみました。

##前回との相違点
前回は、「数日分の日経平均を使用して、次の日の日経平均が上がるか、下がるか、変わらないか（3択）を予想する」ものでした。
Googleのデモでは、「数日分の世界中の株価指数（ダウ、日経平均、FTSE100、DAXなど）を使用して、次の日のS&Pが上がるか下がるか（2択）を予想する」という内容でした。
ということで、下記が前回からの主な変更点となります。
- 「上がるか」「下がるか」の2択
- 日経平均だけでなく、他国の株価指数も使用
- 隠れ層x2、ユニット数は50,25
予想するのは前回と同じく、次の日の日経平均です。

Googleのデモの詳細は[こちらの動画](https://www.youtube.com/watch?v=N422_CYuzZg&feature=youtu.be)から。（英語）

##環境
TensorFlow 0.7
Ubuntu 14.04
Python 2.7
AWS EC2 micro instance

##実装
###準備
[Quandl](https://www.quandl.com/data/YAHOO)というサイトから、データがダウンロードできるようです。
しかし、掲載されているデータが膨大過ぎて、どれをダウンロードしたら良いのか分からないという問題が発生したため、今回は日経、ダウ、香港ハンセン、ドイツの4つのみ利用することにします。Googleのデモでは8つくらい使用していました。

落としてきたデータは一つの表にまとめてCSVで保存します。特にスクリプトとかありません。ExcelのVLOOKUP関数とか使った手作業でやりました。出来る人ならDBに入れて上手く扱うのでしょうが・・・

###ラベル
今回は「上がる」か「下がる」かの2択なので、正解のフラグは下記のような感じ。

```
if array_base[idx][3] > array_base[idx+1][3]:
  y_flg_array.append([1., 0.])
  up += 1
else:
  y_flg_array.append([0., 1.])
  down += 1
```
サンプル全体としては、
上がる：50.6%
下がる：49.4%
となりました。

###グラフ作成
グラフはほぼGoogleのコードを真似ています。
隠れ層の数やユニット数もGoogleのコードのままです。
ドロップアウトは無かったかもしれないですが。

```
NUM_HIDDEN1 = 50
NUM_HIDDEN2 = 25
```

```
def inference(x_ph, keep_prob):

  with tf.name_scope('hidden1'):
    weights = tf.Variable(tf.truncated_normal([data_num * price_num, NUM_HIDDEN1], stddev=stddev), name='weights')
    biases = tf.Variable(tf.zeros([NUM_HIDDEN1]), name='biases')
    hidden1 = tf.nn.relu(tf.matmul(x_ph, weights) + biases)
  
  with tf.name_scope('hidden2'):
    weights = tf.Variable(tf.truncated_normal([NUM_HIDDEN1, NUM_HIDDEN2], stddev=stddev), name='weights')
    biases = tf.Variable(tf.zeros([NUM_HIDDEN2]), name='biases')
    hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)
  
  #DropOut
  dropout = tf.nn.dropout(hidden2, keep_prob)
  
  with tf.name_scope('softmax'):
    weights = tf.Variable(tf.truncated_normal([NUM_HIDDEN2, 2], stddev=stddev), name='weights')
    biases = tf.Variable(tf.zeros([2]), name='biases')
    y = tf.nn.softmax(tf.matmul(dropout, weights) + biases)
  
  return y
```

###損失
前回書き忘れた損失は下記のように定義。
前回と変わっていませんが、こちらもGoogleさんと同じです。

```
def loss(y, target):
  return -tf.reduce_sum(target * tf.log(y))
```

###最適化
最適化は前回と変わらず。

```
def optimize(loss):
  optimizer = tf.train.AdamOptimizer(learning_rate)
  train_step = optimizer.minimize(loss)
  return train_step
```

###訓練
訓練も何一つ前回と変わらず。

```
def training(sess, train_step, loss, x_train_array, y_flg_train_array):
  
  summary_op = tf.merge_all_summaries()
  init = tf.initialize_all_variables()
  sess.run(init)
  
  summary_writer = tf.train.SummaryWriter(LOG_DIR, graph_def=sess.graph_def)
  
  for i in range(int(len(x_train_array) / bach_size)):
    batch_xs = getBachArray(x_train_array, i * bach_size, bach_size)
    batch_ys = getBachArray(y_flg_train_array, i * bach_size, bach_size)
    sess.run(train_step, feed_dict={x_ph: batch_xs, y_ph: batch_ys, keep_prob: 0.8})
    ce = sess.run(loss, feed_dict={x_ph: batch_xs, y_ph: batch_ys, keep_prob: 1.0})

    summary_str = sess.run(summary_op, feed_dict={x_ph: batch_xs, y_ph: batch_ys, keep_prob: 1.0})
    summary_writer.add_summary(summary_str, i)
```

###評価
評価も前回と変わらず。

```
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_ph, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

print(sess.run(accuracy, feed_dict={x_ph: x_test_array, y_ph: y_flg_test_array, keep_prob: 1.0}))
```

##結果～その１～
上記の結果、精度は

```
0.50295
```
**・・・ダメやん。**

##修正
入力として、各国の株価指数をそのままぶっこんでましたが、ここを少し修正。入力は「その株価指数が前日と比べてどれくらい上下したか」をいれることにしました。（定かではないが、ビデオ内でGoogleもそうやっているように見えた）
コードにすると、こんな感じ。（分かりにくいかもしれませんが）

```
tmp_array = []
for j in xrange(idx+1, idx + data_num + 1):
  for row, sprice in enumerate(array_base[j]):
    tmp_array.append(sprice)

x_array.append(tmp_array)
```
上記を、下のように変更。

```
tmp_array = []
for j in xrange(idx+1, idx + data_num + 1):
  for k in range(price_num):
    tmp_array.append(((array_base[j][k]) - (array_base[j+1][k])) / array_base[j][k] * 100)

x_array.append(tmp_array)
```

##結果～その２～

```
0.63185
```
なんとか意味のある数字になったもよう。63%で「上がるか」「下がるか」を予想できているので、当てずっぽうよりかは良い結果が出せていると言えます。
というわけで、一応成功とします(^_^;)
Googleのデモだと72%程度の精度になっていたと思いますが、まぁこっちは使用している株価指数の数も少ないし、こんなものだと思います。

##考察
- 入力は「独立した数字群」より、「全体として意味がある数字群」とした方が良い結果が出るのかもしれない。
- 前回どうでもいいのかと思っていたtf.truncated_normal()のstddev指定が割と大事。デフォルトのままだと発散しやすいので0.001とかを指定しています。発散しているようなら調整した方が良いパラメータのようです。
- Googleの指定している隠れ層のユニット数とか結構適当に見える。今回、50と25だけど、最初のユニット数は入力の2倍くらい見ておけばいいのか？（よく分からない）
- Googleのデモ同様にもっと多くの株価指数を入れたり、為替などのデータを入れるともっと精度が上がるかも。

##所感
- とりあえずそれっぽくなって良かった！
- TensorFlowはデータが多少変わっても、訓練部分など変えなくて良い部分が多いので、一回テンプレートのようなものを作ってしまえばコーディングが楽。どっちかというと入力データの加工の方が面倒。
- 上記理由で、数字はなるべく定数化しておいた方が後々楽。

##ところで・・・
どなたか、為替の5, 15, 30分足データとかダウンロードできるサイトをご存知の方いましたら教えてください。m(__)m