47979


最近、RNNについて勉強しています。今ちょうど、LSTMについて勉強しているところなのですが、tensorflowやkerasの使い方はなんとなく分かるけれど応用できるほどの技量はないので、イメージを膨らますためにもとりあえずPythonのnumpyのみでコードを書いてみようと思いました。一応、入出力ゲート、忘却ゲート、CEC、覗き穴結合などを搭載した自作LSTMを作ってみたのですが、うまく動きません。うまく動かないというのは、出力がnanになってしまいます。多分、重みを更新する際にすでに重み自体がnanになっているのではないかと考えられます。そこで、以下のコードで怪しい点などありますか？（個人の見解としては誤差逆伝播あたりが原因な気がしてます。）  
また、そもそもtensorflowなどを使わないのは無謀ですか？そこらへんまだよくわからないので教えていただきたいです。

---コードについて---  
alpha、gammaは学習率、equation()はy=xの線形変換をしてるだけなので意味はないです。（見やすかったので...）tanhとSigmoidの微分は頭にdをつけてます。また、各行列の宣言は行っていますが、長くなるので省きました。重みの初期値は平均0、標準偏差0.1の正規分布です。バイアスの初期値は0行列です。

# コード

[code]

            #input gate
            i_[:, t] = np.dot(w[0, t, :, :], x[:, t]) + np.dot(u[0, t, :, :], h[:, t-1]) + np.dot(v[0, t, :, :], c[:, t-1]) + b[0, :, t]
            i[:, t] = Sigmoid(i_[:, t])
            #forget gate
            f_[:, t] = np.dot(w[2, t, :, :] , x[:, t]) + np.dot(u[2, t, :, :], h[:, t-1]) + np.dot(v[2, t, :, :], c[:, t-1]) + b[2, :, t]
            f[:, t] = Sigmoid(f_[:, t])
            #activated
            a_[:, t] = np.dot(w[3, t, :, :] , x[:, t]) + np.dot(u[3, t, :, :], h[:, t-1]) + b[3, :, t]
            a[:, t] = tanh(a_[:, t])
            #CEC
            c[:, t] = i[:, t]*a[:, t] + f[:, t]*c[:, t-1]
            #output gate
            o_[:, t] = np.dot(w[1, t, :, :] , x[:, t]) + np.dot(u[1, t, :, :], h[:, t-1]) + np.dot(v[1, t, :, :], c[:, t]) + b[1, :, t]
            o[:, t] = Sigmoid(o_[:, t])
            #hidden 
            h[:, t] = o[:, t] * tanh(c[:, t])
            #output layer
            q[:, t] = np.dot(vv[t, :, :], h[:, t]) + d1[:, t]
            #output
            y[:, t] = equation(q[:, t])
            
            e_h = 1
            e_c[:, t] = e_h * o[:, t] * dtanh(c[:, t])
            e_c[:, t-1] = e_c[:, t] * f[:, t]
            e_o[:, t] = y[:, t] - T[:, t]
            
            e_i_[:, t] = e_c[:, t] * a[:, t] * dSigmoid(i_[:, t])
            e_o_[:, t] = e_h * tanh(c[:, t]) * dSigmoid(o_[:, t])
            e_f_[:, t] = e_c[:, t] * c[:, t-1] * dSigmoid(f_[:, t])
            e_a_[:, t] = e_c[:, t] * i[:, t] * dtanh(a_[:, t])
            
            dw[0, t, :, :] = np.dot(e_i_[:, t], x[:, t])
            du[0, t, :, :] = np.dot(e_i_[:, t], h[:, t-1])
            dv[0, t, :, :] = np.dot(e_i_[:, t], c[:, t-1])
            db[0, :, t] = e_i_[:, t]
            
            dw[1, t, :, :] = np.dot(e_o_[:, t], x[:, t])
            du[1, t, :, :] = np.dot(e_o_[:, t], h[:, t-1])
            dv[1, t, :, :] = np.dot(e_o_[:, t], c[:, t])
            db[1, :, t] = e_o_[:, t]
            
            dw[2, t, :, :] = np.dot(e_f_[:, t], x[:, t])
            du[2, t, :, :] = np.dot(e_f_[:, t], h[:, t-1])
            dv[2, t, :, :] = np.dot(e_f_[:, t], c[:, t-1])
            db[2, :, t] = e_f_[:, t]
            
            dw[3, t, :, :] = np.dot(e_a_[:, t], x[:, t])
            du[3, t, :, :] = np.dot(e_a_[:, t], h[:, t-1])
            db[3, :, t] = e_a_[:, t]
            
            dvv[t, :, :] = np.dot(e_o[:, t], h[:, t])
            dd1[:, t] = e_o[:, t]
            
            w[:, t+1, :, :] = w[:, t, :, :] - alpha * dw[:, t, :, :]
            u[:, t+1, :, :] = u[:, t, :, :] - alpha * du[:, t, :, :]
            v[:, t+1, :, :] = v[:, t, :, :] - alpha * dv[:, t, :, :]
            b[:, :, t+1] = b[:, :, t] - alpha * db[:, :, t]
            
            vv[t+1, :, :] = vv[t, :, :] - gamma * dvv[t, :, :]
            d1[:, t+1] = d1[:, t] - gamma * dd1[:, t]
    
[/code]


CC BY-SA 4.0
2018-08-30T15:13:21.933
47948
2018-09-01T12:48:52.297
2020-06-17T08:14:45.997
-1
29506
question
python 機械学習
LSTMをPythonで


質問のコード中には現れていませんが、`log` を使っていませんか?

`log` は、微小な値を引数に与えると `-inf` を返します。この `-inf` を元に計算を続けると `nan` になる場合があります。

`log` を使っているなら、その引数と返り値をじっくり確認することをお勧めします。  
もし `log` を使っていて、その引数が微小な場合があるなら、下限を設けるのが良いと思います。

* * *

コメントを受けて追記。

闇雲に上限下限を設定すると、学習がうまくいかない等の問題を引き起こしかねません。  
まずはどこで異常が起きているのかを調べるべきかと思います。

例えば以下のような関数を作り、

[code]

    def checkvalue(v):
        if np.count_nonzero(np.isnan(v)) + np.count_nonzero(np.isinf(v)) > 0:
            raise ValueError('nan or inf found.')
    
[/code]

以下のような感じで `checkvalue()` を追加していけば、

[code]

        #input gate
        i_[:, t] = np.dot(w[0, t, :, :], x[:, t]) + np.dot(u[0, t, :, :], h[:, t-1]) + np.dot(v[0, t, :, :], c[:, t-1]) + b[0, :, t]
        checkvalue(i_[:, t])
        i[:, t] = Sigmoid(i_[:, t])
        checkvalue(i[:, t])
        #forget gate
        f_[:, t] = np.dot(w[2, t, :, :] , x[:, t]) + np.dot(u[2, t, :, :], h[:, t-1]) + np.dot(v[2, t, :, :], c[:, t-1]) + b[2, :, t]
        checkvalue(f_[:, t])
        f[:, t] = Sigmoid(f_[:, t])
        checkvalue(f[:, t])
    
[/code]

どこで異常が起きているかは判るはずです。

それが判ったら、次はその計算の元になった行列の値を `print()` すれば、  
具体的にどこで何故異常が起きているのか調べられるはずです。


CC BY-SA 4.0
2018-08-31T12:56:41.397
47979
2018-09-01T12:48:52.297
2018-09-01T12:48:52.297
5288
5288
47948
answer