T5(Text-to-Text Transfer Transformer)の理論と設計思想を完全解説

翻訳、要約、感情分類、質問応答 — 自然言語処理(NLP)にはさまざまなタスクがありますが、これらをすべて同じ1つのフレームワークで解けるとしたらどうでしょうか? 従来のアプローチでは、分類タスクには分類ヘッドを、生成タスクにはデコーダを、とタスクごとに異なる出力層やアーキテクチャを設計する必要がありました。「タスクが10種類あれば10種類の出力設計が必要」という状況は、研究にも実務にも大きな負担です。

2019年にGoogleが発表したT5(Text-to-Text Transfer Transformer)は、この問題に対して驚くほどシンプルな解決策を提示しました。すべてのNLPタスクを「テキストを入力し、テキストを出力する」という統一フォーマットに変換するのです。翻訳なら "translate English to German: That is good" と入力すれば "Das ist gut" が出力される。分類なら "classify: This movie was great" と入力すれば "positive" というテキストが出力される。出力層のカスタマイズは一切不要です。

T5の設計思想を理解することは、以下のような場面で直接的に役立ちます。

  • マルチタスク学習の設計: 1つのモデルで翻訳・要約・分類・QAを同時に学習させるシステムの構築
  • 事前学習戦略の理解: Span Corruptionという独自の事前学習タスクと、その後のファインチューニングの流れ
  • 最新モデルへの橋渡し: Flan-T5やmT5、UL2など、T5から派生した重要なモデル群を理解するための土台
  • Encoder-Decoderの再評価: GPT系のDecoder-onlyモデルが主流の今、なぜEncoder-Decoderが有効な場面があるのかを理解する

さらに、T5の論文(Raffel et al., 2020, “Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer”)は、単にモデルを提案するだけでなく、NLPの転移学習に関する体系的な実験をまとめた論文でもあります。アーキテクチャの比較、事前学習タスクの比較、データセットの設計、スケーリングの効果など、NLPの研究者が知るべき知見が凝縮されています。

本記事の内容

  • Text-to-Textフレームワークの概念と設計思想
  • T5のEncoder-Decoderアーキテクチャ(相対位置バイアス、Pre-Norm)
  • Span Corruption事前学習の仕組みと目的関数
  • C4データセットの設計とクリーニング方針
  • Googleの体系的実験から得られた重要な知見
  • T5の後継モデル(Flan-T5, mT5, UL2)
  • PyTorchでの相対位置バイアスとSpan Corruptionの実装
  • Hugging Face T5を使った推論デモ

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

画像なし
Transformerアーキテクチャの全体像をわかりやすく解説
Encoder-Decoder構造、位置エンコーディング、残差接続とLayer Normalizationの仕組みを解説します。
画像なし
Transformer Encoderの構造
EncoderブロックのSelf-Attention、Add&Norm、FFNの流れを解説します。
画像なし
Transformer Decoderの構造
DecoderブロックのMasked Self-Attention、Cross-Attention、FFNの3段構成を解説します。
画像なし
BERTとGPTの違い — エンコーダ・デコーダアーキテクチャの比較
BERTとGPTの事前学習戦略とアーキテクチャの違いを比較し、Encoder-Decoderモデルの位置付けを解説します。

BERTのマスク言語モデル(MLM)やGPTの自己回帰言語モデルの基本を理解していると、T5のSpan Corruptionがなぜそのように設計されたかが自然に理解できます。

それでは、T5の核心である「すべてをテキスト生成に統一する」というアイデアから見ていきましょう。

Text-to-Textフレームワークとは

「統一フォーマット」というアイデア

NLPには多種多様なタスクがあります。翻訳、要約、感情分類、質問応答、文法チェック、文の類似度判定 — これらは従来、それぞれ異なる形式で扱われてきました。分類タスクなら出力はクラスラベル(数値やカテゴリ)、翻訳なら出力はテキスト列、類似度判定なら出力はスコアという具合です。

この多様性は、研究者やエンジニアにとって厄介な問題を引き起こします。タスクごとに出力層を設計し、損失関数を選び、評価指標を決める必要があるからです。BERT系のモデルでは、事前学習済みのEncoderの上にタスク固有の「ヘッド」(線形層 + Softmax)を載せ替える方式が主流でしたが、これでも「どのようなヘッドを設計するか」はタスクごとに考える必要がありました。

T5のText-to-Textフレームワークは、この問題をエレガントに解決します。あらゆるNLPタスクを次の形式に統一するのです。

$$ \text{テキスト入力} \longrightarrow \text{テキスト出力} $$

入力テキストの先頭にタスクプレフィックス(task prefix)を付けることで、モデルに「何をすべきか」を伝えます。出力は常にテキストです。分類であっても、数値やカテゴリIDではなく、"positive""entailment" のようなテキスト文字列を出力させます。

具体例で理解する

Text-to-Textフレームワークの具体例を見てみましょう。

英独翻訳

入力: "translate English to German: That is good."
出力: "Das ist gut."

感情分類

入力: "sst2 sentence: This movie was absolutely wonderful."
出力: "positive"

文の要約

入力: "summarize: The quick brown fox jumps over the lazy dog. The dog was sleeping peacefully in the garden..."
出力: "A fox jumped over a sleeping dog in a garden."

質問応答(SQuAD形式)

入力: "question: What is the capital of France? context: France is a country in Western Europe. Its capital city is Paris."
出力: "Paris"

文の含意関係判定(NLI)

入力: "mnli premise: A man is playing guitar. hypothesis: Someone is making music."
出力: "entailment"

回帰タスク(STS-B: 文の類似度)

入力: "stsb sentence1: The cat sat on the mat. sentence2: A cat is sitting on a mat."
出力: "5.0"

最後の例が特に興味深いのは、回帰タスク(連続値の予測)すらもテキスト出力として扱っている点です。類似度スコアの 5.0 は数値ではなく、文字列 "5.0" として生成されます。一見非効率に思えますが、これによりすべてのタスクで同じモデル、同じ損失関数(トークン単位のクロスエントロピー)、同じデコーディング手順を使えるという大きな利点が生まれます。

BERTやGPTとの設計思想の違い

Text-to-Textフレームワークの位置付けをより明確にするために、BERTとGPTのアプローチと比較しましょう。

BERT(Encoder-only) は、入力テキストを双方向に読んで文脈表現を構築し、その表現の上にタスク固有のヘッドを載せる方式です。分類なら [CLS] トークンの表現に線形層を接続し、固有表現抽出なら各トークンの表現にラベル分類層を載せます。事前学習タスクはMLM(Masked Language Modeling)で、ランダムにマスクされたトークンを予測します。BERTは「理解」に特化したモデルであり、テキスト生成は不得意です。

