概要
Seq2Seq(Sequence-to-Sequence)は、任意長の入力系列を任意長の出力系列に変換するエンコーダ・デコーダアーキテクチャである。2014年にSutskeverらが提案した。RNN/LSTM/GRUをエンコーダとデコーダに使い、入力系列全体をコンテキストベクトルに圧縮してから出力系列を生成する。機械翻訳・文書要約・対話システムなど「系列→系列」変換タスクの基盤となった。
直感・モチベーション
「I love natural language processing」を「私は自然言語処理が好きです」に翻訳するとき、入力と出力の長さが異なり、語順も対応しない。通常のRNNは入力と同じ長さの出力しか作れない。
Seq2Seqは問題を2段階に分ける。
- エンコーダ: 入力系列全体を読み込み、意味を1つのベクトル(コンテキストベクトル)に圧縮する
- デコーダ: コンテキストベクトルを初期状態として、出力系列を1トークンずつ生成する
この「圧縮→生成」の分離により、入出力の長さが独立になる。
トレードオフ:
- 利点: 入出力長が異なるタスクを自然に扱える。エンコーダとデコーダを独立して設計・改良できる
- 限界: 入力系列全体を固定長ベクトルに圧縮するため、長い入力で情報が失われる(Attentionで解決)
数学的定式化
入力系列 、出力系列 に対して:
エンコーダ(LSTMを使う場合):
最終ステップの状態がコンテキストベクトルとなる:
デコーダ:
学習は教師強制(Teacher Forcing)で行い、損失は各ステップのクロスエントロピーの和:
導出を見る
教師強制(Teacher Forcing):
学習時にデコーダへの入力として、モデル自身の予測 ではなく正解トークン を使う手法。
- 利点: 学習が安定・高速になる(誤差が蓄積しない)
- 欠点: 推論時はモデルの予測を入力に使うため、学習との乖離が生じる(Exposure Bias)
ビームサーチ(Beam Search):
推論時に最良の系列を探索する手法。幅 のビームを保持し、各ステップで上位 個の候補を展開する。
貪欲法(greedy)は のビームサーチに相当する。 が実用的なトレードオフ。
長さペナルティ ()を加えることで短い系列への偏りを補正する。
コンテキストベクトルのボトルネック:
入力が長いほどエンコーダの最終状態に圧縮しきれない情報が増える。Bahdanauらが2015年に提案したAttentionは、デコーダが各ステップでエンコーダの全隠れ状態を参照することでこの問題を解消した。
重要な性質・注意点
- 固定長ボトルネック: コンテキストベクトルは固定次元のため、長い入力(50単語以上)で性能が低下する。Attentionの導入が事実上必須
- Exposure Bias: 教師強制で学習したモデルは推論時に誤差が蓄積する。Scheduled Samplingで緩和できる
<SOS>/<EOS>トークン: デコーダの開始・終了を表す特殊トークンが必要
まとめ
- エンコーダが入力全体をコンテキストベクトルに圧縮し、デコーダがそこから出力を生成する
- 入出力の長さが独立になり、任意長変換が可能になる
- 教師強制で学習し、ビームサーチで推論する
- 長入力でのボトルネックはAttentionで解消される。Seq2Seq+AttentionがTransformerの直接の前身
実装例
pythonimport math import random def sigmoid(x): return 1.0 / (1.0 + math.exp(-x)) class LSTMCell: def __init__(self, in_dim, hid_dim): d = in_dim + hid_dim s = 0.01 self.W = [[random.gauss(0, s) for _ in range(d)] for _ in range(4 * hid_dim)] self.b = [0.0] * (4 * hid_dim) self.H = hid_dim def mv(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, h, c): H = self.H g = [self.mv(self.W, x + h)[i] + self.b[i] for i in range(4 * H)] f = [sigmoid(g[i]) for i in range(0*H, 1*H)] i = [sigmoid(g[i]) for i in range(1*H, 2*H)] u = [math.tanh(g[i]) for i in range(2*H, 3*H)] o = [sigmoid(g[i]) for i in range(3*H, 4*H)] c2 = [f[j]*c[j] + i[j]*u[j] for j in range(H)] h2 = [o[j]*math.tanh(c2[j]) for j in range(H)] return h2, c2 class Seq2Seq: def __init__(self, src_dim, tgt_dim, hidden_dim): self.encoder = LSTMCell(src_dim, hidden_dim) self.decoder = LSTMCell(tgt_dim, hidden_dim) self.H = hidden_dim self.Wo = [[random.gauss(0, 0.01) for _ in range(hidden_dim)] for _ in range(tgt_dim)] self.bo = [0.0] * tgt_dim def softmax(self, xs): m = max(xs) es = [math.exp(x - m) for x in xs] s = sum(es) return [e / s for e in es] def encode(self, src_seq): h = [0.0] * self.H c = [0.0] * self.H for x in src_seq: h, c = self.encoder.forward(x, h, c) return h, c # コンテキストベクトル def decode_step(self, y_prev, h, c): h, c = self.decoder.forward(y_prev, h, c) logits = [sum(self.Wo[i][j] * h[j] for j in range(self.H)) + self.bo[i] for i in range(len(self.bo))] probs = self.softmax(logits) return probs, h, c def forward(self, src_seq, tgt_seq): """教師強制による順伝播""" h, c = self.encode(src_seq) outputs = [] for y in tgt_seq: probs, h, c = self.decode_step(y, h, c) outputs.append(probs) return outputs # 使用例 model = Seq2Seq(src_dim=4, tgt_dim=3, hidden_dim=8) src = [[random.random() for _ in range(4)] for _ in range(5)] # 入力系列(長さ5) tgt = [[random.random() for _ in range(3)] for _ in range(3)] # 出力系列(長さ3) outputs = model.forward(src, tgt) print(f"出力確率分布(最初のステップ): {[round(p, 3) for p in outputs[0]]}")
関連記事
前提知識
- RNN — Seq2Seqを構成するセルの基礎
- LSTM — Seq2Seqで一般的に使われるエンコーダ・デコーダの実装
- GRU — LSTMの軽量代替としてSeq2Seqに使われる
派生技術
- Attention — コンテキストベクトルのボトルネックを解消する機構
- Transformer — Seq2Seq+Attentionを発展させ、RNNを排除したアーキテクチャ
応用事例
- 機械翻訳 — 原言語文から目標言語文への変換
- 文書要約 — 長文から短い要約文を生成
- 対話システム — ユーザー発話から応答文を生成
用語解説
| 用語 | 説明 |
|---|---|
| エンコーダ | 入力系列を読み込みコンテキストベクトルに圧縮するRNN |
| デコーダ | コンテキストベクトルを初期状態として出力系列を生成するRNN |
| コンテキストベクトル | エンコーダの最終隠れ状態。入力系列の意味を圧縮したベクトル |
| 教師強制 | 学習時に正解トークンをデコーダの次ステップ入力として使う手法 |
| Exposure Bias | 教師強制で学習したモデルが推論時に誤差を蓄積する問題 |
| ビームサーチ | 上位B候補を保持しながら出力系列を探索するデコーディング手法 |
<SOS> / <EOS> | デコーダの生成開始・終了を示す特殊トークン |