この記事は個人的なお勉強用のメモです。
このお勉強の位置
深層学習 Day 3
Section 1 再帰型ニューラルネットワークの概念
Section 2 LSTM ← これ
Section 3 GRU
Section 4 双方向RNN
Section 5 Seq2Seq
Section 6 Word2vec
Section 7 Attention Mechanism
講義
RNNの課題
時系列を遡ると勾配が消失する。
長い時系列の学習が困難。
LSTMで上記の課題を解決できる。
用語
勾配爆発
逆伝播すると勾配が指数関数に大きくなってしまうこと。
勾配クリッピング
勾配のノルムがしきい値を超えたら、
勾配のノルムを正規化すること。
シンプルなRNNの勾配爆発は、勾配クリッピングによって解決できる。
演習チャレンジ(勾配クリッピング)
def gradient_clipping(grad, threshold):
norm = np.linalg.norm(grad)
rate = threshold / norm
if rate < 1:
return grad * rate
return grad
np.linalg.norm関数:ノルムを計算して出力
講義では 「gradient * rate」が正解と言っているが
「grad * rate」が正解だと思う。
また、素朴な疑問だが、「勾のノルムがしきい値より大きいとき」なので
rateという変数を使うのではなく、
def gradient_clipping(grad, threshold):
norm = np.linalg.norm(grad)
rate = threshold / norm
if norm > threshhold:
return grad * (norm / threshold)
return grad
の方がわかりやすいと思ったが、後で赤本2の 6.1.4 項を読んで、納得した。
rate 変数を先に作っておけば、複数の勾配を更新するときに
rate 変数を使えて便利なため。
LSTM
Long short-term memory
RNNとは構造が全く異なる。
CEC
Constant Error Carousel
勾配を1に保つ。
※ 勾配消失:勾配 < 1
勾配爆発:勾配 > 1
CECの課題1:
単にCECを導入すると、重みが一律になってしまう(NNの学習特性が無い)。
解決法:
入力ゲートと出力ゲートの追加
CECの課題2:
CECは過去の情報をすべて保管し続けてしまう。
解決法:
忘却ゲートの追加
CECの課題3:
CECに保存されている値が他のゲートに影響を与えていない。
(任意のタイミングで他のノードに伝播させたり、任意のタイミングで忘却させたい。)
解決法:
覗き穴結合の追加
入力ゲートと出力ゲート
重みを持つ入力ゲートと出力ゲートを加えることで
CECの課題を解決。
入力や出力によって重みを変化させる。
忘却ゲート
過去の情報が要らなくなったときに、情報を忘却する。
演習チャレンジ(LSTM)
def lstm(x, prev_h, prev_c, W, U, b):
lstm_in = _activation(x.dot(W.T)) + prev_h.dot(U.T) + b)
a, i, f, o = np.hsplit(lstm_in, 4)
a = np.tanh(a)
input_gate = _sigmoid(i)
forget_gate = _sigmoid(f)
output_gate = _sigmoid(o)
c = input_gate * a + forget_gate * c
h = output_gate * np.tanh(c)
return c, h
新しいセルの状態=計算されたセルへの入力 × 入力ゲート +
1ステップ前のセルの状態 × 忘却ゲート
覗き穴結合
CEC自身の値に、重み行列を介して伝播可能にした構造。
- CECから入力ゲートへの覗き穴結合
- CECから出力ゲートへの覗き穴結合
- CECから忘却ゲートへの覗き穴結合
実装演習
なし
確認テスト
シグモイド関数の微分
シグモイド関数を微分すると $f(z)(1-f(z))$ になる。
この値が最大になるのは、$f(z)=1-f(z)$ のとき。
すなわち、$f(z)=0.5$ のとき。
このとき、シグモイド関数の値は $0.5\times (1-0.5)=0.25$ になる。
LSTMのゲート
「映画おもしろかったね。ところで、とてもお腹が空いたから何か____。」
「とても」が無くなっても空欄に影響を及ぼさないのだから、
「とても」は不要な言葉になる。
つまり、忘却ゲートによって忘却されても問題ない。
修了テスト~練習問題~
問題15(LSTM)
LSTMの構造
(i):入力ゲート
(f):忘却ゲート
(o):出力ゲート
問題16と問題17(LSTM)
LSTMのサンプルコード
class LSTM:
def __init__(self, input_size=input_size, hidden_size=hidden_size):
self.input_size, self.hidden_size = input_size, hidden_size
self.weight = np.random.randn(self.input_size + self.hidden_size, self.hidden_size * 4)
self.bios = np.random.randn(self.hidden_size * 4)
def forward(self, x, h_prev, c_prev):
inputs = np.concaterate([x, h_prev], axis=1) # xとh_prevを一つにまとめる。
inputs = np.matmul(inputs, self.weight) + self.bios
i, f, o, g = np.hsplit(inputs, 4)
input_gate, forget_gate, output_gate, g = sigmoid(i), sigmoid(f), sigmoid(o), np.tanh(g)
c_next = (c_prev * forget_gate) + (g * input_gate) #
h_next = np.tanh(c_next) * output_gate
return h_next, c_next
問題16
各ゲートの値はスカラー値である(sigmoid関数の戻り値なので)。
スカラー値を行列に掛けるには、*(アスタリスク)の演算子を使う。
※ 選択肢1の「gorget_gate」は、おそらく「forget_gate」のスペルミス。
問題17
次の隠れ状態 h_next は、次の記憶セル c_next に tanh を作用させて、
出力ゲート output_get を掛けることによって更新する。