GPT(Decoder-only) は、左から右へテキストを自己回帰的に生成するモデルです。次のトークンを予測することで学習し、プロンプトに続くテキストを生成できます。GPT-2以降は、タスクプレフィックスを使ってタスクを指定するアイデア(zero-shot / few-shot)が登場しましたが、アーキテクチャは一方向のDecoderのみであり、入力の双方向理解はできません。

T5(Encoder-Decoder) は、両者の利点を組み合わせます。Encoderが入力テキスト全体を双方向に理解し、Decoderがその理解を基にテキストを自己回帰的に生成します。これにより、「理解」と「生成」の両方が必要なタスク(翻訳、要約、QAなど)で優れた性能を発揮します。

特徴 BERT GPT T5
アーキテクチャ Encoder-only Decoder-only Encoder-Decoder
入力の処理 双方向 一方向(左→右) 双方向(Encoder)
出力の形式 タスク固有ヘッド テキスト生成 テキスト生成
事前学習 MLM + NSP 自己回帰LM Span Corruption
タスク適応 ヘッド載せ替え プロンプト設計 Text-to-Text統一
得意分野 理解・分類 生成 理解+生成

T5の核心は、「出力を常にテキストにする」という割り切りです。分類の正解ラベルも "positive" という文字列にしてしまえば、分類も翻訳も要約も全て「テキスト生成」として同じパイプラインで処理できます。この統一性こそが、T5がマルチタスク学習やスケーリングで成功した理由の一つです。

ここまでで、T5が「何をするモデルか」が明確になりました。次に、このText-to-Textフレームワークを実現する具体的なアーキテクチャ — つまり「どのように実現するか」を見ていきましょう。

T5のアーキテクチャ

標準的なEncoder-Decoder Transformer

T5のアーキテクチャは、原論文(Vaswani et al., 2017)のTransformerとほぼ同じEncoder-Decoder構造を採用しています。革新的なアーキテクチャの提案ではなく、既存のEncoder-Decoderをそのまま使い、「何を入力し、何を出力するか」のフレームワークを変えたところにT5のポイントがあります。

処理の流れは以下のとおりです。

  1. 入力テキスト(タスクプレフィックス付き)をトークン化し、Encoderに入力する
  2. Encoderが入力系列全体を双方向Self-Attentionで処理し、文脈表現を生成する
  3. DecoderがEncoderの出力をCross-Attentionで参照しながら、出力テキストを自己回帰的に生成する
  4. 学習時はteacher forcing(正解の出力系列をDecoderに与える)を使用する

図式的に表すと以下のようになります。

入力: "translate English to German: That is good."

[トークン化]
    ↓
┌───────────────────────────┐
│ Encoder (双方向Self-Attn) │
│  × N layers               │
└───────────────────────────┘
    ↓ 文脈表現
    ↓ (Cross-Attentionで参照)
┌──────────────────────────────┐
│ Decoder (因果Self-Attn       │
│        + Cross-Attn)         │
│  × N layers                  │
└──────────────────────────────┘
    ↓
[語彙上のSoftmax]
    ↓
出力: "Das ist gut."

ただし、オリジナルのTransformerからいくつかの変更点があります。これらの変更はそれぞれ明確な理由を持っています。

相対位置バイアス(Relative Position Bias)

オリジナルのTransformerでは、トークンの位置情報をsin/cos関数で計算した固定的な位置エンコーディングとして入力埋め込みに加算していました。T5では、この方式を使わず、相対位置バイアス(Relative Position Bias)を採用しています。

位置エンコーディングが「各トークンに絶対位置の情報を付与する」のに対し、相対位置バイアスは「2つのトークン間の距離に応じたバイアスをAttentionスコアに直接加算する」アプローチです。

直感的に言えば、これは次のような考え方です。文を読むとき、「3番目の単語と5番目の単語」という絶対的な位置よりも、「2つ隣の単語」という相対的な距離の方が言語的に重要な情報です。「The cat sat on the mat」という文で、”sat” と “cat” の関係は、文のどの位置に現れても「1つ隣」という関係は変わりません。相対位置バイアスは、この直感を直接モデルに組み込みます。

標準的なScaled Dot-Product Attentionでは、Attentionスコアを次のように計算します。

$$ \text{Attention}(\bm{Q}, \bm{K}, \bm{V}) = \text{softmax}\left(\frac{\bm{Q}\bm{K}^T}{\sqrt{d_k}}\right)\bm{V} $$

T5の相対位置バイアスでは、Softmaxの引数にバイアス項 $\bm{B}$ を加えます。

$$ \text{Attention}(\bm{Q}, \bm{K}, \bm{V}) = \text{softmax}\left(\frac{\bm{Q}\bm{K}^T}{\sqrt{d_k}} + \bm{B}\right)\bm{V} $$

ここで $\bm{B} \in \mathbb{R}^{n \times n}$ はバイアス行列で、$B_{ij}$ は位置 $i$ のトークンから位置 $j$ のトークンへのバイアスを表します。このバイアスは、相対位置 $i – j$ によって決まります。

しかし、相対位置をそのまま使うと、文が長くなるほどバイアスの種類が際限なく増えます。位置 $0$ と位置 $1000$ の距離は $1000$ ですが、距離 $999$ との区別がそこまで重要かというと、そうではありません。T5では、この問題をバケット化(bucketing)で解決します。

近い距離は細かく区別し、遠い距離は粗く区別するバケット化を行います。具体的には、距離 $0$ から $n_{\text{exact}}$(論文では $n_{\text{exact}}=16$ 前後に設定)までは1対1でバケットに割り当て、それ以降は対数スケールでバケットに割り当てます。この設計により、有限個のバケット数(T5-Baseでは32個)で、理論上任意の長さの系列を扱えます。

バケットインデックスを $b(i, j)$ とすると、バイアスは学習可能な埋め込みテーブルから取得されます。

$$ B_{ij} = \text{embedding}[b(i – j)] $$

この埋め込みテーブルはAttentionヘッドごとに独立しています。つまり、異なるヘッドが異なる距離パターンに注目できるようになっています。

もう一つ重要な点として、T5の相対位置バイアスは最初のEncoder層とDecoder層でのみ計算され、残りの層では同じバイアスが共有されます。これはパラメータ効率の観点からの設計選択です。

Pre-Norm(Pre-Layer Normalization)

オリジナルのTransformerでは、各サブレイヤー(Self-Attention や FFN)のに残差接続とLayer Normalizationを適用していました(Post-Norm)。

