TensorFlowで株価予想シリーズ
- 0 - Google のサンプルコードを動かしてみる
- 1 - 終値が始値よりも高くなるかで判定してみる
- 2 - 日経平均225銘柄の株価予想正解率ランキング〜
- 3 - 日本3506銘柄の株価予想ランキング
- 4 - 実際に売買したら儲かるのかシミュレーションしてみる
- 5 - 大きく上がると予想されたときだけ買ってみるシミュレーション
- 6 - 学習データの項目を増やす!隠れ層のサイズも増やす!
- 7 - 株価が何%上昇すると予測したら買えばいいのか?
- 8 - どの銘柄を買うか
- 9 - 年利6.79%
はじめに
前回はクラスを2種類から3種類に分け結果がよくなるかを試しました。次は学習データや隠れ層のサイズを変更して結果が良くなるかを試してみます。
コード
今回のコードはこちらです。
学習データに安値/高値/出来高を追加する
今までは Google のコードをそのまま利用していたので、学習データには終値しか使っていませんでした。そこに安値/高値/出来高を追加して精度が上がるか試します。
適当に追加してみたところ、学習が全然進まない状態になりました。そこで各項目の値を標準化という方法で正規化します。
今までは終値をこのような正規化をして使っていました。
log_return_data['{}_CLOSE_RATE'.format(name)] = np.log(using_data[close_column]/using_data[close_column].shift())
using_data[close_column]
には日付毎の終値が入っています。using_data[close_column].shift()
を行うと配列内での位置が一つズレます。配列は日付に対応しているので1日ズレて「当日終値/前日終値」となり、前日終値からどれくらいの割合で変化したのかが入ります。np.log()でその値を対数に変換しています。
たとえば、当日の終値が110円で前日の終値が100円だった場合は、110/100 = 1.1、log(1.1) = 0.041.. 逆に当日の終値が100円で前日の終値が110円の場合は 0.9 となり log(0.9) -0.045 になります。上がればプラス。下がればマイナスの値になります。
安値と高値の場合は同じように計算すれば同じような値になるのですが、出来高は単位が「円」から「株数」に変わるのでとても大きな値が入り上手く学習できなかったのではないかと思いました。
そこで標準化を行いました。
統計学における標準化 - バイオインフォマティクス入門
標準化とは「与えられたデータを平均が0で分散が1のデータに変換する操作のことをいう」とのことだそうです。わかりにくいですが、どんな単位や桁のデータ群であっても平均あたりのデータが0になり、全体が0から1あたり(マイナスもあるよ)の値にまとめてくれる便利な計算のようです。これで株価も出来高も同じような値に正規化できそうです。
データの保持は numpy で行っているので標準化の参考にしたページがこちら。
Python: データセットの標準化について
実際のコードはこちら。
def zscore(np_array):
'''配列の標準化を行う
'''
a1 = np_array.replace(0, 1.).replace(np.inf, 1.).replace(np.nan, 1.)
a2 = a1 - a1.mean()
a3 = a2 / a2.std()
return a3
標準化を行う前の終値/安値/高値/出来高を計算するコードはこちら。計算した値を zscore()
に与えて結果をもらってます。
# np.log(当日終値 / 前日終値) で前日からの変化率を算出
# 前日よりも上がっていればプラス、下がっていればマイナスになる
log_return_data['{}_Close_RATE'.format(name)] = zscore(using_data[close_column]/using_data[close_column].shift())
# 当日高値 / 当日始値
log_return_data['{}_High_RATE'.format(name)] = zscore(using_data[high_column]/using_data[open_column])
# 当日安値 / 当日始値
log_return_data['{}_Low_RATE'.format(name)] = zscore(using_data[low_column]/using_data[open_column])
# 当日出来高 / 前日出来高
log_return_data['{}_Volume_RATE'.format(name)] = zscore(using_data[volume_column]/using_data[volume_column].shift())
終値は今までと変わらず「当日終値/前日終値」とし前日終値からどれだけ変化したかが入ります。安値と高値は「当日安値(高値)/当日始値」にしました。始値からどれだけ変化したかが入ります。出来高は「当日出来高/前日出来高」として前日からどれだけ変化したかが入ります。
ドロップアウト
ドロップアウトも入れました。今まで検証を行っていて学習データとその答では結果がいいのに、テストデータではイマイチ結果が悪いのでノイズを与えてみます。
model = tf.nn.dropout(model, keep_prob)
keep_prob
は学習時には0.9をテスト時には1.0を入れて学習の時だけドロップアウトが入るようにします。
隠れ層のサイズはどれくらいがいい?
隠れ層は Google のコードのままで第一層は50、第二層は25でやってきました。そこを調整してどの数値にするのが一番良いのかを検証してみます。
いろいろなサイズで検証したいので値を引数で受け取れるようにコードを変更します。
while not layerLog.is_code_full(code):
commena = 'python goognet.py {} --layer1={} --layer2={}'.format(code, layer1, layer2)
os.system(commena)
任意の数とサイズで隠れ層を作れるように変更します。
stddev = 1e-4
layer_counts = [layer1, layer2, CLASS_COUNT]
weights = []
biases = []
model = None
for i, count in enumerate(layer_counts):
# 重み付けの変数定義
if i == 0:
weights = tf.Variable(tf.truncated_normal([num_predictors, count], stddev=stddev))
else:
weights = tf.Variable(tf.truncated_normal([layer_counts[i - 1], count], stddev=stddev))
# バイアスの変数定義
biases = tf.Variable(tf.ones([count]))
if model == None:
# 一番最初のレイヤー
model = tf.nn.relu(tf.matmul(feature_data, weights) + biases)
else:
if (i + 1) < len(layer_counts):
# 最後ではないレイヤー
model = tf.nn.relu(tf.matmul(model, weights) + biases)
else:
# 最終レイヤーの前には dropout を入れる
model = tf.nn.dropout(model, keep_prob)
model = tf.nn.softmax(tf.matmul(model, weights) + biases)
# 予測が正しいかを計算(学習に使用する)
cost = -tf.reduce_sum(actual_classes*tf.log(model))
training_step = tf.train.AdamOptimizer(learning_rate=stddev).minimize(cost)
各層のサイズを [50, 25], [50, 50], [256, 128], [256, 256], [512, 256], [512, 512], [1024, 512], [1024, 1024], [2048, 1024], [2048, 2048] にして売買シミュレーションをしてみます。[50, 50, 50]と第三層も作れるのですが、ちょっと試したところあまり良い結果が出なかったので今回は二層まででおこないます。
実行
以下のコードで環境を作ります。
$ virtualenv --python=/usr/local/bin/python2.7 .pyenv
$ . .pyenv/bin/activate
$ pip install -r requirements.txt
$ pip install https://storage.googleapis.com/tensorflow/mac/tensorflow-0.8.0-py2-none-any.whl
実行する前に隠れ層のサイズが run_jp.py
に定義されているので書き換えます。
layer1 = 512
layer2 = 512
実行します。
$ python run_jp.py
結果は layout_logs/512_512.csv
に出力されます。
売買シミュレーション
元金は1000万円で直近200日のデータで売買を行います。手数料は以前と同じでGMOクリック証券のものを使用します。また、今まで売買シミュレーションを行ってきて、同一の銘柄でも学習器を作り変えると結果が大きく変わることがありました。そこでブレを少なくするため同じ銘柄で10回シミュレーションを行いその平均をとることにします。
銘柄は日経225の中から23銘柄を選びました。多くの銘柄でシミュレーションしたかったのですが時間がかかりすぎます。(今回の計算は TITAN X を使っていても2日ほどかかりました)
結果
隠れ層のサイズは 50_50 と 512_512 の組み合わせの成績が良いようです。
隠れ層サイズ | 最大残高 | 最大正解率 | 平均残高 | 平均正解率 |
---|---|---|---|---|
50_25 | ¥10,816,295 | 38% | ¥10,112,777 | 20% |
50_50 | ¥11,094,236 | 36% | ¥10,196,534 | 20% |
256_128 | ¥10,800,639 | 31% | ¥9,963,363 | 17% |
256_256 | ¥10,978,486 | 45% | ¥10,018,288 | 18% |
512_256 | ¥10,884,485 | 61% | ¥10,034,537 | 20% |
512_512 | ¥11,648,986 | 40% | ¥10,192,925 | 19% |
1024_512 | ¥10,850,686 | 30% | ¥9,970,042 | 17% |
1024_1024 | ¥10,770,945 | 34% | ¥10,060,519 | 17% |
2048_1024 | ¥10,936,680 | 42% | ¥9,968,070 | 19% |
2048_2048 | ¥11,115,373 | 52% | ¥9,958,142 | 18% |