GPT-1を読む⑤事前2
前回は、OpenAIが2018年に発表したGPTの最初のバージョンの論文「Improving Language Understanding by Generative Pre-Training」の「3 Framework」(セクション3「フレームワーク」)を読み始めました。
特に、セクション3.1「教師なしの事前学習」で登場する目的関数を中心に解説しました。今回は、この続きで登場するトランスフォーマーのデコーダなどを中心に読み進めていきます。
ここでやっと下図の左側の仕組みについて触れることになります。

次に来るトークンを予測する事前学習の続きを読み進めます。
事前学習の目的関数のおさらい
前回の内容を簡単におさらいします。
論文は、「標準的な言語モデルの目的は、次の尤度を最大化することです」と述べ、最大化するべき目的関数を次のように数学で表現しています。
$$
L_1(\mathcal{U}) = \sum\limits_i \log P(u_i | u_{i-k}, \dots, u_{i-1}; \Theta)
$$
ここで、$${\mathcal{U} = \{u_1, \dots, u_n\}}$$は、ラベルなしのトークン・コーパスです。$${\Theta}$$はモデルの全てのパラメータ(重み、バイアス、など)を一つの記号で表したものです。
$${L_1(\mathcal{U})}$$は対数尤度(log likelihood)です。論文では単に尤度(likelihood)と呼んでいます。この尤度を最大化するのが言語モデルの標準的な目的だというわけです。
尤度とは、ある観測データが特定のモデルとパラメータのもとでどれだけ起こりやすいか(確率が高いか)を示す尺度です。つまり、モデル(とパラメータ)が観測されたデータをどれだけ「尤もらしい」かを評価していることになります。観測されたデータは、事実なのでノイズを含んでいるとしても大方それを尤もらしいと評価できる(つまりは高い確率を予測できる)モデルがより正確なモデルであると言えます。
よって、GPTの事前学習は、上述の目的関数(尤度)を最大化する最尤法に基づいています。
つまり、次の単語を予測するタスクを通じて、与えられたシーケンスに対する条件付き確率を最大化するように訓練されます。これにより、モデルが大量のラベルなし(教師なし)コーパスから効果的に学習します。
そして、そのような学習に最適と考えられたのがトランスフォーマーのデコーダでした。なので、トランスフォーマーのデコーダについても簡単におさらいします。
トランスフォーマーのデコーダ
オリジナルのトランスフォーマーは、機械翻訳モデルでした。それは、下図のようにエンコーダ・デコーダの構造を持っています。

上図を簡略化すると、下図に示すように「入力文章から文脈を抽出する」エンコーダと、その文脈に基づいて「翻訳された出力文章を生成する」デコーダとに別れています。

機械翻訳では、入力文章と出力文章は異なる自然言語になります。例えば、フランス語から英語への翻訳といった具合です。よって、ここでの文脈とは、フランス語の文章からの内容はある種の中間言語に変換したものと捉えることができます。それに基づいてデコーダが出力文章へと変換しているわけです。
また、翻訳の際に、入力と出力ではトークン数が異なる可能性があります。

しかし、デコーダは、次のような自己回帰(auto-regressive)の構造を持つので問題なく対処できます。
まず、デコーダは、入力文章からの文脈と同時に、文章の始まりを意味するトークン<SOS>(Start Of Sentence)を受け取ります。

なお、<SOS>を<BOS>(Beginning Of Sentence)と表記する場合もあります。
つまり、入力文章の全体を受け取って、「翻訳文章の出力となるはじめのトークンを予測してください」と指令を受けているわけです。
予測された最初のトークンが、デコーダの入力として後尾に追加されます。

すると、デコーダは、<SOS>と前回の出力を基に次のトークンを予測します。

これが繰り返されます。<EOS>(End Of Sentence)が予測されると、その文章は終わりとなります。よって、出力文章の長さは可変となります。
このように、常にもっとも確率の高いトークンをデコーダへの入力に回す手法をグリーディ法(Greedy Method、貪欲法)と呼びます。他にも複数の文章の可能性を探るビームサーチ(beam search)という手法もあります。
つまり、デコーダは与えられた文脈を基に、翻訳文章の続きを予測していることになります。

