PyTorchで作るRNN 実験編
こんにちは。
前回PyTorchを使ってRNNを実装してみました。
せっかく活性化関数を変更できるようになったので、今回は実験編ということで、活性化関数による精度や処理時間の違いを見ていきたいと思います。
実験準備
まずは実験をするにあたって、前回のソースコードに処理を追加します。
追加する内容は、
1.乱数の固定
2.時間の計測
こちらをまずは実装していきます。
ディープラーニングのアルゴリズムには様々な乱数によって変化する箇所が存在します。
具体的にはニューラルネットワークにおける重みパラメータの初期値等があります。
これが毎回実験する度に、異なる結果になってしまっては比較できないため、乱数のパターンを固定する必要があります。
それが「乱数の固定」です。
時間計測の方はPythonではお馴染みのtimeモジュールを使っていきます。
それでは早速ソースコードを書いていきます。
まずはimportから。変更するソースコードは「train.py」です。
import random import time
randomモジュールとtimeモジュールをまずは先頭行でimportしましょう。
乱数の固定はmain関数の実行前に行いましょう。
if __name__ == "__main__": # 乱数の固定 np.random.seed(7) random.seed(7) torch.manual_seed(7) torch.cuda.manual_seed(7) print('Start') main() print('End')
seed()の()内に好きな数字入れてください。数字自体に意味はないので、なんでもいいです。
これで乱数の出方は固定されます。何回やっても同じ乱数です。
時間計測は86行目あたりにあるy_pred = model.forward(x_test)の時間を計測したいので、ここに以下を追加します。
# 時間の計測 start = time.time() y_pred = model.forward(x_test) end = time.time() - start print('処理時間:{}sec'.format(end))
これで一回あたりの推論の処理時間を計測できます。
以上で実験の準備は完了です。
評価指標
見るべき項目は、精度と処理時間です。
精度については、前回同様100epoch目の予測結果とlossの推移で比較します。
処理時間については今回実装した計測時間で比較します。
実験
今回の実験は、活性化関数の違いによる精度と処理時間の差をみたいので、
・tanh vs hardtanh
・tanh vs ReLu
で比較してみようと思います。
tanh vs hardtanh
比較の前に、前回のmodel.pyはhardtanhでしたので、tanhに変更しましょう。
また比較のため、乱数を固定し、hardtanh側でも再度学習・評価を実施します。
活性化関数の変更箇所はclass RNNHardCellのforward関数です。
def forward(self, x, state=None): self.state = state if self.state is None: #self.state = torch.tanh(self.in_h(x)) self.state = F.hardtanh(self.in_h(x)) else: #self.state = torch.tanh(self.in_h(x) + self.h_h(self.state)) self.state = F.hardtanh(self.in_h(x) + self.h_h(self.state)) return self.state
F.tanhを使おうとすると以下のような警告が出たので、tanh側はtorch.tanhを使用しました。
UserWarning: nn.functional.tanh is deprecated. Use torch.tanh instead.
なぜ非推奨なのかは説明してくれませんね。フレームワークでよくある仕様変更中なのかもしれません。
公式確認しましたが、計算上はどちらも問題なさそうなので、素直に警告に従ってみました。
ではそれぞれ学習・評価した結果を張っていきますね。まずはhardtanh。
続いてtanh。
ちょっと想定外の結果になってしまいましたね。。
hardtanhがわずかながら処理時間が短いのは想定通りです。
しかし精度面においてはtanhの方が良い結果が出ることを想定していました。
tanh側の推論結果ひどいですね・・。lossもhardtanhと比べると全然下がってない。
単純にepoch数が少ないというのもあるかもしれませんが、とはいえこれほど精度に差が出てしまうとは。
これならtanhじゃなくてhardtanh一択ですよね。
ちょっとepoch数を増やしてみますね。
epoch数を100→200へ。
train.pyのepochs = 200に設定して、再学習。
同様にhardtanhから。
続いてtanh。
なんかtanh側は更に酷くなってしまいましたね。
lossもhardtanhの方が一桁分ぐらい低いし、epoch数の問題ではなさそう。
tanhのtest側のlossの動き、なんだこれ・・。
ちなみにF.tanhも試してみましたが、結果は全く同じ数値を示したのでtanhとしてどちらも同じです。
これ以上考えるとハマりそうなので一旦やめておきます。
原因については後日考えてみることにします。
何かわかったらまた記事にしてみますね。
何か原因について知ってる方がいましたらコメント等いただけると助かります。
tanh vs ReLu
それでは次はReLuで学習・評価をしてみましょう。
そもそもなぜReLu?と思う方もいるかもしれませんので、理由をさらっといくつか書いておきます。
・一般的にtanhは勾配消失問題を抱えているため
・RNNは長期記憶が苦手という課題の対策のため
→次回以降のLSTMに繋がります。
・PyTorchにも標準のRNNにも引数としてtanhかReLuか選べるため
勾配消失や長期記憶等について知りたい方は、私のディープラーニングの教科書である『ゼロから作るDeepLearning②』をお読みください。
https://www.amazon.co.jp/%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89%E4%BD%9C%E3%82%8BDeep-Learning-%E2%80%95%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E%E5%87%A6%E7%90%86%E7%B7%A8-%E6%96%8E%E8%97%A4-%E5%BA%B7%E6%AF%85/dp/4873118360www.amazon.co.jp
def forward(self, x, state=None): self.state = state if self.state is None: #self.state = torch.tanh(self.in_h(x)) #self.state = F.hardtanh(self.in_h(x)) self.state = F.relu(self.in_h(x)) else: #self.state = torch.tanh(self.in_h(x) + self.h_h(self.state)) #self.state = F.hardtanh(self.in_h(x) + self.h_h(self.state)) self.state = F.relu(self.in_h(x) + self.h_h(self.state)) return self.state
を追加します。
それではReLuで動かした結果を貼っていきます。
条件は同じくepoch200で。
全く予測できてないですね。。
lossも全然ダメですね。。
処理速度も別段hardtanhより優れているわけでもなし。。
一番ReLuが精度的に良い結果になる予定だったのですが、これではLSTMにまだ進めませんね。。
ちょっとデータセット変えてみたり、PyTorch公式のRNN調べてみて、どこかバグってないか調査してみます。
どなたかRNN詳しい方いましたらアドバイスをコメント欄にいただけると幸いです。
それでは次回は色々対策した結果を書いてみようと思います。
今回はここまで!