トランスフォーマーの自己アテンションの理解①トークン数値化の歴史
2017年に、Googleの研究者であるAshish Vaswaniらのチームが発表したトランスフォーマー (Transformer)のアーキテクチャは、近年におけるディープラーニングの進展に大きく貢献しました。GPT3やBERTなどの巨大な言語モデル(LLMs, Large Language Models)の土台にもなっています。
特に自己アテンションと呼ばれる仕組みが重要です。どのようなシーケンス(順番に並んでいるもの)でも応用が効くため、最近では言語モデルだけでなく、画像処理や画像生成の分野にも広く応用されています。とは言っても、もともとトランスフォーマーは機械翻訳のモデルとして登場したので、その文脈で話を進めます。その方が歴史を辿りやすいのが理由です。
この記事ではあまり数式を使わずに、トランスフォーマー以前の言語モデルの歴史を遡ります。そこに見られるのは言語の数値化が進化する過程です。なぜなら、単語や文脈をうまく数値化すれば機械学習で利用することができるからです。
歴史を振り返りながら、文章の数値化、トークンの分散表現、文脈の数値化を順番に辿っていきます。
では、始めましょう。
文章の数値化
まずは文章を数値化する努力を見ていきます。機械学習は突き詰めると数学なので、数値化しないことには何も始まりません。
BoW
Wikipediaによると、1950年代にはBag of Words(BoW)と呼ばれる手法による文章の分類などが研究されていました。
BoWとは単純にいうと、文章の特徴をその文章の中にある単語の出現数で定義するものです。正確にいうと、単語というより、形態素(最小の意味単位)を数えます。
例えば、以下の文章があったとします。
「カエルが飛ぶ、犬が飛ぶ」句点は無視するとして、この文章を以下のように分解します。
['カエル', 'が', '飛ぶ', '犬', 'が', '飛ぶ']分解された要素が現れる回数を数えて、この文章を数値化すると、
{'カエル':1, 'が':2, '飛ぶ':2, '犬':1 }といった感じになります。これがBag of Words(言葉の袋)に相当します。