一旦、翻訳というタスクを忘れて考えると、トランスフォーマーのデコーダは、与えられた文章の続きを考える能力を学習できることがわかります。
よって、ある程度の文脈を与えれば、同じ言語でその続きを予測できるモデルを訓練することが出来そうです。さらに、豊富なコーパスがあれば事前学習のデータも十分まかなえます。
では、このような背景を頭の片隅に置いてGPT-1の構造と事前学習に関して続きを読んでいきましょう。
GPT-1の構造と事前学習
トランスフォーマーのバリアント
論文のセクション3.1「教師なしの事前学習」の続きを読みます。
私たちの実験では、言語モデルとしてマルチレイヤーのトランスフォーマーデコーダ [34] を使用しており、これはトランスフォーマー [62] のバリアント(多少変更したもの)です。
In our experiments, we use a multi-layer Transformer decoder [34] for the language model, which is a variant of the transformer [62].
GPT(GPT-1とその後のバージョン)は言語生成モデルであり、トランスフォーマーのデコーダをベースにしています。
ここでマルチレイヤーとは、デコーダの中に複数の層(ブロック)があることを意味します。下図に「12x」とあるのは、12段に積み重ねられたブロックがあるということです。

なお、このデコーダ図をオリジナルのトランスフォーマーのデコーダ部分の図と比べると真ん中の多頭アテンション(Multi-Head Attention)が無いのが分かります。

これは、GPTは機械翻訳のモデルではないので入力文章の文脈を必要としないからです。そのため、GPTはトランスフォーマーのバリアント(多少変更したもの)となります。その中身は、簡略化されたトランスフォーマーのデコーダ・ブロックです。
論文では、これをトランスフォーマー・ブロックと読んでいます。
トランスフォーマー・ブロック
論文の続きを読みます。
このモデルは、入力文脈トークン(のシーケンス)に対して多頭自己アテンション機構を適用し、その後、位置ごとのフィードフォワード層を通して、ターゲットのトークンの出力分布を生成します:
$${\quad\ h_0 = UW_e + W_p}$$
$${\quad\ h_l = \text{transformer\_block}(h_l), \ \forall l \in [1, n]}$$
$${P(u) = \text{softmax}(h_n W_e^\top)}$$
ここで、$${U = (u_{-k}, \dots, u_{-1})}$$ はトークンの文脈ベクトルであり、$${n}$$ は層の数、$${W_e}$$ はトークン埋め込み行列、そして $${W_p}$$ は位置埋め込み行列です。
This model applies a multi-headed self-attention operation over the input context tokens followed by position-wise feedforward layers to produce an output distribution over target tokens:
$${\quad\ h_0 = UW_e + W_p}$$
$${\quad\ h_l = \text{transformer\_block}(h_l), \ \forall l \in [1, n]}$$
$${P(u) = \text{softmax}(h_n W_e^\top)}$$
where $${U = (u_{-k}, \dots, u_{-1})}$$ is the context vector of tokens, n is the number of layers, $${W_e}$$ is the token embedding matrix, and $${W_p}$$ is the position embedding matrix.
いろんなことを簡潔に述べているので、一つずつ解説します。なお、これらの数式はあくまでも数学的なモデルの説明であって、実装は必ずしも同じ構造を持つとは限りませんし、もっと効率的にプログラムされています。
テキストと位置の埋め込み
上式の一番最初にある$${h_0}$$を説明します。これは、下図の一番下の「テキストと位置の埋め込み」(Text & Position Embed)と書かれているところに相当します。

