概要
LSTMは、RNNの勾配消失問題を解決するために1997年にHochreiterとSchmidhuberが提案したアーキテクチャである。隠れ状態に加えてセル状態という長期記憶専用のベクトルを導入し、入力ゲート・忘却ゲート・出力ゲートの3つのゲート機構で情報の流れを制御する。ゲートにより勾配が長距離を遡りやすくなり、数百ステップ先の依存関係を学習できる。
直感・モチベーション
通常のRNNでは隠れ状態が毎ステップ を通過するため、勾配が指数的に小さくなる(勾配消失)。「100単語前の主語が現在の動詞の形を決める」といった長距離依存を学習できない。
LSTMの設計思想は「情報の高速道路(セル状態)を作る」ことである。セル状態は加算のみで更新されるため、勾配が直接遡れる経路が生まれる。各ゲートが「何を記憶し、何を忘れ、何を出力するか」を入力に応じて動的に決める。
- 忘却ゲート: 過去の記憶のうち不要な部分を消す
- 入力ゲート: 新しい情報のうち記憶すべき部分を選ぶ
- 出力ゲート: 現在のセル状態のうち外部に出力する部分を選ぶ
数学的定式化
時刻 の入力 、前ステップの隠れ状態 、セル状態 に対して:
ここで はシグモイド関数、 は要素積、 はベクトルの結合。
導出を見る
なぜセル状態で勾配消失が緩和されるか:
セル状態の更新式 を時刻 から まで逆伝播すると:
(忘却ゲートが開いている)の場合、この積は1に近くなり勾配が消えない。通常のRNNでは が掛かるため積が0に近づく。
忘却ゲートの初期化:
忘却ゲートのバイアス を大きな正の値(例:1〜5)で初期化すると、学習初期から となり長期記憶が保たれやすい。
重要な性質・注意点
- パラメータ数: RNNの約4倍のパラメータを持つ(ゲートごとに重み行列が必要)
- 計算コスト: ゲート演算が増えるため、同じ隠れ次元のRNNより約4倍遅い
- 双方向LSTM: 順方向と逆方向の2つのLSTMを組み合わせると、未来の文脈も利用できる
まとめ
- セル状態という長期記憶専用の経路を追加し、勾配消失を大幅に緩和
- 3つのゲートが入力に応じて情報の記憶・忘却・出力を動的に制御
- RNNより約4倍のパラメータと計算コストが必要
- 現在はTransformerに多くのタスクで置き換えられたが、低レイテンシ・省メモリな系列処理では依然有力
実装例
pythonimport math def sigmoid(x: float) -> float: return 1.0 / (1.0 + math.exp(-x)) class LSTMCell: def __init__(self, input_dim: int, hidden_dim: int): import random self.hidden_dim = hidden_dim d = input_dim + hidden_dim scale = 0.01 # 4つのゲートをまとめた重み行列(f, i, c_tilde, o の順) self.W = [[random.gauss(0, scale) for _ in range(d)] for _ in range(4 * hidden_dim)] self.b = [0.0] * (4 * hidden_dim) # 忘却ゲートのバイアスを大きく初期化(長期記憶を保ちやすくする) for j in range(hidden_dim): self.b[j] = 1.0 def matvec(self, W, x): return [sum(W[i][j] * x[j] for j in range(len(x))) for i in range(len(W))] def forward(self, x: list[float], h: list[float], c: list[float]): xh = x + h gates = [self.matvec(self.W, xh)[i] + self.b[i] for i in range(4 * self.hidden_dim)] H = self.hidden_dim f = [sigmoid(gates[i]) for i in range(0 * H, 1 * H)] i = [sigmoid(gates[i]) for i in range(1 * H, 2 * H)] g = [math.tanh(gates[i]) for i in range(2 * H, 3 * H)] o = [sigmoid(gates[i]) for i in range(3 * H, 4 * H)] c_new = [f[j] * c[j] + i[j] * g[j] for j in range(H)] h_new = [o[j] * math.tanh(c_new[j]) for j in range(H)] return h_new, c_new class LSTM: def __init__(self, input_dim: int, hidden_dim: int): self.cell = LSTMCell(input_dim, hidden_dim) self.hidden_dim = hidden_dim def forward(self, xs: list[list[float]]): h = [0.0] * self.hidden_dim c = [0.0] * self.hidden_dim hs = [] for x in xs: h, c = self.cell.forward(x, h, c) hs.append(h[:]) return hs # 使用例 model = LSTM(input_dim=4, hidden_dim=8) xs = [[0.1, 0.2, 0.3, 0.4]] * 10 # 長さ10の系列 hs = model.forward(xs) print("最終隠れ状態:", hs[-1][:3], "...")
関連記事
前提知識
- RNN — LSTMが解決する勾配消失問題の元となるアーキテクチャ
- ニューラルネットワーク基礎 — 活性化関数・誤差逆伝播
派生技術
- GRU — LSTMのゲートを2つに簡略化した軽量版
- Seq2Seq — LSTMをエンコーダ・デコーダに使った系列変換モデル
- Attention — LSTMの長距離依存の限界をさらに補う機構
応用事例
- 機械翻訳 — Seq2SeqのエンコーダとデコーダにLSTMを使用
- 感情分析 — 文章全体を読んだ後の最終隠れ状態で分類
用語解説
| 用語 | 説明 |
|---|---|
| セル状態 | LSTMが保持する長期記憶専用のベクトル |
| 忘却ゲート | 前ステップのセル状態をどれだけ保持するかを決めるゲート |
| 入力ゲート | 新しい候補情報をどれだけセル状態に加えるかを決めるゲート |
| 出力ゲート | セル状態のどの部分を隠れ状態として出力するかを決めるゲート |
| 要素積() | 同じ次元のベクトルを要素ごとに掛け合わせる演算 |
| 双方向LSTM | 順方向・逆方向の2つのLSTMを組み合わせたアーキテクチャ |
関連リンク
この記事への参照
- 比較対象RNN