BoWは言葉の出現頻度を利用して文章の特徴を掴むことにより分類ができるという仕組みです。例えば、スポーツ関係の言語要素が多い記事はスポーツ・ニュースだろうといったイメージです。
しかし、BoWには弱点があります。それは、文章における要素の出現順序を全く考慮していない点です。つまり、言葉の前後がわからないので、文脈や要素感の関係を扱うことがほぼできません。
トークン化
文章を分解した最小要素をトークンと呼ぶことも多いです。単語とか形態素とか要素とか曖昧な呼び名をいろいろと出すと混乱を招く恐れがあるので今後はなるべく「トークン」で統一します。
また、文章を最小要素に分解することをトークン化と呼びます。トークン化には、いろいろな方法がありますが、そこは深掘りしないことにします。単純に「名詞、動詞、形容詞、助詞などある程度の意味や役割がある要素に分解すること」としておきます。
さて、BoWの話に戻すと、やっていることは「文章における各トークンの出現数を数える」ことになります。ただし、上記の例のままだと、トークン化された要素自体は文字列(文字が並んだもの)なので、これを数値化する必要があります。人間は文字を読んで理解していますが、コンピューターや計算などで使うためには、トークンを数値で表した方が便利なのです。
One-Hotエンコーディング
トークンを文字から数値に変換する方法として、One-Hotエンコーディング(ダミー変数とも呼ぶ)があります。今でも機械学習などで使われることのある手法です。
仮に、扱うトークンの種類が「カエル」、「が」、「飛ぶ」、「犬」の4つのみとすると、以下のように0と1の位置で単語を数値化できます。
カエル = [1, 0, 0, 0]
が = [0, 1, 0, 0]
飛ぶ = [0, 0, 1, 0]
犬 = [0, 0, 0, 1]つまり、4つの位置の1つに、1を設定することで、トークンの表現しています。これがOne-Hotエンコーディングです。
この手法では1を設定する位置がトークンの種類を決めるので、その位置にトークンの出現数を設定すると、文章全体を数値化することができます。
具体的に見ていきましょう。「カエル」が1回、「が」が2回、「飛ぶ」が2回、「犬」が1回の出現数なので所定の位置に頻度を設定します。例えば、「が」の位置は2番目なのでそこに出現数の2を入れます。また、句点は無視しています。
「カエルが飛ぶ、犬が飛ぶ」= [1, 2, 2, 1]これで、文章とトークンがBoWとOne-Hotエンコーディングで完全に数値化されました。
逆に、この数値列を見れば、「カエル」が1回、「が」が2回、「飛ぶ」が2回、「犬」が1回、それぞれ出現する文章であることがわかります。もちろん、上述のOne-Hotエンコーディングでトークンが数値化されているという前提が必要です。このように自然言語処理では、言葉やトークンを文字列として直接扱わずに、数値化されたものを使って計算をします。
ここまでで、文章の数値化が可能になりましたが、One-Hotエンコーディンングによるトークンの数値化は取扱難い部分もあります。特に問題なのは、次元がたくさん必要なことです。次のセクションでは、次元の問題を解決する手法が出現します。
トークンの分散表現
ベクトルと次元
One-Hotエンコーディングのように、数値を並べたものをベクトルと呼び、ベクトルで使う位置の数をベクトルの次元と呼びます。上記の例では、4つの位置を使っているので4次元ベクトルになります。
なお、One-Hotエンコーディングではトークンの種類が増えると、このベクトルの次元も大きくなります。例えば、トークンの種類が千個ならば、1000次元のベクトルが必要です。なぜなら、1を設定する位置でトークンの種類が決まるので、999個の0と1個の1でトークンを表現することになるからです。これはあまり効率的ではありません。
カエル = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]
が = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]
飛ぶ = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]
犬 = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]
...実際の文章では、トークンの種類は何万個に及ぶかもしれません。よって、One-Hotエンコーディングはたくさんの語彙を必要とする場合に不向きです。
分散表現
そこで、分散表現(Distributed Representation)という手法が現れます。分散表現もトークンをベクトルで表現しますが、扱う数値は0と1だけに限定されません。
例えば、以下のように、2次元のベクトルでトークンを表現したとします。
カエル = [-0.31, 0.28]
が = [ 0.32, -0.16]
飛ぶ = [-0.18, -0.19]
犬 = [ 0.25, 0.29]図にするともっとイメージがしやすいです。