$$ \text{Post-Norm: } \bm{x} + \text{LayerNorm}(\text{SubLayer}(\bm{x})) $$

T5では、SubLayerのにLayer Normalizationを適用するPre-Normを採用しています。

$$ \text{Pre-Norm: } \bm{x} + \text{SubLayer}(\text{LayerNorm}(\bm{x})) $$

この違いは一見些細に見えますが、深いネットワークの学習安定性に大きく影響します。Pre-Normでは、残差接続のパスを通る勾配がLayer Normalizationを経由しないため、勾配がより安定して逆伝播できます。特に層数が多いモデル(T5-11Bは24層)では、この安定性が学習の成否を左右します。

また、T5のLayer NormalizationはRMSNorm(Root Mean Square Layer Normalization)に近い実装で、平均の引き算を省略しています。通常のLayer Normは $\hat{x} = \frac{x – \mu}{\sigma}$ と正規化しますが、T5では平均の中心化を行わず、RMSで割るだけです。

$$ \text{RMSNorm}(\bm{x}) = \frac{\bm{x}}{\text{RMS}(\bm{x})} \odot \bm{\gamma} $$

ここで、RMSは二乗平均平方根です。

$$ \text{RMS}(\bm{x}) = \sqrt{\frac{1}{d}\sum_{i=1}^{d}x_i^2} $$

この簡略化により、計算コストが僅かに削減されつつ、実質的な性能低下がないことが経験的に確認されています。

パラメータ構成

T5にはパラメータ共有のようなトリックは使われておらず、EncoderとDecoderは独立したパラメータを持ちます。これはAlbert(BERT系の軽量モデル)がEncoder内の全層でパラメータを共有したのとは対照的です。T5は「シンプルなアーキテクチャを大規模に訓練する」という方針を取っており、パラメータ効率よりも表現力を優先しています。

ただし、例外として入力埋め込み行列はEncoderとDecoderで共有されます。これは、入力と出力が同じ語彙空間を使うため、自然な設計選択です。Decoderの最終出力層(語彙への射影)でも同じ埋め込み行列が転置して再利用されます(weight tying)。

T5-Baseモデルの主要パラメータをまとめると、以下のようになります。

パラメータ T5-Base
Encoder層数 12
Decoder層数 12
隠れ層の次元 $d_{\text{model}}$ 768
FFN内部次元 $d_{\text{ff}}$ 3072
Attentionヘッド数 12
キー/バリューの次元 $d_k = d_v$ 64
相対位置バケット数 32
最大相対位置距離 128
語彙サイズ 32,128(SentencePiece)
総パラメータ数 約220M

ここまでで、T5のアーキテクチャの全体像が見えてきました。次に、このアーキテクチャをどのように事前学習するか — T5のもう一つの重要な要素であるSpan Corruption事前学習を見ていきましょう。

Span Corruption事前学習

マスクの粒度という問題

事前学習タスクの設計は、モデルの最終的な性能を大きく左右します。BERTのMLM(Masked Language Modeling)は、入力テキストのトークンをランダムに15%マスクし、そのトークンを予測する方式でした。この方法は強力ですが、1つ気になる点があります — マスクの粒度が1トークンに限定されている点です。

自然言語では、意味の単位はしばしば複数トークンにまたがります。「New York」「machine learning」「in order to」のようなフレーズは、個々のトークンではなくまとまりとして理解すべきです。1トークンずつ独立にマスクするMLMでは、こうしたフレーズレベルの理解を直接促す力が弱いと考えられます。

また、BERTのMLMには構造的な問題もあります。BERTはEncoder-onlyモデルなので、マスクされたトークンの予測は各位置で独立に行われます。つまり、マスクされたトークン同士の依存関係(例えば「New [MASK]」で「York」を予測する際に、近くの別のマスクの予測結果を参照すること)は直接モデル化されません。

T5のSpan Corruptionは、これらの問題に対処するために設計されました。

Span Corruptionの仕組み

Span Corruptionでは、入力テキストから連続するトークンのスパン(span) をランダムに選択し、そのスパン全体を1つのsentinelトークン(特殊トークン)で置換します。Decoderは、sentinelトークンとともにマスクされた部分を復元します。

具体例で見てみましょう。

元のテキスト:
  "The quick brown fox jumps over the lazy dog in the park"

マスク後の入力(Encoderへ):
  "The <extra_id_0> fox jumps <extra_id_1> the park"

ターゲット出力(Decoderが生成):
  "<extra_id_0> quick brown <extra_id_1> over the lazy dog in"

ここで、<extra_id_0><extra_id_1> がsentinelトークンです。2つのスパン(”quick brown” と “over the lazy dog in”)がそれぞれ1つのsentinelトークンに置換されています。

Decoderの出力では、各sentinelトークンの後にそのスパンの元の内容が続きます。最後に文末トークンが付加されます。この形式により、Decoderは「どのsentinelがどのスパンに対応するか」を明示的に出力する必要があり、構造的な理解が促されます。

目的関数

Span Corruptionの目的関数は、標準的な条件付き言語モデリングの損失関数です。sentinelトークンを含むターゲット系列 $\bm{y} = (y_1, y_2, \dots, y_m)$ に対して、以下のクロスエントロピー損失を最小化します。

