注目の記事

ChatGPTを探す旅に出させていただきます

おはこんハロチャオ~!何者(なにもん)なんじゃ?じょんすみすです。

ChatGPTさんは凄いですね。 人間とお話ししてるとの変わりないお話の仕方で、時には人間より詳しい情報を教えてくれたりもします。 時に嘘を言ったりもしますが、ネット上の膨大なデータから学習していることを考えると、 本当のことだったり嘘だったりを最初から教えられて育ったわけですね。

さて、そんなAIによる自然言語処理の世界もある日突然凄いことができるようになったわけではありません。 今回は、Deep Learningが流行りだして以降の自然言語処理の世界にどのような変化が訪れていったのかを振り返ってみましょう。

なお、本記事の注意点としては

  • 一般的なフィードフォワード型のニューラルネットワークに順伝播及び誤差逆伝播による学習の方法は理解している前提としています
  • 個々の内容は手法の概要を解説しているため、評価結果・関連情報などの詳細には立ち入っていませんので詳細は元論文などを参照して下さい

まずは全体像

まずは全体像としてどのようなお話をしていくのか見ていきましょう。

自然言語処理と一言で言っても、そのタスクは多岐にわたります。 ここでは、Deep Learningブーム以降によく見るタスクを前提に見ていきたいと思います。

単語の意味を探し求めた分散表現の日々

機械学習で扱うデータは何らかの方法で数値にしてやる必要があります。 自然言語を扱う場合、単語や文章などを数値または数値の列で表現する方法が必要になってきます。

いろいろな方法がありますが、最もシンプルなものはOne-Hotエンコーディングと呼ばれるものです。 One-Hotエンコーディングでは、文章ごとに単語のダミー変数を作成するような形式となります。 文書を行方向に、単語を列方向に展開して、文書に含まれる単語の部分を「1」、それ以外を「0」にしていきます (出現回数をカウントしたり、TF-IDFで正規化したりなどの関連する方法が他にもあります)。

単語1 単語2 ... 単語n
文書1 1 1 ... 0
文書2 0 1 ... 1
文書3 1 0 ... 1

文書の数が多い場合、単語の種類(ボキャブラリ)も多くなり単語の次元が大幅に増えていきます。 一方、一つの文書に含まれる単語の数には限りがあるため、これは全体として疎行列になります。

また、単語が各次元として扱われますが、文書ごとの出現順序など、単語間での関連性を示す情報は抜け落ちたものとなります。

それに対して低次元(通常数百次元程度)の密な行列で単語の意味を定義する方法があります。 これは、「分散表現」や「埋め込み表現」と言われるものになっております。 この表現を獲得するため手法は様々なものがありますが、ここではWord2Vecを紹介します。

Word2Vecの元論文はかなりあっさりと手法が記載されているのみなので、具体的な実現方法については解説論文もセットで読むことをお勧めします。

Word2Vecはある単語に対するベクトル表現を獲得するために、 単語の意味は前後の単語によって決まるであろうという分布仮説に基づいています。 One-Hotエンコーディングされた単語列とある文書に置いてある単語の周囲にどのような単語があるかによってベクトル表現を学習します。

Word2VecにはCBOWとSkip-Gramという2つの手法があります。

CBOW

CBOW(Continuous Bag-of-Words)では、周辺の単語から間に挟まる単語を予測するように学習します。 例えば

  • 今日 は いい (天気) です ね

ような文章中の括弧内の単語を予測出るようにする手法です。

文章中の前後One-Hotエンコーディングされた単語を入力して、この単語を予測します。 この学習に3層構造のニューラルネットワークが利用されています。 前後1単語を対象として学習を行う場合は以下のような構造となります。

この構造で、学習したモデルに対して単語を入れた際の中間層の出力の値がCBOWにおける分散表現となります。 そのため、中間層のユニットの数が分散表現における次元数となります。

Skip-Gram

Skip-GramはCBOWの逆のような構造となっています。 ある単語から周辺の単語を予測します。

学習時の仕組みも逆の構造を取っています。

Word2Vecでできるようになったこと

Word2Vecのような単語の分散表現で最も話題になったことは、単語間の関係性を計算できるようになったことです。 コサイン類似度を使って似ている単語を調べたりもできますし、有名な例では「King - Man + Woman」が「Queen」に近いベクトルになったという例があります。

分散表現の使い道

Word2Vecのような分散表現には他にも様々な手法があります。 単語の分散表現自体そのものを出力として利用することももちろん可能です。

