<?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/zh-tw/categories/ai/</link><description>Recent content in AI on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-tw</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 18:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-tw/categories/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Microsoft.Windows.AI 的最新 API 應用案例與範例程式碼</title><link>http://kenji.blog/zh-tw/p/microsoft-windows-ai-api-guide/</link><pubDate>Fri, 11 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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 是一套 AI 堆疊，專為讓開發者能輕鬆將 AI 模型整合至 Windows 上，並發揮最高效能而設計。此執行階段在 OS 層級將硬體加速抽象化，為開發者提供統一的介面。&lt;/p>
&lt;div class="mermaid">graph TD
App["Windows 應用程式 (C# / C++)"] --> API["Microsoft.Windows.AI APIs"]
App --> ORT["ONNX Runtime"]
API --> WCR["Windows Copilot Runtime (OS 層)"]
WCR --> SLM["本機模型 (Phi-Silica 等)"]
ORT --> DML["DirectML 執行提供者"]
SLM --> DML
DML --> DXCore["DXCore / DirectX 12"]
DXCore --> NPU["NPU (神經處理單元)"]
DXCore --> GPU["GPU (圖形處理單元)"]
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（神經處理單元）。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}}$ 是每個核心的 MAC（乘積累加）單元數量&lt;/li>
&lt;li>最後的 $2$ 是因為一次 MAC 運算包含乘法與加法兩個操作（FLOPs/OPs）。&lt;/li>
&lt;/ul>
&lt;p>例如，若 NPU 頻率為 1.5GHz、4 個核心、每個核心有 4096 個 MACs：
&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 的推論（解碼階段）往往受限於&lt;strong>記憶體頻寬（Memory-Bound）&lt;/strong>。系統記憶體的理論頻寬 $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-1使用-c-活用本機語言模型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-2透過-c-與-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 的峰值效能。以下是使用 DirectML 在 C++ 中執行 ONNX 格式自訂模型（例：YOLOv8）的進階初始化與推論管線核心實作。&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 執行提供者 (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 執行提供者。&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（非極大值抑制）或繪製邊界方塊的處理
&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-工具組">7.1 模型量化 (Quantization) 與 Olive 工具組
&lt;/h3>&lt;p>要發揮 NPU 的真正實力，將 AI 模型的權重與激勵值從 FP32（單精度浮點數）**量化（Quantization）**至 INT8 或 INT4 絕對是必要條件。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 的情況下，決定使用者體驗（UX）的並非吞吐量，而是顯示第一個權杖所需的時間（TTFT: Time To First Token）。
因此，在互動式 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 "使用者"
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 硬體"
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>: 不僅限於文字，還能無縫同時處理語音、影像，甚至是即時視訊來源，在 OS 層級標準提供跨模態的 AI 推論。&lt;/li>
&lt;li>&lt;strong>RAG (檢索增強生成) 的系統層級支援&lt;/strong>: 在 OS 安全的沙盒中，將本機 PC 內的個人文件庫或 Windows Search 索引與 AI 模型結合，在完全保護使用者隱私的狀態下建構超高階的個人 AI 助理。&lt;/li>
&lt;li>&lt;strong>NPU 的動態資源擴展&lt;/strong>: 當多個 AI 應用程式（例如，背景中的降噪功能與前景的程式碼生成）同時運作時，Windows 核心排程器會動態切換 NPU 的執行上下文，以保證服務品質（QoS）的機制。&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/zh-tw/p/building-ai-inference-engine-cpp-only/</link><pubDate>Fri, 11 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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（全域直譯器鎖）以及動態型別帶來的開銷。特別是在需要即時性的系統中，毫秒級的延遲都是致命的。&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等資源嚴重受限的環境中，沒有餘裕去執行會消耗數GB記憶體的Python執行階段（Runtime）。&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["輸入資料 (Tokens/Images)"] --> B["張量管理 (Tensor Management)"]
B --> C["計算圖 (DAG)"]
C --> D["記憶體池與分配器 (Memory Arena &amp; Allocator)"]
C --> E["排程器與執行緒池 (Scheduler &amp; Thread Pool)"]
E --> F["CPU後端 (AVX2/ARM NEON)"]
E --> G["GPU後端 (CUDA/Metal)"]
F --> H["輸出結果"]
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等特定硬體最佳化的運算實作（Kernel）。&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>來分配與釋放，堆積（Heap）的碎片化以及作業系統的上下文切換將會導致致命的速度下降。&lt;/p>
&lt;p>因此，我們採用了「&lt;strong>記憶體池（Memory Arena）&lt;/strong>」的方法。這是在推論開始時計算（或寫死）所需的最大記憶體量並一次性分配，之後只需遞增指標就能切割出記憶體的手法。&lt;/p>
&lt;h3 id="31-對齊alignment的重要性">3.1 對齊（Alignment）的重要性
&lt;/h3>&lt;p>現代CPU支援SIMD（Single Instruction, Multiple Data）指令。例如Intel/AMD的AVX2/AVX-512，以及ARM的NEON等。這些指令可以一次處理256位元（32位元組）或512位元（64位元組）的資料，但處理對象的資料記憶體必須對齊（Alignment）在特定的位元組邊界（通常是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>像這樣，建立張量時必定透過這個記憶體池來取得記憶體。每次推論步驟（例如每次生成Token）結束後，只要呼叫 &lt;code>reset()&lt;/code> 就能瞬間重新利用記憶體。&lt;/p>
&lt;hr>
&lt;h2 id="4-張量資料結構與步幅的魔法">4. 張量資料結構與步幅的魔法
&lt;/h2>&lt;p>張量是純量、向量、矩陣的廣義概念。在實作上重要的是，雖然實際資料在記憶體上是以&lt;strong>一維的連續陣列&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">// 指向有效負載（Payload）的指標
&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 (位元組) ：往行（Column）方向移動&lt;/li>
&lt;li>&lt;code>nb[1]&lt;/code> = $N \times 4$ (位元組) ：往列（Row）方向移動&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>兩個矩陣 $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>如果用樸素的三層迴圈來實作，會頻繁發生快取未命中（Cache Miss），完全發揮不出效能。&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>迴圈平鋪（Loop Tiling/快取區塊化）&lt;/strong>：將矩陣分割成能放進L1/L2快取的小區塊來進行計算。&lt;/li>
&lt;li>&lt;strong>資料打包（Data Packing）&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上有一定的運行表現，但要以實用的速度（例如：每秒生成20個Token以上）來運行LLM等巨大模型，GPU的平行計算能力是不可或缺的。因此，我們在引擎中導入後端（Backend）的抽象層。&lt;/p>
&lt;h3 id="71-後端抽象化">7.1 後端抽象化
&lt;/h3>&lt;p>利用C++的多型（Polymorphism），讓我們能切換運算的執行器（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++擴充功能來實作後端。雖然可以自己寫核心（Kernel），但關於矩陣乘法，活用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推論機非常優秀。其原因在於「統一記憶體（Unified Memory）」。由於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++的語法撰寫）來編寫核心（Kernel）。&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-cache">8. Transformer模型特有的處理：Attention與KV Cache
&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）的Token生成中，必須保留過去Token的計算結果（Key與Value）。我們稱之為「&lt;strong>KV快取（Key-Value Cache）&lt;/strong>」。&lt;/p>
&lt;div class="mermaid">graph TD
T["當前Token (Current Token)"] --> Q["Query"]
T --> K["Key"]
T --> V["Value"]
K --> KCache["附加至KV快取 (Append to KV Cache)"]
V --> VCache["附加至KV快取 (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 Tokens）的記憶體空間，並採用類似環狀緩衝區（Ring Buffer）的方式來運作。這樣就能防止在每個生成步驟重新分配記憶體。&lt;/p>
&lt;p>另外，針對位置編碼（Positional Encoding），我們將實作近年來成為主流的「RoPE（Rotary Position Embedding）」。這是一種將位置資訊作為複數空間旋轉向量嵌入的手法，在C++中對 &lt;code>sin&lt;/code> 及 &lt;code>cos&lt;/code> 函式呼叫的最佳化（例如建立尋找表 Lookup Table 等）是影響效能的關鍵。&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。若使用獨特的區塊化（Blocking）與縮放因子（Scaling Factor），即可進行具實用性的推論。&lt;/li>
&lt;/ul>
&lt;p>在推論引擎端，會從記憶體讀出被壓縮為INT4（或INT8）的權重，&lt;strong>在載入到CPU或GPU的暫存器後，立刻解壓縮（Dequantize）回FP16或FP32再進行計算&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>在現代的多插槽（Multi-socket）伺服器或Ryzen Threadripper等高階CPU上，採用了**NUMA（Non-Uniform Memory Access）**架構。從某個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的記憶體分配（NUMA-aware Allocation）&lt;/strong>：在處理資料的執行緒所在的同一個NUMA節點上分配記憶體。&lt;/li>
&lt;li>&lt;strong>工作竊取型執行緒池（Work-stealing Thread Pool）&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頻寬搏鬥一邊打造出的推論引擎，看著它在終端機上陸續生成自然的文字（Token）時，那種成就感，是呼叫Python框架中 &lt;code>model.generate()&lt;/code> 時絕對無法體會到的「身為工程師純粹的喜悅」。&lt;/p>
&lt;p>AI技術往往容易變成「黑盒子（Black Box）」，但透過自己親手用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/zh-tw/p/tinyllama-on-premises-fast-tuning-guide/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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 時對安全性與資料隱私的擔憂，已成為巨大的障礙。特別是在處理高度機密的內部資料或個人資訊的業務中，基於合規性（如 GDPR 或 APPI 等）的考量，將資料傳送至雲端上的公開 LLM API 往往是不被允許的。&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 兆個 Token 的龐大資料集進行預訓練，與同級別的模型相比，展現出驚人的效能。&lt;/p>
&lt;p>本文將提供一份完整指南，教您如何在本地環境（本地伺服器或工作站）中，針對公司專屬任務「最快且高效」地對 TinyLLaMA 進行微調 (Fine-Tuning)。從數學背景到最新的最佳化技術，再到具體的 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) 中，採用了 SwiGLU 來取代傳統的 ReLU 或 GELU。這在數學上可表示如下：
$$ \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) 之間的方法，透過將 Key 和 Value 的注意力頭進行分組，節省了記憶體頻寬，並顯著提升了推論速度。&lt;/li>
&lt;/ol>
&lt;p>以下的 Mermaid 圖表展示了 TinyLLaMA 的整體資料流與 Transformer 區塊的結構。&lt;/p>
&lt;div class="mermaid">graph TD
A["輸入文字"] --> B["分詞器 (BPE)"]
B --> C["嵌入層"]
C --> D["Transformer 區塊 (TinyLLaMA 為 22 層)"]
D --> E["RMSNorm (最終)"]
E --> F["線性投影 (詞彙表大小)"]
F --> G["輸出機率 (Softmax)"]
subgraph "Transformer 區塊剖析"
D1["輸入隱藏狀態"] --> D2["RMSNorm"]
D2 --> D3["分組查詢注意力機制 (GQA)"]
D3 --> D4["殘差相加"]
D4 --> D5["RMSNorm"]
D5 --> D6["SwiGLU 全連接層"]
D6 --> D7["殘差相加"]
D7 --> D8["輸出至下一層"]
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$）近似為兩個低秩小矩陣相乘的方法。&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["輸入向量 x"] --> W0["凍結的預訓練權重 (W_0)"]
X --> A["可訓練的 LoRA 矩陣 A (r x k)"]
A --> B["可訓練的 LoRA 矩陣 B (d x r)"]
W0 --> Add["向量相加"]
B --> Add
Add --> Y["輸出向量 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 整合了三個關鍵技術：&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>藉此，通常需要 16GB 至 24GB VRAM 的微調，現在即使是在消費級 GPU（如 RTX 3060 12GB 或 RTX 4070 等）上也能游刃有餘地執行。&lt;/p>
&lt;hr>
&lt;h2 id="4-本地環境的硬體需求與設定">4. 本地環境的硬體需求與設定
&lt;/h2>&lt;p>在使用 QLoRA 對 TinyLLaMA (1.1B) 進行微調時，硬體需求可以降得非常低。&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"># 安裝 Transformer 相關函式庫&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>標準的注意力機制對於序列長度 $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> 相同，發生上溢 (Overflow) 或下溢 (Underflow) 的風險極低，訓練會更加穩定。&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 步。正式上線請改用 epoch 數指定&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>若使用的是 Float16 而非 Bfloat16，可能發生了梯度下溢。請確認是否設定了 &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> 等特殊 Token）是否與基礎模型在預訓練時一致。&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) 最大化，強烈建議使用 &lt;strong>vLLM&lt;/strong> 或 &lt;strong>TGI (Text Generation Inference)&lt;/strong>，而不是 Hugging Face 標準的 &lt;code>pipeline&lt;/code>。vLLM 使用了 PagedAttention 技術，可防止 GPU 記憶體碎片化，並大幅提升平行請求的處理能力。&lt;/p>
&lt;p>以下的 Mermaid 圖表展示了從訓練到部署推論伺服器的管線 (Pipeline)。&lt;/p>
&lt;div class="mermaid">graph TD
A["原始私有資料"] --> B["預處理與格式化 (JSONL)"]
B --> C["QLoRA 微調 (SFTTrainer)"]
C --> D["LoRA 適配器權重 (.safetensors)"]
D --> E["與基礎 TinyLLaMA 1.1B 合併"]
E --> F["合併後的模型"]
F --> G["透過 vLLM 伺服器部署"]
G --> H["API 端點 / 使用者介面 (如聊天機器人)"]&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>在本地運作的 Local LLM，不僅能保護資料的機密性，更是能以低成本建構專注於特定領域（法務、醫療、公司內部規定等）之專業 AI 的最強武器。請務必參考本指南，試著培育出專屬於貴公司的 TinyLLaMA 吧。&lt;/p></description></item><item><title>AI時代的程式設計師該如何生存？</title><link>http://kenji.blog/zh-tw/p/how-programmers-survive-in-ai-era/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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 寫程式」的概念充其量只停留在生成樣板程式碼（Boilerplate）或自動補全函式等程式設計師的「輔助工具」角色。然而，隨著大型語言模型（LLM）的驚人進化，情況發生了根本性的翻轉。現代的 AI 已不再只是個「聰明的打字機」，只要提供需求定義文件，它就能化身為「自主型初階工程師」，具備瞬間且自主地將從前端到後端邏輯、資料庫結構設計，甚至是建構 CI/CD 管線等整個系統組合起來的能力。&lt;/p>
&lt;p>在這樣的時代，我們「程式設計師」或「軟體工程師」該如何生存？在「寫程式」這個行為本身的經濟價值正迅速通貨緊縮的當下，只懂得特定程式語言語法（Syntax）、熟悉特定框架 API 的「寫碼工（Coder）」，正快速地被市場淘汰。&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的排列）向電腦下達指令。之後，組合語言出現了，讓人們可以用較易理解的助記符號（Mnemonic）來操作硬體。隨著時代進步，C 語言、Fortran 等高階語言問世，成功封裝了記憶體管理或 CPU 暫存器等硬體複雜的細節。接著出現的 Java, Python, Ruby, TypeScript 等現代語言，讓程式設計師不再需要專注於「如何讓電腦運作（How）」，而能更專注於「想讓電腦做什麼（What）」。&lt;/p>
&lt;p>AI（LLM）的出現，是這段抽象化歷史中最新、也是最大的一次典範轉移。如果說程式語言的進化是「隱藏硬體」，那麼 LLM 的進化就是「隱藏語法（Syntax）」。&lt;/p>
&lt;div class="mermaid">graph TD
A["機器碼 / 打孔卡片 (1940年代)"] --> B["組合語言 (1950年代)"]
B --> C["編譯型高階語言 (1970年代)"]
C --> D["託管 / 腳本語言 (1990年代)"]
D --> E["透過 LLM 的自然語言 (2020年代)"]
E --> F["自主型 AI 代理 (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-從語法syntax回歸到架構architecture">4. 從語法（Syntax）回歸到架構（Architecture）
&lt;/h2>&lt;p>明確劃分 AI 擅長的事物與人類擅長的事物，是生存的絕對條件。&lt;/p>
&lt;div class="mermaid">graph LR
Sub1["AI 的卓越領域"]
Sub2["人類的卓越領域"]
A["從規格生成程式碼"] --> Sub1
B["語法錯誤與 Bug 修復"] --> Sub1
C["樣板 / 測試生成"] --> Sub1
D["日誌分析與模式匹配"] --> Sub1
E["系統架構設計"] --> Sub2
F["解決模糊的需求"] --> Sub2
G["跨團隊協商"] --> Sub2
H["倫理判斷 / 責任"] --> Sub2&lt;/div>
&lt;p>AI 在「局部最佳化」方面已凌駕人類。在撰寫單一函式、單一類別或單一模組的速度和準確性上，人類毫無勝算。然而，AI 對於「全域最佳化」或「上下文缺失（Missing Context）」卻非常脆弱。&lt;/p>
&lt;p>未來的程式設計師必須將角色從「寫程式的勞工」，轉變為「協調編排 AI 生成的無數元件的架構師」。俯瞰整個系統，微服務的邊界該畫在哪裡、如何配合商業語境來解決 CAP 定理中可用性與一致性的權衡、如何控制技術債。這些都是只有理解全貌與商業目標的人類才能勝任的高階智力工作。&lt;/p>
&lt;hr>
&lt;h2 id="5-需求定義才是真正的提示工程prompt-engineering">5. 需求定義才是「真正的提示工程（Prompt Engineering）」
&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>: 使用者輸入預期外內容時的回退（Fallback）處理。&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 自主生成的程式碼在正式環境中引發嚴重的 Bug，導致企業損失數億元，或者在攸關人命的醫療系統中發生故障，該由誰來承擔責任？是開發 AI 模型的企業嗎？還是輸入提示的工程師？我們無法將 AI「解僱」或「逮捕」。&lt;/p>
&lt;p>作為承擔系統對社會造成影響之「法律與倫理責任（Accountability）」的主體，「人類」的角色無論技術如何進步都不會消失。倒不如說，程式碼生成過程越是黑箱化，人類作為系統「最終批准者（Approver）」與「監督者（Supervisor）」所背負的責任就越重。&lt;/p>
&lt;p>審核 AI 提出的架構或程式碼是否符合安全標準、是否存在倫理問題（是否包含偏見）、是否遵守合規性，並給出最終的通行許可。這種「承擔責任」的行為本身，將成為工程師重要工作的一部分。&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 生成的程式碼，有時包含了人類想不到的高階最佳化，但同時也可能包含無視上下文的「幻覺（Hallucination）」。&lt;/p>
&lt;p>為了防止這種情況，必須將對 AI 的審查流程系統化。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant H as "人類工程師 (架構師)"
participant A as "AI 代理"
participant S as "CI/CD &amp; 測試"
H->>A: "定義嚴格的需求與限制"
A->>H: "提出架構與初始程式碼"
Note over H,A: 審查階段：高認知負荷
H->>A: "評論設計選擇，要求重構"
A->>S: "生成最終程式碼並推送"
S-->>H: "自動化測試結果與靜態分析"
H->>H: "最終批准與承擔責任"&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 就好。但是，對作業系統的運作原理、網路協定（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>磨練溝通與引導（Facilitation）的技巧&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）出現時，也曾蔓延著工作將會消失的悲觀論調。但現實是，隨著生產力的飛躍性提升，創造了新的需求，並誕生了更高階的工作。軟體世界也將發生同樣的事。因為「能以低廉成本打造系統」，軟體將滲透到過去因成本考量而無法資訊化的所有領域，工程師需要解決的課題（What）將無限擴張。&lt;/p>
&lt;p>我們程式設計師現在正獲得一個機會，從寫程式的工匠，進化為指揮 AI 這個強大智慧的「交響樂團指揮家」。與其畏懼科技的浪潮而停留在岸邊，不如儘早駕馭這股浪潮，啟程前往創造更大、更有價值系統的旅程。AI 時代，正是真正意義上的「工程學」展開的時代。&lt;/p></description></item><item><title>使用C++開發小規模AI模型（如TinyLLaMA）的步驟指南</title><link>http://kenji.blog/zh-tw/p/cpp-small-ai-model-tinyllama-dev-guide/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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參數）這樣的小規模模型，即使在資源受限的邊緣裝置或一般筆記型電腦（包含 Windows 環境）上，也能以實用的速度進行推論。雖然使用 Python 和 PyTorch 進行開發是目前的主流，但若要追求極致的效能與記憶體節省，結合 C++ 與基於 C 語言的張量函式庫「ggml」已成為業界的標準。&lt;/p>
&lt;p>本文將提供非常詳細的開發步驟，解說如何從零開始建構（或深入理解現有 llama.cpp 的內部結構）一個使用 C++ 載入 TinyLLaMA 並進行文字生成的推論引擎。&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>減少額外負擔（Overhead）&lt;/strong>: 可以完全消除 Python 的全域直譯器鎖（GIL）以及執行時期的額外負擔。&lt;/li>
&lt;li>&lt;strong>記憶體效率與 Arena 分配（Arena Allocation）&lt;/strong>: 由於可以手動控制記憶體的配置與釋放，能防止垃圾回收（Garbage Collection）所造成無法預測的效能突波。&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>推論管線（Pipeline）整體的流程如下方 Mermaid 圖表所示。這是一連串從使用者的輸入文字開始，直到最終生成下一個 Token 的過程。&lt;/p>
&lt;div class="mermaid">graph TD
A["使用者輸入文字"] --> B["BPE 分詞器"]
B --> C["Token IDs 陣列"]
C --> D["嵌入層查找"]
D --> E["Transformer 區塊"]
E --> F["RMSNorm"]
F --> G["LM Head 層"]
G --> H["Logits 陣列"]
H --> I["採樣模組"]
I --> J["下一個 Token ID"]
J --> K["反分詞器"]
K --> L["輸出文字區塊"]
J -.-> |"附加到上下文"| C&lt;/div>
&lt;p>因為是自迴歸模型，輸出的 Token 會再次被加入到上下文中，作為預測下一個 Token 的輸入進行循環（圖中虛線部分）。&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>: 資料會從磁碟直接讀取到核心的分頁快取（Page Cache）中，不會在使用者空間產生多餘的拷貝。&lt;/li>
&lt;li>&lt;strong>按需載入（Page Fault）&lt;/strong>: 只有在 CPU 實際存取該記憶體位址的瞬間，才會發生分頁錯誤（Page Fault），並僅將需要的區塊（通常是 4KB）載入到實體記憶體中。&lt;/li>
&lt;/ul>
&lt;p>在 Windows 環境中，我們使用 Win32 API 的 &lt;code>CreateFileMapping&lt;/code> 和 &lt;code>MapViewOfFile&lt;/code> 來取代 POSIX 的 &lt;code>mmap&lt;/code>。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant OS["Windows 作業系統"]
participant RAM["實體記憶體"]
participant App["C++ 應用程式"]
App->>OS: "CreateFileMapping / MapViewOfFile"
OS-->>App: "虛擬記憶體位址指標"
App->>App: "在指標處讀取張量資料"
OS->>RAM: "分頁錯誤 / 從磁碟載入分頁"
RAM-->>App: "準備好進行 SIMD 運算的資料"&lt;/div>
&lt;h3 id="32-gguf-格式的二進位結構">3.2 GGUF 格式的二進位結構
&lt;/h3>&lt;p>從 Hugging Face 等平台的 &lt;code>.safetensors&lt;/code> 格式轉換而來的 &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong>，是專為推論打造的終極格式。它具有以下嚴謹的二進位佈局（Binary Layout）：&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>: 張量數量與 Metadata 的鍵值對（Key-Value Pairs）數量。&lt;/li>
&lt;li>&lt;strong>Metadata (Key-Value Pairs)&lt;/strong>: 帶有字串長度前綴的鍵（Key）以及具有型別的值（Value）。&lt;/li>
&lt;li>&lt;strong>Tensor Info&lt;/strong>: 每個張量的名稱、維度數、資料型別（如 FP16, Q4_K 等），以及在檔案內的偏移位置（Offset）。&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>這是一項將 Token 的位置資訊視為在張量空間中的旋轉（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$ 是 Token 的絕對位置索引，$\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 各自擁有相同數量的注意力頭（Head）。然而，TinyLLaMA 為了大幅減少記憶體頻寬與 KV Cache 的消耗，採用了 &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 頭會共用一個 Key/Value 頭。在 C++ 實作中，在執行矩陣乘法 &lt;code>ggml_mul_mat&lt;/code> 之前，必須先執行將 KV 張量依照 Query 數量進行廣播（Broadcast）的操作。&lt;/p>
&lt;h3 id="44-swiglu-激勵函式">4.4 SwiGLU 激勵函式
&lt;/h3>&lt;p>在前饋神經網路（Feed-Forward Network, FFN）層中，使用的是 SwiGLU 而非 GELU。&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 採用「Define-and-Run」的方法，也就是先為推論建構靜態的計算圖，之後再對其進行評估（Evaluate）。&lt;/p>
&lt;h3 id="51-ggml_context-與-arena-allocator">5.1 ggml_context 與 Arena Allocator
&lt;/h3>&lt;p>ggml 最獨特的一點在於「Arena Allocation」，它在推論迴圈內完全不進行動態的記憶體配置（如 &lt;code>malloc&lt;/code> 或 &lt;code>new&lt;/code>）。
在初始化時，會先保留一塊巨大的連續記憶體區域（Arena），每當呼叫 &lt;code>ggml_new_tensor&lt;/code> 等函式時，這塊區域的指標就會遞增。當推論的一個步驟完成後，只需將配置指標重設回初始位置，就能立即完成下一個推論步驟的記憶體配置準備。&lt;/p>
&lt;h3 id="52-計算圖建構的具體範例">5.2 計算圖建構的具體範例
&lt;/h3>&lt;p>在每個推論步驟中，都會在記憶體上組合出如下的計算圖。&lt;/p>
&lt;div class="mermaid">graph TD
A["Tokens 輸入 ID"] --> B["嵌入層查找"]
B --> C["ggml_rms_norm"]
C --> D["Q / K / V 投影"]
D --> E["ggml_rope 位置編碼"]
E --> F["儲存 KV Cache"]
E --> G["載入 KV Cache"]
G --> H["自注意力機制"]
H --> I["縮放與 Softmax"]
I --> J["注意力輸出"]
J --> K["輸出投影"]
K --> L["添加殘差"]&lt;/div>
&lt;hr>
&lt;h2 id="6-量子化-quantization-與-windows--simd-最佳化">6. 量子化 (Quantization) 與 Windows / SIMD 最佳化
&lt;/h2>&lt;p>若以 FP16 來處理 TinyLLaMA (1.1B)，大約需要 2.2GB 的記憶體，但透過 4 位元量子化（如 Q4_K），可以大幅壓縮至約 600MB 左右。&lt;/p>
&lt;h3 id="61-區塊量子化架構">6.1 區塊量子化架構
&lt;/h3>&lt;p>ggml 並非將整個張量統一進行量子化，而是以「區塊（Block）」為單位進行。
在 &lt;code>Q4_0&lt;/code> 格式中，會將 32 個 FP16 數值組成一個區塊。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>縮放因子 (Scale Factor)&lt;/strong>: 1 個 FP16 數值（2 位元組）&lt;/li>
&lt;li>&lt;strong>量子化資料&lt;/strong>: 32 個 4 位元數值（16 位元組）
藉此能將局部異常值（Outlier）的影響降到最低。&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>載入 (Load)&lt;/strong>: 從記憶體將 4 位元量子化資料載入至 256 位元的 AVX 暫存器中。&lt;/li>
&lt;li>&lt;strong>展開與解包 (Unpack)&lt;/strong>: 利用位元遮罩（Bitmask）與移位運算，將 4 位元數值展開為 Int8 或 Int16。&lt;/li>
&lt;li>&lt;strong>反量子化 (Dequantize)&lt;/strong>: 乘上縮放因子將其轉換為浮點數。&lt;/li>
&lt;li>&lt;strong>FMA 運算&lt;/strong>: 將結果與激勵值（Activation）使用 &lt;code>_mm256_fmadd_ps&lt;/code> (Fused Multiply-Add) 平行執行乘加運算。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="7-kv-cache的實作細節">7. KV Cache的實作細節
&lt;/h2>&lt;p>在自迴歸生成中，為了省略過去 Token 的 Key 和 Value 計算，「KV Cache」是不可或缺的功能。&lt;/p>
&lt;p>在 C++ 中實作的重點如下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>預先配置張量&lt;/strong>: 初始化一個對應最大上下文長度（例如：2048 Tokens）的巨大張量作為 KV Cache（建議使用 FP16）。&lt;/li>
&lt;li>&lt;strong>偏移拷貝 (Offset Copy)&lt;/strong>: 當對位置 $N$ 的 Token 進行計算後，將該步驟得到的 K 和 V 向量，使用 &lt;code>ggml_cpy&lt;/code> 等方式儲存到 KV Cache 張量的第 $N$ 列中。&lt;/li>
&lt;li>&lt;strong>注意力機制時建立視圖 (View)&lt;/strong>: 在計算注意力時，建立一個只指向第 0 到第 $N$ 個 Token 部分的「視圖」，並將其傳遞給矩陣乘法操作。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-bpe-tokenizer-與解碼">8. BPE Tokenizer 與解碼
&lt;/h2>&lt;p>將輸入字串視為 UTF-8 位元組序列，並與預先定義好的詞彙表（Vocabulary）進行比對。在 C++ 中，為了加速詞彙表的搜尋，通常會實作 &lt;strong>Trie 樹（前綴樹）&lt;/strong> 或使用優先權佇列（Priority Queue）的演算法。&lt;/p>
&lt;p>從 LM Head 輸出的 Logits 中，會使用 Temperature 參數來縮放機率，再透過 Top-K 提取或 Top-P（Nucleus Sampling）方法來縮小候選範圍，最後使用亂數來決定最終的下一個 Token。&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 Cache 等系統程式設計的精髓，一邊開拓邊緣 AI（Edge AI）的未來吧。&lt;/p></description></item><item><title>【RAG 實作入門】讓本機 AI 讀取自己文件的教學</title><link>http://kenji.blog/zh-tw/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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 存在著明顯的弱點。那就是它們只知道「訓練時的公開資訊」。對於公司內部規定、個人筆記、未公開的專案資料等「私有文件」相關的提問，它們自然無法回答。如果硬要它們回答，就會增加產生與事實不符、似是而非的謊言（幻覺，Hallucination）的風險。&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>（在自己的電腦或地端伺服器上獨立運作的 LLM）來建構「本機 RAG（Local 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 模型，而是多個元件協同運作的系統架構。大致可分為「攝取（資料匯入）階段」與「檢索與生成階段」兩個部分。&lt;/p>
&lt;p>以下的 Mermaid 圖表展示了 RAG 系統的整體樣貌。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "攝取階段 (事前準備)"
Doc["專屬文件 (PDF, TXT 等)"] --> 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 的輸入限制（上下文視窗大小）並提高檢索精準度，將長篇文章分割成有意義的區塊（Chunk）。&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>當向量資料庫在檢索相關文件時，最常使用的距離指標是&lt;strong>餘弦相似度（Cosine Similarity）&lt;/strong>。與歐幾里得距離（空間上的絕對距離）不同，餘弦相似度關注的是「兩個向量之間的夾角」。因為它較不易受文章長度（向量的範數）影響，所以非常適合用於文字的相似度計算。&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>：兩個向量的方向幾乎相同（語意非常相似）。&lt;/li>
&lt;li>&lt;strong>接近 0&lt;/strong>：兩個向量互相垂直（毫無關聯）。&lt;/li>
&lt;li>&lt;strong>接近 -1&lt;/strong>：兩個向量的方向完全相反（語意相反）。&lt;/li>
&lt;/ul>
&lt;p>最近的向量資料庫（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>編排框架 (Orchestration Framework)&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。請先在電腦上安裝 Ollama 並讓它在背景執行。同時，請先在 Ollama 上拉取（pull）好模型（例如：&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"># 每個分塊的最大字元數&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 Chain&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>，可以在下次執行時跳過向量化的過程，直接從磁碟讀取資料庫。&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>：從向量資料庫中，廣泛且初步地取得約 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>還有一種被稱為「語意分塊 (Semantic Chunking)」的技術，不是根據固定的字元數死板地分割文字，而是由 AI 偵測文章語意的轉折點來進行分割。
此外，「父文件檢索 (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 以實用的速度（每秒數十個 Token）運作，必須將模型載入 GPU 的 VRAM 中。要以 fp16（16 位元浮點數）執行 8B 等級的模型大約需要 16GB 的 VRAM。但透過**量化（Quantization）**技術（例如 GGUF 或 AWQ 格式，將其壓縮至 4bit 或 8bit 等），即使是 8GB 的 VRAM（如一般的電競電腦等）也能以足夠快的速度運作。Llama.cpp 或 Ollama 預設都有支援這些量化格式。&lt;/li>
&lt;li>&lt;strong>上下文視窗的限制&lt;/strong>：
如果檢索後取得的上下文數量過多，可能會超過 LLM 的輸入上限（Token 限制），或者模型會忘記資訊中間部分的內容（Lost in the middle 現象）。因此，調整提取的分塊數量，以及導入前述的重新排序（Re-ranking）技術進行嚴格篩選是不可或缺的。&lt;/li>
&lt;li>&lt;strong>資料的新鮮度管理&lt;/strong>：
當來源文件被更新時，必須同步更新或刪除（CRUD 操作）向量資料庫中相對應的向量資料。因為 ChromaDB 支援基於文件 ID 的更新操作，所以管理檔案的雜湊值（Hash），並撰寫批次處理程式來僅同步差異部分是比較實用的做法。&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/zh-tw/p/chatgpt-gemini-claude-api-comparison/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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>本文將針對這三大AI供應商的API，不僅列舉規格，更從開發者視角，深入比較與解說架構設計、詳細的計費結構、延遲（Latency）的數學分析、使用Python與Node.js的具體實作範例，乃至於提示快取（Prompt Caching）等最新的成本最佳化手法。&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）」為使命，始終引領著業界的實質標準（De facto standard）。提供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萬個Token的壓倒性上下文長度（Context Window），能一次處理長篇文件或數小時的影片、音訊。與Google Cloud (Vertex AI)的強大整合能力，對企業而言也極具吸引力。&lt;/p>
&lt;h3 id="13-anthropic-claude">1.3 Anthropic (Claude)
&lt;/h3>&lt;p>Anthropic是由前OpenAI成員所創立的企業，採用獨特的「合憲AI（Constitutional AI）」安全性途徑。Claude 3.5 Sonnet與Opus以其高度的推論能力、程式碼生成能力，以及最重要的是「如同人類般自然的對話」與「極少的幻覺（Hallucination）」，獲得了許多開發者的狂熱支持。&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請求，到以串流（Streaming）方式返回Token的整體流程。&lt;/p>
&lt;div class="mermaid">graph TD
A["用戶端應用程式"] -->|HTTP/REST or gRPC| B["API閘道器"]
B --> C["負載平衡器"]
C --> D["推論叢集"]
D --> E["分詞器 (BPE / SentencePiece)"]
E --> F["KV快取與注意力機制"]
F --> G["Transformer區塊 (前向傳遞)"]
G --> H["輸出層 (Logits)"]
H --> I["採樣器 (Temperature, Top-p, Top-k)"]
I --> J["反分詞器"]
J -->|串流回應 (區塊)| A&lt;/div>
&lt;h3 id="31-tokenization分詞演算法">3.1 Tokenization（分詞）演算法
&lt;/h3>&lt;p>輸入API的文字會在內部被分割為稱為「Token」的單位。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI (tiktoken)&lt;/strong>: 採用Byte-Pair Encoding (BPE)。特別是在英文方面能極高效率地壓縮，但在日文或中文等非字母語言中，Token數量往往會暴增。&lt;/li>
&lt;li>&lt;strong>Google (Gemini)&lt;/strong>: 採用SentencePiece（Unigram Language Model）。對多語言支援度高，即便是日文或中文文字，也傾向於能以較少的Token數來表示。&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude)&lt;/strong>: 使用客製化版本的BPE。強化了多語言支援，Claude 3之後日文與中文的Token效率也有了大幅改善。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-延遲與效能的數學分析">4. 延遲與效能的數學分析
&lt;/h2>&lt;p>在即時應用程式中，延遲（Latency）直接關係到使用者體驗（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): 生成第一個字元所需的時間。很大程度上取決於與提示詞長度（輸入Token數）平方成正比的注意力（Attention）運算成本。&lt;/li>
&lt;li>$N$: 輸出的Token總數。&lt;/li>
&lt;li>$T_{TPOT}$ (Time Per Output Token): 生成單一Token所需的時間。由於是自迴歸（Autoregressive）模型，會依賴先前的輸出進行串行運算。&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$ 為嵌入向量（Embedding Vector）的維度數。受限於此，通常提示詞一旦變長，$T_{TTFT}$ 就會急遽惡化。
然而，Google的Gemini 1.5採用了「Ring Attention」與「Block-wise Compute」等創新最佳化架構，即便輸入高達200萬Token的長文，也成功將生成第一個Token的時間控制在合理範圍（數秒至數十秒）內。&lt;/p>
&lt;hr>
&lt;h2 id="5-計費體系與成本最佳化策略">5. 計費體系與成本最佳化策略
&lt;/h2>&lt;p>API的成本基本上是根據輸入Token數與輸出Token數來計算的。&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）功能，保證回傳100%符合JSON Schema的結果，讓系統整合變得非常容易。&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）兩種方式，初學者可能會稍微感到混亂。然而，面向企業的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三個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;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"># 平行執行三個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-calling">8. 使用Node.js實作工具呼叫 (Tool Calling)
&lt;/h2>&lt;p>為了讓LLM不僅僅是聊天機器人，而是能與外部系統串聯的「AI代理（AI Agent）」，工具呼叫（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也具備同等的工具呼叫功能，儘管在Schema定義方式上有些微差異，但基本流程是共通的。&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萬token">Long Context (如Gemini 1.5 Pro的200萬Token)
&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>: Token消耗量龐大導致成本增加、延遲（$T_{TTFT}$）增加。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>結論&lt;/strong>: 2026年的最佳實務是**「混合式途徑（Hybrid Approach）」**。針對日常的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 "使用者"
participant Client as "前端應用程式"
participant API as "LLM API (多模態)"
User->>Client: 上傳影片與文字提示詞
Client->>API: 傳送影片Bytes/URI + 文字
Note over API: 影片分塊與音訊分離
Note over API: 多模態嵌入模型
API-->>Client: 回傳文字摘要與時間戳記
Client-->>User: 顯示分析結果&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聊天介面不在此限）。&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>
在理解程式碼上下文、重構（Refactoring）以及撰寫自然且具備人性的文章方面，目前展現出最佳表現。API的易用性與提示快取帶來的成本效益也極為出色。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>超長篇文件分析・影片/音訊批次處理&lt;/strong>:
&lt;strong>👑 贏家: Gemini 1.5 Pro (Google)&lt;/strong>
高達200萬Token的上下文長度是無可取代的武器。在需要掌握資料全貌的任務（如分析數百頁的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="推薦多模型路由-multi-model-routing">推薦多模型路由 (Multi-model Routing)
&lt;/h3>&lt;p>未來的趨勢將不再依賴單一API（避免供應商鎖定），而是根據任務難易度或重要性動態切換模型的**「LLM路由」**架構。
例如，對於使用者簡單的提問，使用廉價且快速的 &lt;code>GPT-4o-mini&lt;/code> 或 &lt;code>Gemini 1.5 Flash&lt;/code> 來回應；僅當判斷需要複雜處理時，才將任務回退（Fallback）給 &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/zh-tw/p/llama-cpp-cxx-customization/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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 推理引擎。它最初是為了在 Apple Silicon (M1/M2 Mac) 上高速運行 Meta 的 LLaMA 模型而誕生的，但現在已經支援各種架構和模型。&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>：在將模型權重載入記憶體時，透過利用作業系統的 &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$（在生成階段由於每次處理一個 Token，因此 $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["使用者輸入 (字串)"] --> B["llama.cpp Tokenizer"]
B --> C["Token ID (int32 陣列)"]
C --> D["上下文緩衝區 (KV Cache)"]
D --> E["ggml 計算圖"]
E --> F["Transformer 層"]
subgraph "ggml 引擎"
F --> G["Self-Attention (RoPE)"]
G --> H["前饋神經網路"]
H --> F
end
F --> I["Logits (詞彙表大小)"]
I --> J["採樣器 (Temperature, Top-K, Top-P)"]
J --> K["選定的 Token ID"]
K --> L["llama.cpp Detokenizer"]
L --> M["輸出字串"]
K -. "自迴歸迴圈" .-> D&lt;/div>
&lt;p>文字生成是一個自迴歸迴圈，每輸出一個 Token，它就會作為下一個輸入被加入到 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-必要最少的-include-與設定">5.1 必要最少的 Include 與設定
&lt;/h3>&lt;p>在自己的專案中使用 llama.cpp 時，請 Include 以下內容。&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 Cache）。&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">// 最大上下文大小 (Token 數)
&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-提示詞的-token-化-tokenization">5.3 提示詞的 Token 化 (Tokenization)
&lt;/h3>&lt;p>LLM 並不是直接理解文字，而是將其作為整數 ID（Token）的序列來處理。因此需要將輸入字串轉換為 Token。&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">// 是否在開頭加入特殊 Token（如 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">// 將字串轉換為 Token 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>將 Token 輸入到模型中，獲取下一個 Token 的機率分佈 (Logits)，然後從中進行採樣以決定下一個 Token，如此建構一個迴圈。&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">// 要生成的最大 Token 數
&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">// 將提示詞的 Token 加入批次
&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">// 設定僅在提示詞的最後一個 Token 輸出 Logit (預測結果)
&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. 採樣：根據目前的上下文預測下一個 Token
&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. 如果 Token 是 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. 將 Token 解碼為字串 (文字) 並顯示
&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. 將新生成的 Token 準備為下一個批次
&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 Cache 並預測下一個）
&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> 結構體來管理 Token 群，並透過 &lt;code>llama_decode&lt;/code> 執行神經網路的前向傳播 (Forward Pass)。&lt;/p>
&lt;hr>
&lt;h2 id="6-進階客製化案例透過-c-進行-logit-操作與懲罰控制">6. 進階客製化案例：透過 C++ 進行 Logit 操作與懲罰控制
&lt;/h2>&lt;p>如果不只是單純的文字生成，而是強制輸出特定格式（例如僅限 JSON），或是控制不輸出特定的禁止詞彙時，我們可以在 C++ 端直接操作採樣前的 &lt;strong>Logit (Logits)&lt;/strong>。&lt;/p>
&lt;p>可以獲取模型在輸出每個 Token 之前，原始分數（轉換為機率前的值）的陣列。&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">// 在推理之後、進行採樣之前，取得原始的 Logit 陣列
&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">// 禁止 Token 的 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">// 將禁止 Token 的出現機率設為 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;/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 計算。在處理長上下文（數萬個 Token）時，這是必須的設定。&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年最新】在Windows環境下運行本地LLM的完全指南</title><link>http://kenji.blog/zh-tw/p/local-llm-windows-2026/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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年最新】在Windows環境下運行本地LLM的完全指南" />&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 計算的數學方法、架構的深入理解，以及本地的微調（Fine-tuning），將以壓倒性的豐富內容為您進行徹底解說。&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>：將元數據（Metadata）與張量（Tensor）整合到單一檔案的 GGUF（GPT-Generated Unified Format）已經完全成為業界標準（De facto standard）。這使得使用者只需從 Hugging Face 下載一個檔案，就能在任何環境下執行。&lt;/li>
&lt;li>&lt;strong>MoE（Mixture of Experts）架構的民主化&lt;/strong>：市面上發布了許多小規模卻高效能的 MoE 模型，透過在推論時僅啟動部分專家網路（Expert），在降低消費級 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 的筆記型電腦，利用內建的 NPU（神經網絡處理單元）以低功耗運行小型 LLM（SLM: Small Language Models）的技術也已進入實用階段。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-硬體需求與作業系統準備">2. 硬體需求與作業系統準備
&lt;/h1>&lt;p>為了讓本地 LLM 能以實用的速度（每秒 15 到 30 個 Token 以上）運行，硬體的選擇是最為重要的。&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 推論或卸載（Offload）時會成為決定性的瓶頸。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 版) - CP值最高。非常適合 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)"]
Logits --> Sampler["取樣器 (Temperature, Top-K, Top-P)"]
Sampler --> OutputToken["輸出 Token"]
OutputToken --> |"自迴歸生成"| Tokenizer
OutputToken --> Decoder["反分詞器 (Detokenizer)"]
Decoder --> FinalOutput["最終輸出文字"]&lt;/div>
&lt;h2 id="31-兩個階段prefill-與-decode">3.1 兩個階段：Prefill 與 Decode
&lt;/h2>&lt;p>LLM 的文字生成分為兩個具有不同計算特性的階段。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Prefill（提示詞處理）階段&lt;/strong>：將輸入的完整提示詞一次性處理並進行理解的階段。由於可以平行計算，GPU 的運算能力（FLOPS）將直接影響速度。如果提示詞很長，這個階段可能會需要花費數秒鐘的時間。&lt;/li>
&lt;li>&lt;strong>Decode（Token 生成）階段&lt;/strong>：逐一預測 Token 並將其作為下一次輸入（自迴歸）的階段。因為在這個階段平行計算會受到限制，所以 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）與每個參數所佔的位元組數 $B$ 來進行計算。&lt;/p>
$$
V_{base} = P \times B \quad \text{(GB)}
$$
&lt;p>例如，如果以 FP16（半精度浮點數，16位元=2位元組）載入一個 8B（80億）參數的模型：&lt;/p>
$$
V_{base} = 8 \times 2 = 16 \text{ GB}
$$
&lt;p>也就是說，即使 GPU 擁有 16GB 的 VRAM，光是載入模型就會幾乎達到極限。&lt;/p>
&lt;h2 id="42-量化-quantization-的魔法">4.2 量化 (Quantization) 的魔法
&lt;/h2>&lt;p>這時「量化」就派上用場了。透過降低參數的精度，可以大幅縮減模型大小。以最常見的 4bit 量化（例：Q4_K_M）來說，每個參數平均大約僅佔 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}$（GB）可以用以下的數學公式來表示：&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>整理後，可以使用 Key 和 Value 的注意力頭數量 $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-實踐-1使用-ollama-進行最快最短的環境建置">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>您可以輕鬆建立具有特定人格特質（Persona）的 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 端點（Endpoint）。
只需在 Cursor 或 Continue.dev 等 VS Code 擴充套件的後端設定中，將 URL 指定為上述位置，並將模型名稱指定為 &lt;code>SeniorDev&lt;/code> 等，就能免費實現強大的本地程式碼助手。&lt;/p>
&lt;hr>
&lt;h1 id="6-實踐-2使用-llamacpp-進行極限效能調校">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> 來託管（Host）模型。&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>如果您對命令列（Command Line）有所抗拒，或者想要更直覺地進行 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)"]
Chunking --> EmbedModel["嵌入模型 (Embedding Model)"]
EmbedModel --> VectorDB["向量資料庫 (Vector DB)"]
UserQuery["使用者提問"] --> EmbedQuery["提問的嵌入向量化"]
EmbedQuery --> VectorDB
VectorDB --> |"相似度搜尋"| RetrievedDocs["擷取相關文件"]
UserQuery --> PromptBuilder["生成提示詞 (Prompt)"]
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）來進行微調（Fine-tuning）。在 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 函式庫相比，訓練速度約為兩倍，VRAM 消耗量則減半。只要開啟 Jupyter Notebook 並載入資料集（JSONL 格式），即使是 VRAM 為 12GB 到 16GB 的 RTX 4060 Ti 等顯示卡，也能夠完成幾個 Epoch 的訓練。&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 中，導致被卸載（Offload）到系統記憶體（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>：提示詞模板（Prompt Template）不一致，或是使用了不支援該語言的模型。
&lt;strong>解決方案&lt;/strong>：請使用模型名稱中包含 &lt;code>Instruct&lt;/code> 的版本，並確認工具端是否選擇了模型作者指定的正確模板格式（如 ChatML 或 Llama 3 格式）。&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>活用 &lt;strong>Unsloth (WSL2)&lt;/strong>，培育出擁有您專屬專業知識的客製化 AI。&lt;/li>
&lt;/ol>
&lt;p>AI 的「民主化」不再只是一個流行語（Buzzword），而是真實在您的 Windows 桌面系統上運作的系統。擺脫雲端 API 的使用成本與資料外洩風險，現在就踏入自由且強大的私有 AI 世界吧。&lt;/p></description></item></channel></rss>