まず、$${U = (u_{-k}, \dots, u_{-1})}$$は、条件として与えられた文章のトークンのシーケンスです。これを論文では、「入力文脈トークン」や「トークンの文脈ベクトル」と呼んでいます。
「トークンの文脈ベクトル」$${U}$$と埋め込みベクトルの行列$${W_e}$$との積を取ることで、トークンの埋め込みベクトル(のシーケンス)に変換しています。
論文では、$${U}$$、$${W_e}$$、$${W_p}$$の内部における、ベクトルの具体的な配列については述べられていません。しかし、上式から推測すると次のような配列になっていると考えられます。
まず、トークン$${u_i}$$は、一つの要素だけが1で他は0になっているワンホット・エンコーディング(one-hot encoding)されたベクトルだと推測されます。これを行ベクトル(横ベクトル)として上から下へと並べたものが入力トークンのシーケンスとなります。
$$
U = \begin{bmatrix}
u_{-k} \\
\vdots \\
u_{-1}
\end{bmatrix} = \begin{bmatrix}
\cdots 1 \cdots \cdots \cdots \\
\vdots \\
\cdots \cdots \cdots 1 \cdots \\
\end{bmatrix}
$$
これに対して、$${W_e}$$は埋め込みベクトルを行ベクトル(横ベクトル)として上から下へと並べたものです。
$$
W_e = \begin{bmatrix}
- 埋め込みベクトル - \\
- 埋め込みベクトル - \\
\vdots \\
- 埋め込みベクトル -
\end{bmatrix}
$$
話が抽象的なので、小さい具体例を使います。まず、条件文章からの入力シーケンスが、次の3つのトークンからなるとします。
$$
U = (u_{-3}, u_{-2}, u_{-1})
$$
これに続いて生成されるトークンは、$${u_0, u_1, u_2, \dots}$$となるわけです。仮に、各トークンが次のようなone-hotエンコーディングになっているとします。
$$
u_{-3} = \begin{bmatrix}
0 & 0 & 1 & 0 & 0
\end{bmatrix} \\
u_{-2} = \begin{bmatrix}
0 & 1 & 0 & 0 & 0
\end{bmatrix} \\
u_{-1} = \begin{bmatrix}
0 & 0 & 0 & 0 & 1
\end{bmatrix} \\
$$
これを$${U}$$としてまとめると次のようになります。
$$
U = \begin{bmatrix}
u_{-3} \\
u_{-2} \\
u_{-1}
\end{bmatrix} = \begin{bmatrix}
0 & 0 & 1 & 0 & 0 \\
0 & 1 & 0 & 0 & 0 \\
0 & 0 & 0 & 0 & 1 \\
\end{bmatrix}
$$
ここでは、使えるトークンの総数は5個しかありません。よって、対応する埋め込み行列$${W_e}$$は次のようになります。
$$
W_e = \begin{bmatrix}
- 埋め込みベクトル 1 - \\
- 埋め込みベクトル 2 - \\
- 埋め込みベクトル 3 - \\
- 埋め込みベクトル 4 - \\
- 埋め込みベクトル 5 -
\end{bmatrix}
$$
各埋め込みベクトルは、実数のベクトルですがここではその次元は問いません。たくさんの数値が横に並んでいると想像してください。
以上より、$${UW_e}$$は次のように計算されます。
$$
\begin{aligned}
UW_e &= \begin{bmatrix}
u_{-3} \\
u_{-2} \\
u_{-1}
\end{bmatrix} W_e \\
&= \begin{bmatrix}
0 & 0 & 1 & 0 & 0 \\
0 & 1 & 0 & 0 & 0 \\
0 & 0 & 0 & 0 & 1 \\
\end{bmatrix}\begin{bmatrix}
- 埋め込みベクトル 1 - \\
- 埋め込みベクトル 2 - \\
- 埋め込みベクトル 3 - \\
- 埋め込みベクトル 4 - \\
- 埋め込みベクトル 5-
\end{bmatrix} \\
&= \begin{bmatrix}
- 埋め込みベクトル 3 - \\
- 埋め込みベクトル 2 - \\
- 埋め込みベクトル 5 -
\end{bmatrix}
\end{aligned}
$$
よって、入力トークンのシーケンスが埋め込みベクトルのシーケンスとなりました。
このような操作は、トークンに対応する埋め込みベクトルを引き出しているので、実際のプログラムでは、ワンホット・エンコーディングのようにメモリや計算量を浪費することはせずに、単にトークンの番号から埋め込みベクトルを呼び出すディクショナリや配列のようなデータ構造を使います。
同様に、$${W_p}$$は位置の埋め込みを行ベクトル(横ベクトル)として上から下へと並べたものです。よって、以下の式により、入力トークンのシーケンスが埋め込みベクトルに変換され、かつ位置の埋め込みが加算されます。
$$
h_0 = UW_e + W_p
$$
マスクされた多頭自己アテンション
もう一度図を見てください。ここまで話してきたのは「テキストと位置の埋め込み」(Text & Position Embed)の部分で$${h_0}$$がこれに相当します。