一方で、より高度な処理をする際の特徴量としてニューラルネットワークの入力に利用するような活用方法もあります。 自然言語を扱う際にネットワークの中ではこれらは「Embedding層」として表現されることがあります。 また、大規模なコーパスに対してあらかじめ埋め込み表現の事前学習モデルを作成しておくようなことも行われています。

系列データの始まりの地RNN

Deep Learningブームと言えば画像を扱う領域から始まったわけですが、 自然言語のような順番がある系列データを扱いたい場合はそれ用の構造が必要だということで始まったのがRNN(Recurent Neural Network)です。

RNNは従来のフィードフォワード型のネットワークに再帰的な構造を追加したものとなります。

この構造は、順伝播の際に無限ループを発生するようにも見えるかもしれませんが、そういう訳ではありません。 各ユニットが前段の層からの入力と自身に戻るルートそれぞれに重みを持っており、それらを使って合わせたものを活性化関数に通して次の層への出力とするような動きとなります。 その際、自身に戻るルートは今回の入力に対してではなく、前回の入力から得られたものを利用します。 そのため、この構造は以下のような形式で展開された図で説明されることがよくあります。

新たな入力と、前回までの中間層の入力を足し合わせたものが新たな中間層への入力となるわけです。 「今日は良い天気ですね」のような文章の場合

  • x_1 : 今日
  • x_2 : は
  • x_3 : いい
  • x_4 : 天気
  • x_5 : です
  • x_6 : ね

のように順に入力されて、その前までの文章が情報が伝播していきます。

中間層は、それぞれからの入力を足し合わせて以下のように計算されます。

ここでf(・)は活性関数となります。

これは文章のような系列データの情報を順次与えていくようなイメージと一致しますね。 RNNはこのように系列データを順番に入れていくための構造となっています。

損失関数は以下のように時系列全体で足し合わせたものが利用できます。

学習のいくつかの方法がありますが、シンプルな方法としてBPTT(Backpropagation Through Time)があります。 これは、上述のような系列方向に展開したグラフに対して通常の誤差逆伝播と同様に計算する手法となります。

記憶のゲートを持つRNNとの出会い

RNNでは系列データを扱うために過去の情報を扱う構造になっていました。 これに対して長い系列のデータになると勾配消失問題によって学習が難しくなります。

そこで過去の情報を保持しておくためのゲートと呼ばれる機構を導入したものが考案されました。 このゲートで有名なものにLSTMやGRUというものがあります。 ここでは、LSTMについて紹介します。

LSTMにはいくつかの手法が存在しますが、ここではメモリセル, 入力ゲート, 出力ゲート, 忘却ゲートの構造のみを持つものを紹介します。

RNNの中間層において内部にいくつかの計算を行うものが追加されます。

LSTMでは入力データと前回の中間層を受け取る口として、通常の入力, 入力ゲート, 忘却ゲート, 出力ゲートの4つがあり、それぞれ異なる重み W と活性化関数を持ちます。 また、内部の状態としてメモリセル c があります。

時刻 t における3つのゲートはそれぞれ, F(Forget), I(Input), O(Output)として以下のように計算されます。

それぞれシグモイド関数を通しているので0~1の範囲でそのゲートをどの程度通すかを決めています。

入力は活性化関数tanhを通して以下のように計算されます

このu_tに入力ゲートの値を掛けたものと、t-1のメモリセルcの値に忘却ゲートの値を掛けたものを足し合わせてメモリセルの値を更新します。

最後にメモリセル更新されたメモリセルの値に忘却ゲートの値を掛けてこの層の出力とします。

なお、ここで⊙はベクトルの要素ごとの積を意味します。

この計算によってLSTM内部では、各ユニットごとにそれぞれ

  • 入力をどのくらい範囲させるか
  • 内部の状態をどの程度忘れさせるか
  • 出力としてどの程度通すか

を制御することで長期にわたっての状態の保持を行っています。

Seq2Seqによって世界は彼らに役割の分化を求める

自然言語処理におけるRNNの使い方として自己回帰という方法で文章生成を行うものがあります。 これは、文章の自分自身の次の単語を予測していくものとなります。

この流れで文章を学習させれば、推論時は最初の単語さえ入れれば後続の文章が生成可能になります。

これに対して、入力と出力がそれぞれ異なる長さの文章の対応関係のある文章を学習させたいとします。 これは、機械翻訳のようなものをイメージしていただけると分かるかと思います。

