第3章 llama.cpp の全体像

この章では、第 2 章で整理した推論の流れを、llama.cpp の実際の構成要素に対応づけます。 扱うのは、リポジトリ全体を貫く 2 つの層、主要なディレクトリの役割、そしてデータが通っていく道筋です。

第 2 章では、推論を「テキスト → トークン → ベクトル → モデル本体 → ロジット → サンプリング → 次のトークン」という一本の流れとして捉えました。 本章のゴールは、その抽象的な流れに、ggml やトークナイザー、サンプラーといった具体的な名前と、ソースファイルの住所を割り当てることです。 言い換えると、ここで読者の頭の中に「地図」を一枚描きます。

ここで個々のファイルの中身まで読む必要はありません。 いまは、どこに何があり、データがどの順で通っていくのか、その全体像だけつかめれば十分です。 細部は第 2 部以降で、各ブロックを一段ずつ開いていきます。

3.1 2 つの層:ggmlllama.cpp 本体

最初に、llama.cpp というプロジェクトを理解するうえで最も重要な区別を示します。 それは、 テンソル計算をおこなう下の層(ggml)と、その上で「言語モデルの推論」を組み立てる層(llama.cpp 本体)が、はっきり分かれている という点です。

先に役割を整理しておきます。

  • ggml は、テンソル(多次元配列)に対する計算と、その実行を担う汎用のライブラリです。 「行列を掛ける」「要素ごとに足す」といった演算を計算グラフとして組み立て、CPU や GPU といったバックエンドの上で実際に走らせます。 言語モデル特有の知識は持っていません。あくまで「数値計算のエンジン」です。

  • llama.cpp 本体 は、その ggml を使って「言語モデルの推論」という仕事を組み立てる層です。 モデルファイルを読み込み、トークン化をおこない、Transformer の計算グラフを ggml の演算として組み立て、その結果からロジットを取り出してサンプリングする、こうした推論固有のロジックがここにあります。

この上下関係を図にすると、次のようになります。

llama-cli / llama-server(tools/)examples/simple(examples/)公開 APIinclude/llama.hllama.cpp 本体(src/)トークナイザー・モデル・グラフ構築context / decode・KV キャッシュ・サンプラー共通基盤(common/)引数解析・チャット・ログggml 中核(ggml/)テンソルと計算グラフバックエンド抽象ggml-backendCPUMetalCUDAVulkan ほか

図 3-1 ggml と llama.cpp 本体の 2 層構造

図を上から読みます。 llama-cli のようなコマンドや examples/simple のようなサンプルプログラムは、いきなり内部に触れるのではなく、include/llama.h が公開する API を通して llama.cpp 本体を呼び出します。 本体(src/)は推論のロジックを組み立て、計算が必要になると下の ggml に渡します。 ggml は計算グラフを組み、最終的に CPU や Metal、CUDA、Vulkan といったバックエンドの上で数値計算を実行します。

この 2 層構造を意識しておくと、後の章がぐっと追いやすくなります。 「いま読んでいるコードは、推論ロジックの話なのか、それとも数値計算エンジンの話なのか」を切り分けられるからです。 本書でも、ggml は第 3 部でまとめて詳しく読み、llama.cpp 本体は第 4 部以降で扱う、という形で層ごとに章を分けています。

注釈

ggmlllama.cpp 専用ではなく、whisper.cpp などと並行して育てられてきた、設計上独立したテンソルライブラリです。 リポジトリの中でも ggml/ という独立したディレクトリにまとまっており、画像生成など別の用途にも使われています。 本書では「llama.cpp を動かすための計算エンジン」という立場から読み解きます。

3.2 リポジトリの歩き方

次に、この 2 層がリポジトリのどこにあるのかを見ます。 llama.cpp のリポジトリには多くのディレクトリがありますが、本書で繰り返し戻ってくるのは限られた数か所です。 まずはその主要なディレクトリと役割を一覧で押さえておきましょう。

ディレクトリ

役割

本書で扱う部

ggml/

テンソル計算エンジンとバックエンド実装(CPU、Metal、CUDA、Vulkan ほか)

