機械学習ライブラリを使わずニューラルネットワークで為替レートを予測する

ニューラルネットワークといえば、pylearn2やTensrflowなどのライブラリがありますが、 使い方が独特だったりして習得に時間がかかるため、今回はこれらのようなライブラリを使わずに、 pythonの標準機能とnumpyで、為替レートの予測をやってみました。


ニューラルネットワークのプログラムは、一から作るのが面倒だったため、Wikipediaのソースを改変。

ニューラルネットワーク - Wikipedia

# -*- coding: utf-8 -*-
import numpy as np

dim_in = 10              # 入力
dim_out = 1             # 出力
hidden_count = 1024     # 隠れ層
learn_rate = 0.05      # 学習率
limit=3000             #学習回数

#学習データ読み込み
data=np.loadtxt("USDJPY_1D.csv",delimiter=",")
train_x=data[:,0:dim_in]/150.
train_y=data[:,dim_in:dim_in+dim_out]/150.
train_count=len(data)
    
# 重みパラメータ。-0.5 ? 0.5 でランダムに初期化。この行列の値を学習する。
w1 = np.random.rand(hidden_count, dim_in)-0.5
w2 = np.random.rand(dim_out, hidden_count)-0.5
b1 = np.random.rand(hidden_count)-0.5
b2 = np.random.rand(dim_out)-0.5

# 活性化関数
def activation(x):
    return np.maximum(0, x)
    #return 1.0/(1.0+np.exp(-x))
    
# 活性化関数の微分
def activation_dash(x):
    return (np.sign(x) + 1) / 2
    #return (1-activation(x))@activation(x)

# 順方向。学習結果の利用。
def forward(x):
    return w2 @ activation(w1 @ x + b1) + b2

# 逆方向。学習
def backward(x, diff):
    global w1, w2, b1, b2
    v1 = (diff @ w2) * activation_dash(w1 @ x + b1)
    v2 = activation(w1 @ x + b1)

    w1 -= learn_rate * np.outer(v1, x)  # outerは直積
    b1 -= learn_rate * v1
    w2 -= learn_rate * np.outer(diff, v2)
    b2 -= learn_rate * diff

# メイン処理
idxes = np.arange(train_count)
_error=np.zeros(limit)
for epoc in range(limit):
    np.random.shuffle(idxes)            # 確率的勾配降下法のため、エポックごとにランダムにシャッフルする
    error = 0                           # 二乗和誤差
    cnt=0
    for idx in idxes:
        y = forward(train_x[idx])       # 順方向で x から y を計算する
        diff = y - train_y[idx]         # 訓練データとの誤差
        error += diff ** 2              # 二乗和誤差に蓄積
        cnt+=1
        backward(train_x[idx], diff)    # 誤差を学習
        if cnt==np.round(train_count*0.1):
           break
    _error[epoc]=error    
    print(epoc+1,error.sum(),y)                  # エポックごとに二乗和誤差を出力。徐々に減衰して0に近づく。

#結果表示
result=np.zeros([train_count,2])
for i in range(train_count):
    result[i,0]=train_y[i]*150.
    result[i,1]=forward(train_x[i])*150.
    print(result[i,0],result[i,1])

#ファイル出力
np.savetxt("error.csv",_error,delimiter=",")
np.savetxt("result.csv",result,delimiter=",")

ニューラルネットワークの入力は過去10日分の終値、出力は翌日の終値としています。これを約6年分。 なお、どう正規化するのが良いのかわからなかったため、適当に150で割っています。 このデータ自体はMT4というFX用のソフトを使って取得しています。

3.ヒストリカル・データのCSVエクスポート - とあるMetaTraderの備忘秘録


実際に上記のプログラムで学習させた結果が以下です。上が誤差の推移、下が真値と推定値の比較です。

f:id:oki-lab:20160619003154p:plain

f:id:oki-lab:20160619003205p:plain

パッと見、良く予測できているように見えますが、実際には平均で0.5%ほど誤差があります。 流石にFXに使える程の精度ではありません。

実際にプログラムを動かす時に、学習時間の短縮のために全部のデータを学習するのではなく、 一部を使用するように以下の文を加えたのですが、何故か全データ学習した時より誤差は小さくなっていました。 時間が短縮できて、誤差も小さくなるのは素晴らしいですが、理屈が分からずじまい。

        if cnt==np.round(train_count*0.1):
           break

また、活性化関数としてシグモイド関数も試してみましたが、計算時間が長くなってしまった上に、誤差が大きくなりました。 ニューラルネットワークといえばシグモイドというイメージでしたが、今はReLUが最強?。


確かな力が身につくPython「超」入門 (確かな力が身につく「超」入門シリーズ)