文系エンジニアでもわかる！LLM徹底解説

## はじめに

こんにちは、岩崎と申します。
生成AI,流行ってますよね。
とはいえ、社会で大々的に活用されているかというと疑問を持たれる方も多いのではないでしょうか？
PoC止まりで実際に採用されなかったなどはよくある話です。
今後、社会に生成AIのプロダクトが実装されていくには、やっぱりエンジニアがしっかりといいものを作って社会に提供することが大事だと思います。(そこに自分の存在意義を見出したい笑)
この記事では、文系エンジニア目線で今のLLMってどう動いてるの？仕組みは？といったところを解説していきます。
それをもって、生成AIの根っこのロジックを理解した上で、生成AIによるサービスを開発できる人が増えたらいいな。と思っています。
面倒な数式やAIの詳細なモデルの仕組み等については、キーワードを都度残していくので各自調べてください。
あくまで、この記事ではLLM全体の仕組みを深掘りし「LLMってこんな仕組みで動いてるんだ！」となることが目標です。例えば、上司に「じゃあここってどうなってるの？どうしたらもっと改善できると思う？」って聞かれたら、冷や汗をかきながら調べてください。笑
あくまで体系的に全体を知って、細かいところは適宜キャッチアップしましょう。がこの記事のコンセプトです。

**注意事項**
この記事は、あくまで私の理解を元に記述しており、不正確な内容も含まれている可能性があります。
「なんかエンジニアが変な記事書いてるなwww」と思いながら見ているそこの研究者/有識者の方！！
ぜひ、指摘等いただけると幸いです。
私にとっては、この記事を公開することで、私の理解の整理and知識が間違っていた場合の修正がメインの利益だと思っています。(あとはエンジニアの輪が広がればいいな。。笑)

## 対象読者

- 生成AIを使って何かやれと言われてるエンジニア
- 生成AIを知りたい。と思っているエンジニア
    - **特に、生成AIを使って何か作ってみよ。って思った時に中身の仕組みを調べても「？」だったので、中身は一旦置いておいて、API仕様書に逃げたそこの君！！(私もそのうちの一人です笑)**

*ある程度、ITに関して基礎知識がないとちょっと記載している内容が難しく感じるかもしれません。ただ、そういう人でも理解できるように努力します。(文才ないのであくまで努力で、笑)

## そもそもLLMってなんぞや？

何でもかんでもLLMって言われてますけど、実際のところどういう定義なのか皆さん理解していますか？
ここにおいてのLLMの定義は以下のように定義します。(というかそれが世間的にも正しいと思っています)

**LLMとは、大量の学習データを用いて学習された、**創発的能力**を獲得したAIモデルのこと**

はい、ちょっと胸焼けしましたか？笑
キーワードごとに解説します。
- 学習データ
    - 例えばWikipediaの文章や、このqiitaの文章などLLMが学習に使うデータのこと
        - AIを学習させる時は、いわゆる正解データ(=ラベル)とペアになったデータを使うことがイメージされますが、現在のLLM(GPT4やGemini,Llamaなど)は自己教師あり学習を行うので、正解データのない文章からも学習が可能
            - はい、そこ。自己教師あり学習で胸焼けしない。あとで詳しく解説します。
- 創発的能力
    - LLMのモデル(AI)に大量のデータを学習させていくと、ある時を境に能力が飛躍的に向上することが分かりました。GPT3.5とかで人と話すのと変わらないぐらいに流暢に喋る。意地悪な質問をしても良い感じに返してくれるなど旧来の自然言語処理のAIとは一線を画す能力を獲得して話題になりましたよね。それが創発的能力です。水が凝固点を超えると氷に変わるように、ある一定の境を超えるとAIが急激に進歩したのです。

## LLMってどういう仕組みなの？

### LLMの基礎になっている仕組み

現在のLLMは、猫も杓子も**Transformer**という仕組みをベースに作られています。
GPTも正式名称は"Generative Pre-trained Transformer"です。
つまりは、生成に特化した事前学習済みのトランスフォーマーということです。
じゃあTransformerって何なの？という点ですが、これは2017年にGoogleが出した論文「Attention Is All You Need」という論文の中で、使われている仕組みです。
「Attention Is All You Need」では、Transformerという仕組みを使って翻訳タスクを解く。という課題で従来の自然言語処理のAIを凌駕したパフォーマンスを発揮しました。
これを文章生成に転用したわけですね。

Transformerの仕組みはこんな感じになっています。