第 3 部

src/

llama.cpp 本体。モデル、トークナイザー、グラフ構築、context / decode、KV キャッシュ、サンプラー

第 4、5、6 部

include/

公開 API ヘッダー(llama.h

第 2 部

common/

ツール共通の補助(引数解析、チャット、ログ、サンプリング設定 など)

第 2、6 部

tools/

実行コマンド群(llama-clillama-server 、量子化ツール など)

第 2、7 部

examples/

小さなサンプル(simpleembedding など)

第 2 部

表 3-1 リポジトリの主なディレクトリと役割

それぞれを一段だけ補足します。

ggml/ は計算エンジンの本体で、ggml/src/ggml.c にテンソルと計算グラフの中核があります。 その下に ggml/src/ggml-cpu/ggml/src/ggml-metal/ といったバックエンド別のディレクトリが並びます。

src/llama.cpp 本体です。 ファイル名はおおむね役割を表しており、たとえばトークナイザーは src/llama-vocab.cpp、計算グラフの構築は src/llama-graph.cpp、コンテキストとデコードは src/llama-context.cpp といった具合に分かれています。 モデルアーキテクチャごとの個別実装は src/models/ 以下に分散しています。

include/ には公開 API のヘッダーがあり、外部のプログラムは原則この llama.h を経由して llama.cpp を使います。 tools/examples/ は、その API を実際に呼び出す側です。 common/ は、複数のツールが共有する補助コード(コマンドライン引数の解析やログなど)を集めた場所です。

リポジトリの主要ディレクトリ構成

図 3-2 リポジトリの主要ディレクトリ構成

リポジトリを開いて、src/ の中をざっと眺めてみてください。 llama- で始まるファイル名が、本章や 表 3-2 に出てくる役割と対応していることが見て取れるはずです。 いまは中身を読む必要はありません。「役割ごとにファイルが分かれている」という構造だけ確認できれば十分です。

3.3 データフロー:地図と現地を結ぶ

第 2 章で見た推論の流れを、表 3-1 のディレクトリと結びつけます。 まず、データがどの順で通っていくのかを図で示します。

入力テキストトークン化src/llama-vocab.cpp埋め込み + 計算グラフ構築src/llama-graph.cpp ほかグラフ実行(テンソル計算)ggml + バックエンドロジットサンプリングsrc/llama-sampler.cpp次のトークンデトークナイズsrc/llama-vocab.cpp出力テキストKV キャッシュsrc/llama-kv-cache*.cpp 自己回帰ループ保存 / 再利用

図 3-3 推論のデータフローと対応ソース

図の流れを、上から順にたどります。

  1. 入力テキスト → トークン化 :ユーザーが入力した文字列を、トークン ID の列に変換します。担当は src/llama-vocab.cpp のトークナイザーです。

  2. 埋め込み + 計算グラフ構築 :トークン ID を埋め込みベクトルに変え、Transformer の各層を ggml の演算として組み立てます。グラフの設計図を作る段階で、担当は src/llama-graph.cppsrc/models/ 以下の各アーキテクチャ実装です。

  3. グラフ実行 :組み立てたグラフを、ggml がバックエンドの上で実際に計算します。ここが数値計算の本番で、CPU や GPU が行列計算を回します。

  4. ロジット → サンプリング :最後の層から出てきたロジットをもとに、src/llama-sampler.cpp が次の 1 トークンを選びます。

  5. 自己回帰ループ :選ばれたトークンを末尾に加え、次の 1 トークンを予測するために 2 番に戻ります。第 2 章で見たループが、ここに現れます。

  6. デトークナイズ → 出力テキスト :選ばれたトークンを文字列に戻し、画面に表示します。担当は再びトークナイザーです。

図の右下にある KV キャッシュsrc/llama-kv-cache*.cpp)は、この流れの脇で実行を支える存在です。 第 2 章で触れたとおり、過去のトークンの計算結果を保存しておき、ループのたびに再利用することで、新しいトークン 1 つ分だけの計算で済むようにします。

