<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI on kenji.blog</title><link>http://kenji.blog/categories/ai/</link><description>Recent content in AI on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ja</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 18:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/categories/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Microsoft.Windows.AIの最新API活用事例とサンプルコード</title><link>http://kenji.blog/p/microsoft-windows-ai-api-guide/</link><pubDate>Fri, 11 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/p/microsoft-windows-ai-api-guide/</guid><description>&lt;img src="http://kenji.blog/p/microsoft-windows-ai-api-guide/img/eyecatch.jpg" alt="Featured image of post Microsoft.Windows.AIの最新API活用事例とサンプルコード" />&lt;h1 id="microsoftwindowsaiの最新api活用事例とサンプルコードwindows-copilot-runtimeの深淵を探る">Microsoft.Windows.AIの最新API活用事例とサンプルコード：Windows Copilot Runtimeの深淵を探る
&lt;/h1>&lt;h2 id="1-はじめにaiがネイティブに組み込まれるwindowsの新時代">1. はじめに：AIがネイティブに組み込まれるWindowsの新時代
&lt;/h2>&lt;p>近年、AI技術の進化は目覚ましく、クラウド上での大規模言語モデル（LLM）の活用から、エッジデバイス（ローカルPC）でのAI推論へと急速にパラダイムシフトが起きています。その中核を担うのが、MicrosoftがWindows 11向けに提供している「Windows Copilot Runtime」と、それを操作するための「Microsoft.Windows.AI」APIです。&lt;/p>
&lt;p>クラウドAPI（OpenAIやAzure OpenAIなど）を利用したアプリケーション開発は容易ですが、レイテンシ、プライバシー、そして継続的なコストという課題がつきまといます。一方、ローカルでAIモデルを動かすことで、機密データをデバイス外に出すことなく、オフラインでも機能する超低遅延なアプリケーションを実現できます。&lt;/p>
&lt;p>本記事では、これからのWindowsアプリケーション開発において必須となるローカルAI機能の実装方法について、C#およびC++の実践的なサンプルコードを交えながら、アーキテクチャからパフォーマンスチューニングまで極めて詳細に徹底解説します。単にAPIを叩くだけではなく、背後にあるハードウェア（NPUやGPU）の活用、DirectMLとの連携など、高度な技術的詳細にまで踏み込みます。&lt;/p>
&lt;h2 id="2-windows-copilot-runtimeとアーキテクチャの全体像">2. Windows Copilot Runtimeとアーキテクチャの全体像
&lt;/h2>&lt;p>Windows Copilot Runtimeは、開発者がWindows上でAIモデルを簡単に統合し、かつ最高のパフォーマンスを引き出せるように設計された一連のAIスタックです。このランタイムは、OSレベルでハードウェアアクセラレーションを抽象化し、開発者に統一されたインターフェースを提供します。&lt;/p>
&lt;div class="mermaid">graph TD
App["Windows Application (C# / C++)"] --> API["Microsoft.Windows.AI APIs"]
App --> ORT["ONNX Runtime"]
API --> WCR["Windows Copilot Runtime (OS Layer)"]
WCR --> SLM["Local Models (Phi-Silica, etc.)"]
ORT --> DML["DirectML Execution Provider"]
SLM --> DML
DML --> DXCore["DXCore / DirectX 12"]
DXCore --> NPU["NPU (Neural Processing Unit)"]
DXCore --> GPU["GPU (Graphics Processing Unit)"]
DXCore --> CPU["CPU"]&lt;/div>
&lt;p>上記のアーキテクチャ図が示すように、アプリケーションは高レベルの &lt;code>Microsoft.Windows.AI&lt;/code> APIを利用することで、OSに組み込まれた小規模言語モデル（SLM：Phi-Silicaなど）に直接アクセスできます。また、カスタムモデルを使用する場合は、ONNX RuntimeとDirectMLを介してハードウェアアクセラレーションを明示的に利用することも可能です。OSレイヤーがCPU、GPU、NPUへのワークロードの分散を最適化するため、開発者はハードウェアの差異を深く意識することなく、高パフォーマンスなAIアプリを構築できます。&lt;/p>
&lt;h2 id="3-ハードウェアアクセラレーションとnpuの数理的評価">3. ハードウェアアクセラレーションとNPUの数理的評価
&lt;/h2>&lt;p>最新のCopilot+ PCには、AI処理に特化したプロセッサであるNPU（Neural Processing Unit）が搭載されています。NPUのパフォーマンスは一般にTOPS（Tera Operations Per Second）で評価されます。&lt;/p>
&lt;p>AIモデルの推論において、特に行列積（GEMM: General Matrix Multiply）の計算能力がスループットを決定づけます。ハードウェアの理論上の最大性能 $P_{\text{peak}}$ は、以下の数式で概算されます。&lt;/p>
$$
P_{\text{peak}} = f \times N_{\text{cores}} \times N_{\text{MACs/core}} \times 2
$$
&lt;p>ここで、&lt;/p>
&lt;ul>
&lt;li>$f$ はNPUのクロック周波数（Hz）&lt;/li>
&lt;li>$N_{\text{cores}}$ はNPU内のコア数&lt;/li>
&lt;li>$N_{\text{MACs/core}}$ は1コアあたりのMAC（Multiply-Accumulate）ユニット数&lt;/li>
&lt;li>最後の $2$ は、1回のMAC演算が乗算と加算の2つのオペレーション（FLOPs/OPs）としてカウントされるためです。&lt;/li>
&lt;/ul>
&lt;p>例えば、周波数1.5GHz、4コア、各コアが4096MACsを持つNPUの場合、
&lt;/p>
$$
P_{\text{peak}} = 1.5 \times 10^9 \times 4 \times 4096 \times 2 \approx 49.15 \text{ TOPS}
$$
&lt;p>
となります。Windows 11のCopilot+ PC要件である40 TOPSをクリアする性能であることが数学的に示されます。&lt;/p>
&lt;p>また、AIモデル、特にLLMの推論（デコードフェーズ）は**メモリ律速（Memory-Bound）**になりがちです。システムメモリの理論帯域幅 $BW$ は次のように計算されます。&lt;/p>
$$
BW = f_{\text{mem}} \times W_{\text{bus}} \times \frac{2}{8}
$$
&lt;p>LPDDR5x-8533メモリ（$f_{\text{mem}} = 8533 \text{ MT/s}$）、128ビットバス（$W_{\text{bus}} = 128$）の場合、帯域幅は約 $136 \text{ GB/s}$ となります。AIアプリケーションの最適化では、この帯域幅をいかに節約するかが重要であり、後述するモデルの量子化（Quantization）が不可欠となります。&lt;/p>
&lt;h2 id="4-開発環境のセットアップ">4. 開発環境のセットアップ
&lt;/h2>&lt;p>最新のWindows AI APIを利用するには、以下の環境とツールチェーンを整える必要があります。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>OS&lt;/strong>: Windows 11 バージョン 24H2 以降（Copilot+ PC要件を満たすNPU搭載デバイスを強く推奨）&lt;/li>
&lt;li>&lt;strong>SDK&lt;/strong>: Windows App SDK (v1.5以降のAI拡張対応版)&lt;/li>
&lt;li>&lt;strong>開発環境&lt;/strong>: Visual Studio 2022 (v17.10以降)、C++によるネイティブ開発ワークロードおよび.NETデスクトップ開発ワークロード&lt;/li>
&lt;li>&lt;strong>パッケージ&lt;/strong>: NuGet経由で &lt;code>Microsoft.Windows.AI&lt;/code> および &lt;code>Microsoft.ML.OnnxRuntime.DirectML&lt;/code> をインストール&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- .csproj の設定例 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.Windows.AI&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0-preview1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.WindowsAppSDK&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.5.240311000&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.ML.OnnxRuntime.DirectML&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.17.1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="5-deep-dive-1cを用いたローカル言語モデルphi-silicaの活用">5. 【Deep Dive 1】C#を用いたローカル言語モデル（Phi-Silica）の活用
&lt;/h2>&lt;p>Windows Copilot Runtimeには、Microsoftが開発した高効率な小規模言語モデル「Phi-Silica」がOS標準コンポーネントとして組み込まれています。これにより、GB単位のモデルをネットワークからダウンロードすることなく、オフライン環境で高度な自然言語処理（文章要約、コード生成、チャットボット）が可能になります。&lt;/p>
&lt;p>以下は、&lt;code>Microsoft.Windows.AI.Generative&lt;/code> 名前空間を使用して、C#でチャットAIを構築する高度なサンプルコードです。ストリーミングレスポンスに対応し、UIスレッドをブロックせずにリアルタイムにテキストを生成します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Text&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading.Tasks&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">Microsoft.Windows.AI.Generative&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">namespace&lt;/span> &lt;span class="nn">WindowsAI.Sample&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">LocalLanguageModelService&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">LanguageModel&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// 言語モデルの初期化を行います。NPUの利用可能性をチェックし、最適なデバイスにモデルをロードします。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">InitializeAsync&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ローカルAIモデル（Phi-Silica）のシステム要件と可用性を確認しています...&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// モデルがシステムで利用可能かチェック (非対応の場合はダウンロードが促される場合あり)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">availability&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CheckAvailabilityAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">availability&lt;/span> &lt;span class="p">!=&lt;/span> &lt;span class="n">LanguageModelAvailability&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Available&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">InvalidOperationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;ローカルAIモデルが現在利用できません。状態: {availability}&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// モデルのインスタンスを作成（このタイミングでメモリ空間へのマッピングとNPUの初期化が行われる）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_languageModel&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CreateAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;言語モデルの初期化が完了しました。DirectMLを介したハードウェアアクセラレーションがアクティブです。&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// ユーザーのプロンプトを受け取り、ストリーミングで応答を生成します。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">string&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CancellationToken&lt;/span> &lt;span class="n">cancellationToken&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">InitializeAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[ユーザー入力]: {prompt}\n[AIアシスタント]: &amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 生成時のハイパーパラメータを設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">options&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelOptions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Temperature&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0.7f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TopP&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0.9f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MaxTokens&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">2048&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RepetitionPenalty&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">1.1f&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 会話コンテキストの構築&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">context&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelContext&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddSystemMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;あなたはWindowsのローカルNPU上で直接動作している高度なAIアシスタントです。ステップバイステップで論理的に思考し、簡潔に回答してください。&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddUserMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ストリーミング推論APIの呼び出し&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">responseStream&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// IAsyncEnumerableとして返されるチャンクを非同期で反復処理&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">chunk&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="n">responseStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WithCancellation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cancellationToken&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 生成されたトークン（チャンク）をリアルタイムにコンソールへ出力&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// UIアプリケーションの場合は、ここでDispatcherQueueを用いてTextBox等に反映する&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Text&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">OperationCanceledException&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;\n[ユーザーまたはシステムによって生成がキャンセルされました]&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">ex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[致命的なエラーが発生しました: {ex.Message}]&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="51-c実装におけるアーキテクチャの解説">5.1 C#実装におけるアーキテクチャの解説
&lt;/h3>&lt;p>このコードの核心は、&lt;code>LanguageModel.CheckAvailabilityAsync()&lt;/code> による実行前検証と、&lt;code>GenerateResponseStreamAsync&lt;/code> による非同期ストリーミングです。OSのバックグラウンドで動作するCopilot Runtimeは、このAPI呼び出しを受け取ると、内部的にONNX Runtimeを起動し、システムの構成に応じて最適なExecution Provider（多くの最新PCではDirectML + NPU）を選択します。&lt;/p>
&lt;p>開発者は、モデルのテンソル形状、トークナイザーの実装、KVキャッシュのメモリ管理などを一切意識することなく、数行のC#コードで最先端のAI推論パイプラインをアプリケーションに統合できます。&lt;/p>
&lt;h2 id="6-deep-dive-2cとdirectmlによるカスタムモデルの高速推論">6. 【Deep Dive 2】C++とDirectMLによるカスタムモデルの高速推論
&lt;/h2>&lt;p>OS標準の言語モデルだけではカバーできない特定のドメイン（独自の画像セグメンテーション、音声認識、カスタムの物体検出モデルなど）を扱う場合、開発者は &lt;code>Microsoft.Windows.AI&lt;/code> の低レイヤーに位置するONNX RuntimeとDirectMLを直接操作する必要があります。&lt;/p>
&lt;p>C++を用いることで、メモリの割り当てを極限まで最適化し、NPU/GPUのピークパフォーマンスを引き出すことができます。以下は、ONNX形式のカスタムモデル（例：YOLOv8）をDirectMLを用いてC++で実行するための、高度な初期化および推論パイプラインのコア実装です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;span class="lnt">97
&lt;/span>&lt;span class="lnt">98
&lt;/span>&lt;span class="lnt">99
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;onnxruntime_cxx_api.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;dml_provider_factory.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CustomVisionAIProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Env&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AllocatorWithDefaultOptions&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CustomVisionAIProcessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">wstring&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">modelPath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ORT_LOGGING_LEVEL_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;VisionAIProcessor&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">SessionOptions&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// スレッド数の最適化
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetIntraOpNumThreads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// グラフの最適化レベルを最大に設定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetGraphOptimizationLevel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GraphOptimizationLevel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ORT_ENABLE_ALL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. DirectML Execution Provider (DML EP) の追加
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// device_id = 0 はシステムのデフォルト推奨アダプタ（NPUまたは高性能GPU）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">OrtApi&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">ortApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GetApi&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtDmlApi&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dmlApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtStatus&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetExecutionProviderApi&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;DML&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ORT_API_VERSION&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dmlApi&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">dmlApi&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ThrowOnError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dmlApi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">SessionOptionsAppendExecutionProvider_DML&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] DirectML Execution Provider を正常にアタッチしました。&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Warning] DirectML API の取得に失敗しました。CPUフォールバックモードで実行します。&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ReleaseStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. モデルのロードとセッションの作成
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">modelPath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] ONNXモデルのロードに成功し、計算グラフがコンパイルされました。&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Error] モデルロード失敗: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">imageTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. 入力テンソルのバッファ作成
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span> &lt;span class="n">memoryInfo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateCpu&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OrtArenaAllocator&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">OrtMemTypeDefault&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span> &lt;span class="n">inputTensor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateTensor&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memoryInfo&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">imageTensor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">()),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">imageTensor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. 入出力ノード名の動的取得
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">inputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetInputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">outputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetOutputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">inputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">outputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">outputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. 推論の実行 (DirectMLを介してNPU/GPUにオフロードされる)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] 推論エンジンの実行を開始します...&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">startTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">outputTensors&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">RunOptions&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">inputTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputNames&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">endTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">duration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">duration_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">milliseconds&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">endTime&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">startTime&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 6. 結果テンソルの取得と解析
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">outputData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorMutableData&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">outputSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorTypeAndShapeInfo&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetElementCount&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Result] 推論完了: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">duration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; ms&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Result] 出力テンソルの要素数: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">outputSize&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ※この後、出力テンソルに対してNMS（Non-Maximum Suppression）やバウンディングボックスの描画処理を実装する
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 実行するONNXモデルのパス
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">CustomVisionAIProcessor&lt;/span> &lt;span class="n">processor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;models/yolov8n_quantized.onnx&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 推論用のダミー画像データ (バッチサイズ1 x 3チャンネル x 640 x 640)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">inputShape&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dummyImage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.5f&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">processor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dummyImage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;プログラムが異常終了しました: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="61-cにおけるメモリ管理とゼロコピー推論の重要性">6.1 C++におけるメモリ管理とゼロコピー推論の重要性
&lt;/h3>&lt;p>C++でDirectMLを使用する最大の利点は、DirectX 12 (DX12) との緊密な統合が可能である点です。上記のコードは教育的観点から標準的なCPUメモリからのデータコピーを含んでいますが、実際のゲームエンジンや映像処理アプリケーションでは、DX12を用いて画像（テクスチャ）を既にGPUやNPUのメモリ空間上に保持しているケースが多々あります。&lt;/p>
&lt;p>この場合、&lt;code>OrtDmlApi&lt;/code> の高度なバインディング機能を利用して、DX12のリソースを直接ONNX Runtimeのテンソルとしてマッピングする「&lt;strong>ゼロコピー推論 (Zero-Copy Inference)&lt;/strong>」を実現できます。これにより、PCIeバス間のデータ転送オーバーヘッド（上述した帯域幅 $BW$ の消費）が完全に消失し、リアルタイム動画処理におけるフレームレートが劇的に向上します。&lt;/p>
&lt;h2 id="7-パフォーマンス最適化とベストプラクティス">7. パフォーマンス最適化とベストプラクティス
&lt;/h2>&lt;p>Windows AI APIやDirectMLを活用して最上級のAIアプリケーションを開発する際の、不可欠な最適化戦略を以下にまとめます。&lt;/p>
&lt;h3 id="71-モデルの量子化-quantization-と-olive-toolkit">7.1 モデルの量子化 (Quantization) と Olive Toolkit
&lt;/h3>&lt;p>NPUの真の力を発揮させるには、AIモデルの重みとアクティベーションをFP32（単精度浮動小数点）からINT8またはINT4へと**量子化（Quantization）**することが絶対条件です。NPUのアーキテクチャは整数演算に特化しており、FP32と比較してINT8では理論上4倍のスループットと大幅な省電力を実現します。&lt;/p>
&lt;p>Microsoftが提供する &lt;code>Olive (ONNX Live)&lt;/code> ツールチェーンを使用することで、PyTorch等のモデルをWindows環境向けに自動最適化できます。Oliveは、Transformerモデルに対する特殊なアテンション最適化や、ハードウェアごとのグラフコンパイルを強力に支援します。&lt;/p>
&lt;h3 id="72-バッチ処理-vs-対話型ストリーミングのトレードオフ">7.2 バッチ処理 vs 対話型ストリーミングのトレードオフ
&lt;/h3>&lt;p>API呼び出しにおいて、複数の推論リクエストをまとめてバッチ処理することで、NPUの利用効率（Compute Utilization）を高めることができます。しかし、チャットボットのような対話型UIの場合、スループットよりも最初のトークンが表示されるまでの時間（TTFT: Time To First Token）がユーザー体験（UX）を決定づけます。
したがって、対話型UIではバッチサイズを1に設定し、ストリーミング生成を優先する設計がベストプラクティスとなります。&lt;/p>
&lt;h3 id="73-バックグラウンドタスクとosとの連携">7.3 バックグラウンドタスクとOSとの連携
&lt;/h3>&lt;p>AI推論はローカルの電力とシステムリソースを大量に消費します。Windowsの &lt;code>App Lifecycle API&lt;/code> と連携し、アプリケーションがバックグラウンドに回った際は、優先度の低い推論タスクを一時停止（Suspend）するか、リソース消費を絞る実装が求められます。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "User"
participant App as "Windows App (C#)"
participant API as "Microsoft.Windows.AI"
participant OS as "Windows Copilot Runtime"
participant ORT as "ONNX Runtime (DML)"
participant NPU as "NPU Hardware"
User->>App: "プロンプトを入力"
App->>API: "GenerateResponseStreamAsync()"
API->>OS: "推論ジョブのディスパッチ"
OS->>ORT: "グラフ実行要求"
ORT->>NPU: "DirectML経由でのコマンドリスト実行"
NPU-->>ORT: "計算完了（1トークン生成）"
ORT-->>OS: "テンソル結果"
OS-->>API: "デコード済みテキスト"
API-->>App: "IAsyncEnumerable&lt;string> チャンク"
App-->>User: "UIへのリアルタイム文字描画"
Note over ORT,NPU: "完了するまでこのループを高速で繰り返す"&lt;/div>
&lt;p>このシーケンス図は、UIスレッドを一切ブロックすることなく、最下層のNPUハードウェアからアプリケーションのプレゼンテーション層まで、データが流れるようにストリーミングされる非同期処理の美しさを示しています。&lt;/p>
&lt;h2 id="8-将来の展望とwindows-aiの進化">8. 将来の展望とWindows AIの進化
&lt;/h2>&lt;p>&lt;code>Microsoft.Windows.AI&lt;/code> APIとCopilot Runtimeは、現在進行形で急速な進化を遂げています。将来の開発者向けアップデートでは、以下のようなパラダイムシフトが期待されています。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>マルチモーダルAPIのOSネイティブ統合&lt;/strong>: テキストだけでなく、音声、画像、さらにはライブビデオフィードをシームレスに同時処理し、クロスモーダルなAI推論をOSレベルで標準提供。&lt;/li>
&lt;li>&lt;strong>RAG（Retrieval-Augmented Generation）のシステムレベル対応&lt;/strong>: ローカルPC内のパーソナルなドキュメント群やWindows SearchのインデックスとAIモデルをOSの安全なサンドボックス内で連携させ、ユーザーのプライバシーを完全に保護した状態での超高度なパーソナルAIアシスタントの構築。&lt;/li>
&lt;li>&lt;strong>NPUの動的リソーススケーリング&lt;/strong>: 複数のAIアプリケーション（例えば、バックグラウンドでのノイズキャンセリングと、フォアグラウンドでのコード生成）が同時に稼働する際、WindowsのカーネルスケジューラがNPUの実行コンテキストを動的に切り替え、QoS（Quality of Service）を保証する仕組み。&lt;/li>
&lt;/ul>
&lt;h2 id="9-結論ローカルaiが変えるアプリケーションの未来">9. 結論：ローカルAIが変えるアプリケーションの未来
&lt;/h2>&lt;p>Windows 11のCopilot Runtimeと &lt;code>Microsoft.Windows.AI&lt;/code> APIは、すべてのWindows開発者に「ローカルAI」という極めて強力な武器をもたらしました。もはやクラウドAPIに完全に依存する必要はありません。レイテンシを排除し、プライバシーを堅守しつつ、オフラインでも完全に動作する次世代のAI体験をユーザーに提供することが可能です。&lt;/p>
&lt;p>本記事で解説したC#を用いたシステム標準言語モデルの統合、数理的なパフォーマンス評価、そしてC++とDirectMLを用いた極限のハードウェア最適化の知識を活用し、あなたの手で次世代の「AIネイティブ」なWindowsアプリケーションを創造してください。AIがもたらす無限の可能性は、あなたの書くコードのすぐ先に広がっています。&lt;/p>
&lt;hr>
&lt;p>&lt;em>※注意事項：この記事は2026年9月現在のプレビュー版APIおよび最新の仕様に基づいて執筆されています。WindowsのアップデートによりAPI仕様やハードウェア要件が変更される可能性があるため、実装の際は必ずMicrosoft Learnの公式ドキュメントを併せて参照してください。&lt;/em>&lt;/p></description></item><item><title>Python不要！C++だけでAI推論エンジンを構築してみた</title><link>http://kenji.blog/p/building-ai-inference-engine-cpp-only/</link><pubDate>Fri, 11 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/p/building-ai-inference-engine-cpp-only/</guid><description>&lt;img src="http://kenji.blog/p/building-ai-inference-engine-cpp-only/img/eyecatch.jpg" alt="Featured image of post Python不要！C++だけでAI推論エンジンを構築してみた" />&lt;h2 id="1-はじめになぜpythonを手放しcでai推論エンジンを作るのか">1. はじめに：なぜPythonを手放し、C++でAI推論エンジンを作るのか？
&lt;/h2>&lt;p>現代のAI開発において、Pythonはデファクトスタンダードです。PyTorchやTensorFlowといった強力なフレームワークの恩恵により、数行のコードで複雑なニューラルネットワークを構築・学習・推論させることができます。しかし、これらのフレームワークの裏側では、C++やCUDAといった低レイヤーの言語が計算の重い処理を担っています。Pythonはあくまで「糊（グルー）」の役割を果たしているに過ぎません。&lt;/p>
&lt;p>では、なぜわざわざPythonを排除し、C++単独でAI推論エンジンを作る必要があるのでしょうか？それにはいくつかの強力な理由があります。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>極限のパフォーマンスと低レイテンシ&lt;/strong>: PythonのGIL（Global Interpreter Lock）や動的型付けによるオーバーヘッドを完全に排除できます。特にリアルタイム性が求められるシステムでは、ミリ秒単位の遅延が命取りになります。&lt;/li>
&lt;li>&lt;strong>デプロイの容易さ&lt;/strong>: Python環境（巨大なライブラリ群、依存関係の地獄）をエンドユーザーの環境に構築するのは非常に困難です。C++であれば、静的リンクされた単一の実行バイナリ（&lt;code>.exe&lt;/code>やELFバイナリ）を配布するだけで済みます。&lt;/li>
&lt;li>&lt;strong>エッジデバイスへの対応&lt;/strong>: スマートフォンや組み込み機器、Raspberry Piのようなリソース制約の厳しい環境において、数ギガバイトものメモリを消費するPythonランタイムを動かす余裕はありません。&lt;/li>
&lt;li>&lt;strong>ハードウェアの直接制御&lt;/strong>: メモリアロケーションのタイミング、SIMD命令の明示的な利用、GPUとのメモリ転送の最適化など、低レイヤーの制御がC++なら可能です。&lt;/li>
&lt;/ol>
&lt;p>本記事では、Georgi Gerganov氏によって開発された「GGML」ライブラリのアーキテクチャに多大なインスピレーションを受けつつ、C++だけで大規模言語モデル（LLM）などを動かすための推論エンジンをスクラッチで構築していく過程を、技術的な深淵まで潜って解説します。&lt;/p>
&lt;hr>
&lt;h2 id="2-推論エンジンのアーキテクチャ全体像">2. 推論エンジンのアーキテクチャ全体像
&lt;/h2>&lt;p>AIの推論処理は、本質的には「巨大な行列計算の連続」です。これを効率よく実行するためには、推論エンジンは以下のようなコンポーネントで構成される必要があります。&lt;/p>
&lt;div class="mermaid">graph TD
A["Input Data (Tokens/Images)"] --> B["Tensor Management"]
B --> C["Computation Graph (DAG)"]
C --> D["Memory Arena &amp; Allocator"]
C --> E["Scheduler &amp; Thread Pool"]
E --> F["CPU Backend (AVX2/ARM NEON)"]
E --> G["GPU Backend (CUDA/Metal)"]
F --> H["Output Results"]
G --> H&lt;/div>
&lt;ol>
&lt;li>&lt;strong>テンソル（Tensor）管理&lt;/strong>: 多次元配列のデータ構造と、次元ごとのストライド（Stride）を管理。&lt;/li>
&lt;li>&lt;strong>計算グラフ（Computation Graph）&lt;/strong>: ニューラルネットワークの各層の演算を、有向非巡回グラフ（DAG）として表現。&lt;/li>
&lt;li>&lt;strong>メモリアリーナ（Memory Arena）&lt;/strong>: 動的メモリ確保（&lt;code>malloc&lt;/code>や&lt;code>new&lt;/code>）のオーバーヘッドを避けるための、事前確保型メモリ管理機構。&lt;/li>
&lt;li>&lt;strong>バックエンド（Backend）&lt;/strong>: CPUやGPUなど、特定のハードウェアに最適化された演算の実装（カーネル）。&lt;/li>
&lt;/ol>
&lt;p>これらをC++の強力な機能（テンプレート、ポインタ演算、RAIIなど）を用いて組み上げていきます。&lt;/p>
&lt;hr>
&lt;h2 id="3-メモリ管理の極意メモリアリーナとsimdアライメント">3. メモリ管理の極意：メモリアリーナとSIMDアライメント
&lt;/h2>&lt;p>推論エンジンにおけるメモリ管理は、パフォーマンスに直結する最も重要な要素の一つです。推論中、特にTransformerモデルの各層を通過する際、膨大な数の中間テンソルが生成されます。これを毎回標準の&lt;code>malloc&lt;/code>で確保・解放していては、ヒープの断片化とOSのコンテキストスイッチによって致命的な速度低下を引き起こします。&lt;/p>
&lt;p>そこで、「&lt;strong>メモリアリーナ（Memory Arena）&lt;/strong>」というアプローチを採用します。これは推論開始時に必要な最大メモリ量を計算（または決め打ち）して一括確保し、ポインタのインクリメントだけでメモリを切り出していく手法です。&lt;/p>
&lt;h3 id="31-アライメントの重要性">3.1 アライメントの重要性
&lt;/h3>&lt;p>現代のCPUは、SIMD（Single Instruction, Multiple Data）命令をサポートしています。Intel/AMDのAVX2/AVX-512や、ARMのNEONなどです。これらの命令は、256ビット（32バイト）や512ビット（64バイト）のデータを一度に処理しますが、処理対象のデータメモリが特定のバイト境界（通常は32バイトや64バイト）にアライメント（整列）されている必要があります。&lt;/p>
&lt;p>以下は、アライメントを考慮したメモリアリーナのC++実装例です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstddef&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">MemoryArena&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MemoryArena&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// POSIX系なら posix_memalign、Windowsなら _aligned_malloc を使用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_aligned_malloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">posix_memalign&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">bad_alloc&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">_aligned_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">allocate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">bytes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">alignment&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// アライメントの計算（パディングを求める）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">alignment&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">bytes&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">runtime_error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;OOM: MemoryArena out of memory&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">pad&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">bytes&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">ptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">reset&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// メモリの解放はポインタを戻すだけ（O(1)）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>このように、テンソル作成時には必ずこのアリーナ経由でメモリを取得します。推論の各ステップ（トークン生成ごとなど）が終わるたびに &lt;code>reset()&lt;/code> を呼ぶだけで、瞬時にメモリを再利用できるのです。&lt;/p>
&lt;hr>
&lt;h2 id="4-テンソルデータ構造とストライドの魔法">4. テンソルデータ構造とストライドの魔法
&lt;/h2>&lt;p>テンソルはスカラー、ベクトル、行列を一般化した概念です。実装において重要なのは、実際のデータがメモリ上に&lt;strong>1次元の連続した配列&lt;/strong>として配置されている一方で、それを多次元として解釈するための「ストライド（Stride）」という概念を持つことです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">DataType&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FP32&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FP16&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">INT8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 量子化用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">INT4&lt;/span> &lt;span class="c1">// 量子化用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Tensor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_dims&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 次元の数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 各次元の要素数 (Number of Elements)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">nb&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 各次元のストライド (Number of Bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">DataType&lt;/span> &lt;span class="n">type&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// データ型
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// ペイロードへのポインタ
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 計算グラフ用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">enum&lt;/span> &lt;span class="nc">OpType&lt;/span> &lt;span class="n">op&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ストライド &lt;code>nb[i]&lt;/code> は、次元 &lt;code>i&lt;/code> において隣接する要素間のメモリ上のバイト距離を表します。
例えば、要素数 $M \times N$ の行列（FP32、1要素4バイト）がRow-Major（行優先）で格納されている場合、ストライドは以下のようになります。&lt;/p>
&lt;ul>
&lt;li>&lt;code>nb[0]&lt;/code> = 4 (バイト) ：列方向への移動&lt;/li>
&lt;li>&lt;code>nb[1]&lt;/code> = $N \times 4$ (バイト) ：行方向への移動&lt;/li>
&lt;/ul>
&lt;p>これを利用することで、メモリのコピーを伴わずに「転置（Transpose）」や「ビュー（View）」といった操作をストライドの数値を入れ替えるだけで実現できます。非常にエレガントかつ高速です。&lt;/p>
&lt;hr>
&lt;h2 id="5-計算グラフdagの構築と遅延評価">5. 計算グラフ（DAG）の構築と遅延評価
&lt;/h2>&lt;p>PyTorchなどと同様に、我々の推論エンジンも「Define-by-Run」に近い遅延評価（Lazy Evaluation）を採用します。つまり、演算関数を呼び出した時点では計算を行わず、グラフ（ノード間の依存関係）だけを構築します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">n_dims&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ADD&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">out&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// bは転置されていることが多い
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MUL_MAT&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">out&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>推論処理のフローは以下のようになります。&lt;/p>
&lt;div class="mermaid">graph LR
A["Define Tensors"] --> B["Build Graph via Ops"]
B --> C["Topological Sort"]
C --> D["Allocate Memory for Outputs"]
D --> E["Execute Nodes In Order"]&lt;/div>
&lt;p>グラフを評価する際（フォワードパス）は、トポロジカルソートを用いて依存関係のないノードから順に処理を実行します。推論のみであればバックプロパゲーション用の勾配を保持する必要がないため、メモリ管理は非常にシンプルになります。&lt;/p>
&lt;hr>
&lt;h2 id="6-数学と最適化の核心行列積-gemm">6. 数学と最適化の核心：行列積 (GEMM)
&lt;/h2>&lt;p>AI推論の計算量の90%以上は、行列乗算（GEMM: General Matrix Multiply）に費やされます。Transformerモデルの中核であるアテンション機構もフィードフォワードネットワーク（FFN）も、突き詰めれば巨大な行列積です。&lt;/p>
&lt;p>2つの行列 $A$ (サイズ $M \times K$) と $B$ (サイズ $K \times N$) の積 $C = A B$ (サイズ $M \times N$) は、数式で表すと以下のようになります。&lt;/p>
$$
C_{i,j} = \sum_{k=0}^{K-1} A_{i,k} \cdot B_{k,j}
$$
&lt;p>これを素朴な三重ループで実装すると、キャッシュミスが頻発しパフォーマンスが全く出ません。&lt;/p>
&lt;h3 id="61-cpuでのキャッシュブロッキングとsimd最適化">6.1 CPUでのキャッシュブロッキングとSIMD最適化
&lt;/h3>&lt;p>CPUでGEMMを高速化するための基本戦略は以下の通りです。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>ループタイリング（キャッシュブロッキング）&lt;/strong>: L1/L2キャッシュに収まる小さなブロックに行列を分割して計算します。&lt;/li>
&lt;li>&lt;strong>データのパック&lt;/strong>: メモリアクセスパターンが連続になるように、内部的にデータを並べ替えます。&lt;/li>
&lt;li>&lt;strong>SIMDの活用&lt;/strong>: AVX-512における &lt;code>_mm512_fmadd_ps&lt;/code> のようなFMA（Fused Multiply-Add）命令を使い、一度のクロックサイクルで多数の積和演算をこなします。&lt;/li>
&lt;/ol>
&lt;p>C++とSIMD Intrinsicsを用いた、単純化されたベクトルの内積（Dot Product）の例を示します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;immintrin.h&amp;gt;&lt;/span>&lt;span class="cp"> &lt;/span>&lt;span class="c1">// AVX命令用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// AVX2を用いたFP32の高速内積
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="nf">dot_product_avx2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_setzero_ps&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 8要素ずつ一度に処理（256ビット = 32バイト = 8 * 4バイト）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">va&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">vb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// FMA命令: sum256 = va * vb + sum256
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_fmadd_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">va&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// SIMDレジスタ内の値を水平加算
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_mm256_storeu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">dot&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 余りの処理
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dot&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dot&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>この小さな工夫だけでも、素朴な実装に比べて数倍〜十数倍の速度向上が得られます。&lt;/p>
&lt;hr>
&lt;h2 id="7-ハードウェアの壁を越えるcudaとmetalバックエンドの統合">7. ハードウェアの壁を越える：CUDAとMetalバックエンドの統合
&lt;/h2>&lt;p>純粋なC++実装だけでもCPU上ではそこそこ動きますが、LLMなどの巨大なモデルを実用的な速度（例：1秒間に20トークン以上生成）で動かすには、GPUの並列計算能力が不可欠です。そこで、我々のエンジンにバックエンドの抽象化レイヤーを導入します。&lt;/p>
&lt;h3 id="71-バックエンド抽象化">7.1 バックエンド抽象化
&lt;/h3>&lt;p>C++のポリモーフィズムを用いて、演算の実行器（Executor）を切り替えられるようにします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">Backend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">alloc_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">free_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_device&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_host&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 各種演算の実行
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="72-nvidia-cuda-バックエンドの実装">7.2 NVIDIA CUDA バックエンドの実装
&lt;/h3>&lt;p>NVIDIAのGPUを活用するために、CUDA C++拡張を用いてバックエンドを実装します。独自のカーネルを書くことも可能ですが、行列積に関してはNVIDIAが提供する最高峰のライブラリである「cuBLAS」を活用するのが最善手です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cublas_v2.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cuda_runtime.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CUDABackend&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasHandle_t&lt;/span> &lt;span class="n">handle&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CUDABackend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">CUDABackend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasDestroy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// CUDAではデフォルトがColumn-Majorのため、パラメータに注意が必要
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">1.0f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">beta&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// src1は転置されている前提
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasSgemm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_T&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_N&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">alpha&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">beta&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cudaDeviceSynchronize&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>CUDAメモリとホスト（CPU）メモリ間のデータ転送（&lt;code>cudaMemcpy&lt;/code>）は非常に重いため、推論中は極力全てのウェイト（重みテンソル）と中間テンソルをVRAM上に保持し続ける設計が重要になります。&lt;/p>
&lt;h3 id="73-apple-silicon-metal-バックエンド">7.3 Apple Silicon (Metal) バックエンド
&lt;/h3>&lt;p>近年、MacのM1/M2/M3チップ（Apple Silicon）はAI推論機として非常に優秀です。その理由は「ユニファイドメモリ」にあります。CPUとGPUが同一のメモリ領域を共有しているため、前述したCUDAのようなPCIeバス経由での高コストなホスト・デバイス間のメモリ転送が完全に不要になります。&lt;/p>
&lt;p>C++からMetalを呼び出すには、Objective-C++ (&lt;code>.mm&lt;/code> ファイル) をブリッジとして使用するか、&lt;code>metal-cpp&lt;/code>ライブラリを利用します。
MetalのCompute Shader（&lt;code>.metal&lt;/code>ファイルにC++ライクに記述）を用いてカーネルを記述します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Metalシェーダ (kernel.metal)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;metal_stdlib&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">metal&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">kernel&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">mul_mat_kernel&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">A&lt;/span> &lt;span class="p">[[&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)]],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="na">[[buffer(1)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">C&lt;/span> &lt;span class="na">[[buffer(2)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">constant&lt;/span> &lt;span class="n">uint3&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">dims&lt;/span> &lt;span class="na">[[buffer(3)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint2&lt;/span> &lt;span class="n">gid&lt;/span> &lt;span class="na">[[thread_position_in_grid]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">z&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">sum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">uint&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sum&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">A&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 簡略化
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">C&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sum&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Apple Silicon環境では、MPS（Metal Performance Shaders）という行列積用の最適化ライブラリも提供されているため、実運用ではこれを利用することで驚異的な推論速度を叩き出すことができます。&lt;/p>
&lt;hr>
&lt;h2 id="8-transformerモデル特有の処理attentionとkvキャッシュ">8. Transformerモデル特有の処理：AttentionとKVキャッシュ
&lt;/h2>&lt;p>LLaMA 2/3やGPTといった最先端のLLMはTransformerアーキテクチャに基づいています。これをC++で実装するためには、以下の数式で表される「Scaled Dot-Product Attention」の構築が必須です。&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>また、自己回帰型（Autoregressive）のトークン生成では、過去のトークンの計算結果（KeyとValue）を保持しておく必要があります。これを「&lt;strong>KVキャッシュ（Key-Value Cache）&lt;/strong>」と呼びます。&lt;/p>
&lt;div class="mermaid">graph TD
T["Current Token"] --> Q["Query"]
T --> K["Key"]
T --> V["Value"]
K --> KCache["Append to KV Cache"]
V --> VCache["Append to KV Cache"]
Q --> Dot1["Q * K_Cache^T"]
KCache --> Dot1
Dot1 --> Scale["Scale (1/sqrt(d))"]
Scale --> Softmax["Softmax"]
Softmax --> Dot2["SoftmaxOut * V_Cache"]
VCache --> Dot2
Dot2 --> Out["Context Vector"]&lt;/div>
&lt;p>KVキャッシュのメモリ確保も、事前に最大コンテキスト長（例えば4096や8192トークン）分のメモリ空間をアリーナに確保しておくリングバッファのような運用を行います。これにより、生成ステップごとの再配置を防ぐことができます。&lt;/p>
&lt;p>また、位置エンコーディング（Positional Encoding）には近年主流となっている「RoPE（Rotary Position Embedding）」を実装します。これは複素空間での回転ベクトルとして位置情報を埋め込む手法で、C++での &lt;code>sin&lt;/code> および &lt;code>cos&lt;/code> 関数の呼び出し最適化（ルックアップテーブル化など）がパフォーマンスの鍵を握ります。&lt;/p>
&lt;hr>
&lt;h2 id="9-モデルの量子化quantizationによる極限の最適化">9. モデルの量子化（Quantization）による極限の最適化
&lt;/h2>&lt;p>大規模モデル（例えば70億パラメータのLLaMAモデル）をFP32（32ビット浮動小数点）のまま読み込むと、重みだけで約28GBのメモリ（VRAM）を消費します。さらにKVキャッシュや推論用バッファを含めると30GBを容易に超え、一般的なコンシューマ向けGPUでは実行不可能です。&lt;/p>
&lt;p>そこで必須となるのが「&lt;strong>量子化（Quantization）&lt;/strong>」です。GGMLフォーマットの真骨頂でもあります。&lt;/p>
&lt;p>量子化とは、重みの精度を意図的に落とす技術です。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FP16 (16-bit)&lt;/strong>: サイズ半減。ほぼ精度劣化なし。&lt;/li>
&lt;li>&lt;strong>INT8 (8-bit)&lt;/strong>: サイズ1/4。僅かな劣化。&lt;/li>
&lt;li>&lt;strong>INT4 (4-bit)&lt;/strong>: サイズ1/8。独自のブロッキングとスケーリングファクターを用いれば実用的な推論が可能。&lt;/li>
&lt;/ul>
&lt;p>推論エンジン側では、メモリからINT4（またはINT8）で圧縮されたウェイトを読み出し、&lt;strong>CPUまたはGPUのレジスタにロードした直後にFP16またはFP32に展開（Dequantize）して計算&lt;/strong>を行います。&lt;/p>
&lt;p>驚くべきことに、計算量を増やしてでもメモリから読み込むデータ量を減らした方が速くなります。これは現代のハードウェアにおいて、推論タスクのボトルネックが「計算力（Compute Bound）」ではなく「&lt;strong>メモリ帯域幅（Memory Bandwidth Bound）&lt;/strong>」にあるからです。INT4量子化を施したC++実装のエンジンであれば、8GB VRAMのMacBook AirなどでもサクサクとローカルLLMを動かすことが可能になります。&lt;/p>
&lt;hr>
&lt;h2 id="10-パフォーマンスチューニングnumaアーキテクチャとスレッドプール">10. パフォーマンスチューニング：NUMAアーキテクチャとスレッドプール
&lt;/h2>&lt;p>CPUを用いた推論を行う場合、マルチスレッド化は必須です。しかし、単純に &lt;code>std::thread&lt;/code> を多数起動するだけでは最適とは言えません。&lt;/p>
&lt;p>現代のマルチソケットサーバーやRyzen ThreadripperのようなハイエンドCPUでは、&lt;strong>NUMA（Non-Uniform Memory Access）&lt;/strong> アーキテクチャが採用されています。あるCPUコアから物理的に近いメモリ（ローカルメモリ）へのアクセスは高速ですが、別のプロセッサに紐付いたメモリへのアクセスは極端に遅くなります。&lt;/p>
&lt;p>高度なC++推論エンジンでは、以下のテクニックを駆使します。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>スレッドピンニング（Thread Pinning）&lt;/strong>: 各スレッドを特定のCPUコアに固定（Affinityを設定）し、コンテキストスイッチによるキャッシュ破棄を防ぐ。&lt;/li>
&lt;li>&lt;strong>NUMAアウェアなアロケーション&lt;/strong>: データを処理するスレッドと同じNUMAノード上にメモリを確保する。&lt;/li>
&lt;li>&lt;strong>ワークスティーリング型スレッドプール&lt;/strong>: 計算グラフの各ノードを細かなタスクに分割し、空いているスレッドが自動的にタスクを奪取して実行する効率的なスケジューラを実装。&lt;/li>
&lt;/ol>
&lt;p>これらを駆使することで、CPU使用率を100%付近にピタリと張り付かせ、理論値に近いスループットを叩き出すことができます。&lt;/p>
&lt;hr>
&lt;h2 id="11-まとめcの筋肉でaiを駆動する楽しさ">11. まとめ：C++の「筋肉」でAIを駆動する楽しさ
&lt;/h2>&lt;p>Pythonは確かに便利です。研究開発やプロトタイピングにおいて、その生産性に敵う言語はありません。しかし、出来上がったモデルを「実世界で、効率よく、あらゆるデバイスで動かす」というフェーズに移行した瞬間、C++の出番がやってきます。&lt;/p>
&lt;p>メモリのバイト列を直接操作し、SIMD命令でレジスタを限界まで叩き、GPUのVRAM帯域幅と格闘しながら作り上げた推論エンジンが、コンソール上に次々と自然な日本語のテキスト（トークン）を生成していくのを見た時の達成感は、Pythonのフレームワークで &lt;code>model.generate()&lt;/code> を呼び出した時には決して得られない「エンジニアとしての純粋な喜び」があります。&lt;/p>
&lt;p>「Black Box」となりがちなAI技術ですが、テンソルの演算からメモリ確保に至るまですべてを自分の手でC++で書き上げることで、LLMがどのようにして「考えている」のか、その真のメカニズムを深く理解することができます。&lt;/p>
&lt;p>もしあなたがC++の基礎知識を持っていて、現在のAI技術に強い興味があるなら、ぜひ自作の推論エンジンの開発に挑戦してみてください。GGMLやllama.cppのソースコードは、最高の生きた教科書になるはずです。&lt;/p>
&lt;p>&lt;strong>さあ、Pythonの重いランタイムを捨てて、C++の筋肉で最先端のAIを走らせましょう！&lt;/strong>&lt;/p></description></item><item><title>TinyLLaMAをオンプレミス環境で最速チューニングする方法</title><link>http://kenji.blog/p/tinyllama-on-premises-fast-tuning-guide/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/p/tinyllama-on-premises-fast-tuning-guide/</guid><description>&lt;img src="http://kenji.blog/p/tinyllama-on-premises-fast-tuning-guide/img/eyecatch.jpg" alt="Featured image of post TinyLLaMAをオンプレミス環境で最速チューニングする方法" />&lt;h2 id="1-はじめになぜ今tinyllamaとオンプレミスなのか">1. はじめに：なぜ今、TinyLLaMAとオンプレミスなのか？
&lt;/h2>&lt;p>大規模言語モデル（LLM）の進化は凄まじいスピードで進んでいますが、それに伴いモデルのパラメータ数も数千億規模へと膨張し続けています。GPT-4やClaude 3のような超巨大モデルは比類なき性能を誇る一方で、推論や学習にかかる計算コスト、そして外部APIを利用する際のセキュリティやデータプライバシーの懸念が企業にとって大きなハードルとなっています。特に機密性の高い社内データや個人情報を扱う業務においては、クラウド上のパブリックなLLM APIへデータを送信することは、コンプライアンス（GDPRやAPPIなど）の観点から許容されないケースが多々あります。&lt;/p>
&lt;p>そこで脚光を浴びているのが、&lt;strong>小規模言語モデル（SLM: Small Language Models）&lt;/strong> と &lt;strong>オンプレミス環境でのローカル運用&lt;/strong> です。その中でも「&lt;strong>TinyLLaMA&lt;/strong>」は、わずか1.1B（11億）パラメータというコンパクトなサイズでありながら、約3兆トークンという膨大なデータセットで事前学習されており、同クラスのモデルと比較して驚異的な性能を発揮します。&lt;/p>
&lt;p>本記事では、このTinyLLaMAをオンプレミス環境（ローカルサーバーやワークステーション）で、自社専用のタスクに向けて「最速かつ高効率」にファインチューニング（微調整）するための完全ガイドを提供します。数学的な背景から、最新の最適化技術、そして具体的なPyTorchの実装コードまで、網羅的に解説していきます。&lt;/p>
&lt;hr>
&lt;h2 id="2-tinyllamaのアーキテクチャと特徴">2. TinyLLaMAのアーキテクチャと特徴
&lt;/h2>&lt;p>TinyLLaMAは、Meta社が開発したLLaMA（Large Language Model Meta AI）アーキテクチャを踏襲しています。パラメータ数を1.1Bに抑えつつも、LLaMA 2と同じ技術スタックを利用しているため、エコシステムの互換性が非常に高いのが特徴です。&lt;/p>
&lt;h3 id="主要なアーキテクチャコンポーネント">主要なアーキテクチャコンポーネント
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>RMSNorm (Root Mean Square Normalization):&lt;/strong>
従来のLayerNormの計算から平均の減算を省略し、計算効率を向上させた正規化手法です。学習の安定性を保ちながらスループットを向上させます。&lt;/li>
&lt;li>&lt;strong>SwiGLU活性化関数:&lt;/strong>
Feed Forward Network (FFN) において、従来のReLUやGELUの代わりにSwiGLUを採用しています。これは数学的には以下のように表されます。
$$ \text{SwiGLU}(x, W, V) = \text{Swish}(xW) \otimes (xV) $$
ここで、$\otimes$ は要素ごとの積（Hadamard積）を表し、Swish関数は $\text{Swish}(z) = z \cdot \sigma(\beta z)$ です。これにより表現力が大幅に向上します。&lt;/li>
&lt;li>&lt;strong>RoPE (Rotary Position Embedding):&lt;/strong>
絶対的な位置エンコーディングと相対的な位置エンコーディングの利点を組み合わせた手法です。シーケンス長が拡張された際にも高い汎化性能を持ちます。&lt;/li>
&lt;li>&lt;strong>Grouped Query Attention (GQA):&lt;/strong>
Multi-Head Attention (MHA) と Multi-Query Attention (MQA) の中間的なアプローチであり、キーとバリューのヘッドをグループ化することで、メモリ帯域幅を節約し推論速度を劇的に向上させます。&lt;/li>
&lt;/ol>
&lt;p>以下のMermaid図は、TinyLLaMAの全体的なデータフローとTransformerブロックの構造を示しています。&lt;/p>
&lt;div class="mermaid">graph TD
A["Input Text"] --> B["Tokenizer (BPE)"]
B --> C["Embedding Layer"]
C --> D["Transformer Blocks (x22 Layers for TinyLLaMA)"]
D --> E["RMSNorm (Final)"]
E --> F["Linear Projection (Vocab Size)"]
F --> G["Output Probabilities (Softmax)"]
subgraph "Transformer Block Anatomy"
D1["Input Hidden State"] --> D2["RMSNorm"]
D2 --> D3["Grouped Query Attention (GQA)"]
D3 --> D4["Residual Add"]
D4 --> D5["RMSNorm"]
D5 --> D6["SwiGLU FFN"]
D6 --> D7["Residual Add"]
D7 --> D8["Output to Next Layer"]
D1 -.-> D4
D4 -.-> D7
end&lt;/div>
&lt;hr>
&lt;h2 id="3-ファインチューニングのブレイクスルーloraとqlora">3. ファインチューニングのブレイクスルー：LoRAとQLoRA
&lt;/h2>&lt;p>オンプレミス環境でフルパラメータのファインチューニングを行うには、1.1Bモデルであってもオプティマイザのステートや勾配を保持するために数十GBのVRAM（ビデオメモリ）を消費します。限られたリソースで効率的に学習を行うために必須となるのが、&lt;strong>PEFT (Parameter-Efficient Fine-Tuning)&lt;/strong> 手法である「&lt;strong>LoRA&lt;/strong>」とその量子化拡張である「&lt;strong>QLoRA&lt;/strong>」です。&lt;/p>
&lt;h3 id="31-lora-low-rank-adaptation-の数学的背景">3.1 LoRA (Low-Rank Adaptation) の数学的背景
&lt;/h3>&lt;p>LoRAは、事前学習済みの重み行列を固定（フリーズ）し、その重みの更新量（$\Delta W$）を低ランクの2つの小さな行列の積として近似する手法です。&lt;/p>
&lt;p>事前学習済みの重みを $W_0 \in \mathbb{R}^{d \times k}$ とします。フルファインチューニングでは、$W_0$ 自体を更新して $W_0 + \Delta W$ としますが、LoRAでは更新行列 $\Delta W$ を以下のように分解します。&lt;/p>
$$ \Delta W = B \times A $$
&lt;p>ここで、$B \in \mathbb{R}^{d \times r}$、$A \in \mathbb{R}^{r \times k}$ であり、$r$ はランク（Rank）と呼ばれるハイパーパラメータで、$r \ll \min(d, k)$ を満たす非常に小さな値（通常は8, 16, 32など）です。&lt;/p>
&lt;p>フォワードパスの計算は以下のようになります。&lt;/p>
$$ h = W_0 x + \Delta W x = W_0 x + B A x $$
&lt;p>初期状態において、行列 $A$ は正規分布（ガウス分布）でランダムに初期化され、行列 $B$ はゼロ行列で初期化されます。これにより、学習開始時の $\Delta W$ はゼロとなり、ベースモデルの出力を完全に保持した状態から学習をスタートできます。&lt;/p>
&lt;div class="mermaid">graph LR
X["Input Vector x"] --> W0["Frozen Pre-trained Weight (W_0)"]
X --> A["Trainable LoRA Matrix A (r x k)"]
A --> B["Trainable LoRA Matrix B (d x r)"]
W0 --> Add["Vector Addition"]
B --> Add
Add --> Y["Output Vector h"]&lt;/div>
&lt;h3 id="32-qlora-quantized-lora-の革新性">3.2 QLoRA (Quantized LoRA) の革新性
&lt;/h3>&lt;p>QLoRAは、LoRAのアプローチをさらに推し進め、ベースモデル $W_0$ を4-bit精度（NormalFloat 4, NF4）で量子化してメモリにロードする手法です。これにより、VRAM消費量を劇的に削減します。&lt;/p>
&lt;p>QLoRAには3つの重要な技術が組み込まれています。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>4-bit NormalFloat (NF4) 量子化:&lt;/strong> 正規分布に従う重みに最適化された理論的に最適なデータ型。&lt;/li>
&lt;li>&lt;strong>Double Quantization (二重量子化):&lt;/strong> 量子化定数（スケールファクタ）自体も量子化することで、さらにメモリを節約。&lt;/li>
&lt;li>&lt;strong>Paged Optimizers:&lt;/strong> NVIDIAの統合メモリ機能を利用し、VRAMが不足した際にオプティマイザのステータスをCPUのRAMへ一時的に退避させる仕組み。&lt;/li>
&lt;/ol>
&lt;p>これにより、通常はVRAMが16GB〜24GB必要なチューニングが、コンシューマー向けのGPU（RTX 3060 12GBやRTX 4070など）でも余裕を持って実行可能になります。&lt;/p>
&lt;hr>
&lt;h2 id="4-オンプレミス環境におけるハードウェア要件とセットアップ">4. オンプレミス環境におけるハードウェア要件とセットアップ
&lt;/h2>&lt;p>TinyLLaMA (1.1B) をQLoRAでチューニングする場合のハードウェア要件は非常に低く抑えられます。&lt;/p>
&lt;h3 id="推奨ハードウェアスペック">推奨ハードウェアスペック
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>GPU:&lt;/strong> NVIDIA RTX 3060 (12GB), RTX 3090/4090 (24GB), または NVIDIA A10G/A100 など。VRAMは最低8GBあれば動作しますが、バッチサイズを稼ぐためには12GB以上を推奨します。&lt;/li>
&lt;li>&lt;strong>CPU:&lt;/strong> 8コア以上のモダンなCPU（Intel Core i7/i9, AMD Ryzen 7/9）&lt;/li>
&lt;li>&lt;strong>RAM:&lt;/strong> 32GB以上（Paged Optimizersを利用する場合、VRAMからの退避先として重要）&lt;/li>
&lt;li>&lt;strong>ストレージ:&lt;/strong> NVMe SSD（データセットの読み込みやモデルの保存を高速化するため）&lt;/li>
&lt;/ul>
&lt;h3 id="ソフトウェア環境の構築">ソフトウェア環境の構築
&lt;/h3>&lt;p>Ubuntu 22.04 LTS環境を想定したセットアップ手順です。Python 3.10以降を使用します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仮想環境の作成とアクティベート&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python3 -m venv tinyllama_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> tinyllama_env/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># PyTorchのインストール (CUDA 12.1用)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># トランスフォーマー関連ライブラリのインストール&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install transformers datasets peft trl accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="5-最速チューニングのための最適化技術">5. 最速チューニングのための最適化技術
&lt;/h2>&lt;p>ただスクリプトを回すだけでなく、「最速」でチューニングを完了させるためには、以下の最適化手法を組み合わせる必要があります。&lt;/p>
&lt;h3 id="51-flash-attention-2">5.1 Flash Attention 2
&lt;/h3>&lt;p>標準的なAttentionメカニズムは、シーケンス長 $N$ に対して時間・空間計算量が $O(N^2)$ となります。Flash Attention 2は、GPUのSRAMとHBM（High Bandwidth Memory）間のメモリアクセスを最適化することで、計算量を削減せずにIOネックを解消し、学習速度を数倍に引き上げ、メモリ消費を激減させます。&lt;/p>
&lt;h3 id="52-gradient-checkpointing-勾配チェックポイント">5.2 Gradient Checkpointing (勾配チェックポイント)
&lt;/h3>&lt;p>フォワードパスで計算された中間アクティベーションをすべてVRAMに保存するのではなく、一部のみを保存し、バックワードパスで必要になった際に再計算する手法です。計算時間は約20%増加しますが、メモリ消費量を劇的に削減できるため、結果としてより大きなバッチサイズを設定でき、スループット全体が向上します。&lt;/p>
&lt;h3 id="53-mixed-precision-training-混合精度学習-と-bfloat16">5.3 Mixed Precision Training (混合精度学習) と Bfloat16
&lt;/h3>&lt;p>GPUのTensor Coreを最大限に活用するため、学習時の計算を &lt;code>bfloat16&lt;/code> (Brain Floating Point) で行います。&lt;code>float16&lt;/code> と比較して指数部のビット長が &lt;code>float32&lt;/code> と同じであるため、オーバーフロー・アンダーフローのリスクが極めて低く、学習が安定します。&lt;/p>
&lt;hr>
&lt;h2 id="6-実践tinyllamaのqloraファインチューニングコード">6. 実践：TinyLLaMAのQLoRAファインチューニングコード
&lt;/h2>&lt;p>それでは、上記すべての最適化を盛り込んだ最速チューニング用のPyTorchスクリプトを解説します。ここではHugging Faceの &lt;code>trl&lt;/code> (Transformer Reinforcement Learning) ライブラリの &lt;code>SFTTrainer&lt;/code> を利用します。&lt;/p>
&lt;h3 id="61-データセットの準備とモデルのロード">6.1 データセットの準備とモデルのロード
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datasets&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dataset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TrainingArguments&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">trl&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. モデルとトークナイザーの指定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;TinyLlama/TinyLlama-1.1B-Chat-v1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. QLoRA用の4-bit量子化設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">bnb_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">load_in_4bit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_use_double_quant&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_quant_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;nf4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_compute_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span> &lt;span class="c1"># 計算はbfloat16で行う&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. モデルのロード (Flash Attention 2を有効化)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Loading model...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">quantization_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">bnb_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">use_flash_attention_2&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span> &lt;span class="c1"># 最速化の鍵&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. トークナイザーのロード&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">trust_remote_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pad_token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">eos_token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">padding_side&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;right&amp;#34;&lt;/span> &lt;span class="c1"># fp16/bf16トレーニング時のバグ回避のためrightに設定&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-loraアダプタの適用とデータセットの整形">6.2 LoRAアダプタの適用とデータセットの整形
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. k-bit学習の準備と勾配チェックポイントの有効化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gradient_checkpointing_enable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 6. LoRAの設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">peft_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">r&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># ランク&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_alpha&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># スケーリングファクタ&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_dropout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.05&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;none&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;CAUSAL_LM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_modules&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;q_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;k_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;v_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;o_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;gate_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;up_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;down_proj&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 全てのLinear層をターゲットにすると性能が向上&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">peft_config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">print_trainable_parameters&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 出力例: trainable params: 14,286,848 || all params: 1,114,335,232 || trainable%: 1.282%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 7. データセットのロード (ここでは例として日本語Instructionデータセットを使用)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 実際にはオンプレミスのプライベートJSONLファイルなどをロードします&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">load_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kunishou/databricks-dolly-15k-ja&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">split&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;train&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">format_instruction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> ChatMLフォーマットやプロンプトテンプレートに合わせて文字列を成形します
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_start|&amp;gt;user&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;instruction&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">sample&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;input&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_end|&amp;gt;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;lt;|im_start|&amp;gt;assistant&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;output&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;lt;|im_end|&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dataset&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">format_instruction&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-トレーニングの実行">6.3 トレーニングの実行
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 8. トレーニング引数の設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">training_args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TrainingArguments&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-output&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">per_device_train_batch_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># VRAMに余裕があれば上げる&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gradient_accumulation_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 実質的なバッチサイズ = 8 * 2 = 16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">optim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;paged_adamw_32bit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Paged OptimizerによるVRAM節約&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">save_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logging_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">learning_rate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">2e-4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fp16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bf16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 混合精度学習 (bfloat16)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_grad_norm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># テスト用に500ステップ。本番はエポック数で指定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">warmup_ratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.03&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">group_by_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lr_scheduler_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cosine&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 9. SFTTrainerによる学習の開始&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">train_dataset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">peft_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">peft_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset_text_field&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_seq_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 想定する入力長に合わせて調整&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">tokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">training_args&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Starting training...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">train&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 10. LoRAアダプタの保存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Training complete and model saved.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-パフォーマンス評価とトラブルシューティング">7. パフォーマンス評価とトラブルシューティング
&lt;/h2>&lt;p>オンプレミス環境で学習を回す際、よく直面する問題とその解決策です。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>OOM (Out Of Memory) が発生する:&lt;/strong>
&lt;ul>
&lt;li>&lt;code>per_device_train_batch_size&lt;/code> を &lt;code>1&lt;/code> に下げる。&lt;/li>
&lt;li>&lt;code>gradient_accumulation_steps&lt;/code> を増やして実質バッチサイズを維持する。&lt;/li>
&lt;li>&lt;code>max_seq_length&lt;/code> を &lt;code>2048&lt;/code> から &lt;code>1024&lt;/code> や &lt;code>512&lt;/code> に短縮する。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Lossが下がらない・発散する:&lt;/strong>
&lt;ul>
&lt;li>学習率 (&lt;code>learning_rate&lt;/code>) が大きすぎる可能性があります。&lt;code>2e-4&lt;/code> から &lt;code>5e-5&lt;/code> 程度まで下げてみてください。&lt;/li>
&lt;li>Bfloat16ではなくFloat16を使用している場合、勾配のアンダーフローが起きている可能性があります。&lt;code>bf16=True&lt;/code> を確認してください。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>推論時に謎の文字列が生成される:&lt;/strong>
&lt;ul>
&lt;li>&lt;code>padding_side=&amp;quot;right&amp;quot;&lt;/code> が正しく設定されているか確認してください。また、データセットのフォーマット（&lt;code>&amp;lt;|im_start|&amp;gt;&lt;/code> などの特殊トークン）がベースモデルの事前学習時と整合しているか確認が必要です。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-チューニング後のモデル展開-deployment">8. チューニング後のモデル展開 (Deployment)
&lt;/h2>&lt;p>チューニングが完了すると、保存されるのは「ベースモデル全体」ではなく、数MB〜数十MBの「&lt;strong>LoRAアダプタ（差分ウェイト）&lt;/strong>」のみです。推論を高速に行うためには、このLoRAウェイトを元のベースモデルにマージ（統合）し、単一のモデルとして書き出す必要があります。&lt;/p>
&lt;h3 id="モデルのマージスクリプト">モデルのマージスクリプト
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output_dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># FP16/BF16でモデルとアダプタをロード&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">torch_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output_dir&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ウェイトをマージして保存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">merge_and_unload&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_serialization&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Model merged and saved successfully!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="vllmによる爆速推論サーバーの立ち上げ">vLLMによる爆速推論サーバーの立ち上げ
&lt;/h3>&lt;p>オンプレミス環境での展開において、推論速度（Tokens per second）を最大化するためには、Hugging Faceの標準の &lt;code>pipeline&lt;/code> ではなく、&lt;strong>vLLM&lt;/strong> や &lt;strong>TGI (Text Generation Inference)&lt;/strong> の使用を強く推奨します。vLLMはPagedAttention技術を用いて、GPUメモリの断片化を防ぎ、並行リクエストの処理能力を劇的に向上させます。&lt;/p>
&lt;p>以下のMermaid図は、学習から推論サーバー展開までのパイプラインを示しています。&lt;/p>
&lt;div class="mermaid">graph TD
A["Raw Private Data"] --> B["Preprocessing &amp; Formatting (JSONL)"]
B --> C["QLoRA Fine-Tuning (SFTTrainer)"]
C --> D["LoRA Adapter Weights (.safetensors)"]
D --> E["Merge with Base TinyLLaMA 1.1B"]
E --> F["Merged Model"]
F --> G["Deploy via vLLM Server"]
G --> H["API Endpoint / UI (e.g. Chatbot)"]&lt;/div>
&lt;p>vLLMを使ったAPIサーバーの起動は以下の1コマンドで完了します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python -m vllm.entrypoints.openai.api_server &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model ./tinyllama-merged &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --host 0.0.0.0 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --port &lt;span class="m">8000&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --max-model-len &lt;span class="m">2048&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --dtype bfloat16
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これで、OpenAI API互換のエンドポイントがオンプレミス環境に構築され、セキュアかつ高速にローカルAIを活用できるようになります。&lt;/p>
&lt;hr>
&lt;h2 id="9-まとめ">9. まとめ
&lt;/h2>&lt;p>本記事では、パラメータ数が1.1Bと軽量でありながら高性能な「TinyLLaMA」を対象に、オンプレミス環境において最速かつメモリ効率良くファインチューニングを行う手法を解説しました。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>LoRA / QLoRA&lt;/strong> により、コンシューマー向けGPUでも本格的なLLMチューニングが可能に。&lt;/li>
&lt;li>&lt;strong>Flash Attention 2&lt;/strong> と &lt;strong>Gradient Checkpointing&lt;/strong> を駆使することで、学習時間とVRAM消費を極限まで最適化。&lt;/li>
&lt;li>&lt;strong>vLLM&lt;/strong> を活用したデプロイにより、本番環境でも高いスループットを実現。&lt;/li>
&lt;/ul>
&lt;p>オンプレミスでのローカルLLM運用は、データの機密性を守るだけでなく、特定のドメイン（法務、医療、社内規程など）に特化した専門AIを低コストで構築するための最強の武器となります。ぜひ本ガイドを参考に、自社専用のTinyLLaMAを育成してみてください。&lt;/p></description></item><item><title>AI時代のプログラマーはどう生き残るべきか？</title><link>http://kenji.blog/p/how-programmers-survive-in-ai-era/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/p/how-programmers-survive-in-ai-era/</guid><description>&lt;img src="http://kenji.blog/p/how-programmers-survive-in-ai-era/img/eyecatch.jpg" alt="Featured image of post AI時代のプログラマーはどう生き残るべきか？" />&lt;h1 id="ai時代のプログラマーはどう生き残るべきかコーディングの終焉と新たなエンジニアリングの幕開け">AI時代のプログラマーはどう生き残るべきか？コーディングの終焉と新たなエンジニアリングの幕開け
&lt;/h1>&lt;p>2026年現在、ソフトウェア開発の現場はかつてないほどの激変期にある。ほんの数年前まで、「AIがコードを書く」という概念は、せいぜいボイラープレート（定型コード）の生成や関数の自動補完といった、プログラマーの「補助ツール」としての役割に留まっていた。しかし、大規模言語モデル（LLM）の驚異的な進化により、事態は根本から覆った。現代のAIは単なる「賢いタイプライター」ではなく、要件定義書を与えれば、フロントエンドからバックエンドのロジック、データベースのスキーマ設計、さらにはCI/CDパイプラインの構築に至るまで、システム全体を瞬時に、かつ自律的に組み上げる能力を持つ「自律型ジュニア・エンジニア」へと変貌を遂げている。&lt;/p>
&lt;p>このような時代において、我々「プログラマー」や「ソフトウェアエンジニア」はどのように生き残るべきだろうか？「コードを書く」という行為自体の経済的価値が急速にデフレ化していく中で、ただ特定のプログラミング言語の文法（シンタックス）を知り、特定のフレームワークのAPIに精通しているだけの「コーダー」は、急速に市場から淘汰されつつある。&lt;/p>
&lt;p>本稿では、AI時代におけるプログラマーの生存戦略について、技術的、数学的、そして哲学的な視点から極めて詳細に考察していく。これは単なるキャリア論ではなく、ソフトウェアエンジニアリングという学問そのものの再定義である。&lt;/p>
&lt;hr>
&lt;h2 id="1-抽象化abstractionの歴史とプログラミングの再定義">1. 抽象化（Abstraction）の歴史と「プログラミング」の再定義
&lt;/h2>&lt;p>ソフトウェアエンジニアリングの歴史を振り返ると、それは常に「抽象化（Abstraction）」の歴史であったことがわかる。我々は常に、より人間に近い言語で、より複雑なシステムを記述するためのレイヤーを構築してきた。&lt;/p>
&lt;p>初期の計算機科学者は、パンチカードを用いて物理的なハードウェアのスイッチを直接操作し、マシン語（0と1の羅列）で計算機に指示を与えていた。その後、アセンブリ言語が登場し、人間が理解しやすいニーモニックでハードウェアを操作できるようになった。さらに時代が進むと、C言語やFortranといった高級言語が登場し、メモリ管理やCPUのレジスタといったハードウェアの複雑な詳細をカプセル化することに成功した。続いて登場したJava, Python, Ruby, TypeScriptなどのモダンな言語により、プログラマーは「計算機をどう動かすか（How）」ではなく、「何を計算機にさせたいか（What）」により集中できるようになった。&lt;/p>
&lt;p>AI（LLM）の登場は、この抽象化の歴史における最新にして最大のパラダイムシフトである。プログラミング言語の進化が「ハードウェアの隠蔽」であったとすれば、LLMの進化は「シンタックス（文法）の隠蔽」である。&lt;/p>
&lt;div class="mermaid">graph TD
A["Machine Code / Punch Cards (1940s)"] --> B["Assembly Language (1950s)"]
B --> C["Compiled High-Level Languages (1970s)"]
C --> D["Managed / Scripting Languages (1990s)"]
D --> E["Natural Language via LLMs (2020s)"]
E --> F["Autonomous AI Agents (2026-)"]
style E fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#fbb,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>もはや、開発者がメモリリークを心配しながらポインタを操作したり、JSONのパース処理のための定型コードを何百行も書いたりする時代は終わった。自然言語（日本語や英語）という、人類にとって最も抽象度の高い言語を用いてシステムを定義することが、2026年における「プログラミング」のスタンダードとなっている。&lt;/p>
&lt;hr>
&lt;h2 id="2-生産性の数学的モデル指数関数的成長の波に乗る">2. 生産性の数学的モデル：指数関数的成長の波に乗る
&lt;/h2>&lt;p>AIがもたらす生産性の向上を、数学的なモデルを用いて定量的に評価してみよう。
従来のソフトウェア開発における個人の生産性 $P_{traditional}$ は、個人のスキルレベル $S$、ドメイン経験 $E$、そしてツールの効率 $T$ の線形な組み合わせとしてモデル化できた。&lt;/p>
$$ P_{traditional} = c_1 \cdot S + c_2 \cdot E + c_3 \cdot T $$
&lt;p>しかし、AIを活用した現代の開発においては、AIの能力 $A(t)$ が人間の能力を増幅する「強力なレバレッジ（Multiplier）」として働く。AIの能力は時間の経過 $t$ と共に指数関数的に成長（ムーアの法則のAI版）するため、AI時代の生産性 $P_{AI}(t)$ は以下のような方程式で表すことができる。&lt;/p>
$$ P_{AI}(t) = \alpha \cdot S_{core} \cdot e^{\beta \cdot A(t)} $$
&lt;p>ここで各変数は以下を意味する。&lt;/p>
&lt;ul>
&lt;li>$\alpha$: 基底となる人間の生産性係数&lt;/li>
&lt;li>$S_{core}$: AIに代替されない「人間のコアスキル」（アーキテクチャ設計、ビジネス要件の理解、倫理的判断など）&lt;/li>
&lt;li>$A(t)$: 時刻 $t$ におけるAIモデルの絶対的な能力（パラメータ数、コンテキストウィンドウ、推論能力）&lt;/li>
&lt;li>$\beta$: AIツールをどれだけ効果的に引き出せるか（プロンプトエンジニアリングの質や、AIとの協調ワークフローの洗練度）を示す係数&lt;/li>
&lt;/ul>
&lt;p>この数式から導き出される重要な洞察は、&lt;strong>$A(t)$ が指数関数的に増大する世界においては、単なるタイピング速度や特定言語の暗記といった従来型のスキルは、全体の生産性に与える影響が極めて小さくなる&lt;/strong>ということだ。代わりに、指数関数的なAIの成長に乗じるための係数 $\beta$ と、AIがカバーできない領域である $S_{core}$ が、エンジニアの市場価値を決定づける支配的な要因となる。&lt;/p>
&lt;hr>
&lt;h2 id="3-タスクの自動化確率probability-of-automation">3. タスクの自動化確率（Probability of Automation）
&lt;/h2>&lt;p>では、どのようなタスクが自動化され、どのようなタスクが人間の手に残るのだろうか？
あるタスク $T$ がAIによって完全に自動化される確率 $P_{auto}(T)$ は、次のように定式化できる。&lt;/p>
$$ P_{auto}(T) = 1 - \exp\left(-\lambda \cdot \frac{\text{Predictability}(T)}{\text{Complexity}(T) \times \text{Context Dependency}(T)}\right) $$
&lt;ul>
&lt;li>$\text{Predictability}(T)$: タスクの予測可能性（過去のデータにどれだけパターンが存在するか）&lt;/li>
&lt;li>$\text{Complexity}(T)$: タスクの複雑性&lt;/li>
&lt;li>$\text{Context Dependency}(T)$: タスクが依存する「暗黙のコンテキスト（ドメイン固有の知識や人間関係）」の強さ&lt;/li>
&lt;li>$\lambda$: AIの技術進歩率&lt;/li>
&lt;/ul>
&lt;p>APIのルーティング処理を書く、単純なCRUD画面を作る、といった予測可能性が高くコンテキスト依存性が低いタスクは、$P_{auto} \approx 1$ となり、ほぼ完全に自動化される。一方で、「既存のレガシーシステムと新しいマイクロサービスをどう安全に統合するか」「法務部門の要求を満たしつつ、ユーザー体験を損なわない認証フローをどう設計するか」といった、コンテキスト依存性が極めて高いタスクは自動化が難しい。&lt;/p>
&lt;hr>
&lt;h2 id="4-シンタックス文法からアーキテクチャ構造への回帰">4. シンタックス（文法）からアーキテクチャ（構造）への回帰
&lt;/h2>&lt;p>AIが得意なことと、人間が得意なことを明確に分離することが生存の絶対条件となる。&lt;/p>
&lt;div class="mermaid">graph LR
Sub1["AI's Domains of Excellence"]
Sub2["Human's Domains of Excellence"]
A["Code Generation from Spec"] --> Sub1
B["Syntax Error &amp; Bug Fixing"] --> Sub1
C["Boilerplate / Test Generation"] --> Sub1
D["Log Analysis &amp; Pattern Matching"] --> Sub1
E["System Architecture Design"] --> Sub2
F["Resolving Ambiguous Requirements"] --> Sub2
G["Cross-team Negotiation"] --> Sub2
H["Ethical Judgement / Responsibility"] --> Sub2&lt;/div>
&lt;p>AIは「局所的な最適化」において人間を凌駕している。1つの関数、1つのクラス、あるいは単一のモジュールを記述する速度と正確性において、人間に勝ち目はない。しかし、AIは「大域的な最適化」や「コンテキストの欠落（Missing Context）」に対しては非常に脆弱である。&lt;/p>
&lt;p>これからのプログラマーは「コードを書く労働者」から、「AIが生成した無数のコンポーネントをオーケストレーションするアーキテクト」へと役割を変えなければならない。システム全体を俯瞰し、マイクロサービス境界をどこに引くか、CAP定理における可用性と一貫性のトレードオフをどうビジネスの文脈に合わせて解決するか、技術的負債をどうコントロールするか。これらは、全体像とビジネスゴールを理解している人間にしかできない高度な知的作業である。&lt;/p>
&lt;hr>
&lt;h2 id="5-要件定義こそが真のプロンプトエンジニアリングである">5. 要件定義こそが「真のプロンプトエンジニアリング」である
&lt;/h2>&lt;p>最近よく耳にする「プロンプトエンジニアリング」という言葉は、しばしば「AIを騙して狙った出力を得るためのハック」のように誤解されがちである。しかし、ソフトウェア開発におけるプロンプトエンジニアリングの本質は、紛れもなく**「高度な要件定義（Requirements Engineering）」**である。&lt;/p>
&lt;p>自然言語でAIに指示を出し、意図した通りのソフトウェアを出力させるためには、以下のような要素を厳密に言語化しなければならない。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>目的（Why）&lt;/strong>: なぜこの機能が必要なのか。ビジネス上の価値は何か。&lt;/li>
&lt;li>&lt;strong>制約条件（Constraints）&lt;/strong>: パフォーマンスの要件（レイテンシ、スループット）、セキュリティ要件、コスト制約。&lt;/li>
&lt;li>&lt;strong>エッジケース（Edge Cases）&lt;/strong>: ユーザーが予期せぬ入力を行った場合のフォールバック処理。&lt;/li>
&lt;li>&lt;strong>インターフェース（Interfaces）&lt;/strong>: 既存システムとの連携仕様。&lt;/li>
&lt;/ol>
&lt;p>曖昧な指示（プロンプト）からは、曖昧で脆弱なシステムしか生まれない。「顧客が本当に欲しかったもの」を深くヒアリングし、矛盾する要件を整理し、論理的な破綻のない仕様書（プロンプト）を作成する能力。これこそが、AI時代における最強の「コーディングスキル」である。プログラマーはコードエディタではなく、NotionやMarkdownファイルに向き合い、システムのあるべき姿をテキストで精緻に記述する時間が増えていく。&lt;/p>
&lt;hr>
&lt;h2 id="6-ドメイン知識domain-knowledgeの圧倒的優位性">6. ドメイン知識（Domain Knowledge）の圧倒的優位性
&lt;/h2>&lt;p>AIは世界中のオープンソースコードや公開ドキュメントを学習しているため、一般的なWeb技術やアルゴリズムには精通している。しかし、AIがアクセスできないデータがある。それは「あなたの会社固有のビジネスルール」であり、「特定の業界（医療、金融、製造など）に深く根付いたドメイン知識」である。&lt;/p>
&lt;p>例えば、ある医療系スタートアップで電子カルテシステムを開発しているとする。AIは「Reactで表組みのUIを作る方法」や「HL7 FHIRの一般的なデータ構造」は知っている。しかし、「A病院の特定の診療科において、医師がどのような順番で患者のデータを閲覧し、どのようなUIであれば医療ミスのリスクを最小化できるか」という暗黙知までは学習していない。&lt;/p>
&lt;p>技術そのものがコモディティ化（汎用品化）する世界において、エンジニアの真の価値は「テクノロジー」と「ビジネス・ドメイン」の交差点に生まれる。技術力だけで勝負するのではなく、医療、金融、物流、エンタメなど、特定のドメインにおける深い専門知識を持ち、そのドメインの課題をAIという強力なツールを使って解決できる人材が、これからの市場を牽引していく。&lt;/p>
&lt;hr>
&lt;h2 id="7-ソフトウェア開発のトロッコ問題誰が責任を取るのか">7. ソフトウェア開発の「トロッコ問題」：誰が責任を取るのか
&lt;/h2>&lt;p>AIへの依存度が高まるにつれ、我々は重大な哲学・倫理的問題に直面する。ソフトウェアエンジニアリングにおける「責任の所在」という問題である。&lt;/p>
&lt;p>もし、AIが自律的に生成したコードが本番環境で深刻なバグを引き起こし、企業に数億円の損失を与えたり、人命に関わる医療システムで誤動作を起こしたりした場合、誰がその責任を取るのか？AIモデルを開発した企業か？それとも、プロンプトを入力したエンジニアか？AIを「解雇」したり「逮捕」したりすることはできない。&lt;/p>
&lt;p>システムが社会に与える影響に対する「法的・倫理的責任（Accountability）」を引き受ける主体としての「人間」の役割は、どれほど技術が進化したとしても消滅することはない。むしろ、コードの生成プロセスがブラックボックス化するほど、人間はシステムに対する「最終承認者（Approver）」および「監督者（Supervisor）」としての重い責任を負うことになる。&lt;/p>
&lt;p>AIが提示したアーキテクチャやコードが、セキュリティ基準を満たしているか、倫理的に問題がないか（バイアスが含まれていないか）、コンプライアンスを遵守しているかを監査し、最終的なGOサインを出す。この「責任を負う」という行為自体が、エンジニアの重要な仕事の一部となる。&lt;/p>
&lt;hr>
&lt;h2 id="8-aiペアプログラミングと認知負荷cognitive-loadのマネジメント">8. AIペアプログラミングと認知負荷（Cognitive Load）のマネジメント
&lt;/h2>&lt;p>AIと共に働く上で、人間の「認知負荷（Cognitive Load）」の性質も変化している。ゼロからコードを書く際の認知負荷と、AIが生成した数百行の未知のコードを「読んでレビューする」際の認知負荷は全く異なる。&lt;/p>
&lt;p>心理学の認知負荷理論によれば、既存のスキーマ（頭の中の知識構造）に合致しない複雑な情報を処理する際、人間のワーキングメモリはすぐに枯渇する。AIが生成したコードは、時に人間が思いつかないような高度な最適化を含んでいる一方で、コンテキストを無視した「幻覚（ハルシネーション）」を含んでいることもある。&lt;/p>
&lt;p>これを防ぐためには、AIに対するレビュープロセスをシステム化する必要がある。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant H as "Human Engineer (Architect)"
participant A as "AI Agent"
participant S as "CI/CD &amp; Testing"
H->>A: "Define strict requirements &amp; constraints"
A->>H: "Propose Architecture &amp; Initial Code"
Note over H,A: Review Phase: High Cognitive Load
H->>A: "Critique design choices, ask for refactoring"
A->>S: "Generate Final Code &amp; Push"
S-->>H: "Automated Test Results &amp; Static Analysis"
H->>H: "Final Approval &amp; Responsibility Assumption"&lt;/div>
&lt;p>人間は「書く」スキル以上に、「速読し、論理的欠陥を瞬時に見抜く（Code Reading &amp;amp; Auditing）」スキルを極限まで高める必要がある。テスト駆動開発（TDD）の重要性はAI時代においてさらに増している。AIにコードを書かせる前に、人間または別のAIが厳密なテストコードを書き、そのテストを通過するまでAIにコードを修正させるというアプローチが主流になる。&lt;/p>
&lt;hr>
&lt;h2 id="9-具体的な生存戦略明日から何を学ぶべきか">9. 具体的な生存戦略：明日から何を学ぶべきか
&lt;/h2>&lt;p>ここまでの分析を踏まえ、プログラマーがAI時代を生き残るための具体的なアクションプランを提示する。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>技術の「基礎」を徹底的に学び直す&lt;/strong>: フレームワークの使い方はAIに任せればよい。しかし、OSの仕組み、ネットワークプロトコル（TCP/IP, HTTP/3）、データベースの内部構造（B-Tree, トランザクション分離レベル）、データ構造とアルゴリズムに関する深い理解は絶対に必要である。AIの出力が正しいかを判断するためには、コンピュータサイエンスの確固たる基礎が不可欠である。&lt;/li>
&lt;li>&lt;strong>クラウド・アーキテクチャと分散システムをマスターする&lt;/strong>: 個別のコードではなく、AWS, GCP, Azureといったクラウドリソースをどのように組み合わせてスケーラブルなシステムを構築するかに注力する。TerraformなどのIaC（Infrastructure as Code）の概念を理解し、システム全体をコードとして設計する能力を養う。&lt;/li>
&lt;li>&lt;strong>ビジネスドメインの専門家になる&lt;/strong>: 自分が所属する業界のビジネスモデル、法規制、ユーザーの行動心理を深く学ぶ。エンジニアの枠を超え、プロダクトマネージャー（PM）に近い視点を持つこと。&lt;/li>
&lt;li>&lt;strong>コミュニケーションとファシリテーションのスキルを磨く&lt;/strong>: 人間と人間の間にある「曖昧さ」を解決し、合意形成を行うプロセスはAIには代替できない。ステークホルダーと対話し、真の課題を発見するソフトスキルは、最も価値の高いスキルとなる。&lt;/li>
&lt;li>&lt;strong>AIを「同僚」として使い倒す&lt;/strong>: AIツールの進化を恐れるのではなく、最も強力な武器として活用する。最新のLLMやAIコーディングエージェントを日常的に使い、どこでAIが失敗するか、どうプロンプトを工夫すれば最高のパフォーマンスを引き出せるかという「暗黙知」を蓄積する。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="結論恐れるな波を乗りこなせ">結論：恐れるな、波を乗りこなせ
&lt;/h2>&lt;p>AIによるプログラミングの自動化は、プログラマーという職業の「死」を意味するものではない。むしろ、タイポの修正や環境構築のトラブルシューティング、退屈なボイラープレートの記述といった、ソフトウェア開発における「本質的でない労働」から我々を解放してくれる**「ルネサンス（文芸復興）」**である。&lt;/p>
&lt;p>歴史上、自動織機が登場したときも、表計算ソフト（Excel）が登場したときも、仕事が消滅するという悲観論が蔓延した。しかし現実は、生産性が飛躍的に向上したことで新たな需要が創出され、より高度な仕事が生まれてきた。ソフトウェアの世界でも同じことが起きる。「安価にシステムを作れる」ようになることで、これまでコストが見合わずにIT化されてこなかったあらゆる領域にソフトウェアが浸透し、エンジニアが解決すべき課題（What）は無限に広がっていく。&lt;/p>
&lt;p>我々プログラマーは今、コードを書く職人から、AIという強大な知能を指揮する「オーケストラの指揮者」へと進化するチャンスを与えられている。技術の波に怯えて岸辺に留まるのではなく、その波をいち早く乗りこなし、より大きく、より価値のあるシステムを創造する旅へと漕ぎ出そう。AI時代こそが、真の意味での「エンジニアリング」が始まる時代なのである。&lt;/p></description></item><item><title>C++で始める小規模AIモデル（TinyLLaMAなど）の開発手順</title><link>http://kenji.blog/p/cpp-small-ai-model-tinyllama-dev-guide/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/p/cpp-small-ai-model-tinyllama-dev-guide/</guid><description>&lt;img src="http://kenji.blog/p/cpp-small-ai-model-tinyllama-dev-guide/img/eyecatch.jpg" alt="Featured image of post C++で始める小規模AIモデル（TinyLLaMAなど）の開発手順" />&lt;h1 id="cで始める小規模aiモデルtinyllamaなどの開発手順">C++で始める小規模AIモデル（TinyLLaMAなど）の開発手順
&lt;/h1>&lt;p>近年、大規模言語モデル（LLM）のローカル環境での実行に対する関心が急速に高まっています。特に、TinyLLaMA（1.1Bパラメータ）のような小規模モデルは、限られたリソースのエッジデバイスや一般的なノートPC（Windows環境を含む）上でも実用的な速度で推論が可能です。PythonとPyTorchを用いた開発が主流である一方で、究極のパフォーマンスと省メモリ性を追求する場合、C++とC言語ベースのテンソルライブラリである「ggml」の組み合わせがデファクトスタンダードとなっています。&lt;/p>
&lt;p>本記事では、C++を用いてTinyLLaMAをロードし、テキスト生成を行うための推論エンジンをゼロから構築（あるいは既存のllama.cppの内部構造を深く理解）するための非常に詳細な開発手順を解説します。&lt;/p>
&lt;hr>
&lt;h2 id="1-なぜcとggmlなのか">1. なぜC++とggmlなのか？
&lt;/h2>&lt;p>AIの学習段階においては、柔軟性と豊富なエコシステムを持つPythonが圧倒的に有利です。しかし、デプロイや「推論（Inference）」のフェーズにおいては、以下の理由からC++が強力な選択肢となります。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>オーバーヘッドの削減&lt;/strong>: Pythonのグローバルインタプリタロック（GIL）やランタイムのオーバーヘッドを完全に排除できます。&lt;/li>
&lt;li>&lt;strong>メモリ効率とアリーナアロケーション&lt;/strong>: メモリの確保と解放をマニュアルで制御できるため、ガベージコレクションによる予測不能なスパイクを防げます。&lt;/li>
&lt;li>&lt;strong>ハードウェアへの直接アクセス&lt;/strong>: AVX-512、AVX2、ARM NEONなどのSIMD組み込み関数（Intrinsics）を直接呼び出し、CPUの演算能力を極限まで引き出せます。&lt;/li>
&lt;li>&lt;strong>依存関係の排除&lt;/strong>: ggmlは依存関係ゼロ（Zero dependencies）のC/C++ライブラリであり、コンパイラさえあればWindows上のMSVC環境でも容易にビルド可能です。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-アーキテクチャの全体像">2. アーキテクチャの全体像
&lt;/h2>&lt;p>推論パイプライン全体の流れを以下のMermaidダイアグラムに示します。ユーザーの入力テキストから始まり、最終的に次のトークンが生成されるまでの一連のプロセスです。&lt;/p>
&lt;div class="mermaid">graph TD
A["User Input Text"] --> B["BPE Tokenizer"]
B --> C["Token IDs Array"]
C --> D["Embedding Layer Lookup"]
D --> E["Transformer Blocks"]
E --> F["RMSNorm"]
F --> G["LM Head Layer"]
G --> H["Logits Array"]
H --> I["Sampler Module"]
I --> J["Next Token ID"]
J --> K["Detokenizer"]
K --> L["Output Text Chunk"]
J -.-> |"Append to Context"| C&lt;/div>
&lt;p>自己回帰モデルであるため、出力されたトークンは再びコンテキストに追加され、次のトークン予測のための入力として循環します（図の点線部分）。&lt;/p>
&lt;hr>
&lt;h2 id="3-モデルフォーマットとメモリマッピング-mmap">3. モデルフォーマットとメモリマッピング (mmap)
&lt;/h2>&lt;p>巨大なニューラルネットワークの重みを扱う上で最大の障壁となるのが、ディスクI/Oとメモリ消費です。C++実装ではこれを**メモリマッピング（mmap）**で解決します。&lt;/p>
&lt;h3 id="31-メモリマッピングの仕組みとwindowsでの実装">3.1 メモリマッピングの仕組みとWindowsでの実装
&lt;/h3>&lt;p>mmapを使用すると、ファイルの内容をプロセスの仮想メモリ空間に直接マッピングできます。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ゼロコピー（Zero-copy）&lt;/strong>: データはディスクから直接カーネルのページキャッシュに読み込まれ、ユーザー空間への余分なコピーが発生しません。&lt;/li>
&lt;li>&lt;strong>オンデマンド・ロード（Page Fault）&lt;/strong>: 実際にCPUがそのメモリアドレスにアクセスした瞬間に、ページフォールトが発生し、必要なチャンク（通常4KB）だけが物理メモリにロードされます。&lt;/li>
&lt;/ul>
&lt;p>Windows環境では、POSIXの &lt;code>mmap&lt;/code> の代わりにWin32 APIの &lt;code>CreateFileMapping&lt;/code> と &lt;code>MapViewOfFile&lt;/code> を使用します。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant OS["Windows OS"]
participant RAM["Physical Memory"]
participant App["C++ Application"]
App->>OS: "CreateFileMapping / MapViewOfFile"
OS-->>App: "Virtual Memory Address Pointer"
App->>App: "Read Tensor Data at Pointer"
OS->>RAM: "Page Fault / Load page from Disk"
RAM-->>App: "Data ready for SIMD Compute"&lt;/div>
&lt;h3 id="32-ggufフォーマットのバイナリ構造">3.2 GGUFフォーマットのバイナリ構造
&lt;/h3>&lt;p>Hugging Face等の&lt;code>.safetensors&lt;/code>フォーマットから変換された**GGUF (GPT-Generated Unified Format)**は、推論のための究極のフォーマットです。以下のような厳密なバイナリレイアウトを持っています。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Magic Bytes&lt;/strong>: &lt;code>0x46554747&lt;/code> (GGUF)。&lt;/li>
&lt;li>&lt;strong>Version&lt;/strong>: フォーマットのバージョン番号。&lt;/li>
&lt;li>&lt;strong>Tensor Count &amp;amp; Metadata Count&lt;/strong>: テンソル数とメタデータのキーバリューペア数。&lt;/li>
&lt;li>&lt;strong>Metadata (Key-Value Pairs)&lt;/strong>: 文字列長プレフィックス付きのキーと、型付けされた値。&lt;/li>
&lt;li>&lt;strong>Tensor Info&lt;/strong>: 各テンソルの名前、次元数、データ型（FP16, Q4_Kなど）、ファイル内のオフセット位置。&lt;/li>
&lt;li>&lt;strong>Padding&lt;/strong>: テンソルデータが特定の境界（通常は32バイトまたは64バイト）にアライメントされるように挿入されるパディング。SIMD命令（特にAVX）での高速なメモリアクセスに不可欠です。&lt;/li>
&lt;li>&lt;strong>Tensor Data&lt;/strong>: アライメントされた実際の重みデータ配列。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-tinyllamaの数学的基盤とcアルゴリズム">4. TinyLLaMAの数学的基盤とC++アルゴリズム
&lt;/h2>&lt;p>TinyLLaMAは、効率化のためにいくつかの高度なアーキテクチャ上の工夫を取り入れています。これらをC++で正しく実装するための数式表現を解説します。&lt;/p>
&lt;h3 id="41-rmsnorm-root-mean-square-normalization">4.1 RMSNorm (Root Mean Square Normalization)
&lt;/h3>&lt;p>LayerNormから平均のセンタリングを省略し、分散のスケーリングのみを行うことで計算コストを削減します。&lt;/p>
$$ \text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d} x_i^2 + \epsilon}} \odot \gamma $$
&lt;p>$d$は次元数、$\gamma$は学習済みのスケーリングテンソルです。
C++で実装する場合、まず配列の二乗和をAVX2の &lt;code>_mm256_fmadd_ps&lt;/code> 等で高速に計算し、逆平方根（&lt;code>_mm256_rsqrt_ps&lt;/code> 命令など）を掛けることで最適化します。&lt;/p>
&lt;h3 id="42-rope-rotary-position-embedding">4.2 RoPE (Rotary Position Embedding)
&lt;/h3>&lt;p>トークンの位置情報をテンソル空間における回転（Rotate）として適用する技術です。複素数平面上での回転とみなすことができ、ベクトル $x$ の隣り合う次元ペア $(x_1, x_2)$ に対して以下のような回転を適用します。&lt;/p>
$$ \text{RoPE}(x, m) = \begin{pmatrix} x_{1} \cos(m\theta) - x_{2} \sin(m\theta) \\ x_{1} \sin(m\theta) + x_{2} \cos(m\theta) \end{pmatrix} $$
&lt;p>ここで、$m$はトークンの絶対的な位置インデックス、$\theta$は事前に計算された基本周波数です。ggmlでは、推論グラフの構築中に &lt;code>ggml_rope&lt;/code> オペレータを追加するだけで並列実行されます。&lt;/p>
&lt;h3 id="43-grouped-query-attention-gqa">4.3 Grouped-Query Attention (GQA)
&lt;/h3>&lt;p>通常のMulti-Head Attention (MHA) では、Query、Key、Valueのそれぞれに対して同じ数のヘッドを持ちます。しかし、TinyLLaMAはメモリ帯域とKVキャッシュの消費量を激減させるために &lt;strong>Grouped-Query Attention (GQA)&lt;/strong> を採用しています。&lt;/p>
$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>GQAでは、複数のQueryヘッドが1つのKey/Valueヘッドを共有します。C++実装では行列積 &lt;code>ggml_mul_mat&lt;/code> を実行する前に、KVテンソルをQueryの数に合わせてブロードキャストする操作が必要になります。&lt;/p>
&lt;h3 id="44-swiglu-活性化関数">4.4 SwiGLU 活性化関数
&lt;/h3>&lt;p>Feed-Forward Network (FFN) 層では、GELUの代わりにSwiGLUが用いられます。&lt;/p>
$$ \text{SwiGLU}(x) = \text{Swish}(x W_{\text{gate}}) \otimes (x W_{\text{up}}) $$
$$ \text{Swish}(z) = z \cdot \sigma(z) = z \cdot \frac{1}{1 + e^{-z}} $$
&lt;p>計算グラフでは &lt;code>ggml_silu&lt;/code> オペレータと &lt;code>ggml_mul&lt;/code> を組み合わせて表現します。&lt;/p>
&lt;hr>
&lt;h2 id="5-ggmlによる計算グラフの構築とメモリ管理">5. ggmlによる計算グラフの構築とメモリ管理
&lt;/h2>&lt;p>ggmlは、推論のための静的な計算グラフを構築し、それを後から評価（evaluate）する「Define-and-Run」のアプローチを取ります。&lt;/p>
&lt;h3 id="51-ggml_context-とアリーナアロケータ">5.1 ggml_context とアリーナアロケータ
&lt;/h3>&lt;p>ggmlの最もユニークな点は、推論ループ内で動的なメモリ確保（&lt;code>malloc&lt;/code> や &lt;code>new&lt;/code>）を一切行わない「アリーナアロケーション」です。
初期化時に巨大な連続したメモリ領域（アリーナ）を確保し、&lt;code>ggml_new_tensor&lt;/code> などを呼び出すたびに、この領域のポインタがインクリメントされます。推論の1ステップが完了したら、アロケーションポインタを初期位置にリセットするだけで、次の推論ステップのメモリ確保が即座に完了します。&lt;/p>
&lt;h3 id="52-グラフ構築の具体例">5.2 グラフ構築の具体例
&lt;/h3>&lt;p>推論ステップごとに、以下のような計算グラフをメモリ上に組み立てます。&lt;/p>
&lt;div class="mermaid">graph TD
A["Tokens Input ID"] --> B["Embed Lookup"]
B --> C["ggml_rms_norm"]
C --> D["Q / K / V Projections"]
D --> E["ggml_rope Positional"]
E --> F["KV Cache Store"]
E --> G["KV Cache Load"]
G --> H["Self Attention"]
H --> I["Scale &amp; Softmax"]
I --> J["Attention Output"]
J --> K["Out Projection"]
K --> L["Add Residual"]&lt;/div>
&lt;hr>
&lt;h2 id="6-量子化-quantization-と-windows--simd-最適化">6. 量子化 (Quantization) と Windows / SIMD 最適化
&lt;/h2>&lt;p>TinyLLaMA (1.1B) をFP16で扱うと約2.2GBのメモリが必要ですが、4ビット量子化（Q4_Kなど）により約600MB程度まで劇的に圧縮できます。&lt;/p>
&lt;h3 id="61-ブロック量子化アーキテクチャ">6.1 ブロック量子化アーキテクチャ
&lt;/h3>&lt;p>ggmlはテンソル全体を一律に量子化するのではなく、「ブロック」単位で行います。
&lt;code>Q4_0&lt;/code> フォーマットでは、32個のFP16値を1つのブロックにまとめます。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>スケールファクタ&lt;/strong>: 1つのFP16値（2バイト）&lt;/li>
&lt;li>&lt;strong>量子化データ&lt;/strong>: 32個の4ビット値（16バイト）
これにより、局所的な外れ値の影響を最小限に抑えています。&lt;/li>
&lt;/ul>
&lt;h3 id="62-avx2によるドット積の高速化">6.2 AVX2によるドット積の高速化
&lt;/h3>&lt;p>Windows環境の最新のx86 CPU向けにビルドする場合、&lt;code>/arch:AVX2&lt;/code> などのコンパイラフラグを活用し、以下のようなフローでSIMD処理が行われます。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>ロード&lt;/strong>: 256ビットのAVXレジスタに、メモリから4ビット量子化データをロード。&lt;/li>
&lt;li>&lt;strong>展開とアンパック&lt;/strong>: ビットマスクとシフト演算で、4ビット値をInt8またはInt16に展開。&lt;/li>
&lt;li>&lt;strong>逆量子化&lt;/strong>: スケールファクタを乗算して浮動小数点に変換。&lt;/li>
&lt;li>&lt;strong>FMA演算&lt;/strong>: アクティベーション値と &lt;code>_mm256_fmadd_ps&lt;/code>（Fused Multiply-Add）で積和演算を並列実行。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="7-kvキャッシュの実装詳細">7. KVキャッシュの実装詳細
&lt;/h2>&lt;p>自己回帰的生成において、過去のトークンのKeyとValueの計算を省略するための「KVキャッシュ」は必須機能です。&lt;/p>
&lt;p>C++で実装する場合のポイントは以下の通りです。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>テンソルの事前確保&lt;/strong>: 最大コンテキスト長（例: 2048トークン）分の巨大なテンソルをKVキャッシュ用に初期化します（FP16を推奨）。&lt;/li>
&lt;li>&lt;strong>オフセットコピー&lt;/strong>: トークン位置 $N$ に対する計算が行われると、そのステップで得られたKとVのベクトルをKVキャッシュテンソルの $N$ 行目に &lt;code>ggml_cpy&lt;/code> 等を用いてストアします。&lt;/li>
&lt;li>&lt;strong>アテンション時のビュー作成&lt;/strong>: アテンションを計算する際は、0から $N$ 番目までのトークン部分だけを指し示す「ビュー」を作成して行列積に渡します。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-bpe-トークナイザーとデコーディング">8. BPE トークナイザーとデコーディング
&lt;/h2>&lt;p>入力文字列をUTF-8のバイト列として扱い、事前に定義されたボキャブラリーと照らし合わせます。C++では、ボキャブラリーの検索を高速化するために &lt;strong>Trie木（プレフィックスツリー）&lt;/strong> や、優先度付きキューを用いたアルゴリズムを実装します。&lt;/p>
&lt;p>LM Headから出力されるロジットからは、Temperatureパラメータを用いて確率をスケーリングし、Top-K抽出やTop-P（Nucleus Sampling）手法によって候補を絞り込み、乱数を用いて最終的な次のトークンを決定します。&lt;/p>
&lt;hr>
&lt;h2 id="9-cプロジェクトの立ち上げwindows--powershell-環境">9. C++プロジェクトの立ち上げ（Windows / PowerShell 環境）
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.14&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TinyLLaMACpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">17&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Windows (MSVC) 向けの最適化とAVX2フラグの設定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MSVC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/O2&lt;/span> &lt;span class="s">/arch:AVX2&lt;/span> &lt;span class="s">/fp:fast&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/STACK:8388608&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">else&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-O3&lt;/span> &lt;span class="s">-march=native&lt;/span> &lt;span class="s">-ffast-math&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">OBJECT&lt;/span> &lt;span class="s">ggml/ggml.c&lt;/span> &lt;span class="s">ggml/ggml-alloc.c&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">GGML_USE_AVX2&lt;/span> &lt;span class="s">GGML_USE_F16C&lt;/span> &lt;span class="s">GGML_USE_FMA&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">ggml&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>PowerShellでのビルドコマンド例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-G&lt;/span> &lt;span class="s2">&amp;#34;Visual Studio 17 2022&amp;#34;&lt;/span> &lt;span class="n">-A&lt;/span> &lt;span class="n">x64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="10-まとめ">10. まとめ
&lt;/h2>&lt;p>C++とggmlを用いてTinyLLaMAのような小規模AIモデルの推論エンジンをゼロから実装することは、深層学習のブラックボックスを暴き、低レベルのハードウェア制御の美しさを学ぶ絶好の機会です。メモリマッピングを利用したゼロコピー・ロード、SIMD最適化、KVキャッシュの構築など、システムプログラミングのエッセンスを存分に味わいながら、エッジAIの未来を切り拓いていきましょう。&lt;/p></description></item><item><title>【RAG実装入門】ローカルAIに自分のドキュメントを読み込ませる方法</title><link>http://kenji.blog/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/p/rag-local-ai-implementation-guide/</guid><description>&lt;img src="http://kenji.blog/p/rag-local-ai-implementation-guide/img/eyecatch.jpg" alt="Featured image of post 【RAG実装入門】ローカルAIに自分のドキュメントを読み込ませる方法" />&lt;h1 id="はじめに">はじめに
&lt;/h1>&lt;p>近年、大規模言語モデル（LLM）の進化は目覚ましく、ChatGPTやClaudeなどを筆頭に多くのAIが私たちの生活や業務に浸透しています。しかし、一般的なLLMには明確な弱点が存在します。それは「学習時点での公開情報」しか知らないという点です。社内規程、個人的なメモ、未公開のプロジェクト資料といった「プライベートなドキュメント」に関する質問には、当然ながら答えることができません。無理に答えさせようとすると、事実とは異なるもっともらしい嘘（ハルシネーション）を生成してしまうリスクが高まります。&lt;/p>
&lt;p>そこで現在、世界中で爆発的に普及しているのが &lt;strong>RAG (Retrieval-Augmented Generation: 検索拡張生成)&lt;/strong> という技術アーキテクチャです。RAGを用いることで、LLMに独自の知識を外部データベースから動的に与え、それに基づいた正確で根拠のある回答を生成させることが可能になります。&lt;/p>
&lt;p>さらに、エンタープライズ領域や個人の機密情報を扱う場合、OpenAIなどのクラウドベースのAPIにデータを送信することは、セキュリティポリシー上許容されないことが多々あります。そこで求められるのが、&lt;strong>ローカルAI&lt;/strong>（自分のPCやオンプレミスサーバーで完結して動作するLLM）と組み合わせた「ローカルRAG」の構築です。&lt;/p>
&lt;p>本記事では、RAGの基礎理論から、Pythonを用いたローカルRAGの具体的な実装方法、数学的な背景（ベクトル検索の仕組み）、そしてシステムを本番稼働させるための高度なテクニックまでを、徹底的に解説します。&lt;/p>
&lt;hr>
&lt;h1 id="1-ragの全体アーキテクチャ">1. RAGの全体アーキテクチャ
&lt;/h1>&lt;p>RAGは単一のAIモデルではなく、複数のコンポーネントが連携するシステムアーキテクチャです。大きく分けて「インジェスト（データ取り込み）フェーズ」と「リトリーバル＆ジェネレーション（検索と生成）フェーズ」の2つから構成されます。&lt;/p>
&lt;p>以下のMermaid図は、RAGシステムの全体像を示しています。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "インジェストフェーズ (事前の準備)"
Doc["独自ドキュメント (PDF, TXT, etc.)"] --> Loader["ドキュメントローダー"]
Loader --> Splitter["テキスト分割 (チャンキング)"]
Splitter --> EmbedModel1["埋め込みモデル (Embedding)"]
EmbedModel1 --> VectorDB["ベクトルデータベース"]
end
subgraph "推論フェーズ (ユーザーの問い合わせ時)"
User["ユーザーからの質問 (クエリ)"] --> EmbedModel2["埋め込みモデル (Embedding)"]
EmbedModel2 --> QueryVector["クエリベクトル"]
QueryVector --> Search["類似度検索 (ベクトル検索)"]
VectorDB --> Search
Search --> Context["関連チャンク抽出 (コンテキスト)"]
User --> PromptBuilder["プロンプト構築"]
Context --> PromptBuilder
PromptBuilder --> LocalLLM["ローカルLLM"]
LocalLLM --> Answer["最終的な回答生成"]
end&lt;/div>
&lt;h2 id="インジェストフェーズ事前準備">インジェストフェーズ（事前準備）
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>ドキュメントの読み込み&lt;/strong>: PDF、Word、テキストファイルなどの非構造化データを読み込みます。&lt;/li>
&lt;li>&lt;strong>チャンキング（テキスト分割）&lt;/strong>: LLMの入力制限（コンテキストウィンドウ）に収めるため、そして検索精度を上げるために、長い文章を意味のある塊（チャンク）に分割します。&lt;/li>
&lt;li>&lt;strong>エンベディング（ベクトル化）&lt;/strong>: 分割されたチャンクを、埋め込みモデル（Embedding Model）に入力し、数百〜数千次元の数値の配列（ベクトル）に変換します。&lt;/li>
&lt;li>&lt;strong>データベースへの保存&lt;/strong>: 変換されたベクトルと、元のテキストデータを紐付けてベクトルデータベース（Vector DB）に保存します。&lt;/li>
&lt;/ol>
&lt;h2 id="推論フェーズ実行時">推論フェーズ（実行時）
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>クエリのベクトル化&lt;/strong>: ユーザーからの質問文を、事前準備と同じ埋め込みモデルを使ってベクトル化します。&lt;/li>
&lt;li>&lt;strong>類似度検索&lt;/strong>: クエリベクトルと、データベース内のドキュメントベクトルの間で類似度計算を行い、意味的に近い（関連性の高い）テキストチャンクを上位数件取得します。&lt;/li>
&lt;li>&lt;strong>プロンプトの構築&lt;/strong>: 取得した関連テキストを「コンテキスト（背景知識）」としてユーザーの質問文と結合し、LLMへの入力プロンプトを作成します。&lt;/li>
&lt;li>&lt;strong>回答の生成&lt;/strong>: 拡張されたプロンプトを受け取ったLLMが、付与されたコンテキスト情報を基にして回答を生成します。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-ベクトル検索と埋め込みembeddingsの深い理解">2. ベクトル検索と埋め込み（Embeddings）の深い理解
&lt;/h1>&lt;p>RAGの中核をなすのが「ベクトル検索（セマンティック検索）」です。従来のキーワード検索（BM25など）が単語の完全一致や頻度に基づくのに対し、ベクトル検索は「意味の類似性」に基づきます。例えば「犬」と「子犬」、「PC」と「パソコン」のように、異なる単語でも意味が近ければ検索にヒットします。&lt;/p>
&lt;h2 id="埋め込みモデルembedding-modelとは何か">埋め込みモデル（Embedding Model）とは何か？
&lt;/h2>&lt;p>埋め込みモデルは、自然言語のテキストを入力として受け取り、固定長の密なベクトル（Dense Vector）を出力するニューラルネットワークです。一般的なモデル（例えば &lt;code>text-embedding-3-small&lt;/code> やオープンソースの &lt;code>multilingual-e5-large&lt;/code>）は、テキストを384次元や1024次元の実数ベクトルにマッピングします。&lt;/p>
&lt;p>この多次元空間（潜在空間）においては、意味が似ている文章ほど、座標空間上の距離が近くなるように学習されています。&lt;/p>
&lt;h2 id="類似度計算の数学的背景コサイン類似度">類似度計算の数学的背景：コサイン類似度
&lt;/h2>&lt;p>ベクトルデータベースが関連ドキュメントを検索する際、最も一般的に用いられる距離指標が**コサイン類似度（Cosine Similarity）**です。ユークリッド距離（空間的な絶対距離）とは異なり、コサイン類似度は「2つのベクトルのなす角」に注目します。文章の長さ（ベクトルのノルム）に影響されにくいため、テキストの類似度計算に非常に適しています。&lt;/p>
&lt;p>数式で表すと、ベクトル $\mathbf{A}$ と $\mathbf{B}$ のコサイン類似度は以下のようになります。&lt;/p>
$$ \text{Cosine Similarity}(\mathbf{A}, \mathbf{B}) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} $$
&lt;ul>
&lt;li>$\mathbf{A} \cdot \mathbf{B}$ は内積（Dot Product）を表します。&lt;/li>
&lt;li>$\|\mathbf{A}\|$ はベクトル $\mathbf{A}$ のL2ノルム（長さ）を表します。&lt;/li>
&lt;li>$n$ はベクトルの次元数です。&lt;/li>
&lt;/ul>
&lt;p>コサイン類似度は -1 から 1 の値を取ります。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>1 に近い&lt;/strong>: 2つのベクトルの向きがほぼ同じ（意味が非常に似ている）&lt;/li>
&lt;li>&lt;strong>0 に近い&lt;/strong>: 2つのベクトルが直交している（無関係）&lt;/li>
&lt;li>&lt;strong>-1 に近い&lt;/strong>: 2つのベクトルが正反対の向き（意味が逆）&lt;/li>
&lt;/ul>
&lt;p>最近のベクトルDB（Chroma, FAISS, Qdrantなど）は、HNSW（Hierarchical Navigable Small World）と呼ばれる近似最近傍探索（ANN）アルゴリズムを採用しており、数百万件のベクトルデータからでもミリ秒単位でコサイン類似度が高いドキュメントを検索できるよう最適化されています。&lt;/p>
&lt;hr>
&lt;h1 id="3-ローカルragを構築するための技術スタック">3. ローカルRAGを構築するための技術スタック
&lt;/h1>&lt;p>クラウドに依存しない完全なローカルRAGを構築するには、オープンソースのエコシステムを活用します。以下に推奨される技術スタックを紹介します。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>言語モデル (LLM)&lt;/strong>
&lt;ul>
&lt;li>ツール: &lt;code>Ollama&lt;/code> または &lt;code>Llama.cpp&lt;/code>&lt;/li>
&lt;li>モデル: &lt;code>Llama-3-8B-Instruct&lt;/code>, &lt;code>Gemma-2-9B-It&lt;/code>, &lt;code>Qwen2-7B-Instruct&lt;/code> などの軽量・高性能なオープンモデル。日本語タスクには日本語チューンされた &lt;code>Llama-3-ELYZA-JP-8B&lt;/code> などが適しています。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>埋め込みモデル (Embedding)&lt;/strong>
&lt;ul>
&lt;li>モデル: &lt;code>intfloat/multilingual-e5-large&lt;/code> または &lt;code>BAAI/bge-m3&lt;/code>。ローカルで動かす場合、Hugging FaceからダウンロードしてSentence-Transformersで実行するのが一般的です。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>ベクトルデータベース (Vector DB)&lt;/strong>
&lt;ul>
&lt;li>&lt;code>ChromaDB&lt;/code>: Pythonベースでセットアップが極めて簡単。ローカル開発に最適。&lt;/li>
&lt;li>&lt;code>FAISS&lt;/code>: Metaが開発した高速なベクトル検索ライブラリ。&lt;/li>
&lt;li>&lt;code>Qdrant&lt;/code> / &lt;code>Milvus&lt;/code>: より大規模で本番環境向け。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>オーケストレーションフレームワーク&lt;/strong>
&lt;ul>
&lt;li>&lt;code>LangChain&lt;/code>: コンポーネントを繋ぎ合わせる（Chain）ためのデファクトスタンダード。&lt;/li>
&lt;li>&lt;code>LlamaIndex&lt;/code>: 特にRAGに特化したデータ接続フレームワーク。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>今回は、最も導入が簡単な &lt;strong>LangChain + ChromaDB + Ollama + HuggingFaceEmbeddings&lt;/strong> の組み合わせで実装します。&lt;/p>
&lt;hr>
&lt;h1 id="4-実装チュートリアルpythonによる完全なローカルrag構築">4. 実装チュートリアル：Pythonによる完全なローカルRAG構築
&lt;/h1>&lt;p>ここからは、実際にPythonコードを書きながらローカルRAGを構築していきます。あらかじめ、PCにOllamaをインストールし、バックグラウンドで起動しておいてください。また、Ollama上でモデルをプルしておきます（例：&lt;code>ollama run llama3&lt;/code>）。&lt;/p>
&lt;h2 id="step-1-必要なライブラリのインストール">Step 1: 必要なライブラリのインストール
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain langchain-community langchain-huggingface
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install chromadb sentence-transformers pypdf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="step-2-実装コード全体像">Step 2: 実装コード全体像
&lt;/h2>&lt;p>以下は、PDFファイルを読み込み、ベクトル化してローカルのLLMに質問応答させるための完全なPythonスクリプトです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.document_loaders&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_text_splitters&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_huggingface&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.llms&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Ollama&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. ドキュメントの読み込み&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ドキュメントを読み込んでいます...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 読み込ませたいPDFのパスを指定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sample_company_policy.pdf&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">loader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loader&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. チャンク分割（Text Splitting）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 文章の意味を壊さないように、適度なサイズに分割する&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text_splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 1チャンクあたりの最大文字数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_overlap&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 前後のチャンクでの重複文字数（文脈の断絶を防ぐ）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">separators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;、&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text_splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">個のチャンクに分割されました。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 埋め込みモデルの初期化 (Local HuggingFace Model)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 日本語に強い多言語モデルを使用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;埋め込みモデルをロードしています...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;intfloat/multilingual-e5-large&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;device&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;cpu&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># GPUがある場合は &amp;#39;cuda&amp;#39; または &amp;#39;mps&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. ベクトルデータベースの構築 (Chroma)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ベクトルデータベースを構築しています...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./chroma_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Chroma&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_documents&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">persist_directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 検索器（Retriever）の作成。上位3件の関連ドキュメントを取得するように設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. ローカルLLMの初期化 (Ollama)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ローカルLLMに接続しています...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 事前に &amp;#39;ollama pull llama3&amp;#39; などでモデルを取得しておくこと&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ollama&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;llama3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 6. プロンプトテンプレートの定義&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;あなたは会社の規程や社内情報に詳しい優秀なアシスタントです。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">以下のコンテキスト（背景情報）のみを使用して、ユーザーの質問に日本語で詳細に答えてください。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">コンテキストから答えが見つからない場合は、勝手に推測せず「提供された情報からは分かりません」と正直に答えてください。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【コンテキスト】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{context}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【質問】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【回答】:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PROMPT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">prompt_template&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;context&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 7. RAGチェーンの構築&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qa_chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_chain_type&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;stuff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">return_source_documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 情報源を返すか設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;prompt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">PROMPT&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 8. 質問の実行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;リモートワークに関する交通費の支給条件について教えてください。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">質問: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">qa_chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【回答】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;result&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【参照した情報源】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">doc&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;source_documents&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- ページ &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;不明&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="コードのポイント解説">コードのポイント解説
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>RecursiveCharacterTextSplitter&lt;/strong>:
自然言語の分割において最も推奨されるスプリッターです。段落(&lt;code>\n\n&lt;/code>)、行(&lt;code>\n&lt;/code>)、句点(&lt;code>。&lt;/code>)の順で分割を試み、意味のまとまりを可能な限り維持したまま指定の &lt;code>chunk_size&lt;/code> に収まるように分割します。&lt;code>chunk_overlap&lt;/code> を設定することで、文脈の境目が切れて情報が失われるのを防ぎます。&lt;/li>
&lt;li>&lt;strong>HuggingFaceEmbeddings&lt;/strong>:
&lt;code>intfloat/multilingual-e5-large&lt;/code> は多言語に対応した非常に強力なオープンソースの埋め込みモデルです。クラウドAPI（OpenAIの &lt;code>text-embedding-ada-002&lt;/code> など）を使わずに、オフラインでローカルメモリ上でテキストをベクトル化できます。&lt;/li>
&lt;li>&lt;strong>ChromaDB&lt;/strong>:
インメモリまたはローカルストレージ（SQLiteベース）で動作するため、複雑なデータベースサーバーの立ち上げが不要です。&lt;code>persist_directory&lt;/code> を指定することで、再実行時にベクトル化のプロセスをスキップし、ディスクからDBを読み込むことができます。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="5-発展的なrag手法advanced-rag-techniques">5. 発展的なRAG手法（Advanced RAG Techniques）
&lt;/h1>&lt;p>上記のチュートリアルで構築した基本のRAGシステム（Naive RAG）でも動作しますが、本番環境で高い回答精度を要求される場合、以下のような高度なテクニックの導入が必要になります。&lt;/p>
&lt;h2 id="51-ハイブリッド検索-hybrid-search">5.1 ハイブリッド検索 (Hybrid Search)
&lt;/h2>&lt;p>ベクトル検索は「意味」を捉えるのが得意ですが、「特定の固有名詞」「製品型番」「従業員ID」などの厳密なキーワード検索を苦手とすることがあります。
そこで、ベクトル検索による&lt;strong>セマンティック検索&lt;/strong>と、BM25アルゴリズムなどを用いた&lt;strong>キーワード検索&lt;/strong>を並行して行い、両者の結果をスコアリングして統合する（Reciprocal Rank Fusion; RRFなどの手法を用いる）ことで、検索漏れを劇的に減らすことができます。&lt;/p>
&lt;h2 id="52-リランキング-re-ranking">5.2 リランキング (Re-ranking)
&lt;/h2>&lt;p>ベクトル検索は高速ですが、必ずしもコンテキストの正確な文脈適合性を評価しているわけではありません。検索精度を向上させるための一般的なパイプラインは以下のようになります。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>初期検索 (First-stage Retrieval)&lt;/strong>: ベクトルDBから、広く浅く関連チャンクを20件〜30件程度取得します。&lt;/li>
&lt;li>&lt;strong>再評価 (Re-ranking)&lt;/strong>: Cross-Encoderと呼ばれる別のより重い機械学習モデル（例: &lt;code>bge-reranker&lt;/code> など）を使用して、ユーザーのクエリと取得したチャンクのペアを入力し、意味的適合度のスコアを再計算します。&lt;/li>
&lt;li>&lt;strong>選別&lt;/strong>: スコアの高い上位3〜5件のみを最終的なコンテキストとしてLLMのプロンプトに渡します。&lt;/li>
&lt;/ol>
&lt;p>この手法により、無関係なノイズ情報がLLMに渡るのを防ぎ、回答の精度（Precision）を大幅に高めることができます。&lt;/p>
&lt;div class="mermaid">graph LR
Query["クエリ"] --> VSearch["ベクトル検索 (上位20件)"]
VSearch --> Reranker["リランカーモデル (Cross-Encoder)"]
Query --> Reranker
Reranker --> TopK["高精度な上位3件"]
TopK --> LLM["LLM生成"]&lt;/div>
&lt;h2 id="53-セマンティックチャンキングと親ドキュメント検索">5.3 セマンティックチャンキングと親ドキュメント検索
&lt;/h2>&lt;p>固定文字数で機械的にテキストを分割するのではなく、文章の意味の変わり目をAIで検知して分割する「Semantic Chunking」という手法があります。
また、「Parent Document Retriever（親ドキュメント検索）」という手法では、検索用に非常に小さな単位（センテンス等）でベクトル化を行って精度の高い検索を実現しつつ、LLMに渡す際にはそのセンテンスが含まれる「元の大きな段落（親ドキュメント）」を渡すことで、LLMに十分な文脈（コンテキスト）を提供します。&lt;/p>
&lt;hr>
&lt;h1 id="6-ローカルrag運用時の課題と対策">6. ローカルRAG運用時の課題と対策
&lt;/h1>&lt;p>ローカル環境でRAGを構築・運用する際には、特有のハードルが存在します。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>VRAM（ビデオメモリ）の枯渇&lt;/strong>:
ローカルLLMを実用的な速度（1秒間に数十トークン）で動かすには、GPUのVRAMにモデルを載せる必要があります。8Bクラスのモデルをfp16（16ビット浮動小数点）で動かすには約16GBのVRAMが必要ですが、**量子化（Quantization）**技術（GGUFやAWQ形式など、4bitや8bitに圧縮する技術）を使うことで、8GBのVRAM（一般的なゲーミングPC等）でも十分に高速動作させることが可能です。Llama.cppやOllamaは標準でこれらの量子化フォーマットに対応しています。&lt;/li>
&lt;li>&lt;strong>コンテキストウィンドウの制限&lt;/strong>:
検索して取得したコンテキストの量が多すぎると、LLMの入力上限（トークンリミット）を超えてしまったり、モデルが情報の中間部分を忘れてしまう（Lost in the middle現象）ことがあります。抽出するチャンク数の調整や、前述のリランキング技術による厳選が不可欠です。&lt;/li>
&lt;li>&lt;strong>データの鮮度管理&lt;/strong>:
ソースドキュメントが更新された場合、ベクトルデータベース内の該当するドキュメントのベクトルも更新・削除（CRUD操作）する必要があります。ChromaDBではドキュメントIDベースでの更新をサポートしているため、ファイルのハッシュ値を管理し、差分だけを同期するバッチ処理を組むのが実践的です。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="まとめ">まとめ
&lt;/h1>&lt;p>RAG（検索拡張生成）は、AIを一般的な汎用アシスタントから「あなた専属の専門家」や「社内業務に特化したエキスパート」へと進化させる強力なパラダイムです。&lt;/p>
&lt;p>クラウドサービスを利用できない機密性の高い要件であっても、Ollama、LangChain、ChromaDBなどのオープンソースエコシステムを組み合わせることで、完全な「ローカルRAG」環境を比較的容易に構築できることが分かりました。&lt;/p>
&lt;p>本記事で解説したベクトル空間の数学的理解や、テキスト分割、リランキングといった発展的アプローチをベースに、ぜひご自身の保有するデータを使ってオリジナルなAIシステムを開発してみてください。ローカルAIの進化のスピードは驚異的であり、今日構築したシステムは、明日登場するさらに賢い軽量モデルに差し替えるだけで、一瞬にして性能をアップデートすることが可能なのです。&lt;/p>
&lt;hr>
&lt;p>&lt;em>本ブログでは今後もAI技術やRAGに関する深掘り記事を掲載していきます。ご質問やフィードバックがありましたら、ぜひコメント欄にお寄せください。&lt;/em>&lt;/p></description></item><item><title>ChatGPT・Gemini・ClaudeのAPI徹底比較！どれを選ぶべき？</title><link>http://kenji.blog/p/chatgpt-gemini-claude-api-comparison/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/p/chatgpt-gemini-claude-api-comparison/</guid><description>&lt;img src="http://kenji.blog/p/chatgpt-gemini-claude-api-comparison/img/eyecatch.jpg" alt="Featured image of post ChatGPT・Gemini・ClaudeのAPI徹底比較！どれを選ぶべき？" />&lt;h1 id="chatgptgeminiclaudeのapi徹底比較どれを選ぶべき">ChatGPT・Gemini・ClaudeのAPI徹底比較！どれを選ぶべき？
&lt;/h1>&lt;p>AI技術の進化は目覚ましく、特に大規模言語モデル（LLM: Large Language Model）の分野では、OpenAIのChatGPT（GPTシリーズ）、GoogleのGemini、AnthropicのClaudeが三つ巴の激しい覇権争いを繰り広げています。2026年現在、各社は数ヶ月、いや数週間単位で新しいモデルやAPI機能をリリースしており、開発者や企業のITアーキテクトにとって「どのAPIをプロダクトに組み込むべきか」という問いは、プロジェクトの成功を左右する極めて重要な意思決定となっています。&lt;/p>
&lt;p>本記事では、これら3大AIプロバイダーのAPIについて、単なるスペックの羅列にとどまらず、アーキテクチャ設計、詳細な料金構造、レイテンシ（遅延）の数学的分析、PythonおよびNode.jsによる具体的な実装例、プロンプトキャッシュなどの最新のコスト最適化手法に至るまで、開発者の視点から徹底的に比較・解説します。&lt;/p>
&lt;p>読者の皆様が、自身のユースケースに最適なLLM APIを選定し、スケーラブルでコスト効率の高いAIアプリケーションを構築するための完全なガイドとなることを目指しています。&lt;/p>
&lt;hr>
&lt;h2 id="1-各llm-apiの哲学と設計思想">1. 各LLM APIの哲学と設計思想
&lt;/h2>&lt;p>技術選定において、まず各社がどのような思想でモデルとAPIを構築しているかを理解することは非常に重要です。&lt;/p>
&lt;h3 id="11-openai-chatgpt">1.1 OpenAI (ChatGPT)
&lt;/h3>&lt;p>OpenAIは、「汎用人工知能（AGI）の実現」をミッションに掲げ、常に業界のデファクトスタンダードを牽引しています。GPT-4oやGPT-4o-mini、そして推論特化型のo1モデルなど、ユースケースに合わせた多様なモデルを提供しています。エコシステムが最も成熟しており、公式・非公式問わずライブラリやドキュメントが最も豊富です。&lt;/p>
&lt;h3 id="12-google-gemini">1.2 Google (Gemini)
&lt;/h3>&lt;p>Googleは「AI First」を掲げ、自社のインフラ（TPUネットワーク）を最大限に活用したスケーラビリティを武器にしています。Gemini 1.5 Pro/Flashは、最大200万トークンという圧倒的なコンテキストウィンドウ（文脈長）を持ち、長大なドキュメントや数時間の動画・音声を一度に処理できるのが最大の特徴です。Google Cloud (Vertex AI) との強力な統合もエンタープライズにとって魅力的です。&lt;/p>
&lt;h3 id="13-anthropic-claude">1.3 Anthropic (Claude)
&lt;/h3>&lt;p>Anthropicは、元OpenAIのメンバーが設立した企業であり、「Constitutional AI（合憲的AI）」という独自の安全性アプローチを採用しています。Claude 3.5 SonnetやOpusは、その高い推論能力、コード生成能力、そして何より「人間らしい自然な対話」と「ハルシネーション（幻覚）の少なさ」で、多くの開発者から熱狂的な支持を集めています。&lt;/p>
&lt;hr>
&lt;h2 id="2-モデルファミリーのスペック徹底比較">2. モデルファミリーのスペック徹底比較
&lt;/h2>&lt;p>2026年現在の主力モデルのスペックを比較します。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>プロバイダー&lt;/th>
&lt;th>主力モデル&lt;/th>
&lt;th>最大コンテキスト長&lt;/th>
&lt;th>主な強み&lt;/th>
&lt;th>推奨ユースケース&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>GPT-4o&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>スピード、視覚認識、音声対応&lt;/td>
&lt;td>インタラクティブなアプリ、汎用タスク&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>o1-preview&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>高度な論理推論、数学、コーディング&lt;/td>
&lt;td>複雑なアルゴリズム生成、研究用途&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Pro&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>超長文処理、マルチモーダル（動画・音声）&lt;/td>
&lt;td>巨大なコードベースの解析、動画要約&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Flash&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>低遅延、高スループット、圧倒的低コスト&lt;/td>
&lt;td>リアルタイム処理、大量データの一括処理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Sonnet&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>コーディング能力、自然な文章生成&lt;/td>
&lt;td>ソフトウェア開発支援、高度なカスタマーサポート&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Haiku&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>超高速応答、コストパフォーマンス&lt;/td>
&lt;td>エッジAI、リアルタイムチャットボット&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="3-アーキテクチャの深堀りapiリクエストの裏側">3. アーキテクチャの深堀り：APIリクエストの裏側
&lt;/h2>&lt;p>LLMのAPIをコールした際、バックエンドではどのような処理が行われているのでしょうか。パフォーマンスを最適化するためには、このアーキテクチャを理解する必要があります。&lt;/p>
&lt;p>以下のMermaidダイアグラムは、クライアントからAPIリクエストが送信され、トークンがストリーミングで返されるまでの全体像を示しています。&lt;/p>
&lt;div class="mermaid">graph TD
A["Client Application"] -->|HTTP/REST or gRPC| B["API Gateway"]
B --> C["Load Balancer"]
C --> D["Inference Cluster"]
D --> E["Tokenizer (BPE / SentencePiece)"]
E --> F["KV Cache &amp; Attention Mechanism"]
F --> G["Transformer Blocks (Forward Pass)"]
G --> H["Output Layer (Logits)"]
H --> I["Sampler (Temperature, Top-p, Top-k)"]
I --> J["Detokenizer"]
J -->|Streaming Response (Chunk)| A&lt;/div>
&lt;h3 id="31-tokenizationトークン化のアルゴリズム">3.1 Tokenization（トークン化）のアルゴリズム
&lt;/h3>&lt;p>APIに入力されたテキストは、内部で「トークン」という単位に分割されます。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI (tiktoken)&lt;/strong>: Byte-Pair Encoding (BPE) を採用。特に英語において極めて効率的に圧縮されますが、日本語などの非アルファベット言語ではトークン数が膨らみがちです。&lt;/li>
&lt;li>&lt;strong>Google (Gemini)&lt;/strong>: SentencePiece（Unigram Language Model）を採用。多言語に強く、日本語のテキストでも比較的少ないトークン数で表現できる傾向があります。&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude)&lt;/strong>: BPEのカスタマイズ版を使用。多言語対応が強化されており、Claude 3以降は日本語のトークン効率も大幅に改善されています。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-レイテンシとパフォーマンスの数学的分析">4. レイテンシとパフォーマンスの数学的分析
&lt;/h2>&lt;p>リアルタイムアプリケーションにおいて、レイテンシはユーザー体験（UX）に直結します。LLMのAPIのレイテンシ $T_{total}$ は、数学的に以下のようにモデル化できます。&lt;/p>
$$ T_{total} = T_{network} + T_{TTFT} + (N \times T_{TPOT}) $$
&lt;p>ここで、各変数は以下の意味を持ちます。&lt;/p>
&lt;ul>
&lt;li>$T_{network}$: ネットワークのラウンドトリップタイム（RTT）。&lt;/li>
&lt;li>$T_{TTFT}$ (Time To First Token): 最初の文字が生成されるまでの時間。プロンプトの長さ（入力トークン数）の二乗に比例するアテンション計算のコストに大きく依存します。&lt;/li>
&lt;li>$N$: 出力されるトークンの総数。&lt;/li>
&lt;li>$T_{TPOT}$ (Time Per Output Token): 1トークンあたりの生成時間。自己回帰モデルであるため、前の出力に依存して直列に計算されます。&lt;/li>
&lt;/ul>
&lt;h3 id="41-自己アテンション機構の計算量">4.1 自己アテンション機構の計算量
&lt;/h3>&lt;p>Transformerアーキテクチャにおける自己アテンション（Self-Attention）の計算量は、入力シーケンス長 $L$ に対して二次関数的に増加します。&lt;/p>
$$ \text{Complexity} = O(L^2 \cdot d) $$
&lt;p>ここで $d$ は埋め込みベクトルの次元数です。この制約のため、通常はプロンプトが長くなると $T_{TTFT}$ が急激に悪化します。
しかし、GoogleのGemini 1.5は「Ring Attention」や「Block-wise Compute」といった革新的な最適化アーキテクチャを採用しており、200万トークンという長文を入力しても、現実的な時間（数秒〜数十秒）で最初のトークンを生成することに成功しています。&lt;/p>
&lt;hr>
&lt;h2 id="5-料金体系とコスト最適化の戦略">5. 料金体系とコスト最適化の戦略
&lt;/h2>&lt;p>APIのコストは、基本的に入力トークン数と出力トークン数に基づいて計算されます。&lt;/p>
$$ Cost = (Tokens_{in} \times Rate_{in}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>しかし、最新のAPIではコストを劇的に下げるための新しい仕組みが導入されています。&lt;/p>
&lt;h3 id="51-プロンプトキャッシュ-prompt-caching">5.1 プロンプトキャッシュ (Prompt Caching)
&lt;/h3>&lt;p>長大なシステムプロンプトや、RAGで検索した大量のドキュメントを毎回送信すると莫大なコストがかかります。これに対処するため、各社はキャッシュ機能を提供しています。&lt;/p>
&lt;p>Anthropic（Claude）やGoogle（Gemini）では、特定のテキストブロックをキャッシュすることで、入力コストを大幅に（最大90%）削減できます。&lt;/p>
&lt;p>キャッシュを利用した場合のコストモデルは以下のようになります。&lt;/p>
$$ Cost_{cached} = (Tokens_{cache\_write} \times Rate_{cache\_write}) + (Tokens_{cache\_read} \times Rate_{cache\_read}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>ここで $Rate_{cache\_read}$ は通常の $Rate_{in}$ の10%〜25%程度に設定されています。これにより、数万行のコードベースを背景知識として常に保持させながら、安価にチャットボットを運用することが可能になりました。&lt;/p>
&lt;h3 id="52-バッチapi-batch-api">5.2 バッチAPI (Batch API)
&lt;/h3>&lt;p>リアルタイム性が不要なタスク（ログ分析、大量のデータ分類など）向けに、OpenAIやAnthropicはバッチAPIを提供しています。リクエストをまとめて送信し、24時間以内に結果を受け取る代わりに、通常のAPI料金の半額（50%オフ）で利用できる強力な仕組みです。&lt;/p>
&lt;hr>
&lt;h2 id="6-開発者体験dxとsdkの比較">6. 開発者体験（DX）とSDKの比較
&lt;/h2>&lt;p>開発効率の観点から、各社の提供するSDK（Software Development Kit）を比較します。&lt;/p>
&lt;h3 id="61-openai-api">6.1 OpenAI API
&lt;/h3>&lt;p>最も広く使われており、サードパーティ製ライブラリ（LangChain, LlamaIndexなど）の対応も最速です。また、Structured Outputs（構造化出力）機能により、JSONスキーマを100%の精度で遵守したレスポンスを返すことが保証されており、システム連携が非常に容易です。&lt;/p>
&lt;h3 id="62-anthropic-api-claude">6.2 Anthropic API (Claude)
&lt;/h3>&lt;p>SDKのインターフェースが洗練されており、TypeScriptの型定義などが非常に扱いやすいと評判です。特にMessage APIの構造が直感的で、複数画像を含めたマルチモーダルリクエストもシンプルに記述できます。&lt;/p>
&lt;h3 id="63-google-gemini-api">6.3 Google Gemini API
&lt;/h3>&lt;p>Google Cloud Vertex AI経由のアクセスと、AI Studio経由のアクセス（Google Gen AI SDK）の2種類が存在し、初心者は少し混乱するかもしれません。しかし、エンタープライズ向けのVertex AI SDKは、GCPのIAM（認証認可システム）と完全に統合されており、セキュアな開発環境を構築できます。&lt;/p>
&lt;hr>
&lt;h2 id="7-実践pythonによる複数apiの統合テスト実装">7. 実践！Pythonによる複数APIの統合テスト実装
&lt;/h2>&lt;p>ここでは、Pythonを使用して、OpenAI, Anthropic, Geminiの3つのAPIに対して同時に非同期リクエストを送信し、レイテンシを比較するスクpromptリプトを実装してみます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">asyncio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">anthropic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">google.generativeai&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">genai&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># クライアントの初期化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">openai_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;OPENAI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">anthropic_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ANTHROPIC_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;GEMINI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;量子コンピュータの基礎と、それが現在の暗号技術に与える影響について、初心者向けに分かりやすく解説してください。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_openai&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">openai_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;OpenAI (GPT-4o)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_anthropic&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">anthropic_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;claude-3-5-sonnet-20240620&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Anthropic (Claude 3.5 Sonnet)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_gemini&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GenerativeModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gemini-1.5-pro&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Gemini Python SDKの非同期メソッドを利用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_content_async&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Google (Gemini 1.5 Pro)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;各LLM APIへリクエストを送信中...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3つのAPIを並列で実行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gather&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_openai&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_anthropic&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_gemini&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">latency&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;--- &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">provider&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Latency: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">latency&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.2f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> seconds&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Response (Excerpt): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>このスクリプトを実行することで、実際のネットワーク環境においてどのモデルが最も高速に（$T_{total}$を最小化して）応答するかを簡単に計測できます。&lt;/p>
&lt;hr>
&lt;h2 id="8-nodejsによるtool-callingfunction-calling実装">8. Node.jsによるTool Calling（Function Calling）実装
&lt;/h2>&lt;p>LLMを単なるチャットボットではなく、外部システムと連携する「AIエージェント」として機能させるためには、Tool Calling（またはFunction Calling）が不可欠です。以下はNode.js（TypeScript）を使用して、OpenAIのAPIに天気APIを呼び出させる例です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">OpenAI&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;openai&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">openai&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">OpenAI&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiKey&lt;/span>: &lt;span class="kt">process.env.OPENAI_API_KEY&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">runAgent() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;get_weather&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;指定された都市の現在の天気を取得します。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">parameters&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;object&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">properties&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;string&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;都市名（例: 東京, ニューヨーク）&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">required&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;location&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">openai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">chat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">completions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">model&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">messages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nx">role&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;今日の東京の天気はどう？傘は必要かな？&amp;#34;&lt;/span> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tools&lt;/span>: &lt;span class="kt">tools&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tool_choice&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">toolCall&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`LLMがツール呼び出しを要求しました: 関数名 = &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`引数: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ここで実際の天気API（例: OpenWeatherMap）を呼び出す処理を実装します
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// const weather = await fetchWeatherFromAPI(args.location);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 取得した結果を再度LLMに渡して最終的な回答を生成させます
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">runAgent&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Claude 3.5 SonnetやGemini 1.5 Proも同等のTool Calling機能を備えており、スキーマの定義方法に若干の違いはあるものの、基本的なフローは共通しています。&lt;/p>
&lt;hr>
&lt;h2 id="9-rag-vs-long-context-window-どちらを採用すべきか">9. RAG vs Long Context Window: どちらを採用すべきか？
&lt;/h2>&lt;p>現在、エンタープライズAIアーキテクチャにおける最大の議論の一つが「外部知識を取り込むために、RAG（検索拡張生成）を使うべきか、それとも巨大なコンテキストウィンドウ（Long Context）にすべて丸投げすべきか」という問題です。&lt;/p>
&lt;h3 id="rag-retrieval-augmented-generation-の利点と課題">RAG (Retrieval-Augmented Generation) の利点と課題
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>利点&lt;/strong>: コストが安い（必要なチャンクだけをプロンプトに入れるため）、回答の根拠（ソース）を特定しやすい。&lt;/li>
&lt;li>&lt;strong>課題&lt;/strong>: セマンティック検索の精度に依存するため、文脈が複数の文書に散らばっている高度な推論タスク（例：「昨年の全議事録から、Aプロジェクトが遅延した根本原因を時系列で分析して」）には適していません。&lt;/li>
&lt;/ul>
&lt;h3 id="long-context-gemini-15-proの200万トークンなど">Long Context (Gemini 1.5 Proの200万トークンなど)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>利点&lt;/strong>: 検索による情報の取りこぼしがない。「干し草の山から針を探す（Needle In A Haystack: NIAH）」テストにおいても、Gemini 1.5 ProやClaude 3.5 Sonnetは99%以上の精度で情報を抽出できます。&lt;/li>
&lt;li>&lt;strong>課題&lt;/strong>: トークン消費量が膨大になりコストがかさむ、レイテンシ（$T_{TTFT}$）が増加する。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>結論&lt;/strong>: 2026年のベストプラクティスは**「ハイブリッド・アプローチ」**です。日常的なQ&amp;amp;Aにはベクトルデータベースを用いたRAGを使用し、複雑な分析やコード全体のレビューが必要な特化したタスクには、プロンプトキャッシュを活用したLong Contextを用いる設計が主流となっています。&lt;/p>
&lt;hr>
&lt;h2 id="10-マルチモーダル処理能力の比較">10. マルチモーダル処理能力の比較
&lt;/h2>&lt;p>次世代のAIアプリケーションでは、テキストだけでなく、画像、音声、動画を直接理解する能力が求められます。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "User"
participant Client as "Frontend App"
participant API as "LLM API (Multimodal)"
User->>Client: Upload Video &amp; Text Prompt
Client->>API: Send Video Bytes/URI + Text
Note over API: Video chunking &amp; Audio separation
Note over API: Multimodal Embedding Model
API-->>Client: Return Text Summary &amp; Timestamps
Client-->>User: Display Insights&lt;/div>
&lt;ul>
&lt;li>&lt;strong>OpenAI (GPT-4o)&lt;/strong>: 画像認識精度が極めて高く、手書きの図面や複雑なグラフの読み取りに優れます。また、Realtime APIを利用した超低遅延（数百ミリ秒）のネイティブな音声対話も強力です。&lt;/li>
&lt;li>&lt;strong>Google (Gemini 1.5 Pro)&lt;/strong>: &lt;strong>動画解析において他を圧倒しています。&lt;/strong> 1時間の動画ファイル（フレーム群＋音声）をそのまま入力し、「12分45秒で画面右端に映った人物が持っている資料のタイトルは？」といったピンポイントな質問に回答可能です。&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude 3.5 Sonnet)&lt;/strong>: 画像認識（Vision）能力はGPT-4oと同等レベルで非常に優秀です。UIのスクリーンショットを渡して「この画面のReactコンポーネントコードを生成して」といったフロントエンド開発支援において無類の強さを発揮します。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-エンタープライズレベルのセキュリティとコンプライアンス">11. エンタープライズレベルのセキュリティとコンプライアンス
&lt;/h2>&lt;p>企業がLLM APIを本番環境で利用する際、最も懸念されるのが「自社のデータがAIの学習に使われないか」「コンプライアンス要件を満たしているか」という点です。&lt;/p>
&lt;p>3社とも、API経由で送信されたデータ（プロンプトおよびレスポンス）を&lt;strong>モデルの学習に使用しない（Zero Data Retention / No Training on Customer Data）&lt;/strong> ことを明言しています（※無料のコンシューマ向けWebチャットUIは別です）。&lt;/p>
&lt;p>さらに高いセキュリティレベルが求められる場合：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI&lt;/strong>: Azure OpenAI Serviceを経由することで、Microsoftのエンタープライズグレードのセキュリティ、SLA、Azure Private Linkによる閉域網接続を利用できます。&lt;/li>
&lt;li>&lt;strong>Google&lt;/strong>: Google Cloud Vertex AIを経由することで、VPC Service Controlsを利用した厳密なネットワーク分離や、CMEK（顧客管理の暗号鍵）によるデータ保護が可能です。&lt;/li>
&lt;li>&lt;strong>Anthropic&lt;/strong>: AWS BedrockまたはGoogle Cloud Vertex AI経由で利用することで、クラウドプロバイダーの堅牢なセキュリティ基盤に相乗りできます。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="12-結論ユースケース別究極の選択ガイド">12. 結論：ユースケース別、究極の選択ガイド
&lt;/h2>&lt;p>ここまで多角的に比較してきましたが、最終的に「どれを選ぶべきか？」に対する結論は、ユースケースによって異なります。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>複雑なソフトウェア開発・コード生成・高度な推論&lt;/strong>:
&lt;strong>👑 勝者: Claude 3.5 Sonnet (Anthropic)&lt;/strong>
コードの文脈理解、リファクタリング、自然で人間らしい文章の作成において、現在最高のパフォーマンスを発揮します。APIの使いやすさとプロンプトキャッシュによるコスト効率も抜群です。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>超長文のドキュメント解析・動画/音声の一括処理&lt;/strong>:
&lt;strong>👑 勝者: Gemini 1.5 Pro (Google)&lt;/strong>
200万トークンのコンテキストウィンドウは唯一無二の武器です。数百ページのPDFマニュアルの解析や、長時間の会議録画の要約など、データの全体像を把握する必要があるタスクではGeminiの右に出るものはありません。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>汎用性・実行スピード・安定した構造化出力（JSON）&lt;/strong>:
&lt;strong>👑 勝者: GPT-4o / GPT-4o-mini (OpenAI)&lt;/strong>
あらゆるタスクをそつなくこなし、サードパーティのツール対応も最も豊富です。Structured Outputsを利用した確実なJSONパースや、o1モデルを用いた超高度な論理推論が必要な場合はOpenAIエコシステムが不可欠です。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="マルチモデルルーティングのすすめ">マルチモデル・ルーティングのすすめ
&lt;/h3>&lt;p>単一のAPIに依存（ベンダーロックイン）するのではなく、タスクの難易度や重要度に応じてモデルを動的に切り替える**「LLMルーティング」**アーキテクチャが今後のトレンドです。
例えば、ユーザーからの単純な質問には安価で高速な &lt;code>GPT-4o-mini&lt;/code> や &lt;code>Gemini 1.5 Flash&lt;/code> で応答し、複雑な処理が必要と判断された場合のみ &lt;code>Claude 3.5 Sonnet&lt;/code> にタスクをフォールバックさせることで、コストとパフォーマンスの最適なバランスを実現できます。&lt;/p>
&lt;p>AIの進化は止まりません。各APIの強みと弱み、そしてアーキテクチャの特性を深く理解し、柔軟でスケーラブルなAIアプリケーションを構築してください。&lt;/p></description></item><item><title>llama.cppの使い方とC++でのカスタマイズ入門</title><link>http://kenji.blog/p/llama-cpp-cxx-customization/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/p/llama-cpp-cxx-customization/</guid><description>&lt;img src="http://kenji.blog/p/llama-cpp-cxx-customization/img/eyecatch.jpg" alt="Featured image of post llama.cppの使い方とC++でのカスタマイズ入門" />&lt;p>近年、大規模言語モデル (LLM) の進化は凄まじく、その応用範囲は日々拡大しています。しかし、数十億、数百億のパラメータを持つモデルをローカル環境で動かすには、通常、膨大なVRAMを持つハイエンドなGPUが必要となります。この「ハードウェアの壁」を打ち破り、一般的なPCやMac、さらにはRaspberry Piのようなデバイス上でLLMの実用的な推論を可能にしたのが、&lt;strong>llama.cpp&lt;/strong> です。&lt;/p>
&lt;p>本記事では、単なるコマンドラインツールの使い方にとどまらず、その基盤技術である &lt;code>ggml&lt;/code> のアーキテクチャ、Transformerや量子化の数学的背景、そして C++ API を利用して独自アプリケーションにLLMを組み込み、カスタマイズするための方法まで、エンジニア向けに極めて詳細に解説します。&lt;/p>
&lt;hr>
&lt;h2 id="1-llamacpp-と-ggml-の概要">1. llama.cpp と ggml の概要
&lt;/h2>&lt;p>&lt;code>llama.cpp&lt;/code> は、Georgi Gerganov氏によって開発された、C/C++ で記述された軽量なLLM推論エンジンです。元々は Meta の LLaMA モデルを Apple Silicon (M1/M2 Mac) 上で高速に動作させることを目的として誕生しましたが、現在では様々なアーキテクチャやモデルをサポートしています。&lt;/p>
&lt;p>最大の特徴は、&lt;strong>外部依存関係を持たない純粋な C/C++ 実装&lt;/strong> である点です。Python や PyTorch などの巨大なエコシステムを必要とせず、単一の実行ファイルとしてコンパイルできるため、デプロイが非常に容易です。&lt;/p>
&lt;p>この &lt;code>llama.cpp&lt;/code> の心臓部となっているのが、テンソル演算ライブラリ &lt;strong>ggml&lt;/strong> です。ggmlは機械学習における行列演算をCPU（および一部GPU）上で極限まで最適化するためにゼロから設計されています。&lt;/p>
&lt;h3 id="11-なぜ-llamacpp-は速いのか">1.1 なぜ llama.cpp は速いのか？
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>メモリマッピング (mmap) の活用&lt;/strong>: モデルの重みをメモリにロードする際、OSの &lt;code>mmap&lt;/code> を利用することで、RAMへの全ロードを回避し、高速な起動と省メモリを実現します。&lt;/li>
&lt;li>&lt;strong>SIMD命令の徹底的な最適化&lt;/strong>: AVX2, AVX-512, ARM NEON, Apple AMX などのCPU固有の命令セットを活用し、行列積を超高速化しています。&lt;/li>
&lt;li>&lt;strong>量子化 (Quantization)&lt;/strong>: 16-bit 浮動小数点数 (FP16) の重みを 4-bit, 5-bit, 8-bit の整数に圧縮し、メモリ帯域幅のボトルネックを解消します（詳細は後述）。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-数学的背景-transformer-と量子化-quantization">2. 数学的背景: Transformer と量子化 (Quantization)
&lt;/h2>&lt;p>llama.cpp を深く理解するためには、それが計算している数式と、どのように計算を近似しているかを知る必要があります。&lt;/p>
&lt;h3 id="21-transformer-の推論プロセス">2.1 Transformer の推論プロセス
&lt;/h3>&lt;p>LLaMA などのモデルは、自己回帰型 (Auto-regressive) の Transformer デコーダアーキテクチャを採用しています。テキスト生成の核となるのは &lt;strong>Self-Attention&lt;/strong> 機構です。&lt;/p>
&lt;p>入力となる隠れ状態の行列 $X \in \mathbb{R}^{N \times d}$ に対して、クエリ $Q$、キー $K$、バリュー $V$ は重み行列との積で計算されます。&lt;/p>
$$
Q = X W_Q, \quad K = X W_K, \quad V = X W_V
$$
&lt;p>ここで、Attention の出力は次のように定義されます。&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>llama.cpp の推論ループにおいて、ボトルネックとなるのはこの巨大な行列 $W_Q, W_K, W_V$ やフィードフォワードネットワーク (FFN) の重み行列とベクトル $X$（生成フェーズでは1トークンずつ処理するため $N=1$）の積、つまり &lt;strong>GEMV (General Matrix-Vector Multiplication)&lt;/strong> です。&lt;/p>
&lt;h3 id="22-量子化-quantization-の数学的基礎">2.2 量子化 (Quantization) の数学的基礎
&lt;/h3>&lt;p>メモリアクセス帯域がボトルネックとなる推論において、重みパラメータを小さなビット数で表現する量子化は不可欠です。llama.cpp で広く使われているブロック単位の量子化（例えば &lt;code>Q4_K&lt;/code> や &lt;code>Q4_0&lt;/code>）の基本原理を説明します。&lt;/p>
&lt;p>例えば FP16 の重み行列 $W$ の一部である長さ $B$（通常 32 や 64）のブロック $w = [w_1, w_2, \dots, w_B]$ を考えます。このブロックを 4-bit 整数 $q_i \in [-8, 7]$ と、単一のスケーリングファクタ $\Delta$（FP16 または FP32）に近似します。&lt;/p>
$$
w_i \approx \Delta \times q_i
$$
&lt;p>$\Delta$ はブロック内の最大絶対値に基づいて決定されます。&lt;/p>
$$
\Delta = \frac{\max_i |w_i|}{7}
$$
&lt;p>量子化後の重みを用いてドット積 $y = w \cdot x$ を計算する場合、入力ベクトル $x$ も同様に量子化して $x_i \approx \Delta_x \times q_{x, i}$ とすると、&lt;/p>
$$
y = \sum_{i=1}^{B} w_i x_i \approx \Delta \Delta_x \sum_{i=1}^{B} q_i q_{x, i}
$$
&lt;p>この $\sum q_i q_{x, i}$ の部分は &lt;strong>純粋な整数演算&lt;/strong> となり、SIMD 命令を用いて非常に高速に並列計算できます。これが llama.cpp が CPU 上で驚異的な速度を叩き出す数学的なカラクリです。&lt;/p>
&lt;hr>
&lt;h2 id="3-アーキテクチャと推論フロー">3. アーキテクチャと推論フロー
&lt;/h2>&lt;p>llama.cpp の内部動作を理解するために、以下の Mermaid ダイアグラムでシステム全体のアーキテクチャとデータの流れを示します。&lt;/p>
&lt;div class="mermaid">graph TD
A["User Input (String)"] --> B["llama.cpp Tokenizer"]
B --> C["Token IDs (int32 array)"]
C --> D["Context Buffer (KV Cache)"]
D --> E["ggml Compute Graph"]
E --> F["Transformer Layers"]
subgraph "ggml Engine"
F --> G["Self-Attention (RoPE)"]
G --> H["Feed Forward Network"]
H --> F
end
F --> I["Logits (Vocabulary Size)"]
I --> J["Sampler (Temperature, Top-K, Top-P)"]
J --> K["Selected Token ID"]
K --> L["llama.cpp Detokenizer"]
L --> M["Output String"]
K -. "Auto-regressive loop" .-> D&lt;/div>
&lt;p>テキスト生成は、1つのトークンが出力されるたびに、それが次の入力として KV Cache に追加され、再び計算グラフを通過する自己回帰的なループになっています。&lt;/p>
&lt;hr>
&lt;h2 id="4-環境構築とビルド方法">4. 環境構築とビルド方法
&lt;/h2>&lt;p>llama.cpp を C++ のプロジェクトに組み込む前に、まずはソースコードをビルドしてみましょう。&lt;/p>
&lt;h3 id="41-リポジトリのクローン">4.1 リポジトリのクローン
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/ggerganov/llama.cpp.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> llama.cpp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="42-cmake-を使用したビルド">4.2 CMake を使用したビルド
&lt;/h3>&lt;p>C++ プロジェクトとして他のアプリに組み込む場合、CMake を利用するのが最も標準的です。プラットフォームごとのアクセラレータ（バックエンド）を有効化することで、計算を高速化できます。&lt;/p>
&lt;p>&lt;strong>CPU のみ（基本的なビルド）:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>NVIDIA GPU (CUDA) を使用する場合:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_CUDA&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Apple Silicon (Metal) を使用する場合:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_METAL&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>ビルドが成功すると、&lt;code>build/bin/&lt;/code> ディレクトリに &lt;code>llama-cli&lt;/code> などの実行ファイルと、後述の C++ API でリンクするための &lt;code>llama&lt;/code> ライブラリ（および &lt;code>ggml&lt;/code> ライブラリ）が生成されます。&lt;/p>
&lt;hr>
&lt;h2 id="5-c-でのカスタマイズ入門-llamacpp-api-の利用">5. C++ でのカスタマイズ入門: llama.cpp API の利用
&lt;/h2>&lt;p>ここからは、本題である C++ コードからの llama.cpp の制御について解説します。
コマンドラインツールを使うだけでなく、自分のアプリケーション（例えばゲームエンジン、デスクトップアプリ、組み込みシステムなど）に LLM を組み込むためには、C++ API を直接叩く必要があります。&lt;/p>
&lt;p>llama.cpp は主に &lt;code>llama.h&lt;/code> というヘッダーファイルでC言語インターフェースを提供しています。C++ から呼び出す場合もこのインターフェースを利用します。&lt;/p>
&lt;h3 id="51-必要最小限のインクルードと設定">5.1 必要最小限のインクルードと設定
&lt;/h3>&lt;p>自分のプロジェクトで llama.cpp を使う場合、以下をインクルードします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;llama.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// エラーハンドリング用のマクロ
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define LLAMA_ASSERT(x) \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> do { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> if (!(x)) { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::cerr &amp;lt;&amp;lt; &amp;#34;Assertion failed: &amp;#34; &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; std::endl; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::terminate(); \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } while (0)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-モデルのロードとコンテキストの初期化">5.2 モデルのロードとコンテキストの初期化
&lt;/h3>&lt;p>まず、&lt;code>.gguf&lt;/code> 形式のモデルファイルをロードし、推論のためのコンテキスト（メモリ空間とKVキャッシュ）を確保します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">argc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">**&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">argc&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Usage: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; &amp;lt;model.gguf&amp;gt;&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">model_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. バックエンドの初期化（CPU/GPU等の環境セットアップ）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_backend_init&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. モデルパラメータのデフォルト設定を取得
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model_params&lt;/span> &lt;span class="n">model_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_model_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_gpu_layers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">35&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// GPUにオフロードするレイヤー数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. モデルのロード
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_load_model_from_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">model_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to load model&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. コンテキストパラメータの設定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context_params&lt;/span> &lt;span class="n">ctx_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_context_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2048&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 最大コンテキストサイズ (トークン数)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_threads&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 推論に使用するCPUスレッド数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. コンテキストの作成
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_new_context_with_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to create context&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Model and context loaded successfully!&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... 以降の処理
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="53-プロンプトのトークン化-tokenization">5.3 プロンプトのトークン化 (Tokenization)
&lt;/h3>&lt;p>LLM はテキストを直接理解するのではなく、整数の ID（トークン）の羅列として処理します。入力文字列をトークンに変換する必要があります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Q: 日本の首都はどこですか？&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">A:&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 余裕を持たせたバッファサイズ
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 特殊トークン（BOS: Begin of Sequence 等）を先頭に追加するかどうか
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">add_special&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 文字列をトークンIDの配列に変換
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_tokenize&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">add_special&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">false&lt;/span> &lt;span class="c1">// parse_special
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// バッファが足りない場合は再割り当てしてリトライする処理が必要（簡略化のため省略）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to tokenize prompt&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="54-推論ループとサンプリング">5.4 推論ループとサンプリング
&lt;/h3>&lt;p>トークンをモデルに入力し、次のトークンの確率分布（Logits）を取得し、そこからサンプリングを行って次のトークンを決定するループを構築します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 生成する最大トークン数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// バッチ評価のための構造体を初期化
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch&lt;/span> &lt;span class="n">batch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_batch_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">512&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// プロンプトのトークンをバッチに追加
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// プロンプトの最後のトークンでのみロジット（予測結果）を出力するように設定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 初回の評価（プロンプトをモデルに食わせる）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;llama_decode() failed&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 現在のコンテキスト長
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">Output: &amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// サンプラーコンテキストの初期化（Temperature, Top-K, Top-P 等の設定）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">smpl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_chain_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llama_sampler_chain_default_params&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_k&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.9f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_temp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.7f&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_dist&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// シード値
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_decode&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. サンプリング：現在のコンテキストに基づいて次のトークンを予測
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_token&lt;/span> &lt;span class="n">new_token_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_sample&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. トークンが EOS (End of Sequence) ならループ終了
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token_is_eog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. トークンを文字列（テキスト）にデコードして表示
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_chars&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_token_to_piece&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_chars&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_chars&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">flush&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. 新しく生成されたトークンを次のバッチとして準備
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch_clear&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_cur&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. モデルの評価（KVキャッシュを更新し、次を予測）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to evaluate&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// クリーンアップ
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_backend_free&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>このコードは、llama.cpp の基本 API を用いて独自推論ループを実装したものです。
&lt;code>llama_batch&lt;/code> 構造体を用いてトークン群を管理し、&lt;code>llama_decode&lt;/code> でニューラルネットワークのフォワードパス（順伝播）を実行します。&lt;/p>
&lt;hr>
&lt;h2 id="6-高度なカスタマイズ事例-c-によるロジット操作とペナルティ制御">6. 高度なカスタマイズ事例: C++ によるロジット操作とペナルティ制御
&lt;/h2>&lt;p>単純なテキスト生成にとどまらず、特定フォーマット（例えば JSON のみ）の出力を強制させたり、特定の禁止ワードを出力させないように制御したりする場合、サンプリング前の &lt;strong>ロジット（Logits）&lt;/strong> を C++ 側で直接操作します。&lt;/p>
&lt;p>モデルが各トークンを出力する直前の生のスコア（確率に変換される前の値）の配列を取得できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 推論直後、サンプリングを行う前に生のロジット配列を取得
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">logits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_get_logits_ith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n_vocab&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_n_vocab&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 禁止トークンのIDリスト（例として 1234, 5678）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5678&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 禁止トークンの出現確率を 0 (Logitをマイナス無限大) にする
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token&lt;/span> &lt;span class="nl">bad_tok&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">bad_tok&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">INFINITY&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>このように、C++ API を直接扱うことで、LangChain や Python 経由では実現が難しかったり、オーバーヘッドが大きくなるような、&lt;strong>「推論サイクルごとのマイクロミリ秒単位の介入」&lt;/strong> が可能になります。&lt;/p>
&lt;hr>
&lt;h2 id="7-パフォーマンスチューニングの極意">7. パフォーマンスチューニングの極意
&lt;/h2>&lt;p>C++ で実装を終えた後、実運用に向けて速度を限界まで高めるためのチェックポイントをいくつか紹介します。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>バッチ処理の最適化:&lt;/strong> 複数のユーザーからのリクエストを同時に処理する場合、&lt;code>llama_batch&lt;/code> に複数のシーケンスを含めて一度に &lt;code>llama_decode&lt;/code> を呼び出す（Continuous Batching）。これによりメモリアクセスを相乗りさせ、スループットを劇的に向上できます。&lt;/li>
&lt;li>&lt;strong>Flash Attention の有効化:&lt;/strong>
コンテキストパラメータで &lt;code>ctx_params.flash_attn = true;&lt;/code> を設定することで、メモリ使用量を減らしながら Attention 計算を高速化できます。長いコンテキスト（数万トークン）を扱う場合には必須の設定です。&lt;/li>
&lt;li>&lt;strong>Numa 対応:&lt;/strong>
マルチソケットのサーバー環境では &lt;code>llama_backend_init()&lt;/code> 前に NUMA の設定を適切に行うことで、メモリアクセスのレイテンシを削減できます。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-終わりに">8. 終わりに
&lt;/h2>&lt;p>本記事では、&lt;code>llama.cpp&lt;/code> の数学的な背景から始まり、アーキテクチャの解説、そして C++ API を駆使したカスタム推論エンジンの構築方法までを詳細に解説しました。&lt;/p>
&lt;p>Python のエコシステムはプロトタイピングには非常に便利ですが、エッジデバイスへのデプロイメント、ゲームへの組み込み、リアルタイム処理が要求されるプロダクション環境においては、C/C++ ベースの &lt;code>llama.cpp&lt;/code> の直接制御が圧倒的な力を見せます。&lt;/p>
&lt;p>皆さんもぜひ、自分の手で C++ コードを書き、ローカル環境で LLM を自由に操る楽しさを体験してみてください。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>参考リンク集&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>llama.cpp Official Repository&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/ggml" target="_blank" rel="noopener"
>ggml - Tensor Library&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://arxiv.org/abs/1706.03762" target="_blank" rel="noopener"
>Attention Is All You Need (Vaswani et al., 2017)&lt;/a>&lt;/li>
&lt;/ul>
&lt;/blockquote></description></item><item><title>【2026年最新】ローカルLLMをWindows環境で動かす完全ガイド</title><link>http://kenji.blog/p/local-llm-windows-2026/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/p/local-llm-windows-2026/</guid><description>&lt;img src="http://kenji.blog/p/local-llm-windows-2026/img/eyecatch.jpg" alt="Featured image of post 【2026年最新】ローカルLLMをWindows環境で動かす完全ガイド" />&lt;h1 id="1-はじめになぜ今windowsでローカルllmなのか">1. はじめに：なぜ今、WindowsでローカルLLMなのか？
&lt;/h1>&lt;p>2026年現在、生成AIと大規模言語モデル（LLM）の進化は、クラウド上の巨大なAPIサービスから、個人のPCやオンプレミス環境で動作する「ローカルLLM」へと大きなパラダイムシフトを見せています。OpenAIのGPT-5やAnthropicのClaude 3.5といったクラウドAIは非常に強力ですが、企業や個人がすべてのデータをクラウドに送信できるわけではありません。プライバシー、セキュリティ、レイテンシ、そして長期的・持続的なコストの観点から、ローカルLLMの需要はかつてなく爆発的に高まっています。&lt;/p>
&lt;p>特にWindows環境におけるローカルLLMのエコシステムの進化は目覚ましいものがあります。数年前までは「AI開発・実行といえばLinux」が常識でしたが、2026年現在ではWindowsが非常に強力かつ手軽なAIプラットフォームへと変貌を遂げました。&lt;/p>
&lt;p>本記事では、2026年最新の技術動向を踏まえ、Windows環境でローカルLLMを構築・運用・最適化するための完全なガイドを提供します。初心者向けのOllamaを用いた簡単な構築から、上級者向けのllama.cppを活用した極限の最適化、さらにはVRAM計算の数学的アプローチやアーキテクチャの深い理解、そしてローカルでのファインチューニングまで、圧倒的なボリュームで徹底解説します。&lt;/p>
&lt;h2 id="11-2026年のローカルllmを取り巻く技術トレンド">1.1 2026年のローカルLLMを取り巻く技術トレンド
&lt;/h2>&lt;p>現在のローカルLLMエコシステムを形作る主要なトレンドは以下の通りです。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>GGUFフォーマットの完全普及&lt;/strong>: メタデータとテンソルを単一のファイルに統合したGGUF（GPT-Generated Unified Format）が完全にデファクトスタンダード化しました。これにより、Hugging Faceから一つのファイルをダウンロードするだけで、どのような環境でも実行可能になっています。&lt;/li>
&lt;li>&lt;strong>MoE（Mixture of Experts）アーキテクチャの民主化&lt;/strong>: 小規模ながら高性能なMoEモデルが多数リリースされ、推論時に一部のエキスパートのみをアクティブにすることで、コンシューマーPCの計算負荷を抑えながら巨大モデルに匹敵する性能を叩き出しています。&lt;/li>
&lt;li>&lt;strong>推論エンジンの高度な抽象化と最適化&lt;/strong>: Ollama、LM Studio、AnythingLLMなどのツールが洗練され、CUDAドライバのインストールなどの複雑な依存関係をユーザーが意識する必要がなくなりました。また、FlashAttention 3のWindowsネイティブ対応により、推論速度が劇的に向上しています。&lt;/li>
&lt;li>&lt;strong>NPUの活用とWindows Copilot+ PCの台頭&lt;/strong>: GPUを持たないノートPCでも、搭載されているNPU（Neural Processing Unit）を利用して小規模なLLM（SLM: Small Language Models）を低消費電力で動かす技術が実用段階に入りました。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-ハードウェア要件とosの準備">2. ハードウェア要件とOSの準備
&lt;/h1>&lt;p>ローカルLLMを実用的な速度（1秒間に15〜30トークン以上）で動作させるためには、ハードウェアの選定が最も重要です。&lt;/p>
&lt;h2 id="21-推奨ハードウェア構成">2.1 推奨ハードウェア構成
&lt;/h2>&lt;p>AI PCの進化に伴い、要求スペックも変化しています。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OS&lt;/strong>: Windows 11 Pro (24H2以降)。WSL2の完全な機能と高度なメモリ管理、さらにはDirectMLの最新APIを利用するために必須です。&lt;/li>
&lt;li>&lt;strong>CPU&lt;/strong>: Intel Core Ultra 200シリーズ以上、またはAMD Ryzen 9000シリーズ以上。CPU推論を併用する場合、広帯域メモリ通信が不可欠です。&lt;/li>
&lt;li>&lt;strong>RAM&lt;/strong>: 最低32GB、推奨64GB以上。メインメモリの帯域幅（MB/s）がCPU推論時やオフロード時の決定的なボトルネックになります。DDR5-6000以上の高速メモリが理想的です。&lt;/li>
&lt;li>&lt;strong>GPU&lt;/strong>: NVIDIA RTX 4000/5000シリーズ。ローカルLLMにおいて最も重要なのは演算性能ではなく「VRAM容量」です。
&lt;ul>
&lt;li>&lt;strong>エントリー&lt;/strong>: RTX 4060 Ti (16GB版) - コスパ最強。8B〜14Bクラスのモデルに最適。&lt;/li>
&lt;li>&lt;strong>ミドルレンジ&lt;/strong>: RTX 4070 Ti SUPER (16GB) / RTX 4080 SUPER (16GB)&lt;/li>
&lt;li>&lt;strong>ハイエンド&lt;/strong>: RTX 4090 (24GB) / RTX 5090 (32GB) - 30B〜70Bクラスの量子化モデルを動かすために必要です。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>ストレージ&lt;/strong>: PCIe Gen4またはGen5のNVMe SSD。数十GBのモデルロード時間を劇的に短縮します。&lt;/li>
&lt;/ul>
&lt;h2 id="22-wsl2-windows-subsystem-for-linux-2-のセットアップ">2.2 WSL2 (Windows Subsystem for Linux 2) のセットアップ
&lt;/h2>&lt;p>多くのGUIツールはWindowsネイティブで動作しますが、Pythonを用いた開発、最新ツールのコンパイル、後述するLoRAファインチューニングにはWSL2が非常に便利です。Windows 11の最新環境では、ホスト側にNVIDIAドライバを入れるだけで、WSL2から透過的にGPU（CUDA）が利用できます。&lt;/p>
&lt;p>管理者権限でPowerShellを開き、以下を実行します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># WSL2と最新のUbuntuのインストール&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-install&lt;/span> &lt;span class="n">-d&lt;/span> &lt;span class="n">Ubuntu&lt;/span>&lt;span class="p">-&lt;/span>&lt;span class="mf">24.04&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># カーネルのアップデート&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-update&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>インストール後、WSL2ターミナル内で &lt;code>nvidia-smi&lt;/code> を実行し、GPUが正常に認識されていれば成功です。&lt;/p>
&lt;hr>
&lt;h1 id="3-ローカルllmのアーキテクチャと推論メカニズム">3. ローカルLLMのアーキテクチャと推論メカニズム
&lt;/h1>&lt;p>ローカル環境でモデルがどのようにテキストを生成するのか、その内部構造を理解することは、トラブルシューティングや最適化において非常に有用です。&lt;/p>
&lt;p>以下のMermaid図は、典型的なローカルLLMの推論パイプラインを示しています。&lt;/p>
&lt;div class="mermaid">graph TD
User["ユーザー入力 (プロンプト)"] --> Tokenizer["トークナイザー (Tokenizer)"]
Tokenizer --> Embedding["埋め込み層 (Embedding)"]
subgraph "Transformer Block (x Layers)"
Embedding --> Attn["自己アテンション (Self-Attention)"]
Attn --> KVCache["KV キャッシュ (Key/Value保持)"]
Attn --> FFN["フィードフォワードネットワーク (FFN)"]
end
FFN --> Logits["ロジット計算 (Logits)"]
Logits --> Sampler["サンプラー (Temperature, Top-K, Top-P)"]
Sampler --> OutputToken["出力トークン"]
OutputToken --> |"オートレグレッシブ生成"| Tokenizer
OutputToken --> Decoder["デトークナイザー (Detokenizer)"]
Decoder --> FinalOutput["最終出力テキスト"]&lt;/div>
&lt;h2 id="31-2つのフェーズprefill-と-decode">3.1 2つのフェーズ：Prefill と Decode
&lt;/h2>&lt;p>LLMのテキスト生成は、計算特性の異なる2つのフェーズに分かれます。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Prefill（プロンプト処理）フェーズ&lt;/strong>: 入力されたプロンプト全体を一度に処理し、理解するフェーズです。並列計算が可能であるため、GPUの計算能力（FLOPS）が速度に直結します。プロンプトが長い場合、このフェーズに数秒かかることがあります。&lt;/li>
&lt;li>&lt;strong>Decode（トークン生成）フェーズ&lt;/strong>: 1トークンずつ予測し、次の入力に回す（オートレグレッシブ）フェーズです。このフェーズでは並列計算が制限されるため、GPUのVRAM帯域幅（Memory Bandwidth）が決定的なボトルネックになります。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="4-vram消費量の計算とモデルサイズの数学的理解">4. VRAM消費量の計算とモデルサイズの数学的理解
&lt;/h1>&lt;p>「自分のPCでどのモデルが動くのか？」を正しく判断するには、VRAMの計算式を理解する必要があります。VRAM不足によるシステムメモリ（RAM）へのフォールバックが発生すると、推論速度は10倍〜100倍遅くなります。&lt;/p>
&lt;h2 id="41-パラメータサイズに基づくベースvram">4.1 パラメータサイズに基づくベースVRAM
&lt;/h2>&lt;p>モデルのウェイト（重み）をVRAMに読み込むためのメモリ量です。
モデルサイズ $P$ （パラメータ数、単位: 10億 = 1B）と、1パラメータあたりのバイト数 $B$ を用いて計算します。&lt;/p>
$$
V_{base} = P \times B \quad \text{(GB)}
$$
&lt;p>例えば、8B（80億）パラメータのモデルをFP16（半精度浮動小数点、16ビット=2バイト）でロードする場合：&lt;/p>
$$
V_{base} = 8 \times 2 = 16 \text{ GB}
$$
&lt;p>つまり、16GBのVRAMを持つGPUであっても、モデルをロードするだけでほぼ限界に達してしまいます。&lt;/p>
&lt;h2 id="42-量子化quantizationの魔法">4.2 量子化（Quantization）の魔法
&lt;/h2>&lt;p>そこで「量子化」が登場します。パラメータの精度を落とすことでモデルサイズを劇的に縮小します。最も一般的な4bit量子化（例: Q4_K_M）の場合、1パラメータあたり平均して約0.55バイトとなります。&lt;/p>
$$
V_{base\_4bit} = 8 \times 0.55 = 4.4 \text{ GB}
$$
&lt;p>これにより、16GBのVRAMがあれば、十分に余裕を持って8Bモデルを動かすことができます。&lt;/p>
&lt;h2 id="43-kvキャッシュの計算gqa対応版">4.3 KVキャッシュの計算（GQA対応版）
&lt;/h2>&lt;p>推論時には、過去の文脈を保持するための「KVキャッシュ」がVRAMを消費します。Llama 3等の最新モデルでは、メモリ節約のためにGQA（Grouped Query Attention）が採用されています。&lt;/p>
&lt;p>KVキャッシュの消費量 $V_{kv}$ （ギガバイト）は以下の数式で表されます。&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times \left( \frac{h_{kv}}{h_q} \right) \times h_q \times d \times B_{kv} \div 10^9
$$
&lt;p>整理すると、キーとバリューのヘッド数 $h_{kv}$ を用いてシンプルに計算できます。&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times h_{kv} \times d \times B_{kv} \div 10^9
$$
&lt;p>ここで：&lt;/p>
&lt;ul>
&lt;li>$b$: バッチサイズ（個人のローカル利用なら通常1）&lt;/li>
&lt;li>$s$: シーケンス長（コンテキスト長、例: 8192）&lt;/li>
&lt;li>$l$: レイヤー数（例: 32）&lt;/li>
&lt;li>$h_{kv}$: KVヘッド数（例: 8）&lt;/li>
&lt;li>$d$: ヘッドあたりの次元数（例: 128）&lt;/li>
&lt;li>$B_{kv}$: KVキャッシュのバイト数（FP16なら2）&lt;/li>
&lt;/ul>
&lt;p>計算例（Llama 3 8B, コンテキスト8192, FP16キャッシュ）：
$V_{kv} = 2 \times 1 \times 8192 \times 32 \times 8 \times 128 \times 2 \div 10^9 \approx 1.07 \text{ GB}$&lt;/p>
&lt;p>コンテキスト長 $s$ を長くすればするほど、必要なVRAMは線形に増加することに注意してください。&lt;/p>
&lt;hr>
&lt;h1 id="5-実践1ollamaを用いた最速最短セットアップ">5. 実践1：Ollamaを用いた最速・最短セットアップ
&lt;/h1>&lt;p>理論を理解したところで、実際にWindows環境でLLMを動かしてみましょう。
2026年現在、最もユーザーフレンドリーなツールが「Ollama」です。Dockerライクな直感的なCLIを提供します。&lt;/p>
&lt;h2 id="51-インストールと実行">5.1 インストールと実行
&lt;/h2>&lt;ol>
&lt;li>&lt;a class="link" href="https://ollama.com/" target="_blank" rel="noopener"
>Ollama公式サイト&lt;/a> からWindows版インストーラーをダウンロードして実行します。&lt;/li>
&lt;li>PowerShellを開き、以下のコマンドを入力します。ここでは、日本語対応の &lt;code>llama3:8b&lt;/code> を使用します。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">llama3&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="n">8b&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>初回実行時はモデルのダウンロードが行われます。完了すると、そのままターミナル上で対話が可能です。&lt;/p>
&lt;h2 id="52-modelfileによるカスタムaiの作成">5.2 ModelfileによるカスタムAIの作成
&lt;/h2>&lt;p>特定のペルソナを持つAIを簡単に作成できます。任意の場所に &lt;code>Modelfile&lt;/code> を作成します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">FROM llama3:8b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">SYSTEM &amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">あなたは非常に優秀なシニアソフトウェアエンジニアです。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ユーザーの質問に対して、必ずコード例を交えて、論理的かつ簡潔に答えてください。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER temperature 0.3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER num_ctx 8192
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>以下のコマンドで独自のモデルをビルドし、実行します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">create&lt;/span> &lt;span class="n">SeniorDev&lt;/span> &lt;span class="o">-f&lt;/span> &lt;span class="p">./&lt;/span>&lt;span class="n">Modelfile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">SeniorDev&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="53-外部アプリaiエディタからの利用">5.3 外部アプリ（AIエディタ）からの利用
&lt;/h2>&lt;p>Ollamaは &lt;code>http://localhost:11434&lt;/code> にOpenAI互換のAPIエンドポイントを公開します。
CursorやContinue.devといったVS Code拡張機能のバックエンド設定で、URLを上記に指定し、モデル名に &lt;code>SeniorDev&lt;/code> 等を指定するだけで、無料で強力なローカルコーディングアシスタントが実現します。&lt;/p>
&lt;hr>
&lt;h1 id="6-実践2llamacppによる極限のパフォーマンスチューニング">6. 実践2：llama.cppによる極限のパフォーマンスチューニング
&lt;/h1>&lt;p>細かなメモリ管理や、最新フォーマット（EXL2やIQ量子化など）をいち早く試したい場合は、コアエンジンである &lt;code>llama.cpp&lt;/code> を直接操作します。&lt;/p>
&lt;h2 id="61-llamacpp-のビルド手順">6.1 llama.cpp のビルド手順
&lt;/h2>&lt;p>Windows環境では、CUDA ToolkitとCMakeを用いてソースからビルドするのがベストです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">git&lt;/span> &lt;span class="n">clone&lt;/span> &lt;span class="n">https&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="p">//&lt;/span>&lt;span class="n">github&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">ggerganov&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">&lt;/span>&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># CUDA対応として構成しコンパイル&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-DLLAMA_CUBLAS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">ON&lt;/span> &lt;span class="n">-DBUILD_SHARED_LIBS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">OFF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span> &lt;span class="n">-j&lt;/span> &lt;span class="mf">16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="62-サーバーモードでの高度な起動">6.2 サーバーモードでの高度な起動
&lt;/h2>&lt;p>ビルドされた &lt;code>llama-server.exe&lt;/code> を使用して、モデルをホストします。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.\&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="n">Release&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="nb">llama-server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="py">exe&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-model&lt;/span> &lt;span class="s2">&amp;#34;C:\models\Llama-3-8B-Instruct.Q4_K_M.gguf&amp;#34;&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-ctx-size&lt;/span> &lt;span class="mf">8192&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-n-gpu-layers&lt;/span> &lt;span class="mf">99&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-threads&lt;/span> &lt;span class="mf">8&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-flash-attn&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-port&lt;/span> &lt;span class="mf">8080&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>--n-gpu-layers 99&lt;/code>: 可能限りすべてのレイヤーをGPU VRAMにオフロードします。&lt;/li>
&lt;li>&lt;code>--flash-attn&lt;/code>: FlashAttention 3を有効にし、推論速度の向上とKVキャッシュのVRAM消費削減を達成します。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="7-guiフロントエンドlm-studio-とローカルragの構築">7. GUIフロントエンド：LM Studio とローカルRAGの構築
&lt;/h1>&lt;p>コマンドラインに抵抗がある場合や、RAG（検索拡張生成）を直感的に行いたい場合はGUIを利用します。&lt;/p>
&lt;h2 id="71-lm-studio">7.1 LM Studio
&lt;/h2>&lt;p>LM Studioは、モデルの検索、ダウンロード、システム要件の事前チェック、そしてチャットUIまでを一つにまとめた素晴らしいアプリケーションです。アプリ内の「Local Server」ボタンを押すだけでOpenAI互換APIが立ち上がります。&lt;/p>
&lt;h2 id="72-anythingllm-を用いたragアーキテクチャ">7.2 AnythingLLM を用いたRAGアーキテクチャ
&lt;/h2>&lt;p>社内文書や個人メモを読み込ませるRAG環境のアーキテクチャ図です。&lt;/p>
&lt;div class="mermaid">graph LR
Document["ドキュメント (PDF, MD)"] --> Chunking["チャンク分割"]
Chunking --> EmbedModel["埋め込みモデル"]
EmbedModel --> VectorDB["ベクトルデータベース"]
UserQuery["ユーザー質問"] --> EmbedQuery["質問の埋め込み"]
EmbedQuery --> VectorDB
VectorDB --> |"類似度検索"| RetrievedDocs["関連ドキュメント抽出"]
UserQuery --> PromptBuilder["プロンプト生成"]
RetrievedDocs --> PromptBuilder
PromptBuilder --> LocalLLM["ローカルLLM"]
LocalLLM --> Answer["最終的な回答"]&lt;/div>
&lt;p>AnythingLLMデスクトップ版（Windows）を使えば、設定画面からOllama（LLMとEmbedding）を指定し、ローカルのVectorDB（LanceDB）を利用するよう設定するだけで、数分でこのアーキテクチャが完成します。データを外部に一切送信しないプライベートAIの誕生です。&lt;/p>
&lt;hr>
&lt;h1 id="8-windows-wsl2上でのファインチューニング-lora">8. Windows WSL2上でのファインチューニング (LoRA)
&lt;/h1>&lt;p>ローカルで動かすだけでなく、自分のデータでモデルを賢くしたい場合、LoRA（Low-Rank Adaptation）を用いたファインチューニングが可能です。2026年現在、「Unsloth」というライブラリを使えば、WindowsのWSL2環境において、16GBのVRAMでも8Bモデルの学習が数時間で完了します。&lt;/p>
&lt;p>WSL2のUbuntu内で以下を実行して環境を構築します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">conda create --name unsloth_env &lt;span class="nv">python&lt;/span>&lt;span class="o">=&lt;/span>3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">conda activate unsloth_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install &lt;span class="s2">&amp;#34;unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install --no-deps trl peft accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>UnslothはCUDAカーネルを極限まで最適化しており、標準のHugging Faceライブラリと比較して学習速度が約2倍、VRAM消費量が約半分になります。Jupyter Notebookを立ち上げ、データセット（JSONL形式）を読み込ませるだけで、数エポックの学習がVRAM 12GB〜16GBのRTX 4060 Ti等でも可能です。&lt;/p>
&lt;hr>
&lt;h1 id="9-パフォーマンストラブルシューティング">9. パフォーマンス・トラブルシューティング
&lt;/h1>&lt;p>よく直面する問題とその解決策です。&lt;/p>
&lt;h3 id="1-推論速度が極端に遅い12-tokenss">1. 推論速度が極端に遅い（1〜2 tokens/s）
&lt;/h3>&lt;p>&lt;strong>原因&lt;/strong>: モデルがVRAMに収まりきらず、システムメモリ（RAM）へオフロードされています。
&lt;strong>対策&lt;/strong>: タスクマネージャーで「専用GPUメモリ」を確認してください。限界に達している場合は、コンテキストサイズ（&lt;code>-c&lt;/code>）を小さくするか、より低いビット数の量子化モデル（Q4_K_Mなど）を使用してください。&lt;/p>
&lt;h3 id="2-cuda-out-of-memoryエラー">2. 「CUDA out of memory」エラー
&lt;/h3>&lt;p>&lt;strong>原因&lt;/strong>: VRAMが完全に枯渇しました。特に対話が長引いてKVキャッシュが肥大化した際に発生します。
&lt;strong>対策&lt;/strong>: Ollamaの場合は &lt;code>num_ctx&lt;/code>、llama.cppの場合は &lt;code>-c&lt;/code> の値を意図的に小さく制限します。&lt;/p>
&lt;h3 id="3-日本語の生成がおかしい">3. 日本語の生成がおかしい
&lt;/h3>&lt;p>&lt;strong>原因&lt;/strong>: プロンプトテンプレートの不一致、または非対応モデルです。
&lt;strong>対策&lt;/strong>: モデル名に &lt;code>Instruct&lt;/code> が含まれるものを使用し、ChatMLやLlama3フォーマットなど、モデル作者が指定した正しいテンプレートがツール側で選択されているか確認してください。&lt;/p>
&lt;hr>
&lt;h1 id="10-まとめと今後の展望">10. まとめと今後の展望
&lt;/h1>&lt;p>2026年、Windows環境におけるローカルLLMの構築は、限られた一部のエンジニアだけの特権ではなくなりました。GGUFフォーマットのデファクト化、OllamaやLM Studioといった洗練されたエコシステムの登場、そしてFlashAttentionをはじめとするハードウェア最適化により、誰でも簡単にエンタープライズ級のAI環境を手に入れることができます。&lt;/p>
&lt;p>本記事で解説した以下のポイントを是非活用してください。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>VRAMの数学的計算&lt;/strong>を用いて、自分のPCスペックに最適なモデルサイズと量子化レベルを論理的に選択する。&lt;/li>
&lt;li>&lt;strong>Ollama&lt;/strong>を使って最速で環境を構築し、AIエディタと連携させて生産性を劇的に向上させる。&lt;/li>
&lt;li>&lt;strong>llama.cpp&lt;/strong>の高度なパラメータ制御で、ハードウェアの限界性能を引き出す。&lt;/li>
&lt;li>&lt;strong>AnythingLLM&lt;/strong>で機密データを扱うセキュアなローカルRAGシステムを構築する。&lt;/li>
&lt;li>**Unsloth (WSL2)**を活用し、自分だけの専門知識を持ったカスタムAIを育成する。&lt;/li>
&lt;/ol>
&lt;p>AIの「民主化」は、もはやバズワードではなく、あなたのWindowsデスクトップ上で稼働する現実のシステムです。クラウドAPIの利用コストや情報漏洩リスクから解放され、自由で強力なプライベートAIの世界へ、今すぐ足を踏み入れてみてください。&lt;/p></description></item></channel></rss>