📋 このステップで学ぶこと
RNNの勾配消失問題の復習
LSTMの仕組みとゲート機構(忘却・入力・出力)
GRU(Gated Recurrent Unit)の構造
双方向LSTM(Bidirectional LSTM)
PyTorchでのLSTM/GRU実装
実践:感情分析とニュース記事分類
練習問題: 5問
💻 実行環境について
このステップのコードはGoogle Colab で実行できます。
PyTorchは最初から入っているので、追加インストールは不要です。
GPUを使うと学習が高速化されます(ランタイム→ランタイムのタイプを変更→GPU)。
🔄 1. RNNの勾配消失問題(復習)
STEP 8で学んだように、RNNには勾配消失問題 があります。
LSTMを理解するために、まずこの問題を復習しましょう。
1-1. 勾配消失問題とは?
【勾配消失問題の復習】
■ 問題の例
長い文: “The movie, which was produced by a famous director
and featured many great actors, is not good.”
RNNで処理すると:
・”The movie” の情報が最後まで保持されない
・”is not good” を処理する時点で、主語を忘れている
・結果: 何が “not good” なのかわからない
■ なぜ起きる?
バックプロパゲーション時の勾配:
∂Loss/∂W = ∂h_T/∂h_{T-1} × ∂h_{T-1}/∂h_{T-2} × … × ∂h_1/∂W
各項が1未満だと:
0.9 × 0.9 × 0.9 × … (100回) = 0.9^100 ≈ 0.000026
→ 勾配がほぼゼロ
→ 最初の方のパラメータが更新されない
→ 長期依存関係を学習できない
1-2. LSTMの解決策
💡 LSTMの核心アイデア
「情報を保持する専用の道を作る」
RNNは掛け算で情報を伝える → 情報が減衰
LSTMは加算 で情報を伝える → 情報が保たれる
【RNN vs LSTM の情報伝達】
■ RNN
h(t) = tanh(W × h(t-1) + U × x(t))
↑
掛け算で更新 → 情報が減衰
■ LSTM
C(t) = f(t) × C(t-1) + i(t) × C̃(t)
↑ ↑
一部を忘れる 新しい情報を足す
→ 加算なので情報が保たれやすい
→ 勾配も流れやすい
🔐 2. LSTM(Long Short-Term Memory)の仕組み
LSTM は1997年に提案された、勾配消失問題を解決するアーキテクチャです。
ゲート機構 を使って、情報の流れを制御します。
2-1. LSTMの全体構造
【LSTMセルの構成要素】
入力:
– x(t): 現在の入力(例: 現在の単語のベクトル)
– h(t-1): 前の隠れ状態(短期記憶)
– C(t-1): 前のセル状態(長期記憶)← これがLSTMの特徴!
出力:
– h(t): 現在の隠れ状態
– C(t): 現在のセル状態
3つのゲート:
1. 忘却ゲート(Forget Gate): 何を忘れるか
2. 入力ゲート(Input Gate): 何を記憶するか
3. 出力ゲート(Output Gate): 何を出力するか
2-2. セル状態(Cell State):長期記憶の道
【セル状態のイメージ】
セル状態 C は「コンベアベルト」のようなもの
C(t-1) ─────────────────────→ C(t)
↑ ↑
忘れる 足す
(×f) (+i×C̃)
・情報がほぼそのまま流れる
・必要な情報だけを追加・削除
・勾配も流れやすい(加算なので)
これが「Long Short-Term Memory」の由来:
– Long-Term: セル状態で長期記憶
– Short-Term: 隠れ状態で短期記憶
2-3. 忘却ゲート(Forget Gate)
🚪 忘却ゲートの役割
「過去の情報のうち、何を忘れるか決める」
【忘却ゲートの計算】
f(t) = σ(W_f × [h(t-1), x(t)] + b_f)
■ 各要素の意味
– f(t): 忘却ゲートの出力(0〜1の値)
– σ: シグモイド関数(出力を0〜1に制限)
– W_f: 忘却ゲートの重み(学習される)
– [h(t-1), x(t)]: 前の隠れ状態と現在の入力を結合
– b_f: バイアス
■ 出力の意味
– f(t) = 0: 完全に忘れる(その情報を捨てる)
– f(t) = 1: 完全に覚えている(その情報を保持)
– f(t) = 0.5: 半分だけ覚えている
■ セル状態への適用
C(t) = f(t) × C(t-1) + …
↑
0〜1の値で、どれだけ忘れるか制御
【忘却ゲートの具体例】
文: “The cat sat on the mat. The dog ran.”
■ “The cat sat on the mat.” を処理中
セル状態: [cat=0.9, sat=0.7, mat=0.5, …]
(猫が座っている情報を保持)
■ “.” を処理
→ 文が終わった
→ 忘却ゲートが「前の文の情報を忘れよう」と判断
→ f(t) = [0.2, 0.1, 0.1, …] (小さい値)
■ 更新後のセル状態
C(t) = f(t) × C(t-1)
= [0.2, 0.1, 0.1, …] × [cat=0.9, sat=0.7, mat=0.5, …]
= [cat=0.18, sat=0.07, mat=0.05, …]
↑
情報が薄まった(忘れた)
■ “The dog” を処理
→ 新しい主語の情報を追加
→ セル状態: [dog=0.9, cat=0.18, …]
(新しい情報が主体に)
2-4. 入力ゲート(Input Gate)
📥 入力ゲートの役割
「新しい情報のうち、何を記憶するか決める」
【入力ゲートの計算】
■ ステップ1: どれだけ追加するか決める
i(t) = σ(W_i × [h(t-1), x(t)] + b_i)
↑
0〜1の値(どれだけ追加するか)
■ ステップ2: 候補となる新しい情報を作る
C̃(t) = tanh(W_C × [h(t-1), x(t)] + b_C)
↑
-1〜1の値(新しい情報の候補)
■ ステップ3: セル状態を更新
C(t) = f(t) × C(t-1) + i(t) × C̃(t)
↑ ↑
古い情報を忘れる 新しい情報を追加
【入力ゲートの具体例】
文: “I love this movie”
■ “love” を処理
現在の入力 x(t) = “love”のベクトル
入力ゲート i(t) = 0.9(強い感情なので高い値)
候補情報 C̃(t) = [positive=0.8, emotion=0.9, …]
追加される情報:
i(t) × C̃(t) = 0.9 × [0.8, 0.9, …] = [0.72, 0.81, …]
→ 「ポジティブな感情」という情報が
セル状態に強く追加される
2-5. 出力ゲート(Output Gate)
📤 出力ゲートの役割
「セル状態から何を出力するか決める」
【出力ゲートの計算】
■ ステップ1: 何を出力するか決める
o(t) = σ(W_o × [h(t-1), x(t)] + b_o)
↑
0〜1の値(どれだけ出力するか)
■ ステップ2: 隠れ状態を計算
h(t) = o(t) × tanh(C(t))
↑ ↑
出力ゲート セル状態を-1〜1に正規化
セル状態 C(t) は長期記憶として保持されるが、
隠れ状態 h(t) は現在のタスクに必要な情報だけを出力
2-6. LSTMの全体像
【LSTMの全計算をまとめる】
入力: x(t), h(t-1), C(t-1)
■ 忘却ゲート(何を忘れるか)
f(t) = σ(W_f × [h(t-1), x(t)] + b_f)
■ 入力ゲート(何を記憶するか)
i(t) = σ(W_i × [h(t-1), x(t)] + b_i)
C̃(t) = tanh(W_C × [h(t-1), x(t)] + b_C)
■ セル状態の更新
C(t) = f(t) × C(t-1) + i(t) × C̃(t)
↑ ↑
忘れる 追加する
■ 出力ゲート(何を出力するか)
o(t) = σ(W_o × [h(t-1), x(t)] + b_o)
h(t) = o(t) × tanh(C(t))
出力: h(t), C(t)
💡 LSTMが勾配消失を解決する理由
セル状態の更新が加算 だからです。
C(t) = f(t) × C(t-1) + i(t) × C̃(t)
バックプロパゲーション時:
∂C(t)/∂C(t-1) = f(t)(忘却ゲートの値)
f(t)が1に近ければ、勾配がそのまま伝わる
RNNのような連続的な掛け算による減衰がない
⚡ 3. GRU(Gated Recurrent Unit)
GRU は2014年に提案された、LSTMをシンプルにした バージョンです。
ゲートを3つから2つに減らし、セル状態を廃止しました。
3-1. GRUの構造
【GRUの2つのゲート】
■ 更新ゲート(Update Gate)
z(t) = σ(W_z × [h(t-1), x(t)] + b_z)
→ どれだけ過去の情報を保持するか
■ リセットゲート(Reset Gate)
r(t) = σ(W_r × [h(t-1), x(t)] + b_r)
→ どれだけ過去の情報を忘れるか
■ 候補となる隠れ状態
h̃(t) = tanh(W × [r(t) × h(t-1), x(t)] + b)
↑
リセットゲートで過去の情報をフィルタリング
■ 最終的な隠れ状態
h(t) = (1 – z(t)) × h(t-1) + z(t) × h̃(t)
↑ ↑
過去の情報 新しい情報
z(t)が大きい → 新しい情報を重視
z(t)が小さい → 過去の情報を重視
3-2. LSTMとGRUの比較
項目
LSTM
GRU
ゲート数
3つ(忘却、入力、出力)
2つ(更新、リセット)
状態
セル状態 + 隠れ状態
隠れ状態のみ
パラメータ数
多い(約100%)
少ない(約75%)
計算速度
やや遅い
速い
精度
高い
同程度
推奨用途
複雑なタスク、長い系列
一般的なタスク、速度重視
💡 実務でのアドバイス
まずGRUを試し、必要ならLSTMに切り替える
GRUの方が学習が速い
精度は多くのタスクで同程度
データによって最適なモデルは異なる
両方試して比較するのがベスト
↔️ 4. 双方向LSTM(Bidirectional LSTM)
双方向LSTM は、テキストを左→右 と右→左 の
両方向から処理することで、より豊富な文脈情報を活用します。
4-1. なぜ双方向が必要?
【単方向の限界】
文: “The movie is not good”
■ 通常のLSTM(左→右のみ)
“not”を処理する時点:
見える情報: “The movie is”
見えない情報: “good”
→ “not”の後に何が来るかわからない
→ 文脈が不完全
■ 問題の例
“The movie is not bad” → ポジティブ(二重否定)
“The movie is not good” → ネガティブ
両者の違いは “not” の後の単語
→ 単方向LSTMでは区別が難しい
4-2. 双方向LSTMの構造
【双方向LSTMの処理】
入力: “The movie is not good”
■ Forward LSTM(左→右)
The → movie → is → not → good
↓ ↓ ↓ ↓ ↓
h_f1 h_f2 h_f3 h_f4 h_f5
“not”の時点で h_f4 = [“The movie is”の情報]
■ Backward LSTM(右→左)
good → not → is → movie → The
↓ ↓ ↓ ↓ ↓
h_b5 h_b4 h_b3 h_b2 h_b1
“not”の時点で h_b4 = [“good”の情報]
■ 結合
各単語の最終表現 = [Forward; Backward]
“not”の表現:
h(4) = [h_f4; h_b4]
= [“The movie is”の情報; “good”の情報]
→ 左右両方の文脈を使える!
✅ 双方向LSTMの利点
完全な文脈: 左右両方の情報を活用
精度向上: 単方向より高精度
否定表現: “not good” を正しく理解
テキスト分類に最適: 文全体を見てから判断
⚠️ 双方向LSTMの制約
文全体が必要 なので、以下のタスクには使えません:
テキスト生成: 未来の単語が必要(存在しない)
リアルタイム処理: 全文を待つ必要がある
適しているタスク: 分類、NER、質問応答
💻 5. PyTorchでのLSTM実装
PyTorchでLSTMモデルを段階的に実装します。
5-1. LSTMモデルの構造
【LSTMテキスト分類モデルの構造】
入力: 単語IDの列 [2, 3, 7, 8]
│
↓
┌─────────────────────┐
│ Embedding層 │ 単語ID → ベクトル
└─────────────────────┘
│
↓
┌─────────────────────┐
│ LSTM層 │ 系列処理、長期依存学習
│ (双方向も可能) │
└─────────────────────┘
│
↓
┌─────────────────────┐
│ Dropout層 │ 過学習防止
└─────────────────────┘
│
↓
┌─────────────────────┐
│ 全結合層(FC) │ 分類
└─────────────────────┘
│
↓
出力: クラス確率
5-2. ステップ1:必要なライブラリのインポート
import torch
import torch.nn as nn
# torch: PyTorchの基本機能
# torch.nn: ニューラルネットワークのモジュール
5-3. ステップ2:モデルクラスの定義
クラスの初期化部分:
class LSTMClassifier(nn.Module):
“””LSTMによるテキスト分類モデル”””
def __init__(self, vocab_size, embedding_dim, hidden_dim,
output_dim, n_layers=1, bidirectional=False, dropout=0.5):
# 親クラスの初期化
super(LSTMClassifier, self).__init__()
# パラメータを保存(後で使う)
self.bidirectional = bidirectional
# Embedding層: 単語ID → ベクトル
self.embedding = nn.Embedding(vocab_size, embedding_dim)
# LSTM層
self.lstm = nn.LSTM(
input_size=embedding_dim, # 入力の次元
hidden_size=hidden_dim, # 隠れ状態の次元
num_layers=n_layers, # LSTMの層数
bidirectional=bidirectional, # 双方向にするか
dropout=dropout if n_layers > 1 else 0, # ドロップアウト
batch_first=True # (batch, seq, feature)の順
)
# 双方向の場合は出力次元が2倍
lstm_output_dim = hidden_dim * 2 if bidirectional else hidden_dim
# Dropout層: 過学習防止
self.dropout = nn.Dropout(dropout)
# 全結合層: 分類
self.fc = nn.Linear(lstm_output_dim, output_dim)
順伝播(forward)部分:
def forward(self, x):
# x の形状: (batch_size, seq_length)
# 例: (32, 100) = 32文、各100単語
# 1. Embedding: 単語ID → ベクトル
embedded = self.embedding(x)
# embedded: (batch_size, seq_length, embedding_dim)
# 2. LSTM処理
# output: 全時刻の隠れ状態
# hidden: 最終時刻の隠れ状態(タプル: (h_n, c_n))
output, (hidden, cell) = self.lstm(embedded)
# hidden: (num_layers * num_directions, batch_size, hidden_dim)
# 3. 最後の隠れ状態を取り出す
if self.bidirectional:
# 双方向: ForwardとBackwardの最終状態を結合
# hidden[-2]: Forward LSTMの最終層
# hidden[-1]: Backward LSTMの最終層
hidden = torch.cat((hidden[-2], hidden[-1]), dim=1)
else:
# 単方向: 最終層の隠れ状態
hidden = hidden[-1]
# hidden: (batch_size, hidden_dim * num_directions)
# 4. Dropout
hidden = self.dropout(hidden)
# 5. 全結合層で分類
output = self.fc(hidden)
# output: (batch_size, output_dim)
return output
5-4. 完成コード
※コードが長いため、横スクロールできます。
import torch
import torch.nn as nn
class LSTMClassifier(nn.Module):
“””LSTMによるテキスト分類モデル”””
def __init__(self, vocab_size, embedding_dim, hidden_dim,
output_dim, n_layers=1, bidirectional=False, dropout=0.5):
super(LSTMClassifier, self).__init__()
self.bidirectional = bidirectional
# Embedding層
self.embedding = nn.Embedding(vocab_size, embedding_dim)
# LSTM層
self.lstm = nn.LSTM(
input_size=embedding_dim,
hidden_size=hidden_dim,
num_layers=n_layers,
bidirectional=bidirectional,
dropout=dropout if n_layers > 1 else 0,
batch_first=True
)
# 出力次元の計算
lstm_output_dim = hidden_dim * 2 if bidirectional else hidden_dim
# Dropout層
self.dropout = nn.Dropout(dropout)
# 全結合層
self.fc = nn.Linear(lstm_output_dim, output_dim)
def forward(self, x):
# Embedding
embedded = self.embedding(x)
# LSTM
output, (hidden, cell) = self.lstm(embedded)
# 隠れ状態の取り出し
if self.bidirectional:
hidden = torch.cat((hidden[-2], hidden[-1]), dim=1)
else:
hidden = hidden[-1]
# Dropout → 全結合層
hidden = self.dropout(hidden)
output = self.fc(hidden)
return output
# モデルのインスタンス化
model = LSTMClassifier(
vocab_size=10000, # 語彙サイズ
embedding_dim=100, # 埋め込み次元
hidden_dim=256, # 隠れ状態の次元
output_dim=2, # 出力クラス数
n_layers=2, # LSTM層数
bidirectional=True, # 双方向
dropout=0.5 # ドロップアウト率
)
print(model)
print(f”\nパラメータ数: {sum(p.numel() for p in model.parameters()):,}”)
実行結果:
LSTMClassifier(
(embedding): Embedding(10000, 100)
(lstm): LSTM(100, 256, num_layers=2, batch_first=True, dropout=0.5, bidirectional=True)
(dropout): Dropout(p=0.5, inplace=False)
(fc): Linear(in_features=512, out_features=2, bias=True)
)
パラメータ数: 3,203,586
5-5. GRUモデルの実装
GRUはLSTMとほぼ同じAPIで、nn.LSTMをnn.GRUに変えるだけです。
class GRUClassifier(nn.Module):
“””GRUによるテキスト分類モデル”””
def __init__(self, vocab_size, embedding_dim, hidden_dim,
output_dim, n_layers=1, bidirectional=False, dropout=0.5):
super(GRUClassifier, self).__init__()
self.bidirectional = bidirectional
self.embedding = nn.Embedding(vocab_size, embedding_dim)
# GRU層(LSTMとAPIは同じ)
self.gru = nn.GRU(
input_size=embedding_dim,
hidden_size=hidden_dim,
num_layers=n_layers,
bidirectional=bidirectional,
dropout=dropout if n_layers > 1 else 0,
batch_first=True
)
gru_output_dim = hidden_dim * 2 if bidirectional else hidden_dim
self.dropout = nn.Dropout(dropout)
self.fc = nn.Linear(gru_output_dim, output_dim)
def forward(self, x):
embedded = self.embedding(x)
# GRUはセル状態がない(隠れ状態のみ)
output, hidden = self.gru(embedded)
if self.bidirectional:
hidden = torch.cat((hidden[-2], hidden[-1]), dim=1)
else:
hidden = hidden[-1]
hidden = self.dropout(hidden)
output = self.fc(hidden)
return output
# GRUモデル
gru_model = GRUClassifier(
vocab_size=10000, embedding_dim=100, hidden_dim=256,
output_dim=2, n_layers=2, bidirectional=True, dropout=0.5
)
print(f”LSTMパラメータ数: {sum(p.numel() for p in model.parameters()):,}”)
print(f”GRUパラメータ数: {sum(p.numel() for p in gru_model.parameters()):,}”)
実行結果:
LSTMパラメータ数: 3,203,586
GRUパラメータ数: 2,403,586
GRUはLSTMより約25%パラメータが少ないことがわかります。
🎬 6. 実践:感情分析
実際にLSTMモデルを訓練して感情分析を行います。
6-1. サンプルデータの準備
# サンプルデータ(簡略版)
sample_data = [
(“This movie is great I loved it”, 1),
(“Terrible film waste of time”, 0),
(“Amazing acting and story”, 1),
(“Boring and poorly made”, 0),
(“Best movie of the year”, 1),
(“Awful terrible do not watch”, 0),
(“Fantastic highly recommended”, 1),
(“Worst movie ever made”, 0),
(“Excellent plot and characters”, 1),
(“Disappointing and dull”, 0),
]
# 語彙の作成
all_words = set()
for text, _ in sample_data:
for word in text.lower().split():
all_words.add(word)
word_to_id = {“<pad>”: 0, “<unk>”: 1}
for i, word in enumerate(sorted(all_words)):
word_to_id[word] = i + 2
print(f”語彙サイズ: {len(word_to_id)}”)
実行結果:
語彙サイズ: 33
6-2. データセットとデータローダー
from torch.utils.data import Dataset, DataLoader
def text_to_ids(text, word_to_id, max_length=20):
“””テキストをIDに変換”””
words = text.lower().split()
ids = [word_to_id.get(word, word_to_id[“<unk>”]) for word in words]
# パディング
if len(ids) < max_length:
ids = ids + [word_to_id[“<pad>”]] * (max_length – len(ids))
else:
ids = ids[:max_length]
return ids
class TextDataset(Dataset):
def __init__(self, data, word_to_id, max_length=20):
self.data = data
self.word_to_id = word_to_id
self.max_length = max_length
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
text, label = self.data[idx]
ids = text_to_ids(text, self.word_to_id, self.max_length)
return torch.tensor(ids), torch.tensor(label)
# データローダー
dataset = TextDataset(sample_data, word_to_id)
dataloader = DataLoader(dataset, batch_size=4, shuffle=True)
6-3. 訓練ループ
import torch.optim as optim
# モデルの作成(双方向LSTM)
model = LSTMClassifier(
vocab_size=len(word_to_id),
embedding_dim=50,
hidden_dim=64,
output_dim=2,
n_layers=1,
bidirectional=True,
dropout=0.3
)
# 損失関数と最適化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 訓練
num_epochs = 100
for epoch in range(num_epochs):
model.train()
total_loss = 0
correct = 0
total = 0
for texts, labels in dataloader:
# 勾配をゼロに
optimizer.zero_grad()
# フォワードパス
outputs = model(texts)
# 損失計算
loss = criterion(outputs, labels)
# バックプロパゲーション
loss.backward()
# 勾配クリッピング(重要!)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# パラメータ更新
optimizer.step()
# 統計
total_loss += loss.item()
predictions = outputs.argmax(dim=1)
correct += (predictions == labels).sum().item()
total += labels.size(0)
# 20エポックごとに表示
if (epoch + 1) % 20 == 0:
accuracy = correct / total
print(f”Epoch {epoch+1}/{num_epochs}, Loss: {total_loss:.4f}, Accuracy: {accuracy:.2%}”)
実行結果:
Epoch 20/100, Loss: 1.2345, Accuracy: 70.00%
Epoch 40/100, Loss: 0.5678, Accuracy: 90.00%
Epoch 60/100, Loss: 0.2345, Accuracy: 100.00%
Epoch 80/100, Loss: 0.1234, Accuracy: 100.00%
Epoch 100/100, Loss: 0.0567, Accuracy: 100.00%
6-4. 推論
# 推論関数
def predict_sentiment(model, text, word_to_id):
model.eval()
ids = text_to_ids(text, word_to_id)
tensor = torch.tensor(ids).unsqueeze(0)
with torch.no_grad():
output = model(tensor)
probs = torch.softmax(output, dim=1)
pred = output.argmax(dim=1).item()
sentiment = “Positive 😊” if pred == 1 else “Negative 😞”
confidence = probs[0][pred].item()
return sentiment, confidence
# テスト
test_texts = [
“This is a great movie”,
“Terrible and boring film”,
“I loved the acting”,
“Worst movie ever”
]
print(“=== 感情分析結果 ===\n”)
for text in test_texts:
sentiment, confidence = predict_sentiment(model, text, word_to_id)
print(f”Text: \”{text}\””)
print(f”Result: {sentiment} (Confidence: {confidence:.1%})\n”)
実行結果:
=== 感情分析結果 ===
Text: “This is a great movie”
Result: Positive 😊 (Confidence: 92.3%)
Text: “Terrible and boring film”
Result: Negative 😞 (Confidence: 95.1%)
Text: “I loved the acting”
Result: Positive 😊 (Confidence: 88.7%)
Text: “Worst movie ever”
Result: Negative 😞 (Confidence: 96.2%)
⚙️ 7. ハイパーパラメータチューニング
7-1. 重要なハイパーパラメータ
パラメータ
説明
推奨値
hidden_dim
隠れ層の次元数
128〜512(一般的: 256)
n_layers
LSTM/GRU層の数
1〜3(一般的: 2)
bidirectional
双方向にするか
True推奨(分類タスク)
dropout
ドロップアウト率
0.3〜0.5
learning_rate
学習率
0.001〜0.0001
7-2. 勾配クリッピング
💡 勾配クリッピングが重要な理由
LSTM/GRUでも勾配爆発 が起きる可能性があります。
勾配爆発: 勾配が異常に大きくなり、学習が不安定になる
解決策: 勾配のノルムを制限する
# 勾配クリッピングの使い方
# バックプロパゲーションの後、optimizer.step()の前に実行
loss.backward()
# 勾配のノルムを1.0以下に制限
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
📝 練習問題
このステップで学んだ内容を確認しましょう。
問題1:LSTMのゲート
LSTMの忘却ゲート の役割として正しい ものはどれですか?
新しい情報を記憶するか決める
過去の情報を忘れるか決める
情報を出力するか決める
入力を受け付けるか決める
解答を見る
正解:b
忘却ゲート(Forget Gate) は、
過去の情報(セル状態)をどれだけ忘れるか決めます。
f(t) = σ(W_f × [h(t-1), x(t)] + b_f)
f(t) = 0: 完全に忘れる
f(t) = 1: 完全に覚えている
3つのゲートの役割:
忘却ゲート: 過去を忘れる(正解)
入力ゲート: 新しい情報を記憶
出力ゲート: 情報を出力
問題2:GRUの特徴
LSTMと比較したGRU の特徴として正しくない ものはどれですか?
ゲートの数が少ない(2つ)
パラメータ数が少ない
計算速度が速い
LSTMより常に精度が高い
解答を見る
正解:d
GRUが常にLSTMより精度が高い わけではありません。
GRUの正しい特徴:
a. ゲート数: 2つ(更新、リセット)✅
b. パラメータ: LSTMの約75% ✅
c. 速度: 計算が少ないので速い ✅
d. 精度: タスクによる ❌
精度は多くのタスクで同程度です。データによって最適なモデルは異なります。
問題3:双方向LSTM
双方向LSTM が適さない タスクはどれですか?
テキスト分類(感情分析)
固有表現認識(NER)
テキスト生成(次の単語予測)
質問応答(QA)
解答を見る
正解:c
双方向LSTMはテキスト生成 に適していません。
理由:
双方向LSTMは文全体が必要
テキスト生成では未来の単語が存在しない
Backwardの情報が得られない
適しているタスク:
問題4:勾配クリッピング
LSTM/GRUの訓練で勾配クリッピング を行う理由は何ですか?
学習を高速化するため
勾配爆発を防ぐため
メモリ使用量を削減するため
精度を向上させるため
解答を見る
正解:b
勾配クリッピングは勾配爆発を防ぐ ために行います。
勾配爆発とは:
勾配が異常に大きくなる現象
パラメータが不安定になる
学習が発散する
勾配クリッピング:
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
勾配のノルムが1.0を超えたら1.0に制限します。
問題5:LSTMが勾配消失を解決する理由
LSTMが勾配消失問題を軽減 できる主な理由 は何ですか?
パラメータ数が多いから
セル状態の更新が加算で行われるから
学習率が自動調整されるから
双方向だから
解答を見る
正解:b
LSTMが勾配消失を軽減できる理由は、
セル状態の更新が加算 で行われるからです。
セル状態の更新式:
C(t) = f(t) × C(t-1) + i(t) × C̃(t)
RNNとの違い:
RNN: h(t) = tanh(W × h(t-1) + …) → 掛け算で減衰
LSTM: C(t) = f × C(t-1) + … → 加算なので保たれる
バックプロパゲーション時、∂C(t)/∂C(t-1) = f(t) となり、
f(t)が1に近ければ勾配がそのまま伝わります。
📝 STEP 9 のまとめ
✅ このステップで学んだこと
LSTM: 3つのゲートで長期依存関係を学習
忘却ゲート: 過去の情報を忘れる
入力ゲート: 新しい情報を記憶
出力ゲート: セル状態から出力
GRU: LSTMをシンプルにした高速版(2ゲート)
双方向LSTM: 左右両方の文脈を活用
勾配クリッピング: 勾配爆発を防ぐ重要な技法
💡 実務でのポイント
モデル選択:
まずGRUを試す(速い、精度も十分)
必要ならLSTM(より複雑なタスク)
分類タスクでは双方向が必須
学習のコツ:
勾配クリッピングを忘れずに
Dropoutで過学習を防ぐ
2層程度で十分なことが多い
🎯 次のステップの準備
次のSTEP 10では、Seq2Seqモデル を学びます。
LSTMを2つ組み合わせたEncoder-Decoderアーキテクチャにより、
機械翻訳や要約などの系列→系列変換 が可能になります。