<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AN の日記</title><link>https://cayang0802.github.io/</link><description>Recent content on AN の日記</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 12 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://cayang0802.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>優化 RAG 檢索的數個方法</title><link>https://cayang0802.github.io/p/optimize-rag-retrieval-methods/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://cayang0802.github.io/p/optimize-rag-retrieval-methods/</guid><description>&lt;p&gt;RAG (Retrieval Augmented Generation) 透過外部來源取得資訊，並將資訊提供給 LLM 產生回答，成功地解決了 LLM 回答受限於其訓練資料的問題。但是我們希望檢索到的答案是我們想要的，否則只是一坨垃圾話。&lt;/p&gt;
&lt;p&gt;因此這邊整理了一些或許有用的方法，當檢索成效不如預期時，不妨參考看看嘿&lt;/p&gt;
&lt;p&gt;我分成三個階段做說明，分別是&lt;strong&gt;前檢索階段、檢索階段、後檢索階段&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;前檢索階段&lt;/strong&gt;：資料前處理，優化資料索引與使用者查詢&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;檢索階段&lt;/strong&gt;：優化向量搜尋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;後檢索階段&lt;/strong&gt;：過濾檢索到的資訊&lt;/p&gt;
&lt;h2 id="前檢索階段"&gt;&lt;strong&gt;前檢索階段&lt;/strong&gt;
&lt;/h2&gt;&lt;p&gt;此階段重點是如何將資料建立索引 (index)，以及檢索向量前可以優化的方法&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;建立索引&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;滑動視窗 (Sliding Window)&lt;/strong&gt;：相鄰的文字分段 (chunk) 邊緣之間產生重疊部分，避免重要資訊發生在邊緣時被切開來，然後遺失&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意切割邊緣&lt;/strong&gt;：切割時請避免斷在句子一半。以段落結尾為斷點會是相對好的方法。像是 LangChain 的 &lt;em&gt;RecursiveCharacterTextSplitter&lt;/em&gt; 通常比 &lt;em&gt;TokenTextSplitter&lt;/em&gt; 來得好&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;添加 Metadata&lt;/strong&gt;： 附上日期、URL、章節、頁面等資訊，有助於後續檢索能過濾結果&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;優化索引結構&lt;/strong&gt;：使用不同索引技巧，像是使用多種 chunk size 來切分。像是 PDF 內的資料有文字圖片穿插，怎麼切特別重要&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;資料清洗&lt;/strong&gt;：去除垃圾話、驗證事實正確性、更新過時資訊等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;由小到大檢索 (Small-to-Big Retrieval)&lt;/strong&gt;：用一小段中最重要的部分轉化為嵌入作為索引，並將周圍的上下文保留在 metadata 中。生成回應時再提供給 LLM。解決了小區塊不含雜訊但資訊量不足，大區塊資訊充足但雜訊太多的問題。LangChain 可以參考 &lt;em&gt;ParentDocumentRetriever&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;檢索向量前&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;查詢路由 (query routing)&lt;/strong&gt;：當資料爆多時，可以考慮將資料分類。例如資料有文章、程式碼兩種，可以將資料庫分兩個；在使用者問程式碼相關的問題時，將後續檢索範圍導到程式碼資料庫中。增加檢索效率的同時也減少檢索錯誤資料的機會&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查詢改寫 (query rewriting)&lt;/strong&gt;：將使用者的查詢換句話說，但保留原本意思，增加與向量資料庫匹配的機會。例如將較少見的詞彙換成較常見的，擴大搜尋範圍；對於較長的 query 也可以考慮拆成多個 sub-query，分開檢索&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HyDE (Hypothetical Document Embeddings)&lt;/strong&gt;：透過 LLM 先對 query 生成一個假設性回應，然後用原始 query 與上述假設回應進行檢索&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查詢擴展 (query expansion)&lt;/strong&gt;：在使用者的 query 加入額外字詞讓查詢更全面。例如有人搜尋『沙發』，可以加上同義詞像是『長椅』或『Couch』&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;關鍵字過濾&lt;/strong&gt;：先以 LLM 偵測查詢中如果出現特殊關鍵字例如人名地名，可以利用關鍵字過濾向量搜尋的範圍&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="檢索階段"&gt;檢索階段
&lt;/h2&gt;&lt;p&gt;檢索主要有兩種：基於語意的搜索 and 基於關鍵字的搜索&lt;/p&gt;
&lt;p&gt;語意搜索其實就是比對 query vector 與 chunk vector 的向量相似度，相似度越高代表語意越接近&lt;/p&gt;
&lt;p&gt;關鍵字搜索會利用出現文字與出現頻率去計算分數，方法像是 TF-IDF, BM25&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;微調 Embedding 模型&lt;/strong&gt;：向量資料庫是從 embedding 來的，如果應用場合含有大量罕見詞彙，可以考慮微調模型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;利用 LLM 模型引導&lt;/strong&gt;：如果不想微調模型，也可以考慮用 Instructor Embedding Model (例如這個 &lt;a class="link" href="https://huggingface.co/hkunlp/instructor-large" target="_blank" rel="noopener"
 &gt;hkunlp/instructor-large · Hugging Face&lt;/a&gt;) 告訴目前任務背景，在 embedding 時去調整 query 到符合資料庫的資料&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;善用 metadata 過濾與搜尋&lt;/strong&gt;：如果資料庫有附帶 metadata，可以用它幫忙過濾&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;混合搜尋 (hybrid search)&lt;/strong&gt;：語意搜索+關鍵字搜索。兩種搜索各自有優缺點，因此利用兩種搜尋得到各自分數，然後利用 RRF (Reciprocal rank fusion) 根據權重算出最後的排名。權重可以根據領域做調整&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="後檢索階段"&gt;後檢索階段