![191bee57ce6b7e46ab75143c040083f82fbce127.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875774/c9971a27-b8b0-269b-19f8-1cf302e6fc39.png)

はい、これを見て理解できずに挫折した人、正直に手を挙げてください。笑
私も100回ぐらい挫折しました。笑

詳しい解説は諸先輩方の詳細解説記事がqiitaにもいっぱいあるので、そちらにお任せします。
ここでは、ざっくり概念だけ解説します。

まず、Transformerは「エンコーダー部分」と「デコーダー部分」に分かれています。
エンコーダーは左側のブロック、デコーダーは右側のブロックです。

#### エンコーダーの役割
エンコーダーは、ユーザーの入力(翻訳タスクでいうと、翻訳前の原文)を受け取り、**Input Embedding**の部分で、文章をトークン単位で分割し、そのトークンをベクトル化します。
文字にすると難しいですが、ざっくり言うと文章(文字列)をコンピュータにとって理解しやすい形に前処理する。ぐらいの理解でOKです。
その後に、**Self Attention**という仕組みを使って文章の特徴量を抽出します。Attention機構については後述します。
ここでは、入力に対して、Input EmbeddingとAttentionという仕組みで文章の特徴量を出力してるんだな。くらいの理解でOKです。

じゃあ、他の部分は何してるの？って話ですが、
- Feed Forward
    - Attention機構で得た値の後処理を担っている
- Add&Norm
    - **残差接続**をすることで、元の入力を忘れないようにしている。
        - パフォーマンス向上の仕組みの1つなのであまり深く考えなくてOK
- Positional Encoding
    - Input Embeddingでトークン単位で分割してベクトル化するといいましたが、そのトークンがどの順番に存在しているか？といった情報をトークンに付与します。これは並列で計算する際に順序情報がないとうまく並列計算できないからです。
        - 例えば、「日本はアジアです。」と言う文章は"日本""は"”アジア"です""。”と言うようにトークン分割されるとします。順序情報がなければ、一例として「アジアです。は日本」みたいな順序がめちゃくちゃ(順序関係ない)文章として並列計算されてしまうわけです。それを防ぐために順序情報を追加しています。

#### デコーダーの役割
デコーダーは、文字通り文章を実際に生成する部分です。
![スクリーンショット 2024-09-02 2.09.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875774/3f33385b-1622-f3e5-2ecb-ba476e5e78d3.png)


特徴としては、InputにOutputs(Shifted right)が入力されています。
入力なのに出力を入力するってどないやねん！ってなったそこのあなた。私もそう思いました笑

ChatGPTの生成時の動きを思い出してください。1トークンずつ(見え方としては1単語ずつ)出力されてますよね？
あれってすごく合理的な動きなんです。
デコーダーはデコーダー自身のOutputを入力として受け取り1トークンずつ生成していきます。
- **文章を一番最初に生成する時(1トークン目を生成する時)は\<bos>(beginning of sentence)のような特殊なトークンを使って文の最初の生成だよということを宣言します**
- **文章の終わりを宣言する際も同じく特殊トークンを使って文章の終わりを宣言することでデコーダーのループ処理は終了します。**

じゃあエンコーダーの出力(ユーザーの入力をエンコーダで変換したもの)はどこで使われるねん！というツッコミが聞こえてきそうですが、Transformerの全体図を見てください。
エンコーダの出力はデコーダ部のAttention機構に組み込まれています。
これはCross Attentionという仕組みです。(後述します)

それでは、デコーダ部についての処理についても詳しく見ていきましょう

- Output Embedding
    - エンコーダ部と同じく、文字列をトークン分割/ベクトル化する前処理を行う
- Positional Encoding
    - エンコーダ部と同じく、トークンの順序情報をトークンごとに追加する
- Masked Multi-Head Attention(Self Attention)
    - MaskedやMulti-Headの解説は本記事ではしません
    - ここはSelf Attentionを使って現在までのOutputの文章の特徴を抽出しています
- Multi-Head Attention
    - ここで、エンコーダ部分の出力とデコーダの入力をCross Attentionさせることで、エンコーダの出力(=ユーザーの入力)をデコーダの生成タスクの条件に入れ込みます。
- Linear
    - これは全結合ニューラルネットワークによって、どのトークンが次に出力されるべきかの確率を出します。
    - 全結合ニューラルネットワークの最終的な出力層の数=LLMが出力しうるトークンの種類になっています
        - 大体50,000~100,000くらいの出力層になっているみたいです。
    - これは語彙サイズと同じです。
    - ちなみに、固有名詞はどうやってんねん！というツッコミが聞こえてきそうですが、例えば「タナカ」という固有名詞の場合、"タ""ナ""カ"という風に文字単位でトークン化することで対応しています。(賢い!!)