それを実現するために入力部分をエンコーダ、出力部分をデコーダとして分けたネットワークがSequence to Sequenct(以下Seq2Seq)となります。

エンコーダ側では出力yを持たず、順に時系列で文章を流していくのみです。 それに対して、文章の終了後にデコーダ側の最初の単語を出力して、自己回帰を行っていくのがデコーダになります。

この仕組みによって、対応関係のある異なる文章間で学習させたネットワークから入力に対する応答が可能になります。

これは、Q&Aや機械翻訳などで文章生成をしたい場合に便利な構造となっています。

自己回帰やSeq2Seqでは学習時には次の単語などを基準に学習を行いますが、 推論の際には1つ絞らずに確率の高いいくつかの単語を候補にとっておいて、そこから複数の文章を生成してみることがあります。 そうすることで出力して複数の候補を生成することが可能です。 この仕組みをビームサーチと呼んだりします。

二人の関係に注意をはらうもの、Attentionが生まれる

Seq2Seqの構造は長さの異なる2つの系列データ間での変換が可能になる構造です。

この構造の課題としては、エンコーダからデコーダへのつながりは最後の1回のみとなっていることです。 非常に長いデータを入れると最初の頃に入れたものほど情報が欠落していく可能性があります。 そこで、系列全体の情報を保持しておくための機構としてAttention(日本語では「注意」や「注意機構」と呼ばれる)が登場します。

Attentionはエンコーダ側の各系列の中間層の情報を保持しておき、 デコーダ側の各系列に対してどの程度影響を与えるかを算出するものとなります。

エンコーダ(Source)とデコーダ(Target)の各系列ごとの対応関係を示すことからSource-Target Attentionと呼ばれることがあります。

エンコーダ側の中間層をh, デコーダ側の中間層をs、としてAttentionを追加することを考えます。 まず、デコーダ側のt=iの時の出力s_iを以下のよう、Attentionであるc_iを使って計算するように拡張します。

c_iは次のように計算されます。

T_xはエンコーダ側の系列の長さを表します。 エンコーダ側の中間層の各時刻jとデコーダ側の処理対象であるi番目の関係を関数a(・)を使って表しています。

以下のような流れで計算を行うイメージとなります。

関数a(・)にどのようなものを利用するかに関しては複数の方法があります。 ここでは、例として後続のTransformerでも利用されるものを示します。

これは二つのベクトルs, hの内積を計算して、次元数dの平方根で割ったものとなります。

Attentionの可視化について上述の論文からの引用となります。 エンコーダ側を英語、デコーダ側をフランス語として機械翻訳を行うタスクにおいて、それぞれがどの単語に反応しているのかの例が示されています。

そして、世界は変わる...Transformerによって

ついに登場しました。現代の自然言語処理における中核を成す存在であるTransformerです。

どれくらい中核を成すかと言うと、この論文のタイトルとFigure1は「Hidden Technical Debt in Machine Learning Systems」のFigure1の次くらいに私の中では引用されてるランキング上位に入ります(よくわからない例えですね)。

Transformerの主な特徴は

  • Attentionを構成する要素をQuery, Key, Valueの3つに分けて定義
  • Multi-Head Attentionによる並列化
  • Self Attentionの考え方の提案
  • 入力を集合として扱うこととPositional Encodingによる順番の定義

あたりがあげられるかと思います。

まずはTransformerの全体像を見てみましょう(図は元論文より)。

大きく分けて、左側のエンコーダと右側のデコーダに分かれています。

内部の構造としては以下を組み合わせて利用していることが分かります。

  • Multi-Head Attention
  • Add & Norm
  • Feed Forward

また、各構造の手前にPositional Encodingがあります。

Transformerは元々、機械翻訳を行う目的で提案された手法となっており、エンコーダ側の言語をデコーダ側の言語に翻訳することを目的としてます。 そのため、それぞれの内部表現を獲得したのち、組わせて最終的な出力とする構造となっています。

これらを順にみてきましょう。

Positional Encoding

まず、Inputs, Outputsと書かれた双方はEmbeddingの部分で埋め込み表現に変換されたのち、Positional Encodingという処理がされています。 Transformerでは、これまでのRNNベースの手法と異なり系列データとして順に入力していくのではなく単語の集合として扱っています。

これに対して、各単語が文章中のどの位置にあったのかを示すのに利用されるのがPositional Encodingです。

Embedding及び、後続のTransformer内部のパラメータの次元数をd_{model}として、 単語の位置であるposと各単語のベクトルのi番目の次元を使って以下のような正弦波を使ったベクトルを作成します。