2次元空間上には無数の点があるわけで、たくさんのトークンが表現可能になります。
このように何らかの空間におけるベクトルを使ってトークンを表現することを分散表現と呼びます。
局所表現
ちなみに、One-Hotエンコーディングのように単語を別々に表現する(1つの単語を1つの成分で表す)ことを局所表現(local representation)と呼びます。ベクトルの1要素で表現する局所表現に対して、ベクトルの全要素を使うのは分散表現というわけです。
分散表現の利点は、One-Hotエンコーディングよりも効率よく次元を利用できることです。One-Hotエンコーディングではトークンの種類の数だけ次元が必要でしたが、分散表現でははるかに少ない次元数ですみます。
高次元ベクトルの必要性
ただし、2次元では足りません。仮に2次元空間に無数の点があるとしても、2つの独立したベクトルがあれば2次元上の全てのベクトルがその組み合わせで再現できてしまいます。より多くの独立した次元があることで表現できる情報が増えます。
オリジナルのトランスフォーマーでは512次元のベクトルを使ってトークンを表現しました。BERTの基本的なバージョンでも768次元を使っています。
次元が多いほど情報量が増えるのはわかりますが、トークンを表現するだけのために、なぜそんなにも次元が必要なのでしょうか。
実は、言語の要素には、単語そのものの意味や文法的な役割だけでなく、文章内での他の言葉との関係などによって、1要素単体を越えた複雑な情報が含まれるからです。また、1つの言葉でも複合的な意味を持つことが多いです。
例えば、「女王」という言葉には「女」と「王」の意味が込められているし、「女帝」や「女皇」ともニュアンスが異なります。さらに「女王」と「王様」とには関係があります。その関係は、「夫婦」かもしれないし、「主権がある女王」と「主権のない王様」かもしれません。
よって、様々な属性やトークン同士の関係などを表現するには、より多くの次元数が必要となります。
さて、文章やトークンの数値化が可能になってきました。しかし、文章の流れに沿った文脈はどのように数値化するのでしょうか。
どのような手法が発案されてきたのか見ていきましょう。
文脈の数値化
文脈とは
文脈の定義にもいろいろと思想がありますが、トークンを独立したものとして扱わないという点では共通しています。文章の中の他のトークンが与える情報がトークンの役割や意味を補強するからです。
以下に、複数のトークンの関係を何らかの形で表現する手法を見ていきます。
N-グラムによる文脈
前述したように、BoWではトークンを独立に扱うので、文章の文脈やトークン同士の関係などを数値化することはできません。
そこでバイグラム(bi-gram)やN-グラム(n-gram)が公案されました。バイグラムでは、2つの隣り合うトークンを連結して1つの単位として扱います。
例えば、以下のようになります。
['カエル', 'が']
['が','飛ぶ']
['飛ぶ', '犬']
['犬', 'が']
['が', '飛ぶ']2つのトークンを入れた箱が横に移動するイメージで考えるとわかりやすいかもしれません。

これによって、「カエル」と「が」を1つの意味単位として捉えることができるので、他の助詞がつながる場合と区別できます。例えば、「カエルと」、「カエルや」、「カエルは」など。
この文章にはないですが、「白い犬」や「怒った猫」など、形容詞がつく場合もバイグラムの方が特徴をより捉えることができます。
なお、N-グラムはトークンの連結をより一般化したものです。Nが1ならユニグラム(uni-gram)でトークンは全て独立、Nが2ならバイグラム、Nが3ならトリグラム(tri-gram)で3つの連続したトークンを一つの単位として扱います。例えば、以下のようになります。
['カエル', 'が','飛ぶ']
['が','飛ぶ', '犬']
['飛ぶ', '犬', 'が']
['犬', 'が', '飛ぶ']このような仕組みによって、ある範囲での文脈を読み取ろうとしていました。しかし、['が','飛ぶ', '犬']という組み合わせはあまり役に立ちそうもありません。あるトークンが、どのトークンと緊密な関係があるのかを全く考慮していないからです。
また、もっと長い文章では、遠くのトークン同士でも何らかの関係がある可能性があります。文章の最後に否定形が出現したら意味がひっくり返ります。

よって、バイグラムやトリグラムなど複数のトークンを連結しても範囲が限られた文脈で限定的でした。もちろん、独立したトークンより、文脈を読み取れる可能性がありますが、意味のない連結だとむしろ邪魔な連結になる危険もあります。
複数のトークンをまとめて文脈を表現する手法とは逆に、文脈から単語やトークンの意味を抽出する手法も現れました。そこでは、分散表現の考え方が役に立ちます。
Word2vecによる分散表現
分散表現のベクトルは適当に決めたものではありません。何らかの手法によって言語のデータから抽出する必要があります。
Word2vec(word to vectorの略)は、分散表現を生成するための手法です。
Googleのトマス・ミコロフらによって2013年に特許が取得され公開されました。word2vecでは分布仮説(単語の意味はその単語の周辺の単語によって定まる)の考えをもとにしています。
Word2vecではニューラルネットワークに学習をさせることで分散表現を生成します。CBOW(continuous Bag-of-Words)とスキップグラム(skip-gram)の2種類の手法があります。
CBOWでは各単語の文脈(周りの単語)を入力として、その文脈に当てはまる単語をニューラルネットワークを使って予測・推論します。つまり、単なるBoWではなく文章の流れ(文脈)
例えば、下記の文章で赤い箱に入るべき単語は何でしょうか?