その上の水色の箱がトランスフォーマー・ブロック(transformer block)で、これが12個積み重なっています。これは、オリジナルのトランスフォーマーのデコーダを簡略化した構造を持ちます。
その中身の最初は、マスクされた多頭自己アテンション(Masked Multi Self Attention)があります。「マスクされた」というのは、訓練の時に次に続くトークンがアテンションの計算に影響しないように隠しているという意味です。
マスクされる位置に対してソフトマックスの入力を大きな負の値にすることでソフトマックスが計算する重みの値がほぼゼロになることで、最終的なバリューの加重平均から除外されるのがマスクをする仕組みとなっています。
ここでもトランスフォーマーのデコーダの仕組みがうまく利用されています。
位置ごとのフィードフォワード
次に、レイヤー正規化、位置ごとのフィードフォワード、レイヤー正規化と続きます。これもオリジナルのトランスフォーマーと同様です。

水色の箱は、数式として次のように表現されています。
$$
h_l = \text{transformer\_block}(h_{l-1}) \ \forall l \in [1, n]
$$
ここで、$${l}$$は、$${1}$$から$${n}$$となっており、トランスフォーマー・ブロックが$${n}$$個あることを意味します。上図に従えば、$${n=12}$$となります。
このトランスフォーマー・ブロックに以前からの出力を渡して、次々と処理をしていきます。
$$
\begin{aligned}
h_0 &\to \text{transformer\_block}(h_0) \quad \to h_1 \\
h_1 &\to \text{transformer\_block}(h_1) \quad \to h_2 \\
\vdots \\
h_{n-1} &\to \text{transformer\_block}(h_{n-1}) \to h_n \\
\end{aligned}
$$
これもオリジナルのトランスフォーマーのデコーダと同じアプローチです。
次のトークンの予想分布
複数のトランスフォーマー・ブロックによる処理の後に次のトークンの確率を計算することを次の式が表現しています。
$$
P(u) = \text{softmax}(h_n W_e^\top)
$$
この段階で$${h_n}$$は、$${n}$$個のトランスフォーマー・ブロックによる処理を経たシーケンス全体です。また、$${W_e^\top}$$は、埋め込み行列を転置したものです。
この二つの積をsoftmaxに通すことで確率分布を求めることになるのですが、これをそのまま文字通り受け入れると、全てのトークンの確率分布を計算していることになります。しかし、目的関数によると予測する必要があるのは次のトークンだけです。よって、実装ではもっと効率良い処理が行われているはずですが、詳細はこの論文からは得られないので、ここでは数学モデルとして見ていきます。
まず、与えられたシーケンスに対して、GPTは次のトークンを予測します。つまり、「犬が河岸を」という入力文章が与えられたときに、次に続くのが「歩く」だとします。そのような例文をテキストコーパスから得たという意味で、モデルの事前学習ではこの「歩く」の尤度を最も高くするように訓練します。
これを少し見方を変えると「犬が河岸を」から「が河岸を歩く」を予測すると捉えられます。つまり、入力文章の各トークンに対して次のトークンを予測しているという意味です。
仮に、「犬が河岸を」がトークンとして、「犬」、「が」、「河岸」、「を」のシーケンスだとします。各トークンは、$${h_0 = UW_e + W_p}$$の処理によってベクトル化されます。そして、$${n}$$個のトランスフォーマー・ブロックによる処理を経て、出力された$${h_n}$$が次のような行列になっているとします。
$$
h_n = \begin{bmatrix}
- 予測された埋め込みベクトル1 - \\
- 予測された埋め込みベクトル2 - \\
- 予測された埋め込みベクトル3 - \\
- 予測された埋め込みベクトル4 - \\
\end{bmatrix}
$$
この4つの埋め込みベクトルは、それぞれ次のトークンを予測したものです。これと埋め込み行列を転置した$${W_e^\top}$$の積を計算します。
$$
\begin{aligned}
&h_n W_e^\top \\
&= \begin{bmatrix}
- 予測された埋め込みベクトル1 - \\
- 予測された埋め込みベクトル2 - \\
- 予測された埋め込みベクトル3 - \\
- 予測された埋め込みベクトル4 - \\
\end{bmatrix} \begin{bmatrix}
| & | & | & | & | \\
埋 & 埋 & 埋 & 埋 & 埋 \\
め & め & め & め & め \\
込 & 込 & 込 & 込 & 込 \\
み & み & み & み & み \\
ベ & ベ & ベ & ベ & ベ \\
ク & ク & ク & ク & ク \\
ト & ト & ト & ト & ト \\
ル & ル & ル & ル & ル \\
1 & 2 & 3 & 4 & 5 \\
| & | & | & | & | \\
\end{bmatrix} \\
&= \begin{bmatrix}
s_{11} & s_{12} & s_{13} & s_{14} & s_{15} \\
s_{21} & s_{22} & s_{23} & s_{24} & s_{25} \\
s_{31} & s_{32} & s_{33} & s_{34} & s_{35} \\
s_{41} & s_{42} & s_{43} & s_{44} & s_{45} \\
\end{bmatrix}
\end{aligned}
$$
$${W_e^\top}$$は、埋め込み行列を転置しているので、埋め込みベクトルが列ベクトル(縦ベクトル)になっています。よって、上式は「予測された埋め込みベクトル」と「埋め込み行列内のすべての埋め込みベクトル」との内積を計算していることになります。
例えば、
$${s_{11}}$$は、「予測された埋め込みベクトル1」と埋め込み行列の中の「埋め込みベクトル1」との内積
$${s_{12}}$$は、「予測された埋め込みベクトル1」と埋め込み行列の中の「埋め込みベクトル2」との内積
以下、$${s_{13}}$$、$${s_{14}}$$、$${s_{15}}$$と同様
このように計算した内積で一番大きい値を与える「埋め込みベクトル」が「予測された埋め込みベクトル1」と一番近いので、そのインデックスが予測されたトークン(の番号)となります。
このような計算を全ての「予測された埋め込みベクトル」に対して行うには、$${h_n}$$と$${W_e^\top}$$の積を計算すれば良いことがわかります。これが$${W_e}$$を転置している理由です。
さらに、$${h_n}$$と$${W_e^\top}$$の積の結果の各行ごとにソフトマックスを計算すれば、シーケンスの各位置において予想されたトークンの確率分布がわかります。これが$${\text{softmax}(h_n W_e^\top)}$$の意味です。
よって、$${P(u) = \text{softmax}(h_nW_e^\top)}$$は、全ての位置における次のトークンの確率分布を計算していることになります。しかし、実際には最後のトークンの次のトークンの予測だけわかれば良いので全体の確率分布を計算するのは効率的ではありません。上式はあくまでも数学的モデルとして理解してください。
なお、内積が大きいほど確率が高いので、内積の結果をスコアと呼んだりします。そのため内積の結果に$${s}$$という文字を使いました。また、実際にソフトマックスで確率(0から1の値)に変換しなくとも、どのトークンが予測されたかは、最大のスコアのインデックスを見ればわかります。よって、確率分布が必要なければ、ソフトマックスは必要もなく argmax などでどのトークンが最大のスコアに相当するトークンのインデックスを決めることもできます。
以上で、セクション3.1「教師なし事前学習」が読み終わりました。
次回予告
次回は、次のサブセクションであるセクション3.2「教師ありファイン・チューニング」を読み進めます。
お楽しみに!
