Edited at

機械学習を使った株価予想(回帰編)

More than 3 years have passed since last update.

以前(1,2,3)からの続き物。

分類ばかりでは面白くないので、回帰問題を試したい。

というわけで、今回は機械学習のフレームワークTensorFlowやscikit-learnを使用して株価を直接予想するという遊び。前は翌日の株価が「上がる or 下がる」の2択の分類でしたが、今回は「何円か」を直接予想します。

なお、入力に使うデータは使い回しです。毎回スミマセン。


趣旨


  • TensorFlowやscikit-learnを使用して、株価の予想をしてみる。

  • 精度や使用感を確認する。


やること

「数日分の世界中の株価指数(ダウ、日経平均、DAXなど)を使用して、次の日の日経平均株価を予想する」(回帰)


環境


scikit-learn

scikit-learn 0.17.1

Python 2.7

Windows 7


TensorFlow

TensorFlow 0.7

Ubuntu 14.04

Python 2.7

AWS EC2 micro instance


実装

Quandlというサイトから、日経、ダウ、香港ハンセン、ドイツの株価指数をダウンロード。テキストデータとして1つにまとめます。(手作業)


ラベル

正解のデータとして、翌日の終値を使用します。(翌日の終値を予想する)

しかし、直接株価をラベルとしてつっこんでみたところ、ぶれっぶれで発散しまくりな結果となったため一工夫。予想する対象を「翌日の終値が前日より何パーセント上下するか」という間接的なものにし、後に計算し直して終値を求めることにします。後述する入力にも同様の変化率を使うので、理に適っている・・・ということにします。(・・;)

# JUDGE_DAY = 1, 第二添字の[3]は日経平均の終値が入っています。

y_array.append([(array_base[i][3] - array_base[i+JUDGE_DAY][3]) / array_base[i][3] * 100])


入力データ

株価をそのまま入れるのではなく、「前日に比べてどれくらい(%)上下したか」のリストを与えています。(株価をそのまま入れても全然上手くいかなかったため)

tmp_array = []

for j in xrange(i+1, i + data_num + 1):
for k in range(16):
tmp_array.append((array_base[j][k] - array_base[j+1][k]) / array_base[j][k] * 100)
x_array.append(tmp_array)


TensorFlow固有の話


グラフ

TensorFlowは隠れ層2つ、ユニット数はそれぞれ50,25としています。

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('regression'):
weights = tf.Variable(tf.truncated_normal([NUM_HIDDEN2, 1], stddev=stddev), name='weights')
biases = tf.Variable(tf.zeros([1]), name='biases')
y = tf.matmul(dropout, weights) + biases

return y


損失

損失の計算はl2_loss()を使用。

数値の差分が損失に当たるからコレが良いのかと勝手に思っていますが、正しいのか不明。「違うだろ」という方、コメントお待ちしています。

def loss(y, target):

return tf.reduce_mean(tf.nn.l2_loss((y - target)))


最適化

特筆することは無いですかね。



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)


評価

精度として、計算で求めた株価の変動率と、実際の変動率の差を求め、その絶対値の平均を出力しています。

つまり、平均誤差を出しているだけです。

accuracy = tf.reduce_mean(tf.abs(y - y_ph))

print "accuracy"
print(sess.run(accuracy, feed_dict={x_ph: test_batch_xs, y_ph: test_batch_ys, keep_prob: 1.0}))


scikit-learn固有の話


回帰アルゴリズム

色々アルゴリズムがあるのですが、・・・どれが最適なのかよく分からないので、とりあえず3つほどピックアップして引数無しで使うことにします。

# SGDRegressor

clf = linear_model.SGDRegressor()
testClf(clf, x_train_array, y_train_array, x_test_array, y_test_array)

# DecisionTreeRegressor
clf = tree.DecisionTreeRegressor()
testClf(clf, x_train_array, y_train_array, x_test_array, y_test_array)

# SVM
clf = svm.SVR()
testClf(clf, x_train_array, y_train_array, x_test_array, y_test_array)


訓練、評価

訓練はfit()を実行するのみ。評価はscore()を実行してみたところ一部マイナス値になってしまい、どう判断していいのかよく分からなかったので(ご存知の方教えてください)、TensorFlowの時と同様、予測株価変化率を出して、実際の株価の変化率の差の絶対値の平均を取っています(要は平均誤差)。

def testClf(clf, x_train_array, y_flg_train_array, x_test_array, y_flg_test_array):

print clf
clf.fit(x_train_array, y_flg_train_array)
result = clf.predict(x_test_array)
print clf.score(x_test_array, y_flg_test_array)
print np.mean(np.abs(np.array(result) - np.array(y_flg_test_array)))


結果

TensorFlow

1.00044

scikit-learn

SGDRegressor: 0.943171296872

DecisionTreeRegressor: 1.3551351662
SVM: 0.945361479916

というわけで、概ね1%程度の誤差となりました。

株価の予想で1%の誤差って・・・、全く使えな・・・ゲフンゲフン。


実際に予想

せっかくなので、実際に予測値を出してみましょう。

手元のデータが2016/03/24までのデータなので、2016/03/25の日経平均株価の終値を予想します。scikit-learnはSVMを使用します。

※数字の羅列になってしまうので、入力部分は省略します。どちらもdataに過去数日分の株価データが入っています。16892.33は3/24の終値です。


TensorFlow

p = sess.run(y, feed_dict={x_ph: data, keep_prob: 1.0})

price = ((p[0][0] / 100.) + 1.) * 16892.33
print price


scikit-learn

p = clf.predict(data)

price = ((p[0] / 100.) + 1.) * 16892.33
print price


結果


TensorFlow

16804.3398821



scikit-learn

16822.6013292


で、実際の3/25の株価は・・・17,002.75。

・・・まぁそんなものですよね。


考察


  • 機械学習といっても、「なんでもつっこんで訓練してやれば機械が頑張って最適なものが出てくる」わけではないらしい。人間側も機械が考えやすく答えを出しやすいようにするにはどうすべきかを考える必要がありそう。


所感


  • 回帰問題楽しい。

  • 分類のように、「全部最初のカテゴリ」とかやる気の無い回答にならないので、エラーが見つけやすい。

  • 分類の時と多少コードは変わるけど、8割方使いまわせるので開発は楽。