&lt;/h2&gt;&lt;p&gt;檢索出來的資料會餵給 LLM 協助生成答案。為了避免受到 LLM 的 context window size 等限制，這部分也是可以考慮優化的一個階段&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;提示詞壓縮&lt;/strong&gt;：如果訊息太長，在保留核心訊息的前提下，將廢話去除。著名技術有 LLMLingua&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重新排序 (Re-ranking)&lt;/strong&gt;：將檢索後的資料排名重新排序。利用『使用者輸入』與『每一個檢索片段』的匹配分數去排序。匹配分數可以由 Bi-Encoder (兩個片段的向量相似度) 得到，或是利用 Cross-Encoder (Reranker) 得到。後者效果較好但速度較慢。此外HuggingFace 有提供許多 Reranker。要注意有些 Reranker 不支援中文。針對 Reranker 的研究可以看這一篇 &lt;a class="link" href="https://ihower.tw/blog/12227-reranker" target="_blank" rel="noopener"
 &gt;使用繁體中文評測各家 Reranker 模型的重排能力 – ihower { blogging }&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;另外現在也有基於圖的方法 GraphRAG，又名知識圖譜，著名工具有像是 Neo4j&lt;/p&gt;
&lt;p&gt;不同的應用場景適合不同方法，沒有絕對好的方法，只有多實驗並評估才是真理&lt;/p&gt;</description></item><item><title>【筆記】llama.cpp 的 KV Cache</title><link>https://cayang0802.github.io/p/llamacpp-kvcache/</link><pubDate>Mon, 09 Feb 2026 00:00:00 +0000</pubDate><guid>https://cayang0802.github.io/p/llamacpp-kvcache/</guid><description>&lt;p&gt;前陣子在研究 llama.cpp 框架支援的 KV Cache 操作，順手將一些常用或者有關 KV Cache 的參數記錄一下。預設值用粗體表示，隨時可能會變所以僅供參考。&lt;/p&gt;
&lt;h2 id="llama-server-參數"&gt;llama-server 參數
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/ggml-org/llama.cpp/tree/master/tools/server" target="_blank" rel="noopener"
 &gt;https://github.com/ggml-org/llama.cpp/tree/master/tools/server&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;llama-server -m [模型.gguf] -np 1 -c 2048
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;-m [gguf模型]&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;-np [N]&lt;/em&gt;：Slots 數量（可同時處理的 requests）&lt;/li&gt;
&lt;li&gt;&lt;em&gt;-c [N]&lt;/em&gt;：Context window size。預設 0 表示從模型 metadata 讀取&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="kv-量化"&gt;KV 量化
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;-ctk [f32,&lt;/em&gt; &lt;strong&gt;&lt;em&gt;f16&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1]&lt;/em&gt;：Key cache 量化格式&lt;/li&gt;
&lt;li&gt;&lt;em&gt;-ctv [f32,&lt;/em&gt; &lt;strong&gt;&lt;em&gt;f16&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1]&lt;/em&gt;：Value cache 量化格式&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="offloading"&gt;Offloading
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;-ngl [N|&lt;/em&gt;&lt;strong&gt;&lt;em&gt;auto&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;|all]&lt;/em&gt;：放在 GPU 的模型層數。CPU + GPU 混合推論是 llama.cpp 的特色。auto是2025年底這個 &lt;a class="link" href="https://github.com/ggml-org/llama.cpp/pull/16653" target="_blank" rel="noopener"
 &gt;PR&lt;/a&gt; 新加的…&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;-kvo&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;/-nkvo&lt;/em&gt;：KV Cache 是否要 offload 到 GPU&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ndash;mlock&lt;/em&gt;：把模型檔案 lock 在 RAM，避免被 OS swapping 到 disk&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;&amp;ndash;mmap&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;/&amp;ndash;no-mmap&lt;/em&gt;：是否 mmap 模型，預設開啟（&lt;a class="link" href="https://github.com/ggml-org/llama.cpp/discussions/1876" target="_blank" rel="noopener"
 &gt;甚麼時候會想關掉？例如特定情況想減少 pageout 時&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;&lt;em&gt;-ts [N0,N1,N2]&lt;/em&gt;：模型 offload 到 GPU 的比例，例如雙 GPU 給 [3,1]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="context-shift"&gt;Context Shift
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;Prompt + generated tokens 超過 context window size 就不會報 error 了；反之會自動丟棄最前面的訊息&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Context Shift 示意圖 (by Chia-An Yang)" class="gallery-image" data-flex-basis="273px" data-flex-grow="114" height="1060" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/llamacpp-kvcache/context-shift.png" srcset="https://cayang0802.github.io/p/llamacpp-kvcache/context-shift_hu_dfd762e052e4e6e8.png 800w, https://cayang0802.github.io/p/llamacpp-kvcache/context-shift.png 1210w" width="1210"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ndash;context-shift/&lt;/em&gt;&lt;strong&gt;&lt;em&gt;&amp;ndash;no-context-shift&lt;/em&gt;&lt;/strong&gt; (&lt;a class="link" href="https://github.com/ggml-org/llama.cpp/pull/15416" target="_blank" rel="noopener"
 &gt;2025/08 預設變 disabled&lt;/a&gt;)：解決長文問題；context 超過 window size 時會 truncate&lt;/li&gt;
