こんにちは。システムトレーダーの卵ことKenKenです。現在、開催中のKagle”のコンペ”Jane Street Market Prediction”に挑戦中です。前回は、PytorchでCNN(1次元畳み込み)を用いたモデルとして、単純な構造のCNNとTCN(Temporal Convolutional Network)を構築してみました。CNNのスコアが1745.844でTCNが2091.370で、若干ですが、TCNの方が精度が良いという結果が得られました(スコアはかなり悪い)。今回は、TCNを改良してスコアアップを図ってみたので、簡単に記事にしておきます。現時点では、TCNが5785.452まで向上しました。
※本記事は、公開しているNotebookをまとめたものとなっております。
・CNNの学習に関するNotebook ⇒ こちら, 推論に関するNotebook ⇒ こちら
・TCNの学習に関するNotebook ⇒ こちら, 推論に関するNotebook ⇒ こちら
変更①全結合層の追加
最初のモデルでは、TCN-全結合層×1-出力層(全結合層)の構成をしておりました。出力層の前の全結合層を追加することで精度向上が見られました。
- TCN – 全結合層×1 – 出力層(全結合層):2091.370
- TCN – 全結合層×2 – 出力層(全結合層):5785.452
- TCN – 全結合層×3 – 出力層(全結合層):5132.795
上記の結果から、全結合層は出力層を含めて、3層が良いみたい。単純に層を増やすだけでは、精度向上を限界があるようです。
変更②ネットワーク構成の修正
TCNのネットワークを丁寧に書くようにしました。Pytorchについて理解不足のため、この修正が意味があったのかはわかりませんが、丁寧に書いたほうが確実であるということは間違いないと思っております。
以下の図は、ネットワークをprint出力したものになりますが、fc2~fc3でドロップアウト層やバッチノーマライゼーション層が省略されているのがわかります。

これは、横着してfc1で利用しているドロップアウト層やバッチノーマライゼーション層をそのままfc2やfc3に適用してしまっているためです。(もしかしたら、Pytorch内でも共通の認識をされている?)これをきちんと分けて、以下のようになるように修正しました。

変更③ 期間幅(window_size)を変えてみる
最初のモデルでは、一旦、期間幅を20としてました。この期間幅のサイズに応じで、モデルはどれだけ過去のデータを参考にするのかを決めることができます。今回は、この期間幅を短期(window_size=5)と長期(window_size=100)の場合で試してみました。結果は、以下の通り。
- 短期(window_size=5):6951.875
- 中期(window_size=20):5785.452
- 長期(window_size=100):5227.883
上記の結果から、期間は短いほうが良いことがわかりました。時系列データとして扱わない方が精度が良くなるのかもしれません。
(追記)さらに期間を短くしてみた場合(window_size=3)、スコアは0.000となりました。出力結果を見るとaction=1に偏っていたことから過学習の可能性がありました。
変更④ 学習データを選別する
今回のコンペでは、weightが0の予測値はスコア計算に利用されません。上で作成してきたモデルの学習データにはweight=0のものを含めていました。そこで、weight=0の場合は、バッチに含めないように処理を修正してみました。期間幅が5のモデルで比較を行った結果が以下の通り。
- weight=0を含む場合:6951.875
- weight=0を含まない場合:5559.145
スコアが良くなると思っていただけに残念な結果に。時系列でweight=0を含む場合があることが多少作用したのでしょうか。原因は、わかりません。
主なコードの変更点としては、Dataset Classでweightを返すようにしたこと、BatchSamplerクラスをオーバライドし、DataLoaderでは新しく定義したBatchSamplerを用いてバッチデータを作成するようにしました。詳細については、こちらのNotebookを見てください。以下に変更したコードを参考程度に載せておきます。(もう少し綺麗に書くことができた気がしますが、そのスキルを持ち合わせてなかった…。ただ、Pytorchの勉強になったので良かったです。)
from torch.utils.data import Dataset from torch import nn class JSMP_Dataset(Dataset): def __init__(self, file_path, window_size): # valiables self.file_path = file_path self.window_size = window_size # read csv train = pd.read_csv(file_path) # pre processing train = train.query('date > 85').reset_index(drop = True) #train = train[train['weight'] != 0] train.fillna(train.mean(),inplace=True) train['action'] = ((train['resp'].values) > 0).astype(int) resp_cols = ['resp_1', 'resp_2', 'resp_3', 'resp', 'resp_4'] self.features = self.f_mean = np.mean(train[self.features[1:]].values,axis=0) self.X_train = train.loc[:, train.columns.str.contains('feature')].values self.y_train = np.stack([(train > 0).astype('int') for c in resp_cols]).T self.X_train = torch.from_numpy(self.X_train).float() self.y_train = torch.from_numpy(self.y_train).float() self.X_weight = torch.from_numpy(train['weight'].values) # reduce memory del train gc.collect() def __len__(self): return len(self.X_train) - self.window_size def __getitem__(self, i): weight = self.X_weight[i + self.window_size - 1] data = self.X_train[i:(i+ self.window_size), :] label = self.y_train[i + self.window_size - 1] return weight, data, label
from torch.utils.data import RandomSampler from torch.utils.data import BatchSampler class NonZeroWeightBatchSampler(BatchSampler): def __init__(self, batch_size, dataset): self.sampler = RandomSampler(dataset) self.batch_size = batch_size self.dataset = dataset def __iter__(self): batch = [] for idx in self.sampler: # avoid using weight = 0 data if self.dataset[idx][0].item() == 0: continue else: batch.append(idx) if len(batch) == self.batch_size: yield batch batch = [] if len(batch) > 0: yield batch
# make DataLoder train_dataloader = torch.utils.data.DataLoader(train_ds, batch_sampler=batch_sampler_train) valid_dataloader = torch.utils.data.DataLoader(valid_ds, batch_sampler=batch_sampler_valid) # dict dataloaders_dict = {'train': train_dataloader, 'val' : valid_dataloader}
まとめ
今回は簡単にTCNモデルの改良をしてみました。ここまでの結果から、window_size=5で全結合層が3層(出力層を含む)がスコア6951.875と一番良かったです。ここからさらに改善をしていくとなると、アンサンブル学習にするとかになるんですかね。今回は、検証データの損失値が最小となるモデルを最良のモデルとしていたので、Accuracyが最高の場合を最良のモデルにした場合やそれら二つの平均を予測値とするなど改良はできそうです。ただ、ここまでしても簡単に作成したDNNにかなわないのでTCN(時系列データ)には限界があるのかもしれません。
Kagleもひと段落してきたので、そろそろシストレ開発などほかのことに挑戦していこうかなと思ってます。
以上