x_{pos}にこのPEを加算することで位置情報を内部の表現に埋め込んでいます。

いざ、エンコーダ, デコーダの処理へ

続いて、四角で囲われたエンコーダとデコーダの内部を構成する要素についてみていきましょう。

まずはMulti-Head Attentionがあります。 これはMultiという名がついている通り、ある機構を複数個持っています。 そのある機構がScaled Dot-Product Attentionというものになります。

Scaled Dot-Product Attentionは前述のSorce-Target Attentionの解釈を拡張したものになっており、Query, Key, Valueという3つの要素で構成されます。

それぞれがベクトルとなっていて、入力となるQueryとそれに紐づくKey, Keyから取得されるValueで構成されます。 Source-Target Attentionで解説したものはKeyとValueに分かれていませんでしたが、memoryに該当する部分をこの2つに分けることで、内部の処理での非自明な変換が学習できるということです。

最初のMatMulでは、関数a(・)については先ほど紹介したものを使っています。 これらをまとめると、Scaled Dot-Product Attentionの計算は以下のようになります。

Multi-Head Attentionは、このScaled Dot-Product Attentionを複数持つ構造になります。

どのように複数持つのか、という構造が重要になってくるわけですがここでその変換を行う3の行列W_q, W_k, W_vが登場します。

入力の次元よりも少ない次元の行列を用意してQ, K, Vをその次元に写像したものでAttentionの計算を行います。

この時、複数もつヘッドの数をhとして

の値に設定します。 これでこの次元の行列がh個出来上がります。

最後にこれらを結合してMulti-Head Attentionの出力とします。

この時、W^Oを含む各Wは学習によって求める対象となります。 これによって、複数の異なる観点でのQ, K, Vの関係を求めるのがMulti-Head Attentionの目的となります。

この出力をAdd & Normに渡しています。 ここではAttentionの出力をレイヤ正規化する処理を行っています。

次に、TransformerのQ, K, Vにはどのような値を入力するのかを確認していきましょう。 ネットワークの構造を見るとエンコーダ、デコーダ共にPositional Encodingされた後の情報が3つに分岐してMulti-Head Attentionに入力されています。 実は全て同じものが入力されています。 これはSelf-Attentionと呼ばれる手法となります。

Self-Attentionによって計算量を抑えつつ処理を並列化しやすくしています。 また、広範囲の依存関係の学習をすることにも影響を与えているとのことです。

後続のFeed FowardではReLUを活性化関数に使った全結合のネットワークを利用しています。

エンコーダ、デコーダ共にこれで必要なパーツはほぼ全て出そろいました。 最後に、デコーダ側のみにあるMasked Multi-Head Attentionについて確認しておきましょう。

と言っても、これは何か特殊なことをしているという訳でありません。 デコーダ側は正解の文章を与えるため、全ての情報を見せてしまうとカンニングと同等になってしまいます。そのため、入力の最後の単語などの一部をマスクした状態でデータを渡すためのものとなっています。

なお、この段階でのモデルのパラメータは以下のようになっています。

  • d_{model} = 512
  • h = 8
  • レイヤ数N = 6

ついに出会う、生成AIとGPT

ついに辿り着きました。GPT(Generative Pre-trained Transformers)です。 とは言いつつも、実はTransformerまで達成していれば、ベースの構造は理解できていると言ってもいいでしょう。 GPTにはいくつかのバージョンがありますので、共通しているところをベースに見ていきたいと思います。 個々の詳細に関しては以下をご参照ください。

また、GPT-4に関してもモデルやデータセットに関しては公開されていませんが、テクニカルレポートは公開されているようです。

GPTの基本的な構造はTransformerにおけるデコーダの部分のみを取り出したものです。

図は「Improving Language Understanding by Generative Pre-Training」より。

Positional Encodingにおいて、正弦波を使うのでなくパラメータを学習させるなどの違いはありますが、基本的にはデコーダ部分をベースとして、d_{model}やMulti-Head Attentionのh, 層の数といった部分が異なるのがその特徴です。

バージョンが上がるごとにこの層の数や学習に利用するデータ量を増やしていってより大規模なモデルになっていることからもLLM(Large Laungage Model)と呼ばれるゆえんですね。

初期のGPTでは

  • N=12, d_{model}=768で1.17億個のパラメータ
  • BooksCoupusデータセットを利用して学習
  • fine-tuningの実施

となっておりましたが、GPT-2では

  • レイヤ数N=48で15億個のパラメータ
  • クロールした40GBのデータ
  • fine-tuning無し