おそらく「が」が入る確率が高いでしょう。もしかしたら「と」もあり得るかもしれません。「カエルと飛ぶ 犬が飛ぶ」なんて、ちょっと詩的でもあります。ただし、確率は低くなるかもしれません。CBOWでは、このように文脈から当てはまる単語の確率を予測するニューラルネットワークを訓練します。
逆に、スキップグラムではある単語を入力として文脈(周りの単語)を予測します。

「犬」の周りにくる単語(文脈)を予測するわけです。たくさんも文章を正解例として学習すれば、前後に続く単語の確率を計算できるようになるわけです。
CBOWやskip-gramで訓練されたニューラルネットワークから分散表現を生成します。
ニューラルネットワークが確率を計算する出力層の前には隠れ層(Hidden Layer)があります。そこでは予測された単語の出現確率を計算するための情報が含まれており、これがたくさんの文章から学んだ文脈の情報を含んでいると考えます。
よって、CBOWやskip-gramの手法で訓練されたニューラルネットワークに、ある単語を入力して得られる隠れ状態(hidden state:隠れ層からの出力をベクトルとして取り出したもの)をその単語の意味を示す値、つまりは分散表現とします。word2vecではすでに数百の次元による分散表現が可能でした。
埋め込み
自然言語処理では、トークンを分散表現にしたものを埋め込み(Embedding)という呼ぶことも多いです。また、単語の埋め込み(Word Embedding)とも呼びます。
埋め込みという言葉には「トークンの意味をある空間上に位置付ける」といったニュアンスがあります。トークンの種類を決めるだけでなく、トークン同士の関係なども空間上の相対位置や方向から表現できるからです。ただ単にベクトル空間上の適当な位置にトークンを指定するわけではありません。位置や方向に意味があるのです。
ただし、分散表現も埋め込みもベクトルを使ってトークンを表現するので同じことをしています。しいていえばニューラルネットワークなどを使って、トークンを分散表現へと落とし込む場合に、「埋め込み」の方がよく使われる印象はあります。
分散表現とベクトル演算
Word2vecで生成された分散表現ではトークン間の関係をベクトルの関係で表現することができます。有名な例は、「王様」ー「男性」+「女性」=「女王」があります。

つまり、分散表現で数値化された語彙の間でベクトルの演算ができるわけです。これはOne-Hotエンコーディングなどでは実現不可能なことであり、文章の文脈から生成されたWord2vecによる分散表現の強みでもあります。
他にも、国名と首都名の関係性を表すこともできます。以下は、主成分分析(PCA)を行いベクトルを多次元から2次元に次元圧縮した図です。

まとめると、Word2vecではトークンが使われる文脈からトークンの意味を抽出してベクトルとすることで、分散表現を行います。また、トークン同士の関係を見たり、演算が可能であることがわかりました。
以上、文章、トークン、文脈の数値化を見てきました。
機械翻訳を行うにはいろいろと問題もありそうです。N-グラムなどでは、文脈をとる範囲が限定的だし、あるトークンがどのトークンと強い関係があるのかどうかの判断が出来ません。
Word2vecでは、限定的ですが文脈を取り入れた分散表現を行いトークン同士の関係をベクトル分解で表現することが可能でした。しかし、それだけでは機械翻訳をするにはまだ不十分です。
文脈は文章のシーケンス(順番)に沿って伝達していきます。そこで、トランスフォーマー以前には、再帰の仕組みを利用したRNN(リカーレント・ニューラルネットワーク)が主流になりました。特にディープラーニングが成功したことによりその実用性が高まりました。
(続く)