$$ \mathcal{L} = -\sum_{t=1}^{m} \log P(y_t \mid y_{

ここで $\bm{x}$ はsentinelトークンで置換された入力系列、$y_{

この損失関数はGPTの自己回帰損失と同じ形をしていますが、Encoderからの情報 $\bm{x}$ が条件に含まれている点が異なります。Decoderは、Encoderが入力系列から抽出した文脈表現をCross-Attentionで参照しながら、マスクされた部分を復元していきます。

ハイパーパラメータ

Span Corruptionのハイパーパラメータとして、以下の2つが重要です。

  • corruption rate(マスク率): テキスト全体のうち何%のトークンをマスクするか。T5では15%を使用(BERTと同じ割合)
  • mean span length(平均スパン長): マスクされるスパンの平均長。T5では3トークンを使用

スパン長は幾何分布からサンプリングされます。平均スパン長が3の場合、短いスパンが多く、長いスパンは稀になります。この設定は体系的な実験によって選ばれたもので、単一トークンのマスク(BERT式)やランダムな固定長マスクと比較して、下流タスクでの性能が最も良かった組み合わせです。

MLMとの違い

Span Corruptionと BERTのMLMの違いを整理しましょう。

特徴 BERT MLM T5 Span Corruption
マスク単位 1トークン 連続スパン(平均3トークン)
マスク率 15% 15%
出力方式 各位置で独立に予測 自己回帰的に生成
出力の依存関係 マスク間の依存なし 前のトークンの予測が次に影響
出力長 入力長と同じ マスク部分のみ(短い)
sentinelトークン なし あり
アーキテクチャ Encoder-only Encoder-Decoder

特に重要なのは出力長の違いです。BERTのMLMでは入力と同じ長さの出力を生成しますが、T5のSpan Corruptionでは、マスクされた部分(+sentinelトークン)のみを生成すればよいため、Decoderの出力系列が大幅に短くなります。マスク率15%、平均スパン長3の場合、ターゲット系列は元のテキストの約15%程度の長さに圧縮されます。これは計算効率の面で大きな利点です — Decoderのself-attentionは $O(m^2)$ のコストがかかるため、出力長 $m$ が短いほど計算量が削減されます。

ここまでで事前学習のタスク設計が明らかになりました。次に、この事前学習で使用されるデータセット — T5のために特別に構築されたC4について見ていきましょう。

C4(Colossal Clean Crawled Corpus)

データセットの重要性

近年の大規模言語モデルの研究では、「モデルのアーキテクチャ」と「学習データの質と量」のどちらが性能に大きく影響するかという議論が盛んです。T5の論文は、この問いに対してデータの質が極めて重要であることを体系的に示しました。

T5の事前学習に使われたのは、C4(Colossal Clean Crawled Corpus) と呼ばれる、約750GBのクリーンなテキストデータセットです。これはCommon Crawl(ウェブ全体のクロールデータ)を出発点として、厳格なクリーニングを施したものです。

クリーニングの方針

Common Crawlは膨大な量のウェブデータを含みますが、そのままでは品質が低く、言語モデルの学習に適しません。HTML内のボイラープレート、重複コンテンツ、卑猥な表現、コードスニペット、非自然言語テキストなど、大量のノイズが含まれています。

C4のクリーニングでは、以下のフィルタリングが適用されました。

  1. 文末のフィルタ: 句読点(ピリオド、感嘆符、疑問符、引用符)で終わる文のみを保持。これにより、メニュー項目やナビゲーションテキストなどの断片的なテキストが除去されます
  2. 行数・単語数のフィルタ: ページ内の文の数が少なすぎる(5文未満)ページを除外。コンテンツの薄いページを排除する目的です
  3. 不適切コンテンツの除去: 既知の卑猥・不適切な単語リストを含むページを全て除外
  4. JavaScriptの除去: “Javascript” という単語を含む行を除外。これは "Javascript must be enabled" のようなエラーメッセージページを排除するためのヒューリスティックです
  5. ボイラープレートの除去: “cookie policy”、”terms of use”、”privacy policy” 等の定型文を含む行を除外
  6. 重複の除去: 3文以上の連続した文が他のドキュメントと重複している場合に除外。これにより、コピー&ペーストされたコンテンツやテンプレートテキストが排除されます
  7. 言語フィルタ: langdetect ライブラリを使い、英語と判定されたページのみを保持(C4は英語データセット)

これらのフィルタリングの結果、Common Crawlの膨大なデータが約750GBにまで絞り込まれました。この数字は大きく見えますが、元のCommon Crawlと比較すると大幅に圧縮されています。

データの質が性能に与える影響

T5の論文では、C4と他のデータソースを比較した実験も行われています。その結果、以下の知見が得られました。

  • フィルタリングなしのCommon Crawlで学習すると、C4と比較して下流タスクの性能が一貫して低下する
  • Wikipediaのような高品質だが小規模なデータのみで学習すると、データの多様性不足により性能が制限される
  • C4のように「大規模かつクリーン」なデータが最もバランスの良い結果を出す

この結果は、「ゴミを入れればゴミが出る(Garbage In, Garbage Out)」という原則がそのまま当てはまります。モデルのアーキテクチャが優れていても、学習データの質が低ければ性能は頭打ちになるのです。

C4の設計思想は、後続の多くのデータセット(The Pile、RefinedWeb、RedPajama等)に影響を与え、大規模言語モデルのデータ前処理のベースラインとなりました。

データセットの話はここまでにして、次はT5の論文のもう一つの大きな貢献 — NLPの転移学習に関する体系的な実験結果を見ていきましょう。

Googleの体系的実験

論文の特異な位置付け

T5の論文は67ページにも及ぶ長大な論文で、単なるモデル提案ではなく、NLPの転移学習に関する包括的なサーベイ兼実験レポートという性格を持っています。「どのアーキテクチャが良いか」「どの事前学習タスクが良いか」「どのくらいのデータが必要か」「モデルを大きくすると何が起きるか」といった根本的な問いに、大規模な計算資源を投入して答えを出しています。

以下に、論文のSection 3から得られた重要な知見をまとめます。

アーキテクチャの比較

T5の論文では、以下の3つのアーキテクチャ変種を公平な条件で比較しています。

  1. Encoder-Decoder: 標準的なTransformer Encoder-Decoder(T5が採用)
  2. Decoder-only(Language Model): GPT型の自己回帰的デコーダのみ
  3. Prefix LM: デコーダのみだが、入力部分(プレフィックス)は双方向Attentionを許可

比較の結果、以下のことが明らかになりました。

Encoder-Decoderが最も高性能でした。特に、EncoderとDecoderのパラメータを合計した場合でも、同じパラメータ数のDecoder-onlyモデルを上回りました。これは注目に値します。Encoder-Decoderモデルは、EncoderとDecoderにそれぞれ別のパラメータを持つため、合計パラメータ数が Decoder-onlyの2倍に近くなる場合がありますが、パラメータ数を揃えた比較でもEncoder-Decoderが優位でした。

直感的にこの結果を理解すると、入力の理解(Encoder)と出力の生成(Decoder)を別々のモジュールで担当させることで、それぞれが自分の役割に集中できるからと考えられます。Decoder-onlyモデルでは、入力の理解と出力の生成を同じパラメータで行わなければなりません。

Prefix LMはDecoder-onlyより良いが、Encoder-Decoderには及ばない結果でした。Prefix LMは入力部分で双方向Attentionを許可するため、入力の理解がDecoder-onlyより優れますが、EncoderとDecoderが構造的に分離されていないため、Encoder-Decoderの明確な役割分担には劣ります。

事前学習タスクの比較

T5の論文では、以下の事前学習タスクを比較しています。

  • 言語モデリング(LM): 左から右に次のトークンを予測する(GPT式)
  • BERT式MLM: ランダムにトークンをマスクし、Encoderで独立に予測する
  • Deshuffling: シャッフルされた文を元の順序に戻す
  • Span Corruption(T5方式): スパンをマスクしてDecoderで復元する

結果として、Span Corruptionが最も良い成績を記録しました。特に、BERT式MLMとSpan Corruptionの差は、「マスクの粒度(1トークン vs スパン)」と「出力の効率性(入力長 vs マスク部分のみ)」に起因すると考えられます。

Span Corruptionのマスク率と平均スパン長についても体系的に実験が行われ、マスク率15%、平均スパン長3の組み合わせが最適であることが示されました。マスク率を上げすぎるとEncoderに残る情報が少なくなりすぎ、下げすぎるとDecoderが学習するシグナルが少なくなります。平均スパン長については、短すぎると単語レベルの知識しか学べず、長すぎるとタスクが難しすぎて学習が非効率になります。

モデルサイズとスケーリング

T5は5つのサイズで実験されており、モデルを大きくすることの効果を定量的に示しています。

モデル パラメータ数 Encoder/Decoder層数 $d_{\text{model}}$ $d_{\text{ff}}$ ヘッド数
T5-Small 60M 6/6 512 2048 8
T5-Base 220M 12/12 768 3072 12
T5-Large 770M 24/24 1024 4096 16
T5-3B 3B 24/24 1024 16384 32
T5-11B 11B 24/24 1024 65536 128

この表から読み取れる興味深い点があります。T5-3BからT5-11Bへのスケールアップでは、層数と $d_{\text{model}}$ は変えず、FFN内部次元 $d_{\text{ff}}$ とヘッド数だけを増やしています。これは、モデルの「深さ」よりも「幅」を広げる方がスケーリング効率が良いという知見を反映しています。

実験の結果、モデルサイズを大きくするほど下流タスクの性能は一貫して向上しました。ただし、性能向上の幅は逓減します(対数スケールでの向上)。T5-11Bは当時のSuperGLUEベンチマークで人間のベースラインを初めて超えたモデルの一つとなりました。

その他の重要な知見

  • 学習ステップ数: 学習を長く続けるほど性能は向上するが、データを繰り返し学習すると過学習が起きる。C4の大規模さが重要
  • マルチタスク学習 vs 個別ファインチューニング: 事前学習後に個別タスクでファインチューニングする方が、複数タスクを同時に学習するより一般的に良い。ただし、タスク間の比率やサンプリング戦略を適切に設定すれば、マルチタスク学習も競争力がある
  • 教師なし vs 教師あり事前学習: 教師なし事前学習(Span Corruption)の後にファインチューニングする方式が最も安定して高性能

これらの実験結果は、T5のモデル自体が評価されるだけでなく、NLPの転移学習全般に対する実用的なガイドラインとして広く参照されています。

T5の体系的な実験から多くの知見が得られましたが、T5の物語はここで終わりません。T5の設計を基盤として、さまざまな改良モデルが提案されています。次に、これらの後継モデルを概観しましょう。

T5の後継モデル

Flan-T5(Instruction Tuning版)

Flan-T5(2022, Google)は、T5にInstruction Tuning(指示チューニング)を施したモデルです。Instruction Tuningとは、「入力に自然言語の指示を含め、その指示に従った出力を学習させる」ファインチューニング手法です。

T5のText-to-Textフレームワークでは、タスクプレフィックス(”translate English to German:” 等)が固定的なフォーマットでした。Flan-T5では、これをより自然な指示文に拡張します。

T5のプレフィックス:
  "summarize: {記事テキスト}"

Flan-T5の指示:
  "Summarize the following article in 2-3 sentences: {記事テキスト}"
  "Write a brief summary of the text below: {記事テキスト}"
  "What are the key points of this article? {記事テキスト}"

Flan-T5は1,800以上のタスクを473のデータセットからまとめ、それぞれに複数のInstruction Templateを用意してファインチューニングされました。この結果、T5と比較して以下の改善が見られました。

  • ゼロショット性能の大幅向上: 学習時に見ていないタスクにも対応できる汎化能力が向上
  • Few-shot性能の向上: 少数の例を与えるだけで新しいタスクに適応する能力が強化
  • 指示追従の向上: より複雑で多様な自然言語の指示に従えるようになった

Flan-T5は、ChatGPTの登場以前に「指示に従うAI」の可能性を示した重要なモデルの一つであり、後のInstruction Tuning研究に大きな影響を与えました。

mT5(多言語版)

mT5(2021, Google)は、T5のアーキテクチャをそのまま多言語に拡張したモデルです。C4の多言語版であるmC4データセット(101言語をカバー)で事前学習されています。

mT5のアーキテクチャはT5と完全に同一で、変更点は以下の2つだけです。

  • 語彙サイズ: T5の32,128から250,112に拡大。101言語をカバーするために、SentencePieceの語彙サイズが大幅に増加しています
  • 事前学習データ: 英語のみのC4から、101言語のmC4に変更

mT5は、多言語のText-to-Textモデルとして、翻訳、多言語QA、多言語要約などのタスクで広く使われています。特に、低リソース言語(学習データが少ない言語)でも、高リソース言語からの知識転移により、一定の性能が得られることが示されました。

UL2(Mixture of Denoisers)

UL2(Unifying Language Learning Paradigms)(2022, Google)は、T5のSpan Corruptionをさらに一般化したモデルです。UL2の核心アイデアは、複数のデノイジングタスクを混合して事前学習することです。

具体的には、以下の3種類のデノイジングタスクを混合します。

  1. R-Denoiser(Regular Denoiser): T5のSpan Corruptionと同じ。短いスパン(平均3トークン)、低マスク率(15%)
  2. S-Denoiser(Sequential Denoiser): テキストの末尾部分をマスクし、prefix-to-suffix(接頭辞から接尾辞を生成)のタスクとして学習。これはGPT的な自己回帰学習に近い
  3. X-Denoiser(Extreme Denoiser): 長いスパン(平均32トークン)、高マスク率(50%)でマスク。テキストの大部分が欠損した状態から復元する、より困難なタスク

これら3種類のデノイジングを混合することで、BERT的な理解力、GPT的な生成力、そして長距離の依存関係の把握を同時に学習できます。UL2は、Span Corruptionの「一つのタスクで事前学習する」というT5のアプローチを、「複数のタスクで多角的に学習する」方向に発展させたモデルと位置付けられます。

UL2の実験では、NLUタスク(理解)とNLGタスク(生成)の両方で、T5やGPT系のモデルを上回る性能が報告されています。

ここまでで、T5の理論的な全体像と、その後の発展が整理できました。次はいよいよ手を動かして、T5の主要コンポーネントをPyTorchで実装していきましょう。

PyTorchでの実装

相対位置バイアスの実装

まず、T5の特徴的な要素である相対位置バイアスをPyTorchで実装します。相対位置 $i – j$ をバケット化し、学習可能な埋め込みテーブルからバイアス値を取得する仕組みです。

import torch
import torch.nn as nn
import math

class T5RelativePositionBias(nn.Module):
    """T5の相対位置バイアスモジュール"""

    def __init__(self, num_buckets=32, max_distance=128, num_heads=8):
        super().__init__()
        self.num_buckets = num_buckets
        self.max_distance = max_distance
        self.num_heads = num_heads
        # ヘッドごとにバケット数分の学習可能なバイアス
        self.relative_attention_bias = nn.Embedding(num_buckets, num_heads)

    @staticmethod
    def _relative_position_bucket(relative_position, num_buckets=32, max_distance=128):
        """
        相対位置をバケットインデックスに変換する。
        近い距離は細かく、遠い距離は対数スケールで粗く区別する。
        """
        # 負の相対位置(前方参照)と正の相対位置(後方参照)を分ける
        # 半分のバケットを負の方向に、半分を正の方向に割り当て
        num_buckets_half = num_buckets // 2

        # 符号に応じてオフセットを決定
        # relative_position > 0 なら num_buckets_half を加算
        offset = (relative_position > 0).long() * num_buckets_half
        relative_position = relative_position.abs()

        # 近い距離は1対1でバケットに割り当て
        max_exact = num_buckets_half // 2  # 8
        is_small = relative_position < max_exact

        # 遠い距離は対数スケールでバケットに割り当て
        relative_position_if_large = max_exact + (
            torch.log(relative_position.float() / max_exact)
            / math.log(max_distance / max_exact)
            * (num_buckets_half - max_exact)
        ).long()
        # 最大バケットインデックスでクリップ
        relative_position_if_large = torch.clamp(
            relative_position_if_large, max=num_buckets_half - 1
        )

        # 近い距離はそのまま、遠い距離は対数バケットを使用
        bucket = torch.where(is_small, relative_position, relative_position_if_large)
        bucket = bucket + offset
        return bucket

    def forward(self, query_length, key_length, device=None):
        """バイアス行列を計算して返す"""
        if device is None:
            device = self.relative_attention_bias.weight.device

        # 位置インデックスの生成
        context_position = torch.arange(query_length, dtype=torch.long, device=device)[:, None]
        memory_position = torch.arange(key_length, dtype=torch.long, device=device)[None, :]

        # 相対位置: (query_length, key_length)
        relative_position = memory_position - context_position

        # バケット化: (query_length, key_length)
        buckets = self._relative_position_bucket(
            relative_position,
            num_buckets=self.num_buckets,
            max_distance=self.max_distance
        )

        # 埋め込みテーブルからバイアスを取得: (query_length, key_length, num_heads)
        values = self.relative_attention_bias(buckets)

        # (1, num_heads, query_length, key_length) に変形(バッチ次元を追加)
        values = values.permute(2, 0, 1).unsqueeze(0)
        return values

このコードの動作を確認してみましょう。

import torch
import matplotlib.pyplot as plt

# 相対位置バイアスモジュールの作成
bias_module = T5RelativePositionBias(num_buckets=32, max_distance=128, num_heads=8)

# バケット化の挙動を可視化
seq_len = 64
positions = torch.arange(seq_len)
relative_pos = positions[None, :] - positions[:, None]
buckets = T5RelativePositionBias._relative_position_bucket(relative_pos, num_buckets=32, max_distance=128)

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# バケットインデックスのヒートマップ
im0 = axes[0].imshow(buckets.numpy(), cmap='viridis', aspect='auto')
axes[0].set_xlabel('Key position')
axes[0].set_ylabel('Query position')
axes[0].set_title('Relative Position Bucket Indices')
plt.colorbar(im0, ax=axes[0])

# 1次元の相対位置 → バケットの対応
distances = torch.arange(-63, 64)
bucket_1d = T5RelativePositionBias._relative_position_bucket(distances, num_buckets=32, max_distance=128)
axes[1].plot(distances.numpy(), bucket_1d.numpy(), 'b-', linewidth=1.5)
axes[1].set_xlabel('Relative Position (i - j)')
axes[1].set_ylabel('Bucket Index')
axes[1].set_title('Relative Position to Bucket Mapping')
axes[1].grid(True, alpha=0.3)
axes[1].axvline(x=0, color='r', linestyle='--', alpha=0.5)

plt.tight_layout()
plt.savefig('t5_relative_position_buckets.png', dpi=150, bbox_inches='tight')
plt.show()

左のヒートマップから、対角線付近(近い位置同士)ではバケットが細かく変化し、対角線から離れる(遠い位置同士)ほどバケットの変化が緩やかになる様子が確認できます。右のグラフは、相対位置をバケットインデックスに変換する関数の形状です。原点付近は線形に増加し(1対1の割り当て)、距離が大きくなると対数的に飽和していく特性が見て取れます。この「近い距離は精密に、遠い距離はざっくり」という設計が、有限個のバケットで長距離の位置関係を効率的に表現することを可能にしています。

Span Corruptionの前処理実装

次に、T5の事前学習で使われるSpan Corruptionの前処理を実装します。テキストをトークン列に変換した後、ランダムにスパンをマスクし、入力系列とターゲット系列を生成します。

import torch
import numpy as np

def create_span_corruption(
    token_ids,
    corruption_rate=0.15,
    mean_span_length=3,
    sentinel_start_id=32099,
    eos_id=1
):
    """
    T5 Span Corruptionの前処理を行う。

    Parameters:
        token_ids: 元のトークンID列 (list or 1D array)
        corruption_rate: マスクするトークンの割合
        mean_span_length: スパンの平均長(幾何分布のパラメータ)
        sentinel_start_id: sentinelトークンの開始ID(降順に使用)
        eos_id: 終端トークンID

    Returns:
        input_ids: Encoderへの入力(sentinelで置換済み)
        target_ids: Decoderのターゲット(sentinelとマスク内容)
    """
    token_ids = np.array(token_ids)
    length = len(token_ids)

    # マスクするトークン数
    num_to_mask = max(1, int(length * corruption_rate))

    # 幾何分布でスパン長をサンプリング
    # p = 1/mean_span_length として幾何分布からサンプル
    p = 1.0 / mean_span_length

    # スパンの開始位置とスパン長を決定
    mask = np.zeros(length, dtype=bool)
    masked_count = 0
    span_infos = []  # (start, length) のリスト

    while masked_count < num_to_mask:
        # スパン長をサンプリング(最低1)
        span_len = max(1, np.random.geometric(p))
        span_len = min(span_len, num_to_mask - masked_count)

        # 開始位置をランダムに選択(未マスク位置から)
        available = np.where(~mask)[0]
        if len(available) == 0:
            break

        start = np.random.choice(available)
        end = min(start + span_len, length)
        actual_len = end - start

        # 既にマスクされている部分と重ならないように調整
        if mask[start:end].any():
            continue

        mask[start:end] = True
        span_infos.append((start, actual_len))
        masked_count += actual_len

    # スパンを開始位置でソート
    span_infos.sort(key=lambda x: x[0])

    # 入力系列の構築(マスクされたスパンをsentinelトークンに置換)
    input_ids = []
    target_ids = []
    sentinel_id = sentinel_start_id
    prev_end = 0

    for span_start, span_len in span_infos:
        # スパンの前の非マスク部分をそのまま追加
        input_ids.extend(token_ids[prev_end:span_start].tolist())
        # sentinelトークンを挿入
        input_ids.append(sentinel_id)

        # ターゲットにsentinelトークンとスパンの内容を追加
        target_ids.append(sentinel_id)
        target_ids.extend(token_ids[span_start:span_start + span_len].tolist())

        sentinel_id -= 1  # 次のsentinelは1つ小さいIDを使用
        prev_end = span_start + span_len

    # 残りの非マスク部分を追加
    input_ids.extend(token_ids[prev_end:].tolist())

    # ターゲットの最後にEOSトークンを追加
    target_ids.append(eos_id)

    return input_ids, target_ids


# 動作確認
np.random.seed(42)

# 簡易的なトークン列(実際にはSentencePieceで生成される)
# 0-99のIDでダミートークンを作成
original_tokens = list(range(10, 30))  # 20トークンの文
print(f"元のトークン列 (長さ {len(original_tokens)}):")
print(original_tokens)

input_ids, target_ids = create_span_corruption(
    original_tokens,
    corruption_rate=0.15,
    mean_span_length=3
)

print(f"\n入力系列 (長さ {len(input_ids)}):")
print(input_ids)
print(f"\nターゲット系列 (長さ {len(target_ids)}):")
print(target_ids)

実行すると、20トークンの入力から約15%(3トークン程度)がマスクされ、sentinelトークンで置換された入力系列と、sentinelトークン付きのターゲット系列が生成されます。入力系列は元のトークン列より短くなり(スパンが1つのsentinelに圧縮されるため)、ターゲット系列はマスクされた部分のみを含むため、元のテキスト全体よりも大幅に短くなります。これがSpan Corruptionの計算効率上の利点です。

Text-to-Textのフォーマッタ

T5のText-to-Textフレームワークでは、タスクプレフィックスを付けてモデルに入力します。以下に、主要なNLPタスクのフォーマッタを実装します。

def format_translation(source_text, source_lang="English", target_lang="German"):
    """翻訳タスクのフォーマット"""
    return f"translate {source_lang} to {target_lang}: {source_text}"

def format_summarization(text):
    """要約タスクのフォーマット"""
    return f"summarize: {text}"

def format_classification(text, dataset_name="sst2"):
    """分類タスクのフォーマット"""
    return f"{dataset_name} sentence: {text}"

def format_qa(question, context):
    """質問応答タスクのフォーマット"""
    return f"question: {question} context: {context}"

def format_nli(premise, hypothesis, dataset_name="mnli"):
    """自然言語推論タスクのフォーマット"""
    return f"{dataset_name} premise: {premise} hypothesis: {hypothesis}"

def format_similarity(sentence1, sentence2, dataset_name="stsb"):
    """文類似度タスクのフォーマット"""
    return f"{dataset_name} sentence1: {sentence1} sentence2: {sentence2}"


# 各タスクの入力例を表示
print("=== Text-to-Text フォーマットの例 ===\n")

print("【翻訳】")
print(format_translation("That is good."))
print("→ 期待出力: Das ist gut.\n")

print("【要約】")
print(format_summarization("The quick brown fox jumps over the lazy dog in the park."))
print("→ 期待出力: A fox jumped over a dog.\n")

print("【分類】")
print(format_classification("This movie was absolutely wonderful."))
print("→ 期待出力: positive\n")

print("【質問応答】")
print(format_qa("What is the capital of France?",
                "France is a country in Western Europe. Its capital city is Paris."))
print("→ 期待出力: Paris\n")

print("【自然言語推論】")
print(format_nli("A man is playing guitar.", "Someone is making music."))
print("→ 期待出力: entailment\n")

print("【文類似度】")
print(format_similarity("The cat sat on the mat.", "A cat is sitting on a mat."))
print("→ 期待出力: 5.0")

出力を見ると、すべてのタスクが「テキスト入力 → テキスト出力」の統一フォーマットに変換されていることがわかります。タスクプレフィックスは固定的な文字列であり、モデルはこのプレフィックスを見て「今何をすべきか」を判断します。この設計のおかげで、新しいタスクを追加する際にモデルの構造を変更する必要がなく、プレフィックスの設計とファインチューニングデータの準備だけで対応できます。

Hugging Face T5を使った推論デモ

最後に、Hugging Face Transformersライブラリを使って、事前学習済みのT5モデルで実際に推論を行うデモを実装します。要約、翻訳、分類の3つのタスクを1つのモデルで実行します。

from transformers import T5Tokenizer, T5ForConditionalGeneration

# T5-Smallモデルとトークナイザの読み込み
model_name = "t5-small"
tokenizer = T5Tokenizer.from_pretrained(model_name)
model = T5ForConditionalGeneration.from_pretrained(model_name)
model.eval()

def t5_generate(input_text, max_length=128, num_beams=4):
    """T5で推論を実行する汎用関数"""
    input_ids = tokenizer.encode(input_text, return_tensors="pt")
    with torch.no_grad():
        outputs = model.generate(
            input_ids,
            max_length=max_length,
            num_beams=num_beams,
            early_stopping=True
        )
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

# --- タスク1: 英独翻訳 ---
print("=" * 60)
print("【タスク1: 英独翻訳】")
input_text = "translate English to German: The house is wonderful."
output = t5_generate(input_text)
print(f"入力: {input_text}")
print(f"出力: {output}")

# --- タスク2: 要約 ---
print("\n" + "=" * 60)
print("【タスク2: 要約】")
article = (
    "summarize: Transfer learning, where a model is first pre-trained on a data-rich task "
    "before being fine-tuned on a downstream task, has emerged as a powerful technique in "
    "natural language processing. The effectiveness of transfer learning has given rise to "
    "a diversity of approaches, methodology, and practice."
)
output = t5_generate(article, max_length=64)
print(f"入力: {article[:80]}...")
print(f"出力: {output}")

# --- タスク3: 英仏翻訳 ---
print("\n" + "=" * 60)
print("【タスク3: 英仏翻訳】")
input_text = "translate English to French: How are you today?"
output = t5_generate(input_text)
print(f"入力: {input_text}")
print(f"出力: {output}")

print("\n" + "=" * 60)
print(f"\nモデル: {model_name}")
print(f"パラメータ数: {sum(p.numel() for p in model.parameters()):,}")

このコードを実行すると、60Mパラメータの小さなT5-Smallモデルであっても、翻訳と要約のタスクに応答できることが確認できます。重要なのは、翻訳も要約もまったく同じモデル、同じ generate() メソッドで処理されている点です。タスクプレフィックスを変えるだけで、モデルの振る舞いが切り替わります。これがText-to-Textフレームワークの力です。

ただし、T5-Smallは60Mパラメータと小さいため、出力の品質は限定的です。T5-BaseやT5-Largeを使えば大幅に品質が向上しますが、メモリと計算時間のトレードオフがあります。実務では、タスクの要求精度とリソース制約に応じてモデルサイズを選択することになります。

続いて、T5のEncoder-Decoderの内部構造をより詳しく理解するために、Attentionの重みを可視化してみましょう。

from transformers import T5Tokenizer, T5ForConditionalGeneration
import torch
import matplotlib.pyplot as plt
import numpy as np

# モデルの読み込み(Attention重みを出力するよう設定)
model_name = "t5-small"
tokenizer = T5Tokenizer.from_pretrained(model_name)
model = T5ForConditionalGeneration.from_pretrained(model_name, output_attentions=True)
model.eval()

# 翻訳タスクの入力
input_text = "translate English to German: The cat sits on the mat."
input_ids = tokenizer.encode(input_text, return_tensors="pt")
input_tokens = tokenizer.convert_ids_to_tokens(input_ids[0])

# 推論してAttention重みを取得
with torch.no_grad():
    outputs = model.generate(
        input_ids,
        max_length=32,
        num_beams=1,
        output_attentions=True,
        return_dict_in_generate=True
    )

# Encoderの Self-Attention重みを取得(最終層、第1ヘッド)
encoder_outputs = model.encoder(input_ids, output_attentions=True)
encoder_attentions = encoder_outputs.attentions  # (num_layers,) のタプル

# 最終層のAttention重み: (batch, num_heads, seq_len, seq_len)
last_layer_attn = encoder_attentions[-1][0]  # バッチの最初の要素

fig, axes = plt.subplots(2, 4, figsize=(20, 10))
fig.suptitle("T5 Encoder Self-Attention Weights (Last Layer)", fontsize=14)

for head_idx in range(min(8, last_layer_attn.shape[0])):
    ax = axes[head_idx // 4][head_idx % 4]
    attn_weights = last_layer_attn[head_idx].numpy()
    im = ax.imshow(attn_weights, cmap='Blues', vmin=0, vmax=1)
    ax.set_title(f"Head {head_idx}")
    ax.set_xticks(range(len(input_tokens)))
    ax.set_yticks(range(len(input_tokens)))
    ax.set_xticklabels(input_tokens, rotation=90, fontsize=7)
    ax.set_yticklabels(input_tokens, fontsize=7)

plt.tight_layout()
plt.savefig('t5_encoder_attention.png', dpi=150, bbox_inches='tight')
plt.show()

8つのAttentionヘッドの重みを可視化すると、ヘッドによって注目パターンが大きく異なることが確認できます。あるヘッドは隣接トークンに強く注目し(局所的な文法構造の把握)、別のヘッドは離れたトークン間に注目する(長距離の意味的関係の把握)といったパターンが観察されます。特に “translate” や “English” のようなタスクプレフィックスのトークンと、本文のトークンの間のAttentionパターンは興味深く、モデルが「何をすべきか」の情報をどのように伝播させているかの手がかりを与えてくれます。これは、Attentionヘッドごとに異なる相対位置バイアスを学習していることの効果でもあります。

まとめ

本記事では、T5(Text-to-Text Transfer Transformer)のアーキテクチャと設計思想について解説しました。

  • Text-to-Textフレームワーク: すべてのNLPタスクを「テキスト入力 → テキスト出力」に統一することで、タスク固有のヘッド設計が不要になり、マルチタスク学習やスケーリングが容易になります
  • Encoder-Decoderアーキテクチャ: 入力の双方向理解(Encoder)と自己回帰的生成(Decoder)を組み合わせ、相対位置バイアスとPre-Normで学習を安定化させています
  • Span Corruption事前学習: 連続するトークンのスパンをマスクし、sentinelトークンとともにDecoderで復元する方式で、フレーズレベルの理解と計算効率を両立しています
  • C4データセット: Common Crawlを厳格にクリーニングし、「大規模かつクリーン」なデータが性能の鍵であることを示しました
  • 体系的実験: Encoder-Decoderがアーキテクチャとして最適であること、Span Corruptionが事前学習タスクとして最適であること、スケーリングが一貫して性能を向上させることを定量的に示しました
  • 後継モデル: Flan-T5(指示チューニング)、mT5(多言語化)、UL2(複数デノイジングの混合)がT5の設計を発展させています

T5の最大の貢献は、「統一されたフレームワークの力」を示したことです。タスクごとに異なるアーキテクチャを設計するのではなく、シンプルなEncoder-DecoderにText-to-Textフォーマットを組み合わせ、大規模なデータで事前学習するというアプローチが、驚くほど広範なタスクで有効であることを証明しました。この思想は、その後のLLM研究(GPT-3のfew-shot learning、Flan系のinstruction tuning、PaLMのスケーリング)にも大きな影響を与えています。

次のステップとして、以下の記事も参考にしてください。

画像なし
BARTの理論とアーキテクチャ
T5と同じEncoder-Decoder型の事前学習モデルBARTの、ノイズ除去自己符号化器としての設計と実装を解説します。
画像なし
多言語Transformerモデルの世界
mBERT、XLM-R、mT5など、多言語対応Transformerモデルの設計と課題を比較します。
画像なし
Span Corruption事前学習の詳細
T5のSpan Corruptionの数学的定式化と、マスク率・スパン長の最適化について詳細に解説します。