となり、GPT-3ではさらに

  • 1750億個のパラメータ
  • 45TBあるCommon Crawlから質の高いものにフィルタリングした570GBのデータ

と、モデルの規模もデータセットの規模もどんどん大きくなっていることが分かります。

こうしたTransformerをベースにした大規模なモデルがGPTとなっているようです。

人間の好みを知る、InstructGPT

ここまでで、ChatGPTに繋がるベースの機械学習モデルの構造が見えてきました。 最後の課題として、大量のデータを統計的に扱っているだけであるがゆえに越えなければならない壁があります。 Chatbotとして人間の好みに合わせた自然な会話というものを学習させたいわけです。 それを実現するための仕組みが人間による教師データとそれに基づく強化学習を行うInstructGPTとなります。

InstructGPTでは以下のステップで操作を行います。

  1. GTP-3のモデルに対する教師あり学習でfine-tuning
  2. 文章の良さを判定するためのreward modelの獲得
  3. Reward Mdelを最大化する学習

これらを順にみていきましょう

教師ありfine-tuning

最初のステップではモデルのfine-tuningを行います。 論文中ではGTP-3のモデルをベースにしていると記載されています。

ベースのモデルに対して、人間が用意した理想的な入出力のペアを教師あり学習でfine-tuningします。 このモデルをSFT(supervised fine-tuning)モデルと呼びます。

Reward Modelの獲得

次にReward Model(RM)を獲得します。 この工程では、同一の入力に対して複数の出力を得たうえで、それらの良し悪しをランキング化します。

出力の数をKとして、4個から9個の間でこれを得ています。 これらKこの出力から任意の2個に対して、どちらの方が優れているのかランク付けするための関数を憑依して比較します。 全ての組み合わせに対して評価を行って、期待値を取ったものを損失関数として定義しています。

y_w, y_lはそれぞれxに対する出力から組み合わせの中から選んだ2つに対してr_θ(x, y)の差を取ったものを求めています。 r_θ(x, y)はスカラ値でxに対するyのパラメータθのreward関数となっています。

ランク付けされたデータDに対して全ての組み合わせで期待値を取っています。

RMを最大化するSFTモデルの学習

最後のSTFモデルをRMを最大化するように学習させてます。

学習にはPPO(Proximal Policy Optimization)を拡張したPPO-ptxと呼ばれる手法が利用されています。

目的関数は以下のようになります。

強化学習によって得られる期待値を最大化するためにまず、r_θの期待値を最大化する項をおいています。 それに対して強化学習によって得られるポリシーπ_φ^{RL}がSTFモデルのポリシーπ^{SFT}の分布から離れすぎることをペナルティとするためにそれらのKLダイバージェンスを引く項を入れることでPPOモデルとしています。

最後の項のD_{pretrain}で事前学習モデルの分布の情報を取り込んでいます。 これによって事前学習モデルに関する情報が薄れることによる精度低下を防いでいます。

Step2とStep3を繰り返し実行して報酬を最大化していきます。

人はChatGPTに辿り着くのか?

さて、ついに必要なパーツが出そろいました。 まず、ChatGPTに関しては 論文がないため正確なことは分からない と先にお伝えしおきます。 そのため、以下に記載されている内容を元にしています。

GPT-3のモデルに対してInstructGPTで強化学習を行ったモデルをGPT-3.5として、これを利用しているようです。 また、ChatGPT Plusを利用することで、 GPT-3よりも多くのパラメータを有し、テキスト以外のデータも学習対象としていると言われるGPT-4ベースのモデルも利用可能ですが、こちらに関してはモデルや学習に利用したデータの詳細は明かされておりません。

そのため、あくまでもTransformerベースのGPTモデルとInstructGPTによる強化学習が使われていそうだが詳細は不明程度に捕らえておくのがいいでしょう。

その他の関連事項

GPTの流れには直接関係しないものの、この領域における関連するものを一部紹介します。

言語モデル

fine-tuning

LLMのスケールに関する情報

おわりに

今回は、ChatGPTの原点を探るために、Deep Learning以降の自然言語処理に関する手法を振り返って見ました。 数年前の出来事なのに、今となっては手法などもあり改めて振りかってみてるのも楽しいものですね。

また、今回はVision Transformerなど自然言語処理以外のTransformer関連は紹介していませんが、 異なる複数のソースを共通で扱えるものなども登場してるそちらも注目です!

本文中に記載していない参考資料