- Softmax
    - これはSoftmax関数をかけています。
    - Softmax関数とは、例えば「0.8,1.2,2.0」といった数列がある場合、この3つの数字の合計が1となるように変換することです。
    - Softmax関数を使うことで、Linearの出力層を確率として扱うことができます。
    - **ここで一番確率が高いトークンが出力として出力されます。**


### Self AttentionとCross Attentionについて

一旦疲れたので、kikagaku様の記事に丸投げします。笑
もし要望があれば私の方でも記事書こうかと思います。
https://www.kikagaku.co.jp/kikagaku-blog/deep-learning-transformer/

## 今のLLMについて

今まで、Transformerを解説してきましたが、じゃあ実際に社会で使われているLLM(GPT4など)はどういう仕組みなの？という点を解説します。
結論から言うと、もちろん今のLLMはTransformerをそのまま使っているわけではないです。
今主流なのは、Transformerの機構の中でもDecoderだけを使ったCasual Decoderというものです。
GPT4はLlamaなどもそれを使っています
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323732343834312f36613335333637322d373232332d323130362d623439352d3361633263656233643130392e6a706567.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875774/6badfcbe-9b5c-5b5f-4a3c-13577d8e9392.jpeg)
一番右側がCasual decoder


じゃあDecoderだけでどうやって文章生成を学習させて生成タスクを実行するのか？って点気になりますよね？

### Casual Decoderの学習について

前述した通り、Casual DecoderはTransformerの中でもDecoderのみを使っているモデルです。
![スクリーンショット 2024-09-02 2.09.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875774/3f33385b-1622-f3e5-2ecb-ba476e5e78d3.png)

じゃあどうやって学習するのかですが、ここで**自己教師あり学習**が出てきます。
たとえば、「日本で一番高い山は富士山です。」という文章を学習データとして持っており、それを学習させる場合、以下のように学習させます。
1. 学習データの途中までをOutputsに入れる　例：「日本で一番高い山は」
    1. ここでは、「日本で一番高い山は」まで入れているが「日本」から始めて徐々に学習させていくのが主流な気もしています
2. Decoderによって、出力が出力される　例：「日本で一番高い山はエベレスト」
    1. 出力と元の文章を比較し、Decoderの出力が元の文章の出力になるようにAttention及びLinear等のWeight(パラメータ)を更新します。

これを繰り返します。
自己教師あり学習なので、学習データのラベリング(正解などを紐づけること)が不要で大量の文章を読ませて、その文章の傾向を学習できることが直感的に想像できるかと思います。

### Casual Decoderの生成について

生成時には、ユーザーの入力を図で言うOutputsに入力します。
例えば、「日本で一番高い山は？」と言う質問を入れたとします。
すると、LLMは、この文章に続くトークンを出力し始めます。
この出力過程ではもちろん学習したパラメータを使用しています。
そうすると、「富士山」と言うトークンが得られます。
もう一度、Decoderを通します。その時の入力は「日本で一番高い山は？富士山」と出力+入力を入れます
すると、Decoderは「です」という結果を返します。
もう一度、Decoderを通します。その時の入力は「日本で一番高い山は？富士山です」と出力+入力を入れます
すると、Decoderは「。」という結果を返します。
もう一度、Decoderを通します。その時の入力は「日本で一番高い山は？富士山です。」と出力+入力を入れます
すると、Decoderは「\<eos>」という結果を返します。
\<eos>は終了を示す特殊トークンなので、ここでDecoderの出力ループは終了します。

### まとめ
いかがでしたでしょうか？
ブラックボックスだったLLMの仕組みがちょっとでも照らされてわかるようになっていただけたら、とても嬉しいです。
LLMが大量の文書を学習し、学習した内容から文章を生成する。と言う動きの中身が少しでもわかりましたでしょうか？


この記事は途中疲れて妥協している部分や、そもそも知識として間違っている部分もあると思います。
そういった点について「もっと詳しく書いてほしい！」や「ここ間違っている！」と言う指摘をいただければ修正/追記したいと思っています。
最終的には、文系エンジニアにとってこれを読めばLLMの仕組みがわかる！みたいな記事に仕上げたいと思っています。
今後ともよろしくお願いいたします。