そして、この一連のループ全体を制御しているのが src/llama-context.cppsrc/llama-batch.cpp です。 「トークンをまとめて受け取り、グラフを実行させ、ロジットを取り出す」という一回分の処理は、本体では llama_decode という関数を入口にして進みます。 第 2 章で出てきた prefill と decode の 2 フェーズも、この入口の内側で扱われます。

各段階と、対応するソース、そして本書のどこで詳しく読むかを一覧にまとめます。

段階

主に対応するソース

詳しく読む部

モデルの読み込み(前準備)

ggml/src/gguf.cppsrc/llama-model-loader.cppsrc/llama-model.cpp

第 4 部

トークン化 / デトークナイズ

src/llama-vocab.cpp

第 4 部

計算グラフの構築(埋め込み〜各層)

src/llama-graph.cppsrc/models/*.cpp

第 5 部

グラフの実行(テンソル計算)

ggml/src/ggml.cggml-backend.cppggml-cpu/ ほか

第 3 部

コンテキストとデコードの制御

src/llama-context.cppsrc/llama-batch.cpp

第 5 部

KV キャッシュ

src/llama-kv-cache*.cpp

第 5 部

ロジットからサンプリング

src/llama-sampler.cpp

第 6 部

表 3-2 データフローの各段階と対応するソース

この表は、本書全体の見取り図でもあります。 推論のどこか一点について「あれはどのファイルの話だったか」と迷ったときは、ここに戻ってくれば、対応するソースと、詳しく読む部がすぐに分かります。

第 2 章の抽象的な流れは、すべて実在するソースファイルに対応しています。 地図の上の各地点に、現地の住所が割り当てられました。

3.4 最初の推論を動かす

地図ができたところで、最後に「現地を歩く入口」を示しておきます。 全体像を一度に追うには、llama-clillama-server は機能が多すぎます。 そこで本書は、必要最小限のコードで推論の流れを一周できる examples/simple を最初の入口に選びます。

examples/simple/simple.cpp は、include/llama.h の公開 API を順番に呼び出すだけの、短いプログラムです。 モデルを読み込み、プロンプトをトークン化し、llama_decode で計算を回し、サンプリングで次のトークンを選び、デトークナイズして表示する。 本章のデータフロー図とほぼ同じ順序が、そのままコードに現れます。 このプログラムを読み解くことが、第 4 部の最初の仕事になります。

examples/simple の公開 API 呼び出し列

図 3-4 examples/simple の公開 API 呼び出し列

実際に動かすには、まず llama.cpp を CMake でビルドします。 ビルドすると、tools/examples/ のプログラムが実行可能ファイルとして生成されます。 あとは GGUF 形式のモデルファイルを 1 つ用意すれば、推論を走らせられます。

注釈

ggml は環境に応じて使うバックエンドを選びます。 たとえば macOS では、何も指定しないと既定で Metal(GPU)が使われます。 CPU 側の処理を観察したいときは、GPU へ載せる層の数を 0 にする -ngl 0 を付けると、計算が CPU バックエンドに固定されます。 バックエンドの選択そのものは第 3 部で詳しく扱います。ここでは「同じソースでも、実行されるバックエンドは環境や設定で変わる」とだけ押さえてください。

ビルドや実行の細かな手順は付録 B にまとめます。 本章の段階では、コマンドを完璧に動かすことよりも、「examples/simple が、本章で描いた地図をいちばん短く一周するプログラムだ」という位置づけをつかんでおくことが大切です。

この章では、llama.cppggml(計算エンジン)と本体の 2 層として捉え、リポジトリの主要ディレクトリと、推論のデータフローを実際のソースに対応づけました。

第 2 章で見た 「テキスト → トークン → 計算グラフ → 実行 → ロジット → サンプリング → 次のトークン」 という流れは、表 3-2 のとおり、すべて実在するファイルの住所を持っています。 この地図を手にしたことで、本書の第 1 部(導入)は完了です。

次の第 4 章からは、いよいよ現地を歩き始めます。 最初に開くのは examples/simple です。 本章で俯瞰したデータフローが、公開 API の呼び出しとして実際のコードにどう現れるのかを、一行ずつ読み解いていきます。