&lt;li&gt;&lt;em&gt;-keep [N]&lt;/em&gt;：位移時想保留的 initial prompt tokens 數量。預設 0。&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;system prompt 通常不想被丟棄，這時就會用到 keep&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="kv-shifting"&gt;KV Shifting
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;讓 prompt 前綴不用完全一樣也可以複用 KV Cache（模型需要是 RoPE）&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="KV Shifting 示意圖 (by Chia-An Yang)" class="gallery-image" data-flex-basis="580px" data-flex-grow="241" height="436" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/llamacpp-kvcache/kv-shifting.png" srcset="https://cayang0802.github.io/p/llamacpp-kvcache/kv-shifting_hu_8201c1e73b596d6e.png 800w, https://cayang0802.github.io/p/llamacpp-kvcache/kv-shifting.png 1054w" width="1054"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ndash;cache-reuse [N]&lt;/em&gt;：透過 KV Shifting 複用 KV 時的最小 chunk size。預設 0 表示不啟用&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Chunk size 太小有個缺點：復用的 KV Cache 意義上可能會不一樣&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="slots-相關"&gt;Slots 相關
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ndash;slot-save-path [路徑]&lt;/em&gt;：存放 KV Cache of slots 的位置&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="常見-kv-cache-優化-預設都開啟"&gt;常見 KV Cache 優化 (預設都開啟)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;-fa [on|off|&lt;/em&gt;&lt;strong&gt;&lt;em&gt;auto&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;]&lt;/em&gt;：FlashAttention&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;-cb&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;/-nocb&lt;/em&gt;：Continous Batching&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;&amp;ndash;cache-prompt&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;/&amp;ndash;no-cache-prompt&lt;/em&gt;：啟用 Prompt caching（複用同 slot 上一筆 prompt 的 KV Cache）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="server-api-endpoints"&gt;Server API Endpoints
&lt;/h2&gt;&lt;h3 id="post-completion-not-oai-compatible"&gt;POST /completion (not OAI-compatible)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl --request POST \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --url http://localhost:8080/completion \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --header &amp;#34;Content-Type: application/json&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --data &amp;#39;{&amp;#34;prompt&amp;#34;: &amp;#34;1+1=?&amp;#34;, &amp;#34;n_predict&amp;#34;: 128}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;prompt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1+1=?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;n_predict&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;top_k&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;grammar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;root::=[0-9]+&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;cache_prompt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;“cache_prompt”&lt;/em&gt;：啟用 Prompt caching（複用同 slot 上一筆 prompt 的 KV Cache）。&lt;a class="link" href="https://github.com/ggml-org/llama.cpp/pull/10501" target="_blank" rel="noopener"
 &gt;預設打開&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;“id_slot”&lt;/em&gt;：指定的 slot。預設 -1，Server 會選擇 idle slot 中相似度最高的 (common prefix/input tokens)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="kv-slots-操作"&gt;KV slots 操作
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;POST /slots/{id_slot}?action=save&lt;/em&gt; (將 slot N 的 KV cache 寫到指定 filename)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;POST /slots/{id_slot}?action=restore&lt;/em&gt; (將 filename 內的 KV cache 讀到 slot N)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;POST /slots/{id_slot}?action=erase&lt;/em&gt; (刪掉 slot N 的 KV Cache)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="get-slots"&gt;GET /slots
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;查看所有 slots 狀態&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Tokenizer演算法詳解：BPE, WordPiece, Unigram</title><link>https://cayang0802.github.io/p/tokenizer-algorithm/</link><pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate><guid>https://cayang0802.github.io/p/tokenizer-algorithm/</guid><description>&lt;p&gt;&lt;img alt="一個英文句子被 Tokenizer 切割成 tokens。每個顏色表示一個 token，下面是對應的 token ID。" class="gallery-image" data-flex-basis="352px" data-flex-grow="146" height="260" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/tokenizer-algorithm/Tokenizer.png" width="382"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;本文內容難度：★ ★ ☆ ☆ ☆&lt;/p&gt;
&lt;p&gt;建議閱讀對象：想知道 Tokenizer 是如何建立詞彙表（vocabulary），以及如何將句子切割成 subwords 的人。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;大型語言模型 (LLM) 在推論時的第一步為 Tokenization，Tokenizer 會將句子拆解成最小的語意單位（Tokens）。可以是一個單字（word）、一個字母（character）或一個子詞（subword）。來看看有哪些方式吧！&lt;/p&gt;
&lt;h2 id="word-based"&gt;Word-based
&lt;/h2&gt;&lt;p&gt;把句子中的每個單字（word）當成不同 token。使用空白字元與標點符號隔開即可達成。&lt;/p&gt;
&lt;p&gt;&lt;img alt="輸入句子為 “Let’s do tokenization!”，用空白 (也可以加上標點符號一起) 隔開" class="gallery-image" data-flex-basis="1036px" data-flex-grow="431" height="423" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/tokenizer-algorithm/word-based-tokenizer.png" srcset="https://cayang0802.github.io/p/tokenizer-algorithm/word-based-tokenizer_hu_60d7a096ab46e4f1.png 800w, https://cayang0802.github.io/p/tokenizer-algorithm/word-based-tokenizer_hu_a98a8ba600bd741c.png 1600w, https://cayang0802.github.io/p/tokenizer-algorithm/word-based-tokenizer.png 1826w" width="1826"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;優點：簡單&lt;/li&gt;
&lt;li&gt;缺點：如果有 50 萬個英文單字，輸出的 dimension 就高達 50 萬導致參數過大；另外也無法處理不在詞彙表（Out of vocabulary, OOV）的單字&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="character-based"&gt;Character-based
&lt;/h2&gt;&lt;p&gt;把句子中的每個字母（character）切割成不同 token。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Character-based Tokenizer" class="gallery-image" data-flex-basis="2482px" data-flex-grow="1034" height="172" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/tokenizer-algorithm/character-based-tokenizer.png" srcset="https://cayang0802.github.io/p/tokenizer-algorithm/character-based-tokenizer_hu_5b4a7f446840296b.png 800w, https://cayang0802.github.io/p/tokenizer-algorithm/character-based-tokenizer_hu_1a40f6e5c703560f.png 1600w, https://cayang0802.github.io/p/tokenizer-algorithm/character-based-tokenizer.png 1779w" width="1779"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;優點：簡單、字彙量只要 26 個英文字母加特殊符號、不會有 OOV 的問題&lt;/li&gt;
&lt;li&gt;缺點：token 太多導致序列太長，可能會超過上下文上限；單一字母代表的意義不足導致 tokenizer 很難訓練&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上面兩種方法都有明顯的缺點，因此普遍不被使用。接下來要介紹的是目前常見的三種 Subword Tokenizer 方法，會分為&lt;strong&gt;訓練階段&lt;/strong&gt;以及&lt;strong&gt;切割階段&lt;/strong&gt;去說明&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;訓練階段：&lt;/strong&gt; 指定 Vocabulary Size（例如希望最後有 100000 個 token），利用訓練資料 (corpus) 建立出 Vocabulary&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;切割階段：&lt;/strong&gt; 將句子根據 Vocabulary 去切割成 tokens&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;Subword Tokenizer：結合 Word-based 機制只保留常見的單字避免參數過大，同時也保留了 Character-based 的機制，可以把單字拆成更小的單元。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;例如 “unfortunately” 可能被拆成 “un” + “fortunate” + “ly” 共 3 個 tokens。其中字首 un- 與字尾 -ly 都是常見且有意義的。&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;script src="https://gist.github.com/cayang0802/8fcdaf7e62a9005e7df1a0472499efca.js"&gt;&lt;/script&gt;
&lt;center&gt;Subword Tokenizer 的比較&lt;/center&gt;
&lt;h2 id="bpe-byte-pair-encoding"&gt;BPE (Byte-Pair Encoding)
&lt;/h2&gt;&lt;h3 id="a-訓練階段"&gt;A. 訓練階段
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;反覆合併出現頻率最高的 pair 並加入詞彙裡。&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;直接舉個例子～假設今天訓練資料來源如下：&lt;em&gt;“hug”&lt;/em&gt; 出現 10 次，&lt;em&gt;“pug”&lt;/em&gt; 出現 5 次 … 以此類推&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;一開始先把出現過的字母都加進詞彙中&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接著我們會計算詞彙中每個 pair 在訓練資料裡出現的頻率，並挑選頻率最高的合併並加入 vocabulary，並合併 corpus&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 每個 pair 的出現頻率 (由高到低排序)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 將出現頻率最高的 (&amp;#34;ug&amp;#34;) 加入 vocabulary，同時更新 corpus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Corpus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接著就反覆重複這個動作：計算每個 pair 的出現頻率 ➜ 挑選頻率最高的合併並加入 vocabulary 同時合併 corpus …&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 出現頻率 (由高到低排序)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 將出現頻率最高的 &amp;#34;un&amp;#34; 加入 vocabulary，同時更新 corpus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;un&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Corpus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;un&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;un&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;直到 vocabulary size 達到我們設定的目標 (10) 就停止，這就是我們目前 Tokenizer 所有的詞彙了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 這是最終結果 (假設設定的目標為 vocabulary size = 10)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;un&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;到目前為止 Tokenizer 已經訓練好 vocabulary了，現在來看看它是怎麼將句子切割成 subwords&lt;/p&gt;
&lt;h3 id="b-切割階段"&gt;B. 切割階段
&lt;/h3&gt;&lt;p&gt;我們現在給一個句子 &lt;code&gt;&amp;quot;bugs&amp;quot;&lt;/code&gt; ， BPE 用剛剛建立的詞彙表與合併規則去切割 tokens，最後得到結果 &lt;code&gt;[&amp;quot;b&amp;quot;, &amp;quot;ug&amp;quot;, &amp;quot;s&amp;quot;]&lt;/code&gt; ，步驟如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;筆者備註：在這個例子中沒有特別去處理 out of vocabulary 的問題；
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 不過實際應用上不會有 OOV 的問題 (因為訓練資料一定會出現 a-z 與所有符號)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 剛剛建立的詞彙表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;un&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練階段建立的合併規則 (按照產生的先後順序)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mf"&gt;1.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mf"&gt;2.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;un&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mf"&gt;3.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# -------------------------------------------- #&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 給一個輸入句子 s = &amp;#34;bugs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 一開始先拆成最小單位 [&amp;#34;b&amp;#34;, &amp;#34;u&amp;#34;, &amp;#34;g&amp;#34;, &amp;#34;s&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 根據上面的合併規則(1~3)順序，依序檢查是否有出現在句子裡&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 檢查規則1 (u,g) --&amp;gt; 中間有出現，合併它們！&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 檢查規則2 (u,n) --&amp;gt; 沒出現，跳過&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 檢查規則3 (h,ug) --&amp;gt; 沒出現，跳過&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;最後得到結果&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在實務上，BPE 會使用 &lt;code&gt;Ġ&lt;/code&gt; 表示單字前面的空格。例如 &lt;code&gt;&amp;quot;Hello world&amp;quot;&lt;/code&gt; 在最一開始會被解析成兩個單字 &lt;code&gt;(Hello, Ġworld)&lt;/code&gt; ，目的是為了讓 Tokenizer 能夠「看見」空格，以及具有可逆性：如果沒有 &lt;code&gt;Ġ&lt;/code&gt; 來標記空格，當你把 Token 轉換回文字時，會不知道哪裡該加空格。&lt;/p&gt;
&lt;h2 id="wordpiece"&gt;WordPiece
&lt;/h2&gt;&lt;h3 id="a-訓練階段-1"&gt;A. 訓練階段
&lt;/h3&gt;&lt;p&gt;由 Google 發明，用在 BERT 系列模型上。&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;##&lt;/code&gt;來代表該 subword 是前面一個 subword 的&lt;strong&gt;延續&lt;/strong&gt;，它們在原始文本中是連在一起的。例如 “word” 一開始會被切成 &lt;code&gt;w ##o ##r ##d&lt;/code&gt; ；&lt;code&gt;(&amp;quot;play&amp;quot;, &amp;quot;##ing&amp;quot;)&lt;/code&gt; 在原始文本表示單字 &lt;code&gt;playing&lt;/code&gt;，具有可逆性。&lt;/p&gt;
&lt;p&gt;跟 BPE 一樣，WordPiece 學習合併規則。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;跟 BPE 的差別差在 WordPiece 不是選擇最頻繁的 pair 去合併，而是使用以下公式計算 pair 的分數&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 假設 pair 為 &amp;#34;hap&amp;#34;,&amp;#34;py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 分數 = &amp;#34;happy&amp;#34;出現頻率 / (&amp;#34;hap&amp;#34;出現頻率 × &amp;#34;py&amp;#34;出現頻率)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freq_of_pair&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freq_of_first_element&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="n"&gt;freq_of_second_element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;該算法優先合併單個部分在詞彙表中頻率較低的 pair。例如，它不一定會合併 &lt;code&gt;(&amp;quot;un&amp;quot;, &amp;quot;##able&amp;quot;)&lt;/code&gt; 即使它在詞彙表中出現的頻率很高，因為 &lt;code&gt;&amp;quot;un&amp;quot;&lt;/code&gt; 和 &lt;code&gt;&amp;quot;##able&amp;quot;&lt;/code&gt; 很可能頻繁地出現在其他詞中。&lt;/p&gt;
&lt;p&gt;接下來我們以前面 BPE 舉的相同例子進行示範&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;一樣先把出現過的字母都加進詞彙中&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接著利用上面的公式計算每個 pair 的分數，並挑選分數最高的合併並加入 vocabulary，並合併 corpus&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 每個 pair 的分數 (由高到低排序)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 將分數最高的 &amp;#34;##gs&amp;#34; 加入 vocabulary，同時更新 corpus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Corpus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接著就反覆重複這個動作：計算每個 pair 的分數 ➜ 挑選分數最高的合併並加入 vocabulary 同時合併 corpus …&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 每個 pair 的分數 (由高到低排序)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 將分數最高的 &amp;#34;hu&amp;#34; 加入 vocabulary，同時更新 corpus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Corpus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;直到 vocabulary size 達到我們設定的目標 (10) 就停止，這就是我們目前 Tokenizer 所有的詞彙了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 下一輪 pair 的分數 (由高到低排序)，因此第三輪將加入 &amp;#34;hugs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="n"&gt;次&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 這是最終結果 (假設設定的目標為 vocabulary size = 10)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;##gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="b-切割階段-1"&gt;B. 切割階段
&lt;/h3&gt;&lt;p&gt;現在來看看 WordPiece 是怎麼將句子切割成 subwords。前面提到的 BPE 在 tokenization 過程會根據學習到的合併規則去合併，但是 WordPiece 不是！&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;WordPiece 從句子開頭開始，找到 vocabulary 中與句子前綴相同且最長的 subword 然後拆分，反覆直到句尾。&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;一樣舉相同句子 &lt;code&gt;&amp;quot;bugs&amp;quot;&lt;/code&gt;， vocabulary 中 &lt;code&gt;b&lt;/code&gt;開頭只有它自己，所以得到 &lt;code&gt;[&amp;quot;b&amp;quot;, &amp;quot;##ugs&amp;quot;]&lt;/code&gt;。(如果 vocabulary 有&lt;code&gt;&amp;quot;##bu&amp;quot;&lt;/code&gt;則得到的結果不同)&lt;/p&gt;
&lt;p&gt;接著看&lt;code&gt;&amp;quot;##ugs&amp;quot;&lt;/code&gt;，&lt;code&gt;&amp;quot;##u&amp;quot;&lt;/code&gt; 是 vocabulary 中與 &lt;code&gt;&amp;quot;##ugs&amp;quot;&lt;/code&gt; 前綴相同且最長的subword，所以拆分並得到 &lt;code&gt;[&amp;quot;b&amp;quot;, &amp;quot;##u&amp;quot;, &amp;quot;##gs&amp;quot;]&lt;/code&gt; 。最後，&lt;code&gt;&amp;quot;##gs&amp;quot;&lt;/code&gt; 在 vocabulary 中所以不拆，最後結果為 &lt;code&gt;[&amp;quot;b&amp;quot;, &amp;quot;##u&amp;quot;, &amp;quot;##gs&amp;quot;]&lt;/code&gt; 。&lt;/p&gt;
&lt;h2 id="unigram"&gt;Unigram
&lt;/h2&gt;&lt;h3 id="a-訓練階段-2"&gt;A. 訓練階段
&lt;/h3&gt;&lt;p&gt;跟 BPE 和 WordPiece 不同，Unigram 從一個較大的 vocabulary 開始，然後從中刪除 tokens，直到達到所需的 vocabulary size。&lt;/p&gt;
&lt;p&gt;直接使用先前的範例&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我們先列出所有的 substrings 並加入 vocabulary。為了簡化問題所以只保留 strict substrings&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 為了簡化問題，只保留 strict substrings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 排除以下 strings (&amp;#34;hug&amp;#34; 不排除因為它是 &amp;#34;hugs&amp;#34; 的 strict substring)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#39;pug&amp;#39;, &amp;#39;pun&amp;#39;, &amp;#39;ugs&amp;#39;, &amp;#39;hugs&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;un&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 順便算出它們的出現頻率，出現頻率的總和是 210&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#34;p&amp;#34; 出現頻率是 17 所以出現機率是 17/210&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;un&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我們先來為 corpus 的每個 word 定義它的「分數」。我們會對 word 列舉所有的分割方式，並選出其中最高的機率乘積當作「分數」，以 &lt;code&gt;&amp;quot;pug&amp;quot;&lt;/code&gt; 舉例&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#34;pug&amp;#34; 可以切割成三種方式，以下是每種方式的分數&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 「分數」為每個 subword 出現機率的乘積&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;u&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.001321&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.007710&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.007710&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 我們挑分數最高的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我們得到 &lt;code&gt;&amp;quot;pug&amp;quot;&lt;/code&gt; 的「分數」是 0.007709，且我們會傾向將它拆成 &lt;code&gt;(&amp;quot;p&amp;quot;, &amp;quot;ug&amp;quot;)&lt;/code&gt;或&lt;code&gt;(&amp;quot;pu&amp;quot;, &amp;quot;g&amp;quot;)&lt;/code&gt;而不是 &lt;code&gt;(&amp;quot;p&amp;quot;, &amp;quot;u&amp;quot;, &amp;quot;g&amp;quot;)&lt;/code&gt; 。這是每個 word 的分數與拆法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="mf"&gt;0.071428&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;pug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="mf"&gt;0.007710&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;pun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="mf"&gt;0.006168&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;bun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="mf"&gt;0.001451&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;hugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="mf"&gt;0.001701&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;回到目前例子&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 訓練資料 (Corpus)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bun&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;現在要來決定我們要從 vocabulary 中踢掉哪一個 token，我們會檢查&lt;strong&gt;刪除哪個 token 會讓所有 word 的 Loss (NLL, negative log likelihood) 增加最少&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Loss Function (NLL) 為每個 word 的 &lt;code&gt;出現頻率 * -log(該word分數)&lt;/code&gt; 的總和，值為 169.8&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img alt="Unigram 的 Loss Function (NLL)。f 是單字出現頻率，P(W) 是單字的「分數」(i.e. 最佳分割的機率乘積)" class="gallery-image" data-flex-basis="1650px" data-flex-grow="687" height="132" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/tokenizer-algorithm/NLL.png" srcset="https://cayang0802.github.io/p/tokenizer-algorithm/NLL_hu_1b1e772e681b9b3c.png 800w, https://cayang0802.github.io/p/tokenizer-algorithm/NLL.png 908w" width="908"&gt;&lt;/p&gt;
&lt;p&gt;假設我們踢掉 &lt;code&gt;&amp;quot;pu&amp;quot;&lt;/code&gt; ，那 &lt;code&gt;&amp;quot;pug&amp;quot;&lt;/code&gt; 仍然可以拆成 &lt;code&gt;(&amp;quot;p&amp;quot;, &amp;quot;ug&amp;quot;)&lt;/code&gt;並維持相同的分數 (0.7710)。因此踢掉&lt;code&gt;&amp;quot;pu&amp;quot;&lt;/code&gt;將給出完全相同的 Loss&lt;/p&gt;
&lt;p&gt;但如果我們選擇踢掉&lt;code&gt;&amp;quot;hug&amp;quot;&lt;/code&gt; ，那麼 Loss 會增加很多，因為 &lt;code&gt;&amp;quot;hug&amp;quot;&lt;/code&gt; 和 &lt;code&gt;&amp;quot;hugs&amp;quot;&lt;/code&gt; 的標記化會變成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;hug&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;g&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="mf"&gt;0.006802&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;hugs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hu&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="mf"&gt;0.001701&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;這些變化將導致 Loss 上升 23.5&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#34;hug&amp;#34; 增加的Loss&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.071428&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.006802&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;23.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#34;hugs&amp;#34; 則沒有增加Loss，因為 [&amp;#34;hug&amp;#34;, &amp;#34;s&amp;#34;] 與 [&amp;#34;hu&amp;#34;, &amp;#34;gs&amp;#34;] 的 score 都是 0.001701&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;因此我們選擇踢掉 &lt;code&gt;&amp;quot;pu&amp;quot;&lt;/code&gt; 而不是 &lt;code&gt;&amp;quot;hug&amp;quot;&lt;/code&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;Unigram 希望減少 vocabulary 中相似冗餘的 token，確保詞表裡的每一個 token 都是不可或缺的，不希望有「換了別人也能拼出來，且效果差不多」的 Token 存在（在本例&lt;code&gt;&amp;quot;pu&amp;quot;&lt;/code&gt;與其它 token 功能太接近，不需要它）&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;接著就重複上面流程，反覆挑選 token 踢掉，直到 vocabulary size 達到我們設定的目標就可以停止啦～&lt;/p&gt;
&lt;h3 id="b-切割階段-2"&gt;B. 切割階段
&lt;/h3&gt;&lt;p&gt;Unigram 在切割句子中的 word 時，會使用 Viterbi 演算法，以 NLL 最低的方式去切割，有興趣的讀者可以再深入研究&lt;/p&gt;
&lt;p&gt;以上就是 BPE, WordPiece, Unigram 三種演算法的介紹了，希望對大家有幫助～&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://huggingface.co/learn/llm-course/chapter6/1" target="_blank" rel="noopener"
 &gt;Hugging Face LLM Course — chapter 6 (the tokenizers)&lt;/a&gt;&lt;/p&gt;</description></item><item><title>【論文】PagedAttention — 高吞吐量LLM推論框架 vLLM 的設計</title><link>https://cayang0802.github.io/p/paged-attention/</link><pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate><guid>https://cayang0802.github.io/p/paged-attention/</guid><description>&lt;p&gt;&lt;strong&gt;論文重點&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;提出了 PagedAttention 機制管理 KV Cache，大幅提升模型推論時的吞吐量&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;論文原文：&lt;a class="link" href="https://arxiv.org/abs/2309.06180" target="_blank" rel="noopener"
 &gt;Efficient Memory Management for Large Language Model Serving with PagedAttention&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在大型語言模型（Large Language Models, LLM）爆炸式發展的這兩年，大家談論的焦點多半放在模型規模、能力與訓練資料上：參數越來越多、上下文長度越來越長、能做的任務也越來越多。然而，當這些模型真正走出論文、被放進產品裡，例如對話式助理、程式碼自動補全、雲端的文字生成 API，工程團隊很快會遇到另一個現實問題：&lt;strong&gt;推論時 GPU 記憶體的不足，變成推論（Inference）階段真正的瓶頸&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/vllm-project/vllm" target="_blank" rel="noopener"
 &gt;vLLM&lt;/a&gt; 是目前熱門的 AI 推論框架之一，專門針對高吞吐伺服器環境；最初由 UC Berkeley 開發，目前已成為 GitHub 上社群導向的開源專案，至截稿前有 64.9k 的星星與 11.8k 的 Fork，獲得高度關注與採用。&lt;/p&gt;
&lt;p&gt;本文將會介紹 vLLM 創始團隊於 2023 發表的論文。其提出了受到作業系統虛擬記憶體啟發的設計：&lt;strong&gt;PagedAttention&lt;/strong&gt;，解決 LLM 進行推論時，大量 KV Cache 於 GPU 記憶體（VRAM）碎片化而浪費的問題。在開始閱讀之前，建議先了解 Transformer 模型架構中的自注意力機制（self-attention）。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;這邊簡單介紹一下 KV Cache 是什麼；在 Transformer Decoder 模型推論時會逐輪產生 token 與計算句子的 self-attention ，而 Attention 需要句子每個 token 的 Key &amp;amp; Value 向量；圖中上半部描繪了在沒有 Cache 的情況下， Transformer 計算整個句子 Attention 的過程&lt;/p&gt;
&lt;p&gt;&lt;img alt="上方為沒有 KV Cache 時計算 Attention 的過程；下方為有 KV Cache (紫色部分) 時計算 Attention 的過程" class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="1125" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/paged-attention/kv_cache.png" srcset="https://cayang0802.github.io/p/paged-attention/kv_cache_hu_21be847c807d088c.png 800w, https://cayang0802.github.io/p/paged-attention/kv_cache_hu_943b87c32176d7a3.png 1600w, https://cayang0802.github.io/p/paged-attention/kv_cache.png 2000w" width="2000"&gt;&lt;/p&gt;
&lt;p&gt;而 Key &amp;amp; Value 向量是由模型花時間 forward 計算得出的，我們可以把先前計算過的 Key &amp;amp; Value 向量 （上圖紫色部分）存在記憶體中，只需要計算新 token 的 Key &amp;amp; Value 即可，這樣推論時就可以省下很多時間，是目前 LLM 推論階段關鍵的加速技術。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;模型推論時用到的記憶體為了講求快速，都會選擇放在 GPU VRAM上（這邊不考慮 Offloading 到 DRAM/SSD，因為很慢）。&lt;/p&gt;
&lt;p&gt;回到論文，來看看在 vLLM 出現之前，推論系統 Orca （不公開，由論文作者實現）在 KV Cache 利用的表現如何&lt;/p&gt;
&lt;p&gt;&lt;img alt="Orca 在分配記憶體給 sequence 時，出現內部碎片化 (紅色，因為不知道 sequence 最後輸出多長，所以分配過多) 與外部碎片化 (灰色，不同 sequence 記憶體的剩餘空間因為不連續，無法分配給超長句子)" class="gallery-image" data-flex-basis="442px" data-flex-grow="184" height="445" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/paged-attention/vllm-1.png" srcset="https://cayang0802.github.io/p/paged-attention/vllm-1_hu_65fffb674e396a02.png 800w, https://cayang0802.github.io/p/paged-attention/vllm-1.png 821w" width="821"&gt;&lt;/p&gt;
&lt;p&gt;作者分別實現了三種 Orca 版本，分別是 Max（總是分配模型最大句子長度的記憶體）、Pow2（分配output句子長度兩倍的記憶體）、 Oracle（分配output句子剛好的記憶體）。可以看到不管是哪種，KV Cache 都會出現碎片化的問題導致記憶體浪費。來看看 GPU 記憶體有多珍貴？&lt;/p&gt;
&lt;p&gt;論文中以一個 40 GB VRAM 的 NVIDIA A100 GPU 為例子，從下圖左半邊可以看到，目前 LLM 推論時的記憶體用量，模型參數占了65%，KV Cache 占了 &amp;gt;30%，剩下為保留給 activation 的空間。模型參數與 activation 是固定大小，因此可優化的部分就剩 KV Cache 了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="LLM 推論時的記憶體 layout；throughput (batch size) is memory-bounded" class="gallery-image" data-flex-basis="427px" data-flex-grow="178" height="454" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/paged-attention/vllm-2.png" srcset="https://cayang0802.github.io/p/paged-attention/vllm-2_hu_37f3a7d1b718eeab.png 800w, https://cayang0802.github.io/p/paged-attention/vllm-2.png 809w" width="809"&gt;&lt;/p&gt;
&lt;p&gt;從上圖右下方綠線可以看到，Batch size 越大，單位時間內能處理的 requests 就越多，Throughput 就越高。在 40 GB 記憶體的限制下，可以看到藍線（vLLM）相較於橘線 （Orca/others），從 0.3k 提升到 0.9k，總共 3 倍的 Throughput 提升！由此可見避免 KV Cache 白白浪費記憶體有多重要。&lt;/p&gt;
&lt;p&gt;因此作者用了作業系統管理虛擬記憶體的 Paging 技巧，將 KV Cache 分成 “Blocks” 來儲存，稱之為 &lt;strong&gt;“PagedAttention”&lt;/strong&gt;。如下圖，每個 KV Block 包含了 4 個 Tokens （實際上預設是 16，論文 Ablation Study 章節指出 16 在大資料集與小資料集上都達到相對低的 latency），搭配 Block Table（映射表），一個 Logical KV Block 會對應到一個 Physical KV Block （如同虛擬記憶體以 Page 為單位對應到實體記憶體）&lt;/p&gt;
&lt;p&gt;&lt;img alt="靠著 Block Table，將不同 sequence 的 Logical KV Block 對應到相同的 Physical KV Block" class="gallery-image" data-flex-basis="429px" data-flex-grow="179" height="402" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/paged-attention/vllm-3.png" width="720"&gt;&lt;/p&gt;
&lt;p&gt;透過 PagedAttention 在分配 KV Cache 時就能以 Block 為單位分配，因為單位大小固定，可以簡單避免碎片化的問題，不再浪費 GPU 記憶體了！&lt;/p&gt;
&lt;p&gt;從上圖還可以看到，如果不同 sequence 有著完全相同的 KV Block（左方 logical KV block #0 與右方 logical KV block #7），PagedAttention 可以映射到同一個 physical KV block #7，只需要存一份即可！&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️注意⚠️兩個 KV Blocks 前面整段 Tokens 要長度、內容完全一樣才算相同。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Why? 還記得 Transformer 論文提到的 Positional Encoding 嗎？Block 在句子中的絕對位置必須一樣；另外 KV Cache 存在於模型每一層，每層輸入是上一層經過 Attention 機制後的輸出。因此也會受前面 Tokens 影響。&lt;/p&gt;
&lt;p&gt;P.S. 目前普遍的 Encoding 方式是 RoPE&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;在許多場景下，相同前綴 tokens 的多個 sequences 會經常出現。論文中用 Beam search 舉例，如下圖，在搜尋（width=4）時，4 個 sequences 的前綴經常有部分重疊，產生大量相同的 Logical KV Blocks，vLLM 只需要存一份實體的，可以大大減少 GPU 記憶體用量！&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="552px" data-flex-grow="230" height="313" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/paged-attention/vllm-4.png" width="720"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;除了 PagedAttention 架構，論文也針對 Request Scheduling 與 Preemption 兩個策略去說明，基本上目前 LLM 推論框架對這兩項都有參數可以調整&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Request Scheduling：多個 requests 同時正在排隊時，哪一個先做？&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;論文採用了 FCFS (First-Come, First-Served)，先抵達的先做&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vLLM 參數為 &amp;#34;--scheduling-policy&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;Preemption：當產生新 token 且發現 VRAM 不夠時，怎麼辦？&lt;/p&gt;
&lt;p&gt;論文提出兩種方向：Swapping 與 Re-computation&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Swapping (offloading) 就是把暫時用不到的 sequence 的 KV Cache 放到比 VRAM 慢的 DRAM 上，等 VRAM 有空間時再搬回來（當 kv block size 設定較小時表現較差，因為 CPU/GPU 頻繁進行少量數據傳輸，使 PCIE 頻寬無法有效利用；反之較好）&lt;/li&gt;
&lt;li&gt;Re-computation 就是把暫時用不到的 sequence 的 KV Cache 扔了，之後再重新計算（因為一個完整句子的 KV 可以平行計算，在 GPU 算力無限的假設下，只需花費一次 prefilling 的時間）。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vLLM V0 兩個方式都能用，參數為 &amp;#34;--preemption-mode&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vLLM V1 後只支援 Re-computation
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;以上是論文針對 KV Cache 提出的管理方式。此時 KV Cache 只是在一個 request 內複用。&lt;/p&gt;
&lt;p&gt;論文發表時並未實現跨 requests 的 KV Cache 共享。如果新一輪的 request 能夠直接復用上一輪計算好的 KV Cache（例如多輪對話），就能夠大幅降低新一輪 request 的 TTFT，俗稱 Prefix Caching。Prefix Caching 在 SGLang 推論框架&lt;a class="link" href="https://arxiv.org/abs/2312.07104" target="_blank" rel="noopener"
 &gt;論文&lt;/a&gt;中提出，透過 RadixAttention 來實現。&lt;/p&gt;
&lt;p&gt;&lt;img alt="SGLang 首次提出 Prefix Caching。以多輪對話為例，Chat History 的 KV 可以直接複用" class="gallery-image" data-flex-basis="874px" data-flex-grow="364" height="152" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/paged-attention/sglang-1.png" width="554"&gt;&lt;/p&gt;
&lt;p&gt;在 SGLang 論文發表後，vLLM 也透過 Hash RadixAttention 實現了 Prefix Caching（&lt;a class="link" href="https://github.com/vllm-project/vllm/issues/2614" target="_blank" rel="noopener"
 &gt;Issue #2614&lt;/a&gt;），官方稱作 &lt;a class="link" href="https://docs.vllm.ai/en/latest/features/automatic_prefix_caching/" target="_blank" rel="noopener"
 &gt;Automatic Prefix Caching&lt;/a&gt;。&lt;strong&gt;每個 KV Blocks 會以目前+前面所有的 Tokens 經過 Hash 後，用 Hash Key 來檢索；Hash Key 一樣才是相同的 KV Blocks&lt;/strong&gt;。有興趣的讀者可以看&lt;a class="link" href="https://docs.vllm.ai/en/latest/design/prefix_caching/" target="_blank" rel="noopener"
 &gt;官網詳細的說明&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="vLLM 的 Automatic Prefix Caching。句子為 ABC…MNO。Blocks ID#2 是由 A~L 去產生 Hash Key" class="gallery-image" data-flex-basis="1630px" data-flex-grow="679" height="106" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://cayang0802.github.io/p/paged-attention/vllm-5.png" width="720"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;以上就是 vLLM 論文的重點介紹了～&lt;/p&gt;
&lt;p&gt;透過論文，我們可以學到 vLLM 最重要的 PagedAttention 架構，它是如何改善 KV Cache 記憶體分配的，這是未來 LLM 推論框架勢必要面對的一個課題。&lt;/p&gt;
&lt;p&gt;vLLM 從 2023 六月的第一版，到 2024 三月實現了 Prefix Caching，再到 2025 今年陸續把整個架構從 V0 重新翻新變成 V1，程式碼上經歷了許多改變，也多了超多其他 LLM 加速推論的技術，變化很快。&lt;/p&gt;
&lt;p&gt;在 vLLM 2023 推出後同年底，有另一個開源推論框架 &lt;a class="link" href="https://github.com/sgl-project/sglang" target="_blank" rel="noopener"
 &gt;SGLang&lt;/a&gt; 也登場了，使用了 RadixAttention（類似 trie）去管理 KV Cache。這兩個應該算是現在最熱門的 LLM 推論框架了，對最新大模型的支援速度也很快。&lt;/p&gt;</description></item></channel></rss>