このプログラムの目的
このプログラムの目的は,PytorchのネットワークとGAの配列の相互変換を実装することです.
進化計算とニューラルネットワークを組みわせた手法は様々なものが存在します.
これらの総称をNeuro-Evolutionと呼びますが,今回はニューラルネットワークの重みを実数値GAにて最適化するという単純な方法をとります.
この方法の利点は以下の通りです.
- 単純なため実装が簡単
- Pytorch等のネットワークへの変換が容易なため,GPU利用がしやすい
work_share
├07_evolutionary_algorithms
├Dockerfile
├docker-compose.yml
└src
└neuro_evolution_torch
├torch_model.py (これを作成)
├simulation_cython.pyx
└setup.py
使用ライブラリ
import torch
from torch import nn
import numpy as np
ネットワーク定義
入力層,隠れ層,出力層の3層ネットワークです.
あまり複雑にするとGAでの最適化が困難であると考え,最も簡易な構造にしました.
単純すぎると思った場合は自分でカスタマイズしてください.
また,強化学習の時にはネットワークの入力値に,銘柄保有比率や総資産・現金等の環境の状態を入力していました.しかし,これらの入力値は出力に与える影響が少なく,使用しないことで,入力に対する並列計算が可能になるため,今回は利用しないことにしました.
class Neural_Network_3layer(nn.Module):
def __init__(self, input_dim, output_dim, hidden_dim):
super(Neural_Network_3layer, self).__init__()
self.net = torch.nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.Sigmoid(),
nn.Linear(hidden_dim, output_dim),
nn.Softmax()
)
def forward(self, x):
return self.net(x)
重み行列と一次元配列の相互変換
モデルから一次元配列へ変換する場合.この関数は遺伝子長決定の時に利用する.
def model_w_to_vec(model):
vec = []
for key, tensor in model.state_dict().items():
vec.append(tensor.view(-1))
vec = torch.cat(vec)
return vec.to('cpu').detach()
モデルの重みを与えた一次元配列で書き換える.
def vec_to_model_w(model, vec):
s = 0
new_state_dict = {}
for key, tensor in model.state_dict().items():
e = tensor.view(-1).shape[0]
if len(tensor.shape) == 1:
new_state_dict[key] = vec[s:s+e]
if len(tensor.shape) == 2:
new_state_dict[key] = vec[s:s+e].view(tensor.shape[0], tensor.shape[1])
s += e
model.load_state_dict(new_state_dict)
return model
ネットワークの実行
モデルとベクトル(遺伝子),入力値を渡したら,実行結果(P)を返します.実行結果(P)は各銘柄の保有比率であり,銘柄数×日数の行列です.
def model_calculate(model, vec, X, device='cpu', to_numpy=True):
torch_device = torch.device(device)
if device == 'cuda' and X.is_cuda == False:
X = X.to(torch_device)
model = vec_to_model_w(model, vec)
model.to(torch_device)
out = model(X)
if to_numpy:
out = out.to('cpu').detach().numpy().astype(np.float64)
else:
out = out.to('cpu').detach()
return out