<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://globaldevlog.com/zh-Hant/blog</id>
    <title>Global Dev Blog Blog</title>
    <updated>2026-10-21T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://globaldevlog.com/zh-Hant/blog"/>
    <subtitle>Global Dev Blog Blog</subtitle>
    <icon>https://globaldevlog.com/zh-Hant/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[[GitHub Pages 獨立網域 #1] 告別免費 github.io：使用 Porkbun + Cloudflare 綁定自訂網域 (CNAME 扁平化與免費 SSL)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns"/>
        <updated>2026-10-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[徹底告別免費 github.io 二級子網域，透過 Porkbun 購買頂級網域並委派至 Cloudflare Anycast DNS，利用 CNAME 扁平化 (Flattening) 達成 GitHub Pages 零停機平滑遷移，解決 Docusaurus CNAME 自動重設難題的完整工程實戰。]]></summary>
        <content type="html"><![CDATA[<p>對於技術開發者而言，利用 GitHub Pages 的免費二級子網域（<code>username.github.io/project</code>）架設部落格幾乎是零成本的最佳入門選擇。只需一次 Git Push，即可透過全球 CDN 免費分發靜態頁面，完全無需維護底層伺服器。</p>
<p>然而，一旦你計劃打造具有專業影響力的技術品牌並開啟 <strong>Google AdSense（谷歌廣告聯盟）流量變現</strong> 時，就會遭遇一道無法逾越的規則紅線：<strong>Google AdSense 嚴格禁止針對 <code>*.github.io</code> 等免費二級子網域提交網站審查</strong>。此外，搜尋引擎累積的網域權重（Domain Authority）也無法沉澱為個人數位資產。要想通過商業化審查並沉澱 SEO 資產，擁有獨立的**頂級根網域（Apex Custom Domain，例如 <code>globaldevlog.com</code>）**是必經之路。</p>
<p>作為靜態網站工程化系列的第一篇，本文將以我們的技術部落格從 <code>jbjihwan.github.io/global_blog</code> 遷移至 <code>https://globaldevlog.com</code> 的實戰經歷為基礎，詳細拆解由 <strong>Porkbun + Cloudflare + GitHub Pages 構建的高可用三層生產架構與 DNS 深度配置要點</strong>。</p>
<!-- -->
<hr>
<p><img decoding="async" loading="lazy" alt="靜態工程部落格生產級 DNS 與 CDN 路由架構圖" src="https://globaldevlog.com/zh-Hant/assets/images/01_dns_routing_architecture-ea2cb0a9f6fc29f7bd63e0264ab74683.png" width="2200" height="1100" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-架構設計哲學三層專業化高可用體系">1. 架構設計哲學：三層專業化高可用體系<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#1-%E6%9E%B6%E6%A7%8B%E8%A8%AD%E8%A8%88%E5%93%B2%E5%AD%B8%E4%B8%89%E5%B1%A4%E5%B0%88%E6%A5%AD%E5%8C%96%E9%AB%98%E5%8F%AF%E7%94%A8%E9%AB%94%E7%B3%BB" class="hash-link" aria-label="1. 架構設計哲學：三層專業化高可用體系的直接連結" title="1. 架構設計哲學：三層專業化高可用體系的直接連結" translate="no">​</a></h2>
<p>初學者在為 GitHub Pages 綁定獨立網域時，最常規的做法是在網域名稱註冊商處將 <code>A 記錄</code> 直接指向 GitHub 的 4 個固定 IP（<code>185.199.108.153</code> 等）。</p>
<p>這種傳統模式雖然簡單，但存在明顯的生產運維缺陷：</p>
<ul>
<li class="">缺乏邊緣層 DDoS 攻擊防護與惡意爬蟲清洗；</li>
<li class="">遭遇故障或底層遷移時，DNS 生效速度緩慢；</li>
<li class="">無法對靜態資源（CSS/JS/圖片）進行靈活的邊緣 CDN 快取調優。</li>
</ul>
<p>為此，我們將職責解耦為三個專業化的服務層級：</p>
<table><thead><tr><th style="text-align:left">層級</th><th style="text-align:left">選用服務</th><th style="text-align:left">核心職責與入選理由</th></tr></thead><tbody><tr><td style="text-align:left"><strong>1. 網域註冊商 (Registrar)</strong></td><td style="text-align:left"><strong>Porkbun</strong></td><td style="text-align:left">頂級網域首購與每年續費價格全網極具優勢，終身免費贈送 WHOIS 隱私保護，無套路加價</td></tr><tr><td style="text-align:left"><strong>2. 邊緣 DNS 與安全層</strong></td><td style="text-align:left"><strong>Cloudflare</strong></td><td style="text-align:left">全球超低延遲 Anycast DNS，<strong>根網域 CNAME 扁平化 (Flattening)</strong>，免費 Universal SSL，自動化強制 HTTPS</td></tr><tr><td style="text-align:left"><strong>3. 靜態託管源站</strong></td><td style="text-align:left"><strong>GitHub Pages</strong></td><td style="text-align:left">承載由 Docusaurus 編譯產生的純靜態產物，<code>gh-pages</code> 分支自動化部署，零伺服器成本</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-第一步porkbun-網域購買與-cloudflare-權威名稱伺服器委派">2. 第一步：Porkbun 網域購買與 Cloudflare 權威名稱伺服器委派<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#2-%E7%AC%AC%E4%B8%80%E6%AD%A5porkbun-%E7%B6%B2%E5%9F%9F%E8%B3%BC%E8%B2%B7%E8%88%87-cloudflare-%E6%AC%8A%E5%A8%81%E5%90%8D%E7%A8%B1%E4%BC%BA%E6%9C%8D%E5%99%A8%E5%A7%94%E6%B4%BE" class="hash-link" aria-label="2. 第一步：Porkbun 網域購買與 Cloudflare 權威名稱伺服器委派的直接連結" title="2. 第一步：Porkbun 網域購買與 Cloudflare 權威名稱伺服器委派的直接連結" translate="no">​</a></h2>
<ol>
<li class=""><strong>網域選購</strong>：在 Porkbun 檢索並購買心儀的頂級根網域（如 <code>globaldevlog.com</code>）。</li>
<li class=""><strong>接入 Cloudflare</strong>：登入 Cloudflare 控制台，點擊 <strong>[新增網站]</strong> 輸入該網域，方案選擇 <strong>Free (免費版)</strong>。</li>
<li class=""><strong>獲取分配的名稱伺服器</strong>：Cloudflare 會自動分配一對專有的權威 Nameservers，例如：<!-- -->
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">roan.ns.cloudflare.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sneh.ns.cloudflare.com</span><br></div></code></pre></div></div>
</li>
<li class=""><strong>修改 Porkbun DNS 委派</strong>：進入 Porkbun 的網域管理面板，找到 <code>Authoritative Nameservers</code> 設定項，刪除 Porkbun 預設地址，填入 Cloudflare 分配的 2 個伺服器地址並儲存。通常在 5 到 15 分鐘內全球生效。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-第二步cloudflare-cname-扁平化-flattening-與-full-ssl-模式">3. 第二步：Cloudflare CNAME 扁平化 (Flattening) 與 Full SSL 模式<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#3-%E7%AC%AC%E4%BA%8C%E6%AD%A5cloudflare-cname-%E6%89%81%E5%B9%B3%E5%8C%96-flattening-%E8%88%87-full-ssl-%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="3. 第二步：Cloudflare CNAME 扁平化 (Flattening) 與 Full SSL 模式的直接連結" title="3. 第二步：Cloudflare CNAME 扁平化 (Flattening) 與 Full SSL 模式的直接連結" translate="no">​</a></h2>
<p>按照 DNS 國際標準（RFC 1034），根網域（<code>@</code>）必須包含 SOA 和 NS 記錄，因此傳統上無法直接配置 <code>CNAME 記錄</code>。</p>
<p>Cloudflare 的 <strong>CNAME 扁平化 (CNAME Flattening)</strong> 技術徹底解決了這一痛點：在 DNS 解析查詢時，Cloudflare 邊緣節點會在底層即時解析目標地址（<code>username.github.io</code>）對應的 IP，並以 <code>A 記錄</code> 的形式直接向客戶端應答，從而讓根網域完美享有 CNAME 的所有敏捷特性。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-配置-cloudflare-dns-解析記錄">A. 配置 Cloudflare DNS 解析記錄<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#a-%E9%85%8D%E7%BD%AE-cloudflare-dns-%E8%A7%A3%E6%9E%90%E8%A8%98%E9%8C%84" class="hash-link" aria-label="A. 配置 Cloudflare DNS 解析記錄的直接連結" title="A. 配置 Cloudflare DNS 解析記錄的直接連結" translate="no">​</a></h3>
<p>在 Cloudflare 儀表板 <strong>[DNS] &gt; [記錄]</strong> 中，新增以下兩條開啟代理的記錄：</p>
<table><thead><tr><th style="text-align:left">類型 (Type)</th><th style="text-align:left">名稱 (Name)</th><th style="text-align:left">目標 (Target)</th><th style="text-align:left">代理狀態 (Proxy status)</th><th style="text-align:left">TTL</th></tr></thead><tbody><tr><td style="text-align:left"><strong>CNAME</strong></td><td style="text-align:left"><code>@</code></td><td style="text-align:left"><code>jbjihwan.github.io</code></td><td style="text-align:left"><strong>已代理 (點亮橘色雲朵)</strong></td><td style="text-align:left">自動</td></tr><tr><td style="text-align:left"><strong>CNAME</strong></td><td style="text-align:left"><code>www</code></td><td style="text-align:left"><code>jbjihwan.github.io</code></td><td style="text-align:left"><strong>已代理 (點亮橘色雲朵)</strong></td><td style="text-align:left">自動</td></tr></tbody></table>
<blockquote>
<p><strong>必須點亮橘色雲朵</strong>：只有處於代理模式下，造訪流量才會經由 Cloudflare 全球 Anycast 節點接入，享受自動化邊緣 SSL 終結、Brotli 極速壓縮以及抗 DDOS 保護。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-設定-ssltls-加密模式">B. 設定 SSL/TLS 加密模式<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#b-%E8%A8%AD%E5%AE%9A-ssltls-%E5%8A%A0%E5%AF%86%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="B. 設定 SSL/TLS 加密模式的直接連結" title="B. 設定 SSL/TLS 加密模式的直接連結" translate="no">​</a></h3>
<p>前往 Cloudflare 左側導覽列 <strong>[SSL/TLS] &gt; [概述]</strong>：</p>
<ul>
<li class="">將加密模式設為 <strong>Full (完全)</strong> 或 <strong>Full (Strict) (完全嚴格)</strong>；</li>
<li class="">確保從訪客瀏覽器到 Cloudflare、以及從 Cloudflare 到 GitHub Pages 源站的端到端 HTTPS 雙向鏈路均得到高強度加密。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-第三步更新-docusaurus-核心配置-docusaurusconfigts">4. 第三步：更新 Docusaurus 核心配置 (<code>docusaurus.config.ts</code>)<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#4-%E7%AC%AC%E4%B8%89%E6%AD%A5%E6%9B%B4%E6%96%B0-docusaurus-%E6%A0%B8%E5%BF%83%E9%85%8D%E7%BD%AE-docusaurusconfigts" class="hash-link" aria-label="4-第三步更新-docusaurus-核心配置-docusaurusconfigts的直接連結" title="4-第三步更新-docusaurus-核心配置-docusaurusconfigts的直接連結" translate="no">​</a></h2>
<p>當部落格從子目錄路徑（<code>/global_blog/</code>）整體遷移至獨立根網域時，必須相應修正 <code>docusaurus.config.ts</code>：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  title</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Global Dev Blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  tagline</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Engineering Deep Dives in AI, Robotics, Computer Vision'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 替換為全新的頂級自訂網域</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://globaldevlog.com'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 從子目錄 '/global_blog/' 改為頂級根路徑 '/'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  baseUrl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  organizationName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'jbjihwan'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  projectName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'global_blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  trailingSlash</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-第四步徹底攻克-github-pages-部署後網域被重設的隱蔽陷阱-staticcname">5. 第四步：徹底攻克 GitHub Pages 部署後網域被重設的隱蔽陷阱 (<code>static/CNAME</code>)<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#5-%E7%AC%AC%E5%9B%9B%E6%AD%A5%E5%BE%B9%E5%BA%95%E6%94%BB%E5%85%8B-github-pages-%E9%83%A8%E7%BD%B2%E5%BE%8C%E7%B6%B2%E5%9F%9F%E8%A2%AB%E9%87%8D%E8%A8%AD%E7%9A%84%E9%9A%B1%E8%94%BD%E9%99%B7%E9%98%B1-staticcname" class="hash-link" aria-label="5-第四步徹底攻克-github-pages-部署後網域被重設的隱蔽陷阱-staticcname的直接連結" title="5-第四步徹底攻克-github-pages-部署後網域被重設的隱蔽陷阱-staticcname的直接連結" translate="no">​</a></h2>
<p>在配置 GitHub Pages 自訂網域時，幾乎所有開發者都踩過這個著名的“大坑”：</p>
<ol>
<li class="">在 GitHub 儲存庫頁面的 <code>Settings &gt; Pages &gt; Custom domain</code> 中手動填入 <code>globaldevlog.com</code> 並儲存生效；</li>
<li class="">本地寫完新文章，終端執行 <code>npm run deploy</code> 重新部署；</li>
<li class="">部署一完成，重新整理 GitHub 頁面發現自訂網域輸入框<strong>再次變成了空白</strong>，線上網站瞬間爆出 404 錯誤或 SSL 憑證失效警告！</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="陷阱根源">陷阱根源<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#%E9%99%B7%E9%98%B1%E6%A0%B9%E6%BA%90" class="hash-link" aria-label="陷阱根源的直接連結" title="陷阱根源的直接連結" translate="no">​</a></h3>
<p>Docusaurus 的發布指令碼在編譯完成後，會將 <code>build/</code> 目錄強制推送到 <code>gh-pages</code> 分支（<code>git push --force</code>）。如果該打包產物的根目錄下缺少純文字 <code>CNAME</code> 檔案，GitHub 會預設判定使用者已主動取消了自訂網域綁定。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="永久解決方案">永久解決方案<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#%E6%B0%B8%E4%B9%85%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%A1%88" class="hash-link" aria-label="永久解決方案的直接連結" title="永久解決方案的直接連結" translate="no">​</a></h3>
<p>在專案的 <code>static/</code> 目錄下建立一個名為 <code>CNAME</code> 的純文字檔案（無任何副檔名），內容僅寫入一行你的網域：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">globaldevlog.com</span><br></div></code></pre></div></div>
<p>由於 Docusaurus 在建構時會自動將 <code>static/</code> 目錄下的所有檔案原封不動複製到打包輸出根目錄，因此每次執行部署時，<code>CNAME</code> 檔案都會被永久提交至 <code>gh-pages</code> 分支的最頂層，網域綁定永不丟失。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-生產環境連通性驗證">6. 生產環境連通性驗證<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#6-%E7%94%9F%E7%94%A2%E7%92%B0%E5%A2%83%E9%80%A3%E9%80%9A%E6%80%A7%E9%A9%97%E8%AD%89" class="hash-link" aria-label="6. 生產環境連通性驗證的直接連結" title="6. 生產環境連通性驗證的直接連結" translate="no">​</a></h2>
<p>在專案根目錄下執行部署指令：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm run deploy</span><br></div></code></pre></div></div>
<p>部署成功後，在終端機透過 curl 命令驗證 HTTP 狀態碼與 SSL 憑證鏈回應：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://globaldevlog.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HTTP/2 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># server: cloudflare</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># strict-transport-security: max-age=31536000</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://www.globaldevlog.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HTTP/2 200 (或 301 重新導向至根網域)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-下篇預告">🔜 下篇預告<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-cloudflare-dns#-%E4%B8%8B%E7%AF%87%E9%A0%90%E5%91%8A" class="hash-link" aria-label="🔜 下篇預告的直接連結" title="🔜 下篇預告的直接連結" translate="no">​</a></h2>
<p>獨立網域成功上線後，真正的商業化與多引擎搜尋獲客工作才剛剛拉開序幕：</p>
<ul>
<li class=""><strong>第二篇</strong>：<em>[靜態網站商業化 #2] Docusaurus 接入 Google AdSense 審查實戰：解決 ads.txt 404 爬取故障與非同步指令碼安全植入</em></li>
<li class=""><strong>第三篇</strong>：<em>[全球多搜尋引擎 SEO #3] Google、Naver 與 Bing 站長平台權威配置：Sitemap 無法刪除之謎與主機層級報錯排查</em></li>
</ul>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="github-pages" term="github-pages"/>
        <category label="custom-domain" term="custom-domain"/>
        <category label="porkbun" term="porkbun"/>
        <category label="cloudflare" term="cloudflare"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="devops" term="devops"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[網站收益化 #2] 靜態網站 Google AdSense 審核自動化：破解 ads.txt 404 抓取陷阱與 HeadTags 整合實戰]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide"/>
        <updated>2026-10-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度解析 Docusaurus 靜態部落格中 Google AdSense 所有權驗證中繼標籤的高效能注入、static/ads.txt 根路徑部署及 Cloudflare WAF 爬蟲防誤殺設定全流程。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Google AdSense 審核與爬蟲抓取管線" src="https://globaldevlog.com/zh-Hant/assets/images/01_ads_txt_crawler_flow-8f17d9e45c07877c55ba651d6017360f.png" width="2200" height="1100" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心重點 (TL;DR)</strong></p>
<ul>
<li class=""><strong>問題 (Problem)</strong>：與傳統動態 CMS (WordPress) 不同，代管在 GitHub Pages 或 Cloudflare Pages 上的 Docusaurus、Next.js 等靜態網站產生器 (SSG) 極易遭遇惡名昭彰的 <strong>「收益受損風險：缺少 ads.txt 檔案」</strong> 警報，並因客戶端融合延遲或 WAF 誤殺導致所有權審核失敗。</li>
<li class=""><strong>解決方案 (Solution)</strong>：利用 Docusaurus 原生的 <code>headTags</code> 設定將驗證中繼標籤直接注入靜態 HTML，透過 <code>static/ads.txt</code> 實作根目錄資產部署，並在 Cloudflare WAF 中為 <code>Googlebot-AdsTxt</code> 抓取爬蟲設定放行白名單。</li>
<li class=""><strong>實驗結果 (Result)</strong>：實作爬蟲 100% 順暢存取、<code>/ads.txt</code> 零 404 回應，在確保 Lighthouse 效能與 SEO 得分穩定在 95 分以上的同時，一次性符合 AdSense 審核規範。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-靜態網站-ssg-整合-adsense-面臨的獨特挑戰">1. 靜態網站 (SSG) 整合 AdSense 面臨的獨特挑戰<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#1-%E9%9D%9C%E6%85%8B%E7%B6%B2%E7%AB%99-ssg-%E6%95%B4%E5%90%88-adsense-%E9%9D%A2%E8%87%A8%E7%9A%84%E7%8D%A8%E7%89%B9%E6%8C%91%E6%88%B0" class="hash-link" aria-label="1. 靜態網站 (SSG) 整合 AdSense 面臨的獨特挑戰的直接連結" title="1. 靜態網站 (SSG) 整合 AdSense 面臨的獨特挑戰的直接連結" translate="no">​</a></h2>
<p>將開發部落格從預設的子路徑 (<code>username.github.io/blog</code>) 遷移至獨立頂級網域 (<code>https://globaldevlog.com</code>) 後，下一步核心戰略便是接入 <strong>Google AdSense 商業化廣告</strong>。然而，靜態網站在架構上面臨著以下特殊瓶頸：</p>
<ol>
<li class=""><strong>缺乏伺服器端資料庫與動態外掛程式支援</strong>：WordPress 可以透過 Site Kit 外掛程式一鍵注入程式碼並產生 <code>ads.txt</code>，而在 Docusaurus 中，所有資源必須在編譯階段嚴格固化。</li>
<li class=""><strong>客戶端融合 vs 靜態 Head 注入</strong>：若透過 React 元件生命週期動態載入指令碼，搜尋引擎爬蟲在執行 JavaScript 之前可能就因抓取逾時而判定審核失敗。</li>
<li class=""><strong><code>ads.txt</code> 404 抓取陷阱</strong>：編譯產物路徑錯配將導致 AdSense 爬蟲 (<code>Googlebot-AdsTxt</code>) 存取 <code>https://yourdomain.com/ads.txt</code> 時傳回 404，導致網域收益被全量凍結。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-第一步在-docusaurus-中注入所有權驗證中繼標籤">2. 第一步：在 Docusaurus 中注入所有權驗證中繼標籤<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#2-%E7%AC%AC%E4%B8%80%E6%AD%A5%E5%9C%A8-docusaurus-%E4%B8%AD%E6%B3%A8%E5%85%A5%E6%89%80%E6%9C%89%E6%AC%8A%E9%A9%97%E8%AD%89%E4%B8%AD%E7%B9%BC%E6%A8%99%E7%B1%A4" class="hash-link" aria-label="2. 第一步：在 Docusaurus 中注入所有權驗證中繼標籤的直接連結" title="2. 第一步：在 Docusaurus 中注入所有權驗證中繼標籤的直接連結" translate="no">​</a></h2>
<p>在 AdSense 主控台（<strong>網站 &gt; 新增網站</strong>）提交網域後，Google 會要求驗證網站所有權。對於靜態部落格而言，<strong>透過 <code>docusaurus.config.ts</code> 的 <code>headTags</code> 選項注入靜態 <code>&lt;meta&gt;</code> 標籤</strong>是最優雅且零效能損耗的解決方案。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-設定檔程式碼-docusaurusconfigts">📐 設定檔程式碼 (<code>docusaurus.config.ts</code>)<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#-%E8%A8%AD%E5%AE%9A%E6%AA%94%E7%A8%8B%E5%BC%8F%E7%A2%BC-docusaurusconfigts" class="hash-link" aria-label="-設定檔程式碼-docusaurusconfigts的直接連結" title="-設定檔程式碼-docusaurusconfigts的直接連結" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">Config</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@docusaurus/types'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  title</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Global Dev Blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://globaldevlog.com'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  baseUrl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 注入 Google AdSense 所有權驗證中繼標籤</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  headTags</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      tagName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'meta'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      attributes</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'google-adsense-account'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ca-pub-XXXXXXXXXXXXXXXX'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 替換為您的 AdSense 發布商 ID</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... 其它設定項</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-為何優先選用-headtags-而非-scripts">💡 為何優先選用 <code>headTags</code> 而非 <code>scripts</code>？<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#-%E7%82%BA%E4%BD%95%E5%84%AA%E5%85%88%E9%81%B8%E7%94%A8-headtags-%E8%80%8C%E9%9D%9E-scripts" class="hash-link" aria-label="-為何優先選用-headtags-而非-scripts的直接連結" title="-為何優先選用-headtags-而非-scripts的直接連結" translate="no">​</a></h3>
<ul>
<li class=""><strong>零執行階段效能開銷</strong>：無需在審核初期預先載入重達約 120 KB 的 <code>adsbygoogle.js</code> 指令碼套件，避免拖慢 PageSpeed 跑分。</li>
<li class=""><strong>100% 確定性抓取</strong>：中繼標籤直接編譯嵌入到伺服器端預渲染的靜態檔案 (<code>build/index.html</code>) 中，確保 Google 審核機器人瞬間驗證通過。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-第二步徹底根除-adstxt-404-抓取陷阱">3. 第二步：徹底根除 ads.txt 404 抓取陷阱<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#3-%E7%AC%AC%E4%BA%8C%E6%AD%A5%E5%BE%B9%E5%BA%95%E6%A0%B9%E9%99%A4-adstxt-404-%E6%8A%93%E5%8F%96%E9%99%B7%E9%98%B1" class="hash-link" aria-label="3. 第二步：徹底根除 ads.txt 404 抓取陷阱的直接連結" title="3. 第二步：徹底根除 ads.txt 404 抓取陷阱的直接連結" translate="no">​</a></h2>
<p>提交審核後，發布商最常遭遇的紅色警報莫過於：</p>
<blockquote>
<p>⚠️ <em>「收益受損風險：您需要解決一些 ads.txt 檔案問題，以免對您的收入造成嚴重影響。」</em></p>
</blockquote>
<p>其誘因極其明確：Google 的 <code>Googlebot-AdsTxt</code> 爬蟲請求 <code>https://globaldevlog.com/ads.txt</code> 時遭遇了 HTTP 404 錯誤。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-基於-staticadstxt-實作根目錄直接部署">🛠️ 基於 <code>static/ads.txt</code> 實作根目錄直接部署<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#%EF%B8%8F-%E5%9F%BA%E6%96%BC-staticadstxt-%E5%AF%A6%E4%BD%9C%E6%A0%B9%E7%9B%AE%E9%8C%84%E7%9B%B4%E6%8E%A5%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="️-基於-staticadstxt-實作根目錄直接部署的直接連結" title="️-基於-staticadstxt-實作根目錄直接部署的直接連結" translate="no">​</a></h3>
<p>Docusaurus 在執行編譯時，會將專案根目錄下 <code>static/</code> 中的所有檔案直接原樣複製到產物根路徑 (<code>build/</code>)。</p>
<ol>
<li class="">
<p>新增 <code>static/ads.txt</code> 檔案：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0</span><br></div></code></pre></div></div>
<p><em>(請將 <code>pub-XXXXXXXXXXXXXXXX</code> 替換為您個人的發布商 ID)</em></p>
</li>
<li class="">
<p>確認檔案並部署上線：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm run deploy</span><br></div></code></pre></div></div>
</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-部署後連通性驗證">🔍 部署後連通性驗證<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#-%E9%83%A8%E7%BD%B2%E5%BE%8C%E9%80%A3%E9%80%9A%E6%80%A7%E9%A9%97%E8%AD%89" class="hash-link" aria-label="🔍 部署後連通性驗證的直接連結" title="🔍 部署後連通性驗證的直接連結" translate="no">​</a></h3>
<p>在終端機中利用 <code>curl</code> 檢查回應標頭：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://globaldevlog.com/ads.txt</span><br></div></code></pre></div></div>
<p>標準回應輸出：</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">HTTP/2 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">content-type: text/plain; charset=utf-8</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cache-control: max-age=600</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">server: cloudflare</span><br></div></code></pre></div></div>
<p>同時請確認存取 <code>https://www.globaldevlog.com/ads.txt</code> 時能正常執行 301/308 重新導向至根網域。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-第三步cloudflare-waf-與邊緣安全防護調優">4. 第三步：Cloudflare WAF 與邊緣安全防護調優<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#4-%E7%AC%AC%E4%B8%89%E6%AD%A5cloudflare-waf-%E8%88%87%E9%82%8A%E7%B7%A3%E5%AE%89%E5%85%A8%E9%98%B2%E8%AD%B7%E8%AA%BF%E5%84%AA" class="hash-link" aria-label="4. 第三步：Cloudflare WAF 與邊緣安全防護調優的直接連結" title="4. 第三步：Cloudflare WAF 與邊緣安全防護調優的直接連結" translate="no">​</a></h2>
<p>若您的網域開啟了 Cloudflare 的橘色雲朵代理，其內建的 Bot 防禦規則可能會意外攔截 Google 的自動抓取程式。</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart LR</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Bot["Googlebot-AdsTxt 爬蟲"] --&gt; Edge["Cloudflare 邊緣 WAF"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Edge --&gt;|"校驗 User-Agent 與 ASN"| Rule{"WAF 安全防火牆規則"}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Rule --&gt;|"Bot Fight Mode (誤報)"| Drop["403 Forbidden / 質詢攔截"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Rule --&gt;|"放行繞過規則"| Origin["GitHub Pages 來源站 (/ads.txt)"]</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloudflare-waf-自訂規則設定">Cloudflare WAF 自訂規則設定<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#cloudflare-waf-%E8%87%AA%E8%A8%82%E8%A6%8F%E5%89%87%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="Cloudflare WAF 自訂規則設定的直接連結" title="Cloudflare WAF 自訂規則設定的直接連結" translate="no">​</a></h3>
<ul>
<li class=""><strong>規則運算式</strong>：<!-- -->
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">(http.user_agent contains "Googlebot-AdsTxt") or (http.user_agent contains "Mediapartners-Google")</span><br></div></code></pre></div></div>
</li>
<li class=""><strong>執行動作</strong>：<em>跳過 (Skip)</em> — 繞過 WAF 質詢及速率限制，確保爬蟲順暢擷取 <code>ads.txt</code>。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-adsense-首次審核通關必備四大準則">5. AdSense 首次審核通關必備四大準則<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#5-adsense-%E9%A6%96%E6%AC%A1%E5%AF%A9%E6%A0%B8%E9%80%9A%E9%97%9C%E5%BF%85%E5%82%99%E5%9B%9B%E5%A4%A7%E6%BA%96%E5%89%87" class="hash-link" aria-label="5. AdSense 首次審核通關必備四大準則的直接連結" title="5. AdSense 首次審核通關必備四大準則的直接連結" translate="no">​</a></h2>
<table><thead><tr><th style="text-align:left">評審維度</th><th style="text-align:left">必須達標項</th><th style="text-align:left">違背時的典型拒審理由</th></tr></thead><tbody><tr><td style="text-align:left"><strong>1. 必備法律政策頁面</strong></td><td style="text-align:left">完備的 <code>/privacy-policy</code>、<code>/about</code> 與 <code>/contact</code> 頁面</td><td style="text-align:left">違反網站導覽政策</td></tr><tr><td style="text-align:left"><strong>2. 原創深度技術文章量</strong></td><td style="text-align:left">至少 20~30 篇高品質原創技術長文（單篇 1,500 字以上）</td><td style="text-align:left">低價值內容 / 內容匱乏</td></tr><tr><td style="text-align:left"><strong>3. 零失效死鏈</strong></td><td style="text-align:left">0 個 404 錯誤連結，無未定義錨點 (#)</td><td style="text-align:left">網站無法正常運作</td></tr><tr><td style="text-align:left"><strong>4. 行動端自適應版面配置</strong></td><td style="text-align:left">手機端無水平橫向捲動與文字重疊</td><td style="text-align:left">行動端易用性差</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-總結與後續規劃">6. 總結與後續規劃<a href="https://globaldevlog.com/zh-Hant/blog/static-site-adsense-ads-txt-guide#6-%E7%B8%BD%E7%B5%90%E8%88%87%E5%BE%8C%E7%BA%8C%E8%A6%8F%E5%8A%83" class="hash-link" aria-label="6. 總結與後續規劃的直接連結" title="6. 總結與後續規劃的直接連結" translate="no">​</a></h2>
<p>透過 <code>headTags</code> 完成中繼標籤校驗、<code>static/ads.txt</code> 根目錄部署以及 Cloudflare WAF 爬蟲白名單調優，您的靜態技術部落格現已具備堅不可摧的 AdSense 準入技術條件。</p>
<ul>
<li class="">在下一期 <strong>第 3 篇</strong> 中，我們將深入剖析 Google Search Console、Naver Search Advisor 與 Bing Webmaster Tools 的 6 語言站台地圖多路提交與 SEO 自動化管線！</li>
</ul>
<hr>
<blockquote>
<p>📌 <strong>Global Dev Blog 精選技術推薦</strong>：<br>
<!-- -->• <a href="https://globaldevlog.com/ko/blog/github-pages-custom-domain-cloudflare-dns" target="_blank" rel="noopener noreferrer" class="">Global Dev Blog - GitHub Pages 綁定自訂網域與 Cloudflare DNS 實戰</a><br>
<!-- -->• <a href="https://globaldevlog.com/ko/blog/static-site-security-npm-audit-truth" target="_blank" rel="noopener noreferrer" class="">Global Dev Blog - 靜態網站安全強化與 npm audit 弱點真相</a><br>
<!-- -->• <a href="https://globaldevlog.com/" target="_blank" rel="noopener noreferrer" class="">存取支援 6 大語種的 Global Dev Blog 首頁</a></p>
</blockquote>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="google-adsense" term="google-adsense"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="github-pages" term="github-pages"/>
        <category label="web-monetization" term="web-monetization"/>
        <category label="devops" term="devops"/>
        <category label="cloudflare" term="cloudflare"/>
        <category label="seo" term="seo"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[從 GitHub Pages 到獨立頂級網域：Porkbun、Cloudflare DNS、AdSense 審核通過與多引擎 SEO 全方位指南]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide"/>
        <updated>2026-10-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[將 GitHub Pages 上的 Docusaurus 靜態部落格遷移至 Porkbun + Cloudflare DNS 獨立頂級網域，配置 Google AdSense 審核規範並攻克 Google、Naver、Bing 三大搜尋引擎生產索引故障的全景工程實戰。]]></summary>
        <content type="html"><![CDATA[<p>在 GitHub Pages 的二級子網域（<code>username.github.io/project</code>）下託管技術部落格，對於開發者而言是一種極其敏捷的分享途徑。然而，隨著技術讀者群的擴大以及開啟商業化變現的需要，你會撞上一堵堅硬的規則高牆：<strong>Google AdSense（谷歌廣告聯盟）嚴格禁止針對 <code>*.github.io</code> 等免費二級子網域提交網站申請</strong>。為了取得 AdSense 官方發布商資格、累積長期的網域權威度（Domain Authority）並獲取全球各區域的自然搜尋流量，擁有一個專屬的**頂級獨立根網域（Apex Custom Domain）**是必不可少的基石。</p>
<p>將靜態文件或技術部落格遷移至獨立網域，絕非只是簡單修改一條 DNS 解析記錄。它涉及網域註冊商的權威名稱伺服器委派、邊緣 SSL 終結、規範化重新導向、自動化發布商 <code>ads.txt</code> 託管，以及跨越各大搜尋引擎站長工具平台特有的 UI 隱蔽陷阱。</p>
<p>在本指南中，我們將全面復盤從 <code>jbjihwan.github.io/global_blog</code> 零停機遷移至 <code>https://globaldevlog.com</code> 的端到端全過程，涵蓋高可用 DNS 架構設計、AdSense 審核合規設定，以及在 Google Search Console、Naver Search Advisor 和 Bing Webmaster Tools 中遇到的真實索引問題的排查方案。</p>
<!-- -->
<hr>
<p><img decoding="async" loading="lazy" alt="靜態工程部落格生產級 DNS 與 CDN 路由架構圖" src="https://globaldevlog.com/zh-Hant/assets/images/01_dns_routing_architecture-ea2cb0a9f6fc29f7bd63e0264ab74683.png" width="2200" height="1100" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-架構戰略porkbun--cloudflare--github-pages">1. 架構戰略：Porkbun + Cloudflare + GitHub Pages<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#1-%E6%9E%B6%E6%A7%8B%E6%88%B0%E7%95%A5porkbun--cloudflare--github-pages" class="hash-link" aria-label="1. 架構戰略：Porkbun + Cloudflare + GitHub Pages的直接連結" title="1. 架構戰略：Porkbun + Cloudflare + GitHub Pages的直接連結" translate="no">​</a></h2>
<p>將根網域（例如 <code>globaldevlog.com</code>）綁定至 GitHub Pages 時，最原始的方法是將 <code>A 記錄</code> 直接指向 GitHub 官方的 4 個 Anycast IP（<code>185.199.108.153</code> 等）。這種方式雖然可用，但缺失了現代雲端必備的邊緣安全防護、DDoS 攻擊清洗以及自動化 HTTPS 憑證續期。</p>
<p>為此，我們構建了高可用的<strong>三層解耦生產架構</strong>：</p>
<table><thead><tr><th style="text-align:left">層級</th><th style="text-align:left">服務商</th><th style="text-align:left">核心職責</th></tr></thead><tbody><tr><td style="text-align:left"><strong>網域註冊商 (Registrar)</strong></td><td style="text-align:left"><strong>Porkbun</strong></td><td style="text-align:left">網域購買、終身免費 WHOIS 隱私保護、DNSSEC 支援、權威 NS 委派</td></tr><tr><td style="text-align:left"><strong>邊緣 DNS 與安全層</strong></td><td style="text-align:left"><strong>Cloudflare</strong></td><td style="text-align:left">全球 Anycast DNS、<strong>根網域 CNAME 扁平化 (Flattening)</strong>、Universal SSL 邊緣憑證、惡意 Bot 清洗</td></tr><tr><td style="text-align:left"><strong>靜態源站 (Origin SSG)</strong></td><td style="text-align:left"><strong>GitHub Pages</strong></td><td style="text-align:left">承載由 Docusaurus 編譯產生的純靜態產物、零伺服器維護成本、<code>gh-pages</code> 分支自動化同步</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="porkbun-名稱伺服器委派">Porkbun 名稱伺服器委派<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#porkbun-%E5%90%8D%E7%A8%B1%E4%BC%BA%E6%9C%8D%E5%99%A8%E5%A7%94%E6%B4%BE" class="hash-link" aria-label="Porkbun 名稱伺服器委派的直接連結" title="Porkbun 名稱伺服器委派的直接連結" translate="no">​</a></h3>
<p>在 Porkbun 網域管理控制台中，將預設的註冊商 Nameservers 替換為 Cloudflare 分配的專用邊緣伺服器：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">NS: roan.ns.cloudflare.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">NS: sneh.ns.cloudflare.com</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloudflare-cname-扁平化設定">Cloudflare CNAME 扁平化設定<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#cloudflare-cname-%E6%89%81%E5%B9%B3%E5%8C%96%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="Cloudflare CNAME 扁平化設定的直接連結" title="Cloudflare CNAME 扁平化設定的直接連結" translate="no">​</a></h3>
<p>根據 RFC 1034 規範，根網域（<code>@</code>）因需共存 SOA/NS 記錄而在傳統上無法配置 CNAME。Cloudflare 藉助其專利技術 <strong>CNAME 扁平化 (CNAME Flattening)</strong> 完美解決了這一限制，可無縫將 <code>@</code> 解析指向 GitHub Pages：</p>
<ul>
<li class=""><strong>Type</strong>: <code>CNAME</code> | <strong>Name</strong>: <code>@</code> | <strong>Target</strong>: <code>jbjihwan.github.io</code> | <strong>Proxy status</strong>: Proxied (已開啟代理/橘色雲朵)</li>
<li class=""><strong>Type</strong>: <code>CNAME</code> | <strong>Name</strong>: <code>www</code> | <strong>Target</strong>: <code>jbjihwan.github.io</code> | <strong>Proxy status</strong>: Proxied (已開啟代理/橘色雲朵)</li>
<li class=""><strong>SSL/TLS 模式</strong>: 設定為 <strong>Full (完全)</strong> 或 <strong>Full Strict (完全嚴格)</strong>，確保端到端的 HTTPS 雙向加密傳輸。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-docusaurus-核心配置遷移">2. Docusaurus 核心配置遷移<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#2-docusaurus-%E6%A0%B8%E5%BF%83%E9%85%8D%E7%BD%AE%E9%81%B7%E7%A7%BB" class="hash-link" aria-label="2. Docusaurus 核心配置遷移的直接連結" title="2. Docusaurus 核心配置遷移的直接連結" translate="no">​</a></h2>
<p>為了平滑地將子路徑路由（<code>/global_blog/</code>）整體切換為獨立網域根路徑（<code>/</code>），我們需要更新 <code>docusaurus.config.ts</code>：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  title</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Global Dev Blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://globaldevlog.com'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  baseUrl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  organizationName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'jbjihwan'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  projectName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'global_blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  trailingSlash</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="靜態-cname-檔案配置">靜態 CNAME 檔案配置<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#%E9%9D%9C%E6%85%8B-cname-%E6%AA%94%E6%A1%88%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="靜態 CNAME 檔案配置的直接連結" title="靜態 CNAME 檔案配置的直接連結" translate="no">​</a></h3>
<p>GitHub Pages 依靠部署分支根目錄下的純文字 <code>CNAME</code> 檔案來識別流量歸屬。必須將其放置在 <code>static/CNAME</code>：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">globaldevlog.com</span><br></div></code></pre></div></div>
<p>執行 <code>npm run deploy</code> 時，Docusaurus 會自動將 <code>static/</code> 目錄中的所有檔案拷貝到編譯輸出的根目錄，確保 GitHub 路由引擎始終將指向 <code>globaldevlog.com</code> 的造訪請求正確映射到該儲存庫。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-靜態部落格-google-adsense-審核配置">3. 靜態部落格 Google AdSense 審核配置<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#3-%E9%9D%9C%E6%85%8B%E9%83%A8%E8%90%BD%E6%A0%BC-google-adsense-%E5%AF%A9%E6%A0%B8%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="3. 靜態部落格 Google AdSense 審核配置的直接連結" title="3. 靜態部落格 Google AdSense 審核配置的直接連結" translate="no">​</a></h2>
<p>Google AdSense 審核的核心流程包括：確認網站所有權、檢測合規的發布商非同步指令碼、以及抓取位於網站根目錄下的 <code>ads.txt</code> 檔案。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-非同步植入發布商指令碼">A. 非同步植入發布商指令碼<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#a-%E9%9D%9E%E5%90%8C%E6%AD%A5%E6%A4%8D%E5%85%A5%E7%99%BC%E5%B8%83%E5%95%86%E6%8C%87%E4%BB%A4%E7%A2%BC" class="hash-link" aria-label="A. 非同步植入發布商指令碼的直接連結" title="A. 非同步植入發布商指令碼的直接連結" translate="no">​</a></h3>
<p>在 <code>docusaurus.config.ts</code> 的 <code>scripts</code> 陣列中非同步引入 Google AdSense 載入器：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">  scripts</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      src</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4654073335496617'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      async</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      crossorigin</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'anonymous'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-根目錄部署-adstxt-並完成線上驗證">B. 根目錄部署 <code>ads.txt</code> 並完成線上驗證<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#b-%E6%A0%B9%E7%9B%AE%E9%8C%84%E9%83%A8%E7%BD%B2-adstxt-%E4%B8%A6%E5%AE%8C%E6%88%90%E7%B7%9A%E4%B8%8A%E9%A9%97%E8%AD%89" class="hash-link" aria-label="b-根目錄部署-adstxt-並完成線上驗證的直接連結" title="b-根目錄部署-adstxt-並完成線上驗證的直接連結" translate="no">​</a></h3>
<p>在 <code>static/ads.txt</code> 中填入官方認證的發布商憑證：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">google.com, pub-4654073335496617, DIRECT, f08c47fec0942fa0</span><br></div></code></pre></div></div>
<p>部署後透過終端機 curl 立即驗證 HTTP 200 狀態碼：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://globaldevlog.com/ads.txt</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HTTP/2 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># content-type: text/plain; charset=utf-8</span><br></div></code></pre></div></div>
<hr>
<p><img decoding="async" loading="lazy" alt="全球搜尋引擎索引與 AdSense 商業化變現流水線" src="https://globaldevlog.com/zh-Hant/assets/images/02_indexing_pipeline-8f17d9e45c07877c55ba651d6017360f.png" width="2200" height="1100" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-多搜尋引擎-seo-實戰與疑難雜症排查">4. 多搜尋引擎 SEO 實戰與疑難雜症排查<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#4-%E5%A4%9A%E6%90%9C%E5%B0%8B%E5%BC%95%E6%93%8E-seo-%E5%AF%A6%E6%88%B0%E8%88%87%E7%96%91%E9%9B%A3%E9%9B%9C%E7%97%87%E6%8E%92%E6%9F%A5" class="hash-link" aria-label="4. 多搜尋引擎 SEO 實戰與疑難雜症排查的直接連結" title="4. 多搜尋引擎 SEO 實戰與疑難雜症排查的直接連結" translate="no">​</a></h2>
<p>向各大搜尋引擎提交網站地圖時，各家平台往往有著獨特的互動規則和容易誤踩的 UI 陷阱：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-google-search-console無法刪除錯誤網站地圖的互動陷阱">A. Google Search Console：無法刪除錯誤網站地圖的互動陷阱<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#a-google-search-console%E7%84%A1%E6%B3%95%E5%88%AA%E9%99%A4%E9%8C%AF%E8%AA%A4%E7%B6%B2%E7%AB%99%E5%9C%B0%E5%9C%96%E7%9A%84%E4%BA%92%E5%8B%95%E9%99%B7%E9%98%B1" class="hash-link" aria-label="A. Google Search Console：無法刪除錯誤網站地圖的互動陷阱的直接連結" title="A. Google Search Console：無法刪除錯誤網站地圖的互動陷阱的直接連結" translate="no">​</a></h3>
<p>在提交多語言網站地圖（<code>sitemap.xml</code>, <code>ko/sitemap.xml</code>, <code>ja/sitemap.xml</code>, <code>es/sitemap.xml</code> 等）時，若不慎誤將網站根網址 <code>https://globaldevlog.com/</code> 作為網站地圖提交，會在列表裡留下永久性的“包含錯誤”警報。</p>
<p>許多開發者點擊該行表格右側的縱向三點圖示（<code>⋮</code>），卻發現彈出的選單中只有反灰的“查看網頁編入索引情況”，完全找不到任何刪除按鈕。</p>
<p><strong>解決策略：</strong></p>
<ol>
<li class="">切勿點擊表格行最右側的選單按鈕。<strong>請直接點擊該網站地圖的網址文字本身</strong>，進入<strong>網站地圖詳情頁面</strong>。</li>
<li class="">在詳情頁面右上角（靠近搜尋欄下方區域），點擊頁面頂部的三點圖示（<code>⋮</code>）。</li>
<li class="">在彈出的選單中點擊 <strong>[刪除網站地圖]</strong>，即可乾淨徹底地移除該錯誤項目。</li>
<li class="">若多語言網站地圖（如 <code>es/sitemap.xml</code>）因網路或 DNS 快取抖動偶發“無法抓取”，直接在上方“新增網站地圖”輸入框中重新輸入並提交，系統會立即發起二次抓取並重新整理為<strong>成功</strong>狀態。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-naver-search-advisor請按主機層級輸入-url">B. Naver Search Advisor：“請按主機層級輸入 URL”<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#b-naver-search-advisor%E8%AB%8B%E6%8C%89%E4%B8%BB%E6%A9%9F%E5%B1%A4%E7%B4%9A%E8%BC%B8%E5%85%A5-url" class="hash-link" aria-label="B. Naver Search Advisor：“請按主機層級輸入 URL”的直接連結" title="B. Naver Search Advisor：“請按主機層級輸入 URL”的直接連結" translate="no">​</a></h3>
<p>Naver 佔據韓國本土主流搜尋份額。在其站長平台操作時需注意：</p>
<ol>
<li class=""><strong>主機層級報錯</strong>：在初始“網站登記”彈窗中直接提交 <code>https://globaldevlog.com/sitemap.xml</code> 會觸發彈窗警告：<em>“URL을 호스트 단위로 입력해주세요 (請按主機層級輸入 URL)”</em>。該視窗僅用於登記主網域（<code>https://globaldevlog.com</code>）。</li>
<li class=""><strong>所有權雙保險</strong>：同時部署 HTML 校驗檔案（<code>static/naver&lt;hash&gt;.html</code>）與頁面元標籤（<code>themeConfig.metadata</code>），確保無論採用何種檢驗機制均能秒級通過。</li>
<li class=""><strong>提交網站地圖與 RSS</strong>：在管理控制台（點擊網站列表中的網域）：<!-- -->
<ul>
<li class="">進入 <strong>[請求] &gt; [提交網站地圖]</strong>：輸入 <code>sitemap.xml</code></li>
<li class="">進入 <strong>[請求] &gt; [提交 RSS]</strong>：輸入 <code>https://globaldevlog.com/blog/rss.xml</code></li>
</ul>
</li>
<li class=""><strong>加速 Yeti 蜘蛛抓取</strong>：在 <strong>[請求] &gt; [網頁抓取]</strong> 中，手動提交核心路徑（<code>blog</code>, <code>ko/blog</code> 等），促使搜尋引擎蜘蛛快速造訪。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="c-bing-站長工具與-ai-搜尋-copilot--chatgpt-search-接入">C. Bing 站長工具與 AI 搜尋 (Copilot / ChatGPT Search) 接入<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#c-bing-%E7%AB%99%E9%95%B7%E5%B7%A5%E5%85%B7%E8%88%87-ai-%E6%90%9C%E5%B0%8B-copilot--chatgpt-search-%E6%8E%A5%E5%85%A5" class="hash-link" aria-label="C. Bing 站長工具與 AI 搜尋 (Copilot / ChatGPT Search) 接入的直接連結" title="C. Bing 站長工具與 AI 搜尋 (Copilot / ChatGPT Search) 接入的直接連結" translate="no">​</a></h3>
<p>Bing Webmaster Tools 不僅為必應搜尋提供數據，同時也是 Copilot、ChatGPT Search、Yahoo 和 DuckDuckGo 的底層索引源泉。</p>
<ul>
<li class=""><strong>GSC 一鍵匯入</strong>：直接授權 Google 帳號登入，可瞬間同步已驗證的網域所有權與全部網站地圖。</li>
<li class=""><strong>跨越 48 小時數據同步延遲</strong>：GSC 自動同步通常需要 24 至 48 小時。直接進入左側 <strong>[Sitemaps]</strong> &gt; 點擊 <strong>[Submit sitemap]</strong> 手動錄入 <code>https://globaldevlog.com/sitemap.xml</code>，即可免去等待，即刻驅動必應爬蟲全量入庫。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-生產環境最終檢查清單">5. 生產環境最終檢查清單<a href="https://globaldevlog.com/zh-Hant/blog/github-pages-custom-domain-adsense-seo-guide#5-%E7%94%9F%E7%94%A2%E7%92%B0%E5%A2%83%E6%9C%80%E7%B5%82%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE" class="hash-link" aria-label="5. 生產環境最終檢查清單的直接連結" title="5. 生產環境最終檢查清單的直接連結" translate="no">​</a></h2>
<p>在正式推廣你的獨立網域之前，請確保以下項目全部核驗通過：</p>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>DNS 解析狀態</strong>：<code>globaldevlog.com</code> 和 <code>www</code> 均在無 SSL 憑證告警的前提下正常回應。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>規範化 CNAME</strong>：<code>static/CNAME</code> 已打包部署至 GitHub Pages 源站。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>AdSense 核心資產</strong>：<code>https://globaldevlog.com/ads.txt</code> 正常返回 HTTP 200 且發布商 ID 完全符合。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>多語言網站地圖</strong>：各語言專屬網站地圖（<code>/sitemap.xml</code>, <code>/ko/</code>, <code>/ja/</code>, <code>/es/</code>, <code>/zh-Hans/</code>, <code>/zh-Hant/</code>）均返回 HTTP 200。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>三大搜尋引擎收錄</strong>：Google、Naver 和 Bing 均已成功完成所有權驗證，RSS 訂閱源均處於活躍抓取狀態。</li>
</ul>
<p>憑藉這套現代化的工程基礎設施，你的靜態部落格不僅享有邊緣 CDN 的極致載入速度、零伺服器帳單開支，更獲得了覆蓋全球主要語系的頂級搜尋可見度，並為 Google AdSense 商業化變現奠定了堅實的基石。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="docusaurus" term="docusaurus"/>
        <category label="devops" term="devops"/>
        <category label="cloudflare" term="cloudflare"/>
        <category label="seo" term="seo"/>
        <category label="web-monetization" term="web-monetization"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[從 45 個 npm 漏洞到零風險：為何不應在靜態網站上盲目執行 npm audit fix --force]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth"/>
        <updated>2026-10-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度剖析靜態網站威脅模型，拆解為何打包工具中的 45 個 npm 漏洞在 GitHub Pages 上執行時風險為零，以及為何盲目執行 npm audit fix --force 會破壞生產環境。]]></summary>
        <content type="html"><![CDATA[<p>每一位現代前端開發者都曾體驗過腎上腺素飆升的瞬間：當你在一個乾淨整潔的文件或部落格程式碼庫中執行 <code>npm audit</code> 時，終端機突然噴湧出一片觸目驚心的鮮紅警告：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">45 vulnerabilities (15 moderate, 15 high, 15 critical)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">To address all issues (including breaking changes), run:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  npm audit fix --force</span><br></div></code></pre></div></div>
<p>看到諸如 <strong>Critical (嚴重)</strong>、<strong>Remote Code Execution (遠端代碼執行/RCE)</strong> 和 <strong>Prototype Pollution (原型鏈污染)</strong> 這樣的高危詞彙，人類的第一反應往往是毫不猶豫地敲下 <code>npm audit fix --force</code>，試圖讓這些刺眼的紅字立刻消失。</p>
<p>然而，在基於 <strong>靜態網站產生器 (SSG)</strong>（例如部署在 GitHub Pages 或 AWS S3 上的 Docusaurus、Next.js 或 Hugo）的世界裡，這樣做往往是一個嚴重的工程誤判——它不僅無法真正提升你的安全水位，反而會破壞生產環境的建構流程。</p>
<p>在本文中，我們將深入剖析 <strong>靜態網站的威脅模型 (Threat Model)</strong>，揭示打包編譯期漏洞與線上伺服端漏洞的本質差異，並分享一套經生產檢驗的 <strong>4 層靜態安全加固藍圖</strong>，保護真正核心的資產：你的機密憑證、訪客隱私以及程式碼庫完整性。</p>
<!-- -->
<hr>
<p><img decoding="async" loading="lazy" alt="威脅模型對比：動態伺服端 vs 靜態網站" src="https://globaldevlog.com/zh-Hant/assets/images/01_threat_model-4770df6c58430b3bba205124a37d91ab.png" width="2000" height="1000" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-核心工程權衡概述">1. 核心工程權衡概述<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#1-%E6%A0%B8%E5%BF%83%E5%B7%A5%E7%A8%8B%E6%AC%8A%E8%A1%A1%E6%A6%82%E8%BF%B0" class="hash-link" aria-label="1. 核心工程權衡概述的直接連結" title="1. 核心工程權衡概述的直接連結" translate="no">​</a></h2>
<ol>
<li class=""><strong>編譯期工具不等於線上執行時</strong>：在 <code>npm audit</code> 中被標記的依賴套件（例如 <code>tinypool</code>、<code>serialize-javascript</code>、<code>postcss-selector-parser</code>）是僅在開發者本地執行 <code>npm run build</code> 時被 Node.js 呼叫的編譯打包依賴。</li>
<li class=""><strong>CDN 邊緣零後端服務</strong>：一旦編譯完成，GitHub Pages 分發的僅僅是不可變的純靜態 HTML、CSS 和客戶端 JavaScript。在生產環境中，<strong>不存在任何常駐執行的 Node.js 守護程序</strong>，沒有任何開放監聽的後端連接埠，也完全沒有資料庫連線。</li>
<li class=""><strong><code>--force</code> 的破壞性</strong>：盲目執行 <code>npm audit fix --force</code> 會強行降級核心框架（例如將 Docusaurus 回退到舊版本），破壞對 React 19 的相容性、MDX v3 渲染管線以及未來功能開關架構。</li>
<li class=""><strong>靜態網站真正的攻擊面</strong>：靜態部落格面臨的真正威脅是 <strong>公開 Git 提交歷史中的 API 金鑰外洩</strong>、<strong>資料集中的個人隱私 (PII) 暴露</strong> 以及 <strong>客戶端跨域數據洩漏</strong>。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-深度拆解-45-個漏洞編譯期-vs-執行時">2. 深度拆解 45 個漏洞：編譯期 vs 執行時<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#2-%E6%B7%B1%E5%BA%A6%E6%8B%86%E8%A7%A3-45-%E5%80%8B%E6%BC%8F%E6%B4%9E%E7%B7%A8%E8%AD%AF%E6%9C%9F-vs-%E5%9F%B7%E8%A1%8C%E6%99%82" class="hash-link" aria-label="2. 深度拆解 45 個漏洞：編譯期 vs 執行時的直接連結" title="2. 深度拆解 45 個漏洞：編譯期 vs 執行時的直接連結" translate="no">​</a></h2>
<p>讓我們審查一下稽核日誌中實際報告的典型 CVE：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-tinypool-原型鏈污染-ghsa-5gmw-xhrv-c9v3---critical-嚴重">A. Tinypool 原型鏈污染 (<code>GHSA-5gmw-xhrv-c9v3</code> - Critical 嚴重)<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#a-tinypool-%E5%8E%9F%E5%9E%8B%E9%8F%88%E6%B1%A1%E6%9F%93-ghsa-5gmw-xhrv-c9v3---critical-%E5%9A%B4%E9%87%8D" class="hash-link" aria-label="a-tinypool-原型鏈污染-ghsa-5gmw-xhrv-c9v3---critical-嚴重的直接連結" title="a-tinypool-原型鏈污染-ghsa-5gmw-xhrv-c9v3---critical-嚴重的直接連結" translate="no">​</a></h3>
<ul>
<li class=""><strong>漏洞原理</strong>：如果向 <code>tinypool</code> 的 Worker 初始化選項中傳入不可信的使用者輸入，攻擊者可污染 <code>Object.prototype</code>。</li>
<li class=""><strong>靜態網站現實</strong>：<code>tinypool</code> 僅在 <code>@docusaurus/faster</code> 內部用於在建構期間跨 CPU 核心並行處理打包任務。所有的輸入參數均嚴格來自於開發者自身的 <code>docusaurus.config.ts</code>。線上外部訪客根本無法向你本地的 CPU 程序池發送任何 HTTP 負載。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-serialize-javascript-遠端代碼執行-ghsa-5c6j-r48x-rmvq---high-高危">B. Serialize-JavaScript 遠端代碼執行 (<code>GHSA-5c6j-r48x-rmvq</code> - High 高危)<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#b-serialize-javascript-%E9%81%A0%E7%AB%AF%E4%BB%A3%E7%A2%BC%E5%9F%B7%E8%A1%8C-ghsa-5c6j-r48x-rmvq---high-%E9%AB%98%E5%8D%B1" class="hash-link" aria-label="b-serialize-javascript-遠端代碼執行-ghsa-5c6j-r48x-rmvq---high-高危的直接連結" title="b-serialize-javascript-遠端代碼執行-ghsa-5c6j-r48x-rmvq---high-高危的直接連結" translate="no">​</a></h3>
<ul>
<li class=""><strong>漏洞原理</strong>：透過精心建構的 RegExp 標誌或序列化日期實作遠端代碼執行。</li>
<li class=""><strong>靜態網站現實</strong>：Webpack 僅使用它將內部編譯區塊序列化為靜態 <code>.js</code> 代碼束。一旦寫入 <code>build/</code> 目錄，這些 JavaScript 僅僅是透過 HTTPS 分發的靜態資源檔案，伺服端完全不發生任何反序列化過程。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="c-postcss-selector-parser-cpu-耗盡-ghsa-rj75-hqrm-r3gf---moderate-中危">C. PostCSS Selector Parser CPU 耗盡 (<code>GHSA-rj75-hqrm-r3gf</code> - Moderate 中危)<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#c-postcss-selector-parser-cpu-%E8%80%97%E7%9B%A1-ghsa-rj75-hqrm-r3gf---moderate-%E4%B8%AD%E5%8D%B1" class="hash-link" aria-label="c-postcss-selector-parser-cpu-耗盡-ghsa-rj75-hqrm-r3gf---moderate-中危的直接連結" title="c-postcss-selector-parser-cpu-耗盡-ghsa-rj75-hqrm-r3gf---moderate-中危的直接連結" translate="no">​</a></h3>
<ul>
<li class=""><strong>漏洞原理</strong>：解析器在處理特定平鋪選擇器時具有二次方時間複雜度，可透過惡意 CSS 字串導致拒絕服務 (DoS)。</li>
<li class=""><strong>靜態網站現實</strong>：被解析的 CSS 檔案完全是專案自身的主題樣式表（<code>custom.css</code>）。除非攻擊者擁有向你 Git 儲存庫推送程式碼的權限，否則建構機器永遠不會解析不可信的外部 CSS。</li>
</ul>
<p>在這種情況下強行執行 <code>npm audit fix --force</code>，npm 會嘗試將 <code>@docusaurus/core</code> 從 <code>3.10.2</code> 強制降級到 <code>3.7.0</code>。這會直接導致 Docusaurus v4 特性與 React 19 渲染徹底崩潰——為了修復一個在你的物理架構中根本不可能被觸發的漏洞，反而親手向生產程式碼中引入了致命 Bug。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-真正重要的防線4-層靜態安全加固藍圖">3. 真正重要的防線：4 層靜態安全加固藍圖<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#3-%E7%9C%9F%E6%AD%A3%E9%87%8D%E8%A6%81%E7%9A%84%E9%98%B2%E7%B7%9A4-%E5%B1%A4%E9%9D%9C%E6%85%8B%E5%AE%89%E5%85%A8%E5%8A%A0%E5%9B%BA%E8%97%8D%E5%9C%96" class="hash-link" aria-label="3. 真正重要的防線：4 層靜態安全加固藍圖的直接連結" title="3. 真正重要的防線：4 層靜態安全加固藍圖的直接連結" translate="no">​</a></h2>
<p>如果建構工具漏洞風險極低，靜態網站工程師應該將安全精力投入到哪裡？以下是本部落格實施的縱深防禦體系。</p>
<p><img decoding="async" loading="lazy" alt="4 層靜態部落格安全加固管線" src="https://globaldevlog.com/zh-Hant/assets/images/02_security_pipeline-120240a37282a9b3b578a5dc4f6231c0.png" width="2000" height="900" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第一道防線git-提交前自動化金鑰掃描-pre-commit-secret-scanning">第一道防線：Git 提交前自動化金鑰掃描 (Pre-Commit Secret Scanning)<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E4%B8%80%E9%81%93%E9%98%B2%E7%B7%9Agit-%E6%8F%90%E4%BA%A4%E5%89%8D%E8%87%AA%E5%8B%95%E5%8C%96%E9%87%91%E9%91%B0%E6%8E%83%E6%8F%8F-pre-commit-secret-scanning" class="hash-link" aria-label="第一道防線：Git 提交前自動化金鑰掃描 (Pre-Commit Secret Scanning)的直接連結" title="第一道防線：Git 提交前自動化金鑰掃描 (Pre-Commit Secret Scanning)的直接連結" translate="no">​</a></h3>
<p>對於公開開源的技術部落格而言，最大的滅頂之災是在 Git 提交中不慎洩露 OpenAI 金鑰 (<code>sk-...</code>)、GitHub Token (<code>ghp_...</code>) 或 AWS 存取憑證 (<code>AKIA...</code>)。</p>
<p>我們編寫了自動化稽核指令碼（<code>tools/audit_security.py</code>），利用 Git 的底層物件資料庫，在提交前掃描所有追蹤檔案：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">PATTERNS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'(?i)(api[_-]?key|secret|token|password|auth|bearer)\s*[:=]\s*["\']([^"\']{8,})["\']'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Potential Key Assignment"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'ghp_[a-zA-Z0-9]{36}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"GitHub Personal Access Token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'sk-[a-zA-Z0-9]{32,}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"OpenAI API Key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'AIzaSy[a-zA-Z0-9_-]{33}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Google API Key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'AKIA[0-9A-Z]{16}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"AWS Access Key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Private Key Certificate"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></div></code></pre></div></div>
<p>在每次程式碼發布前執行此稽核工具，確保硬編碼的敏感 Token 絕不會逃逸至遠端公開儲存庫。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第二道防線個人身分識別資訊-pii-嚴格去識別化">第二道防線：個人身分識別資訊 (PII) 嚴格去識別化<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E4%BA%8C%E9%81%93%E9%98%B2%E7%B7%9A%E5%80%8B%E4%BA%BA%E8%BA%AB%E5%88%86%E8%AD%98%E5%88%A5%E8%B3%87%E8%A8%8A-pii-%E5%9A%B4%E6%A0%BC%E5%8E%BB%E8%AD%98%E5%88%A5%E5%8C%96" class="hash-link" aria-label="第二道防線：個人身分識別資訊 (PII) 嚴格去識別化的直接連結" title="第二道防線：個人身分識別資訊 (PII) 嚴格去識別化的直接連結" translate="no">​</a></h3>
<p>在發布涉及 AI 資料集盲測的技術專欄時（例如我們的 23 篇心靈守護者心理諮商系列或 5 篇 ANPR 視覺 AI 系列）：</p>
<ul>
<li class=""><strong>合成資料優先</strong>：所有心理對話案例均由大模型流程（<code>synth_gen</code>）端到端合成，絕不使用真實患者對話記錄，從源頭上杜絕醫療隱私外洩。</li>
<li class=""><strong>正則敏感資訊排查</strong>：自動化校驗所有 Markdown 內容，防範電話號碼或身分登記字號等敏感欄位意外流出。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第三道防線深度強化的-gitignore-規範">第三道防線：深度強化的 <code>.gitignore</code> 規範<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E4%B8%89%E9%81%93%E9%98%B2%E7%B7%9A%E6%B7%B1%E5%BA%A6%E5%BC%B7%E5%8C%96%E7%9A%84-gitignore-%E8%A6%8F%E7%AF%84" class="hash-link" aria-label="第三道防線深度強化的-gitignore-規範的直接連結" title="第三道防線深度強化的-gitignore-規範的直接連結" translate="no">​</a></h3>
<p>許多標準的 <code>.gitignore</code> 僅僅忽略了 <code>.env.local</code>，卻留下了 <code>.env</code>。我們將設定進行了工業級強化，徹底阻斷敏感金鑰、憑證及原始分析記錄：</p>
<div class="language-gitignore codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-gitignore codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># 環境變數與機密憑據保護</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">.env</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">.env.*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.pem</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.key</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.pfx</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.cert</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*credential*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.secret</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 資料分析原始匯出檔案</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.csv</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 廢棄的舊版本範本</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_old/</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第四道防線客戶端隱私保護與-iframe-沙盒隔離">第四道防線：客戶端隱私保護與 Iframe 沙盒隔離<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E5%9B%9B%E9%81%93%E9%98%B2%E7%B7%9A%E5%AE%A2%E6%88%B6%E7%AB%AF%E9%9A%B1%E7%A7%81%E4%BF%9D%E8%AD%B7%E8%88%87-iframe-%E6%B2%99%E7%9B%92%E9%9A%94%E9%9B%A2" class="hash-link" aria-label="第四道防線：客戶端隱私保護與 Iframe 沙盒隔離的直接連結" title="第四道防線：客戶端隱私保護與 Iframe 沙盒隔離的直接連結" translate="no">​</a></h3>
<p>即使沒有後端伺服器，靜態網站也必須尊重訪客隱私並防止數據外洩：</p>
<ol>
<li class="">
<p><strong>嚴格的 Referrer 策略</strong>：在 <code>docusaurus.config.ts</code> 的 <code>&lt;head&gt;</code> 中注入安全請求頭：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">themeConfig</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  metadata</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'referrer'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'strict-origin-when-cross-origin'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>確保訪客點擊外鏈跳轉時，瀏覽器會自動截斷部落格的具體文章路徑及參數，防止內部 URL 洩漏。</p>
</li>
<li class="">
<p><strong>Giscus 留言系統沙盒化</strong>：透過 GitHub Discussions 承載互動評論時，配置：</p>
<ul>
<li class=""><code>emitMetadata="0"</code>：禁止 iframe 廣播跨頁面訊息，防範第三方指令碼窺探討論詳情；</li>
<li class=""><code>loading="lazy"</code>：延遲載入留言模組，直到訪客真正滾動至文章底部才觸發執行。</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-給開發者的核心啟示">4. 給開發者的核心啟示<a href="https://globaldevlog.com/zh-Hant/blog/static-site-security-npm-audit-truth#4-%E7%B5%A6%E9%96%8B%E7%99%BC%E8%80%85%E7%9A%84%E6%A0%B8%E5%BF%83%E5%95%9F%E7%A4%BA" class="hash-link" aria-label="4. 給開發者的核心啟示的直接連結" title="4. 給開發者的核心啟示的直接連結" translate="no">​</a></h2>
<ul>
<li class=""><strong>面對 <code>npm audit</code> 保持清醒</strong>：時刻區分 <strong>建構期依賴</strong> 與 <strong>線上執行時依賴</strong>。</li>
<li class=""><strong>警惕 <code>--force</code> 陷阱</strong>：自動降級往往會在修復虛幻安全隱患的同時，親手向生產環境注入真實的破壞性 Bug。</li>
<li class=""><strong>聚焦真正的攻擊面</strong>：建立程式碼提交前金鑰掃描、清洗敏感資料集、配置嚴密的客戶端隱私策略，守護真正的數位核心。</li>
</ul>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="web-security" term="web-security"/>
        <category label="npm-audit" term="npm-audit"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="devops" term="devops"/>
        <category label="supply-chain" term="supply-chain"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 韓國車牌 OCR 引擎、正規表示式規範化與時序多數表決機制 (從 v01 到 v11 的工程演進) (#5)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-06-29-vision-anpr-5</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-06-29-vision-anpr-5"/>
        <updated>2026-06-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[韓國車牌語法校驗、EasyOCR 調校、基於時序滑動視窗的多數表決引擎，以及從最初亂碼到確定性辨識 64가1511 的 11 輪工程迭代全歷程。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="時序多數表決機制與最終車牌確認" src="https://globaldevlog.com/zh-Hant/assets/images/01_voting_evolution-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要點 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>問題痛點</strong>：由於車輛顛簸、車身偏航角、路面反光及運動模糊，單幀靜態 OCR 辨識不可避免會出現偶發誤讀（例如將 <code>64가1511</code> 誤判為 <code>64아1511</code> 或 <code>24가1511</code>）。</li>
<li class=""><strong>解決方案</strong>：引入韓國車牌強語法正規約束 (<code>^\d{2,3}[가-힣]\d{4}$</code>)，並設計基於 7 幀時間緩衝區的滑動視窗多數表決引擎 (<code>plate_confirm_threshold=3</code>)。</li>
<li class=""><strong>落地成效</strong>：經歷了從 6 月 5 日的最初全螢幕亂碼 (<code>ACCIIXOY-1Z01...</code>)，到 6 月 7 日以 99.4% 的超高信賴度確定性鎖定最終車牌 <code>64가1511</code>，完整走通了 11 輪 (v01 至 v11) 系統級工程迭代。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-韓國車牌語法校驗與-easyocr-調校">1. 韓國車牌語法校驗與 EasyOCR 調校<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-29-vision-anpr-5#1-%E9%9F%93%E5%9C%8B%E8%BB%8A%E7%89%8C%E8%AA%9E%E6%B3%95%E6%A0%A1%E9%A9%97%E8%88%87-easyocr-%E8%AA%BF%E6%A0%A1" class="hash-link" aria-label="1. 韓國車牌語法校驗與 EasyOCR 調校的直接連結" title="1. 韓國車牌語法校驗與 EasyOCR 調校的直接連結" translate="no">​</a></h2>
<p>韓國機動車輛牌照遵循非常嚴格的法定語法規則：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">標準語法結構：[2位或3位數字] + [1個韓文字母] + [4位數字]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">標準範例：64가1511, 123가4567</span><br></div></code></pre></div></div>
<p>在推論管線末端，我們將提取到的車牌影像送入經過自訂初始化的 EasyOCR 辨識器，並使用精準的正規表示式清洗誤檢文字：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> easyocr</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> collections </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> deque</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Counter</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 初始化雙語辨識器，強制開啟 GPU 加速</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">reader </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> easyocr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Reader</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'ko'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'en'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gpu</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parse_korean_plate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_img</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 關閉細節返回以最小化資料傳輸開銷</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    results </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> reader</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">readtext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_img</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> detail</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    raw_text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">results</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">" "</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 韓國車牌專屬正則匹配模式</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pattern </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">r'(\d{2,3}[가-힣]\d{4})'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">match</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pattern</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> raw_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">match</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">group</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">match</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-基於時序滑動視窗的多數表決引擎-temporal-majority-voting">2. 基於時序滑動視窗的多數表決引擎 (Temporal Majority Voting)<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-29-vision-anpr-5#2-%E5%9F%BA%E6%96%BC%E6%99%82%E5%BA%8F%E6%BB%91%E5%8B%95%E8%A6%96%E7%AA%97%E7%9A%84%E5%A4%9A%E6%95%B8%E8%A1%A8%E6%B1%BA%E5%BC%95%E6%93%8E-temporal-majority-voting" class="hash-link" aria-label="2. 基於時序滑動視窗的多數表決引擎 (Temporal Majority Voting)的直接連結" title="2. 基於時序滑動視窗的多數表決引擎 (Temporal Majority Voting)的直接連結" translate="no">​</a></h2>
<p>為了抵禦單幀畫面偶然的辨識抖動，我們在時間維度上維護一個定長的滑動歷史佇列（Sliding History Queue）：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 維護最近 7 幀檢測結果</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">plate_history </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> deque</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">maxlen</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"plate_history_size"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 7</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">confirmed_plate </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">update_plate_voting</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">detected_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">global</span><span class="token plain"> confirmed_plate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> detected_text</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        plate_history</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">detected_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 當佇列累計達到確認閾值時發起統計表決</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_history</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"plate_confirm_threshold"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        counts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_history</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        most_common_plate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> counts</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">most_common</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 僅當同一車牌獲得 3 票或以上贊成時，方才判定為最終確信結果</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"plate_confirm_threshold"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            confirmed_plate </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> most_common_plate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> confirmed_plate</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-從-v01-到-v11-的-11-輪工程演進實錄-6月5日-至-6月7日">3. 從 v01 到 v11 的 11 輪工程演進實錄 (6月5日 至 6月7日)<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-29-vision-anpr-5#3-%E5%BE%9E-v01-%E5%88%B0-v11-%E7%9A%84-11-%E8%BC%AA%E5%B7%A5%E7%A8%8B%E6%BC%94%E9%80%B2%E5%AF%A6%E9%8C%84-6%E6%9C%885%E6%97%A5-%E8%87%B3-6%E6%9C%887%E6%97%A5" class="hash-link" aria-label="3. 從 v01 到 v11 的 11 輪工程演進實錄 (6月5日 至 6月7日)的直接連結" title="3. 從 v01 到 v11 的 11 輪工程演進實錄 (6月5日 至 6月7日)的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="最終 v11 完整整合系統實跑效果" src="https://globaldevlog.com/zh-Hant/assets/images/02_final_system-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<p>系統 <code>log.txt</code> 真實見證了這套演算法管線的淬鍊全過程：</p>
<table><thead><tr><th style="text-align:left">時間戳</th><th style="text-align:left">原始 OCR 輸出日誌</th><th style="text-align:left">架構演進與技術突破核心</th></tr></thead><tbody><tr><td style="text-align:left"><strong>06-05 01:13</strong></td><td style="text-align:left"><code>ACCIIXOY-1Z01V1t145V...</code></td><td style="text-align:left"><strong>v01</strong>：無約束全螢幕 OCR 盲掃，路面瀝青紋理被嚴重誤判為字元</td></tr><tr><td style="text-align:left"><strong>06-06 00:13</strong></td><td style="text-align:left"><code>fa이</code>, <code>ealan</code>, <code>cai</code></td><td style="text-align:left"><strong>v04</strong>：引入 YOLOv3 車輛邊界框，縮小文字候選搜尋區域</td></tr><tr><td style="text-align:left"><strong>06-06 01:40</strong></td><td style="text-align:left"><code>64115114</code>, <code>16416114</code></td><td style="text-align:left"><strong>v07</strong>：整合專用 YOLOv5 車牌局部檢測器，成功定位車牌精準 ROI</td></tr><tr><td style="text-align:left"><strong>06-06 16:35</strong></td><td style="text-align:left"><code>64아1511</code> -&gt; <code>64가1511</code></td><td style="text-align:left"><strong>v09</strong>：部署 EDSR_x4 深度超解析度模型，清晰修復韓文字元關鍵筆畫</td></tr><tr><td style="text-align:left"><strong>06-07 01:27</strong></td><td style="text-align:left"><code>24가1511</code> -&gt; <code>64가1511</code></td><td style="text-align:left"><strong>v10</strong>：引入多幀影像疊加降噪演算法，抵消攝影機感光元件的抖動雜訊</td></tr><tr><td style="text-align:left"><strong>06-07 18:20</strong></td><td style="text-align:left"><strong><code>64가1511 (Size: 370x315)</code></strong></td><td style="text-align:left"><strong>v11 終版</strong>：時序多數表決引擎精準鎖定，實作 100% 確定性車牌確認！</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-總結與未來展望">4. 總結與未來展望<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-29-vision-anpr-5#4-%E7%B8%BD%E7%B5%90%E8%88%87%E6%9C%AA%E4%BE%86%E5%B1%95%E6%9C%9B" class="hash-link" aria-label="4. 總結與未來展望的直接連結" title="4. 總結與未來展望的直接連結" translate="no">​</a></h2>
<p>至此，在這個由 5 篇深度專欄構成的 ANPR 視覺工程系列中，我們完整構建了一套涵蓋 <strong>動態車道線擬合、多邊形 IoU 壓線侵入判定、分層目標定位、CSRT 目標追蹤、EDSR 深度超解析度重建與時序多數表決 OCR</strong> 的工業級端到端視覺系統。這套架構為邊緣運算裝置上的高精度即時智慧交通檢測（ITS）提供了經過實戰檢驗的標準範式。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="easyocr" term="easyocr"/>
        <category label="regex" term="regex"/>
        <category label="voting-ensemble" term="voting-ensemble"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 攻克行車記錄器模糊雜訊：深度超解析度 (EDSR vs FSRCNN) 與多幀影像疊加降噪 (#4)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-06-28-vision-anpr-4</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-06-28-vision-anpr-4"/>
        <updated>2026-06-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[透過 EDSR 4倍深度超解析度網路與時序多幀影像疊加降噪，從極度模糊的行車記錄器畫面中高保真重建低解析度車牌局部補丁。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="EDSR 深度超解析度重建前後對比" src="https://globaldevlog.com/zh-Hant/assets/images/01_edsr_sr_comparison-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要點 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>問題痛點</strong>：從高速行車記錄器畫面裁剪出的遠距離車牌區域往往不足 40x20 像素，且伴隨嚴重的 H.264 編碼巨集區塊效應；傳統的雙三次插值（Bicubic）演算法只會讓字體邊緣更加模糊，無法還原字元有效筆畫。</li>
<li class=""><strong>解決方案</strong>：引入移除了 Batch Normalization 層以保留高動態像素範圍的 EDSR_x4 深度殘差模型 (<code>EDSR_x4.pb</code>)，並結合連續 3 至 5 幀的時序多幀加權疊加（Multi-Frame Image Stacking）技術。</li>
<li class=""><strong>落地成效</strong>：車牌像素密度提升 4 倍，筆畫邊緣顯著銳化，使得下游 EasyOCR 對韓文字元的辨識準確率從最初的 32% 飆升至 91% 以上。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-光學解析度極限與深度超解析度的必要性">1. 光學解析度極限與深度超解析度的必要性<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-28-vision-anpr-4#1-%E5%85%89%E5%AD%B8%E8%A7%A3%E6%9E%90%E5%BA%A6%E6%A5%B5%E9%99%90%E8%88%87%E6%B7%B1%E5%BA%A6%E8%B6%85%E8%A7%A3%E6%9E%90%E5%BA%A6%E7%9A%84%E5%BF%85%E8%A6%81%E6%80%A7" class="hash-link" aria-label="1. 光學解析度極限與深度超解析度的必要性的直接連結" title="1. 光學解析度極限與深度超解析度的必要性的直接連結" translate="no">​</a></h2>
<p>在惡劣的高速公路行車環境下，即使透過 YOLOv5 精準裁剪出車牌區域，常規 OCR 依舊頻繁失效：</p>
<ol>
<li class=""><strong>傳統雙三次插值失效</strong>：數學插值演算法僅在相鄰像素間進行線性過渡，根本無法憑空“腦補”出高頻邊緣特徵。</li>
<li class=""><strong>H.264 壓縮與感光雜訊</strong>：視訊編碼壓縮帶來的馬賽克雜訊會導致字元筆畫斷裂粘連。</li>
</ol>
<p>為此，我們在推論管線中透過 <strong>OpenCV DNN Super-Resolution 模組</strong> 深度整合了經過精細預訓練的 <strong>EDSR_x4 深度模型</strong>：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> cv2 </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> dnn_superres</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">init_super_resolution_engine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model_path</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"EDSR_x4.pb"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 建立深度學習超解析度引擎介面</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> dnn_superres</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DnnSuperResImpl_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">readModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 配置模型架構為 edsr，放大倍率為 4x</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"edsr"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 強制將推論後端綁定至 CUDA GPU 加速</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setPreferableBackend</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dnn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DNN_BACKEND_CUDA</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setPreferableTarget</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dnn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DNN_TARGET_CUDA</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> sr</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-基於時序多幀影像疊加的物理級降噪">2. 基於時序多幀影像疊加的物理級降噪<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-28-vision-anpr-4#2-%E5%9F%BA%E6%96%BC%E6%99%82%E5%BA%8F%E5%A4%9A%E5%B9%80%E5%BD%B1%E5%83%8F%E7%96%8A%E5%8A%A0%E7%9A%84%E7%89%A9%E7%90%86%E7%B4%9A%E9%99%8D%E5%99%AA" class="hash-link" aria-label="2. 基於時序多幀影像疊加的物理級降噪的直接連結" title="2. 基於時序多幀影像疊加的物理級降噪的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="多幀影像疊加降噪對比" src="https://globaldevlog.com/zh-Hant/assets/images/02_frame_stacking-0ce024bbbafb08d7449c40abec7b6dc2.png" width="2149" height="1599" class="img_ev3q"></p>
<p>由於車載攝影機感光晶片在每一幀產生的電子熱雜訊是高斯隨機分佈的，而真實車牌的物理文字在連續幀內具有空間相關性。我們透過對齊連續 $N$ 幀車牌補丁進行加權均值疊加：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">I_stacked(x, y) = (1 / N) * Σ Warp(I_i(x, y))</span><br></div></code></pre></div></div>
<p>這種時序疊加機制不僅在不損失邊緣銳度的前提下消除了高達 70% 的高頻隨機雜訊，還顯著增強了字元與反光底板之間的明暗對比度。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-下篇預告">3. 下篇預告<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-28-vision-anpr-4#3-%E4%B8%8B%E7%AF%87%E9%A0%90%E5%91%8A" class="hash-link" aria-label="3. 下篇預告的直接連結" title="3. 下篇預告的直接連結" translate="no">​</a></h2>
<p>在接下來的第 5 篇（收尾篇）中，我們將全面揭秘 <strong>韓國車牌語法校驗、EasyOCR 字元辨識調校</strong> 以及核心的 <strong>時序滑動視窗多數表決引擎</strong>，復盤從最初一堆全螢幕亂碼到最終百分之百鎖定目標車牌（<code>64가1511</code>）的完整工程突破歷程。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="super-resolution" term="super-resolution"/>
        <category label="edsr" term="edsr"/>
        <category label="image-stacking" term="image-stacking"/>
        <category label="anpr" term="anpr"/>
        <category label="deep-learning" term="deep-learning"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 分層目標檢測架構：YOLOv3 車輛粗定位、YOLOv5 車牌精檢與 CSRT 目標追蹤 (#3)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-06-27-vision-anpr-3</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-06-27-vision-anpr-3"/>
        <updated>2026-06-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[兩階段分層目標定位架構：基於 YOLOv3 提取車輛局部感興趣區域 (ROI)，微調 YOLOv5 實作微小車牌局部推論，並結合 CSRT 相關濾波演算法實作 50+ FPS 高速連續追蹤。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="YOLOv5 車牌局部檢測與定位效果" src="https://globaldevlog.com/zh-Hant/assets/images/01_plate_localization-e3a163202d7a73e57bdf504aafdd6919.png" width="957" height="766" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要點 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>問題痛點</strong>：在未經裁剪的 640x480 原圖上直接搜尋僅佔 30x15 像素的微小車牌，會因路邊廣告牌、交通指示牌與建築物反光引發海量誤報，且模型全圖高解析度推論會導致幀率暴跌至 10 FPS 以下。</li>
<li class=""><strong>解決方案</strong>：設計“兩階段分層（Hierarchical）定位”方案：先由輕量級 YOLOv3-tiny 圈定車輛主體邊界框，再將裁剪出的車輛局部 ROI 送入精調的 YOLOv5 車牌專用檢測器 (<code>lp_det.pt</code>, 信賴度=0.6)；同時引入 OpenCV CSRT 相關濾波追蹤器，每隔 15 幀僅重檢一次。</li>
<li class=""><strong>落地成效</strong>：徹底消除了 98% 的背景干擾誤報，GPU 計算開銷驟降 80%，在邊緣運算裝置上穩定跑滿 50+ FPS 的極速即時追蹤。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-全圖檢測的致命陷阱與分層檢測的破解之道">1. 全圖檢測的致命陷阱與分層檢測的破解之道<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-27-vision-anpr-3#1-%E5%85%A8%E5%9C%96%E6%AA%A2%E6%B8%AC%E7%9A%84%E8%87%B4%E5%91%BD%E9%99%B7%E9%98%B1%E8%88%87%E5%88%86%E5%B1%A4%E6%AA%A2%E6%B8%AC%E7%9A%84%E7%A0%B4%E8%A7%A3%E4%B9%8B%E9%81%93" class="hash-link" aria-label="1. 全圖檢測的致命陷阱與分層檢測的破解之道的直接連結" title="1. 全圖檢測的致命陷阱與分層檢測的破解之道的直接連結" translate="no">​</a></h2>
<p>在車載行車記錄器畫面中，嘗試在全螢幕畫面上直接執行車牌檢測存在兩大致命缺陷：</p>
<ol>
<li class=""><strong>複雜背景雜訊吞噬特徵</strong>：街邊商店招牌上的文字、公車車身廣告都會導致車牌檢測器頻繁誤觸發。</li>
<li class=""><strong>下取樣特徵嚴重遺失</strong>：當 640x480 的影像被下取樣至模型輸入尺寸（如 640x640）時，本就只有幾十像素的微小車牌極易在卷積特徵圖中徹底湮滅。</li>
</ol>
<p>為此，我們的兩階段分層設計先嚴格限制搜尋空間：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 第一階段：先粗定位車輛主體邊界框</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">vehicle_boxes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> detect_vehicles_yolov3</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> net</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">vehicle_net</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> conf_thresh</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> box </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> vehicle_boxes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    x1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> box</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 裁剪車輛局部候選區域 (Vehicle ROI)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vehicle_roi </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> frame</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">y1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">y2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">x2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 第二階段：僅在車輛局部高解析度區域內檢測車牌</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    plate_boxes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> plate_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vehicle_roi</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> pbox </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> plate_boxes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        px1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> py1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> px2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> py2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> pbox</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 映射恢復至全局絕對座標系</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        global_plate_box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">x1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> px1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> py1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> px2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> py2</span><span class="token punctuation" style="color:#393A34">]</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-突破即時幀率瓶頸引入-csrt-目標追蹤器">2. 突破即時幀率瓶頸：引入 CSRT 目標追蹤器<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-27-vision-anpr-3#2-%E7%AA%81%E7%A0%B4%E5%8D%B3%E6%99%82%E5%B9%80%E7%8E%87%E7%93%B6%E9%A0%B8%E5%BC%95%E5%85%A5-csrt-%E7%9B%AE%E6%A8%99%E8%BF%BD%E8%B9%A4%E5%99%A8" class="hash-link" aria-label="2. 突破即時幀率瓶頸：引入 CSRT 目標追蹤器的直接連結" title="2. 突破即時幀率瓶頸：引入 CSRT 目標追蹤器的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="基於 OpenCV CSRT 的多幀平滑連續目標追蹤" src="https://globaldevlog.com/zh-Hant/assets/images/02_csrt_tracking-bea82f7c3eb5c74b33968501166fbe5a.png" width="2139" height="1599" class="img_ev3q"></p>
<p>深度學習目標檢測模型即便參數量再小，逐幀推論依然會過度擠占計算資源。我們引入了具有空間可靠性圖的 <strong>CSRT (Channel and Spatial Reliability Tracking) 濾波追蹤器</strong>，採用“間隔檢測 + 逐幀追蹤”的雙核驅動機制：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 每隔 15 幀執行一次 YOLO 重檢，其餘幀完全交由輕量級 CSRT 追蹤</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> frame_count </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"detection_interval"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    boxes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> detect_vehicles_yolov3</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tracker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">TrackerCSRT_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tracker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">init</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">tuple</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">boxes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 純數學相關濾波快速更新目標框，單幀計算開銷僅需 1~2 毫秒</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    success</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tracked_box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tracker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> success</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        tracker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-下篇預告">3. 下篇預告<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-27-vision-anpr-3#3-%E4%B8%8B%E7%AF%87%E9%A0%90%E5%91%8A" class="hash-link" aria-label="3. 下篇預告的直接連結" title="3. 下篇預告的直接連結" translate="no">​</a></h2>
<p>在接下來的第 4 篇中，我們將深入解決裁剪出的微小車牌解析度過低且受嚴重運動模糊污染的挑戰，探索基於 <strong>EDSR_x4 深度超解析度演算法</strong> 與 <strong>多幀影像疊加降噪</strong> 的字元邊緣高保真重建實踐。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="yolo" term="yolo"/>
        <category label="csrt-tracker" term="csrt-tracker"/>
        <category label="object-detection" term="object-detection"/>
        <category label="opencv" term="opencv"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 動態車道線邊界提取與幾何多邊形 IoU 壓線侵入判定演算法 (#2)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-06-26-vision-anpr-2</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-06-26-vision-anpr-2"/>
        <updated>2026-06-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Canny 邊緣濾波、機率霍夫變換 (HoughLinesP)、斜率雙向閾值過濾，以及基於車輛接地接觸面與車道多邊形幾何 IoU 的壓線侵入判定數學原理。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="動態車道線檢測與覆蓋示意圖" src="https://globaldevlog.com/zh-Hant/assets/images/01_lane_detection-0ce024bbbafb08d7449c40abec7b6dc2.png" width="2149" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要點 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>問題痛點</strong>：行車記錄器畫面中普遍充斥著路面積水反光、瀝青修補裂縫、減速帶以及高架橋陰影，極易引發車道線誤檢；簡單的水平/垂直單點座標判斷，無法在車輛變道顛簸時精準界定車輪壓線的物理臨界點。</li>
<li class=""><strong>解決方案</strong>：設計基於消失點的梯形 ROI 感興趣區域遮罩、雙向斜率絕對值閾值過濾 ($|slope| &gt; 0.5$)，並結合 7 幀時間滑動平均佇列進行平滑；將侵入判定嚴格建模為車輛接地接觸面（輪胎下部 30%）與車道多邊形之間的幾何交並比 (IoU)。</li>
<li class=""><strong>落地成效</strong>：成功過濾了 95% 以上的路面雜訊，並以亞幀級的時間精度精準捕獲車輛碾壓車道線的瞬間。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-動態車道線檢測canny-邊緣與機率霍夫變換-houghlinesp">1. 動態車道線檢測：Canny 邊緣與機率霍夫變換 (HoughLinesP)<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-26-vision-anpr-2#1-%E5%8B%95%E6%85%8B%E8%BB%8A%E9%81%93%E7%B7%9A%E6%AA%A2%E6%B8%ACcanny-%E9%82%8A%E7%B7%A3%E8%88%87%E6%A9%9F%E7%8E%87%E9%9C%8D%E5%A4%AB%E8%AE%8A%E6%8F%9B-houghlinesp" class="hash-link" aria-label="1. 動態車道線檢測：Canny 邊緣與機率霍夫變換 (HoughLinesP)的直接連結" title="1. 動態車道線檢測：Canny 邊緣與機率霍夫變換 (HoughLinesP)的直接連結" translate="no">​</a></h2>
<p>為了在低清行車記錄器畫面中穩定提取車道線，我們精心調校了基於 OpenCV 的影像前處理管線：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">detect_lanes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    height</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> width </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> frame</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gray </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cvtColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">COLOR_BGR2GRAY</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    blur </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">GaussianBlur</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    edges </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Canny</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">blur</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">150</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 建構基於視線消失點的梯形感興趣區域 (ROI)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    mask </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">zeros_like</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">edges</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    roi_polygon </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> height</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.45</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">height </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.55</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">height </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.9</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> height</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">int32</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fillPoly</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mask</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> roi_polygon</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    masked_edges </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bitwise_and</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">edges</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 機率霍夫變換擬合直線線段</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    lines </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">HoughLinesP</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        masked_edges</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        rho</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        theta</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">180</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        threshold</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        minLineLength</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        maxLineGap</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> lines</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-斜率幾何過濾與多幀時序平滑">2. 斜率幾何過濾與多幀時序平滑<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-26-vision-anpr-2#2-%E6%96%9C%E7%8E%87%E5%B9%BE%E4%BD%95%E9%81%8E%E6%BF%BE%E8%88%87%E5%A4%9A%E5%B9%80%E6%99%82%E5%BA%8F%E5%B9%B3%E6%BB%91" class="hash-link" aria-label="2. 斜率幾何過濾與多幀時序平滑的直接連結" title="2. 斜率幾何過濾與多幀時序平滑的直接連結" translate="no">​</a></h2>
<p>霍夫變換檢出的原始線段往往夾雜橫向路面接縫，我們透過斜率數學約束予以剔除：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">直線斜率 m = (y2 - y1) / (x2 - x1)</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>左側車道線候選</strong>：<code>m &lt; -0.5</code>（負斜率強約束）</li>
<li class=""><strong>右側車道線候選</strong>：<code>m &gt; 0.5</code>（正斜率強約束）</li>
<li class=""><strong>橫向路面雜音</strong>：<code>|m| &lt;= 0.5</code>（斑馬線、減速帶完全剔除）</li>
</ul>
<p>針對透過初篩的車道線方程式參數，使用 <code>deque(maxlen=7)</code> 的定長雙端佇列維護移動平均值，徹底消除了由於顛簸造成的單幀劇烈晃動。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-基於幾何多邊形-iou-的車道侵入判定">3. 基於幾何多邊形 IoU 的車道侵入判定<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-26-vision-anpr-2#3-%E5%9F%BA%E6%96%BC%E5%B9%BE%E4%BD%95%E5%A4%9A%E9%82%8A%E5%BD%A2-iou-%E7%9A%84%E8%BB%8A%E9%81%93%E4%BE%B5%E5%85%A5%E5%88%A4%E5%AE%9A" class="hash-link" aria-label="3. 基於幾何多邊形 IoU 的車道侵入判定的直接連結" title="3. 基於幾何多邊形 IoU 的車道侵入判定的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="車道壓線侵入多邊形 IoU 判定原理" src="https://globaldevlog.com/zh-Hant/assets/images/02_intrusion_iou-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<p>車輛並非只要車身進入畫面就算侵入，真正的交通違規在於輪胎與標線的物理接觸。我們將車輛接地部分定義為車輛底部 30% 區域（Footprint）：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Footprint = [x_min, y_min + 0.7 * h, x_max, y_max]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">IoU = Area(Footprint ∩ Lane_Polygon) / Area(Footprint)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">check_lane_intrusion</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vehicle_box</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lane_polygon</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> threshold</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vx1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vx2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> vehicle_box</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vh </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> vy2 </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> vy1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 提取車輛物理接地接觸面</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    footprint </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Polygon</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.7</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">vh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.7</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">vh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 計算空間多邊形相交重疊面積比</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    intersection </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> footprint</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">intersection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">lane_polygon</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">area</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    iou </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> intersection </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> footprint</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">area</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> iou </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> threshold</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> iou</span><br></div></code></pre></div></div>
<p>當 <code>IoU &gt; 0.05</code> 時，系統判定發生壓線違規事件，立即啟動下游的 <strong>YOLOv5 車牌局部檢測與超解析度重建</strong>。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-下篇預告">4. 下篇預告<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-26-vision-anpr-2#4-%E4%B8%8B%E7%AF%87%E9%A0%90%E5%91%8A" class="hash-link" aria-label="4. 下篇預告的直接連結" title="4. 下篇預告的直接連結" translate="no">​</a></h2>
<p>在第 3 篇中，我們將深入解析 <strong>兩階段分層目標檢測架構</strong>，展示如何將 YOLOv3 車輛框與精調的 YOLOv5 車牌模型結合，並藉助 <strong>CSRT 追蹤器</strong> 節省 80% 的 GPU 推論開銷。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="lane-detection" term="lane-detection"/>
        <category label="hough-transform" term="hough-transform"/>
        <category label="iou" term="iou"/>
        <category label="opencv" term="opencv"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 行車記錄器車道壓線違規檢測與超解析度車牌辨識：6階段全鏈路管線與即時性約束 (#1)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-06-25-vision-anpr-1</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-06-25-vision-anpr-1"/>
        <updated>2026-06-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[構建融合動態車道線檢測、多邊形 IoU 壓線度量、YOLOv5 車牌定位、EDSR 深度超解析度與時序 OCR 表決的 6 階段電腦視覺系統架構設計。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="6階段 ANPR 管線系統架構圖" src="https://globaldevlog.com/zh-Hant/assets/images/01_pipeline_diagram-fb1b5567c4acb0cd59bd89773f15c5c8.png" width="2200" height="900" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要點 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>問題痛點</strong>：低解析度行車記錄器畫面普遍存在車身顛簸、逆光高光、運動模糊以及 H.264 編碼失真，使得直接進行車牌文字辨識和車道壓線判定極易發生誤判漏檢。</li>
<li class=""><strong>解決方案</strong>：設計高度解耦的 6 階段分層電腦視覺架構：Canny/Hough 動態車道線擬合 -&gt; YOLOv3 車輛粗檢 -&gt; 多邊形 IoU 壓線判定 -&gt; YOLOv5 車牌局部精檢 -&gt; EDSR_x4 深度超解析度重建 -&gt; EasyOCR 時序多數表決集成。</li>
<li class=""><strong>落地成效</strong>：建置了一套純 Python 實作的高吞吐端到端管線，在嚴重劣化的行車記錄器實拍視訊下實作了高強健性的車輛追蹤與車道侵入監控。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-專案研發背景為何車載行車視覺極具挑戰">1. 專案研發背景：為何車載行車視覺極具挑戰？<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-25-vision-anpr-1#1-%E5%B0%88%E6%A1%88%E7%A0%94%E7%99%BC%E8%83%8C%E6%99%AF%E7%82%BA%E4%BD%95%E8%BB%8A%E8%BC%89%E8%A1%8C%E8%BB%8A%E8%A6%96%E8%A6%BA%E6%A5%B5%E5%85%B7%E6%8C%91%E6%88%B0" class="hash-link" aria-label="1. 專案研發背景：為何車載行車視覺極具挑戰？的直接連結" title="1. 專案研發背景：為何車載行車視覺極具挑戰？的直接連結" translate="no">​</a></h2>
<p>在智慧交通系統 (ITS) 與自動化交通執法場景中，<strong>即時車道線侵入檢測與車牌自動辨識 (ANPR)</strong> 是核心支柱能力。然而，在真實高速公路與城市道路環境中，行車記錄器實拍視訊面臨著嚴峻的光學與算力挑戰：</p>
<ol>
<li class=""><strong>極端低解析度與紋理模糊</strong>：在前車或超車車輛中，遠距離車牌在整個 640x480 解析度畫面中往往僅佔據 <code>30x15</code> 至 <code>50x25</code> 像素，字元筆畫粘連嚴重。</li>
<li class=""><strong>車身震動與捲簾快門畸變</strong>：路面坑洞顛簸、車輛急加速/急減速引發的角度劇烈擺動會導致原本筆直的車道線嚴重扭曲。</li>
<li class=""><strong>即時計算延遲瓶頸</strong>：在車載邊緣端逐幀執行龐大的全圖大模型會導致幀率驟降，無法滿足即時視訊串流處理要求。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-6-階段分層管線架構設計">2. 6 階段分層管線架構設計<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-25-vision-anpr-1#2-6-%E9%9A%8E%E6%AE%B5%E5%88%86%E5%B1%A4%E7%AE%A1%E7%B7%9A%E6%9E%B6%E6%A7%8B%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="2. 6 階段分層管線架構設計的直接連結" title="2. 6 階段分層管線架構設計的直接連結" translate="no">​</a></h2>
<p>為了兼顧高召回率與即時高吞吐，我們將系統劃分為模組化的 <strong>6 階段分工體系</strong>：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Raw["原始行車記錄器幀 640x480"] --&gt; L1["第1階段：Canny + 霍夫變換動態車道線提取"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Raw --&gt; L2["第2階段：YOLOv3-tiny 車輛主體快速粗檢"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L1 &amp; L2 --&gt; L3["第3階段：車輛接地接觸面與車道多邊形 IoU 侵入判定"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L3 -- 觸發壓線侵入 --&gt; L4["第4階段：車輛 ROI 局部微調 YOLOv5 車牌定位"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L4 --&gt; L5["第5階段：EDSR_x4 深度神經網路 4倍超解析度重建"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L5 --&gt; L6["第6階段：EasyOCR + 字元正則校驗 + 時序滑動多數表決"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L6 --&gt; Result["最終確信違規車牌：64가1511"]</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="最終檢測定位與車牌辨識輸出展示" src="https://globaldevlog.com/zh-Hant/assets/images/02_final_result-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="各階段核心職責解耦">各階段核心職責解耦<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-25-vision-anpr-1#%E5%90%84%E9%9A%8E%E6%AE%B5%E6%A0%B8%E5%BF%83%E8%81%B7%E8%B2%AC%E8%A7%A3%E8%80%A6" class="hash-link" aria-label="各階段核心職責解耦的直接連結" title="各階段核心職責解耦的直接連結" translate="no">​</a></h3>
<ol>
<li class=""><strong>車道提取</strong>：基於消失點的梯形動態 ROI 遮罩、Canny 算子與斜率幾何約束，快速隔離出行車走廊邊界。</li>
<li class=""><strong>車輛檢測</strong>：輕量級 YOLOv3-tiny 毫秒級輸出畫面內所有候選車輛的外接矩形框。</li>
<li class=""><strong>壓線判定</strong>：計算車輛輪胎接地接觸面（底部 30% 區域）與車道邊界多边形之間的幾何交並比（IoU &gt; 0.05）。</li>
<li class=""><strong>車牌定位</strong>：微調版 YOLOv5 (<code>lp_det.pt</code>) 僅在發生侵入的車輛局部 ROI 區域內進行推論，大幅縮減無效搜尋面積。</li>
<li class=""><strong>超解析度</strong>：將裁剪出的低解析度微小車牌送入 EDSR_x4 深度殘差模型，4 倍高清重建字元邊緣細節。</li>
<li class=""><strong>OCR 與表決</strong>：EasyOCR 提取字元候選，結合韓國法定車牌語法正則進行初篩，並在 7 幀時間視窗內發起多數表決鎖定最終結果。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-核心全域工程參數配置-config">3. 核心全域工程參數配置 (<code>CONFIG</code>)<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-25-vision-anpr-1#3-%E6%A0%B8%E5%BF%83%E5%85%A8%E5%9F%9F%E5%B7%A5%E7%A8%8B%E5%8F%83%E6%95%B8%E9%85%8D%E7%BD%AE-config" class="hash-link" aria-label="3-核心全域工程參數配置-config的直接連結" title="3-核心全域工程參數配置-config的直接連結" translate="no">​</a></h2>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">CONFIG </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"output_width"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">640</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"output_height"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">480</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"conf_threshold_vehicle"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"nms_threshold_vehicle"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"min_vehicle_w"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">70</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"min_vehicle_h"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">70</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"lane_iou_threshold"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"detection_interval"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">15</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"min_plate_area_for_ocr"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1200</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"plate_history_size"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"plate_confirm_threshold"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"sr_model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"EDSR_x4.pb"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"yolo_plate_model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lp_det.pt"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-下篇預告">4. 下篇預告<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-25-vision-anpr-1#4-%E4%B8%8B%E7%AF%87%E9%A0%90%E5%91%8A" class="hash-link" aria-label="4. 下篇預告的直接連結" title="4. 下篇預告的直接連結" translate="no">​</a></h2>
<p>在接下來的第 2 篇中，我們將深入解析 <strong>Canny 邊緣提取演算法、機率霍夫變換數學原理 (HoughLinesP)、斜率幾何異常值剔除</strong> 以及核心的幾何多邊形車道侵入判定規則。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="object-detection" term="object-detection"/>
        <category label="super-resolution" term="super-resolution"/>
        <category label="opencv" term="opencv"/>
        <category label="deep-learning" term="deep-learning"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 畢業設計開發日誌 #23] 雙層 Safety Wrapper 護欄、AWS EC2 GPU 算力成本與第一學期答辯大捷]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23"/>
        <updated>2026-06-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[第一學期圓滿收官：打造雙層 Safety Wrapper 危機干預安全護欄，精算 AWS EC2 g5.2xlarge GPU 伺服器部署預算，順利通過期末終審答辯。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/zh-Hant/assets/images/01_cover-5db6b43f8eb8d83178d8ba6f56f13f4f.png" width="720" height="405" class="img_ev3q"></p>
<p>June 18, 2026, marks the triumphant completion of the 1st semester of our university capstone engineering project. From our humble beginnings as a simple Android convention scraper in March, <strong>Maeumjigi</strong> has evolved into a full-scale, clinical-grade AI mental health counseling ecosystem.</p>
<p>Our final sprint focused on two mission-critical objectives:</p>
<ol>
<li class=""><strong>The Dual-Layer Safety Wrapper</strong>: Engineering a bulletproof guardrail architecture to guarantee zero-hallucination crisis intervention.</li>
<li class=""><strong>Production Cloud Serving &amp; Final Defense</strong>: Sizing AWS EC2 GPU infrastructure budgets and delivering our final faculty presentation.</li>
</ol>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-dual-layer-safety-wrapper-architecture">1. The Dual-Layer Safety Wrapper Architecture<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23#1-the-dual-layer-safety-wrapper-architecture" class="hash-link" aria-label="1. The Dual-Layer Safety Wrapper Architecture的直接連結" title="1. The Dual-Layer Safety Wrapper Architecture的直接連結" translate="no">​</a></h3>
<p>In consumer chatbots, occasional hallucinations or awkward phrasing are minor annoyances. In an AI platform dedicated to youth and military suicide prevention, <strong>a single hallucinated response or dismissive answer could be fatal</strong>.</p>
<p>To protect vulnerable users, we designed a <strong>Dual-Layer Safety Wrapper</strong> that wraps around our fine-tuned Qwen 3.5-9B model (Checkpoint-4400):</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Inbound_Triage ["Layer 1: Pre-Inference Guardrail"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        A[User Message Received] --&gt; B[Regex Crisis Keyword Detector]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        B --&gt; C[Zero-Shot Semantic Crisis Classifier]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        C --&gt; D{Risk Severity Assessment}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Decision_Branch ["Emergency Routing"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        D --&gt;|Level 3: Active Suicidality / Imminent Danger| E[Emergency Protocol Triggered]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        E --&gt;|Bypass Generative Model| F[Deterministic Crisis Intervention &amp; 109 / 1393 Dialing]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        D --&gt;|Level 1-2: Mild to Moderate Emotional Distress| G[Pass to Fine-Tuned LLM]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Inference_Core ["Model Generation"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        G --&gt; H[vLLM Serving Engine: Checkpoint-4400]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        H --&gt; I[Raw Generative Token Stream]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Outbound_Verifier ["Layer 2: Post-Inference Guardrail"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        I --&gt; J[Toxicity &amp; Hallucination Filter]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        J --&gt; K[Clinical Referral Footer Injection]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        K --&gt; L[Sanitized Real-Time SSE to Client]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    F --&gt; L</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="crisis-escalation-protocol-level-1-to-3">Crisis Escalation Protocol (Level 1 to 3)<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23#crisis-escalation-protocol-level-1-to-3" class="hash-link" aria-label="Crisis Escalation Protocol (Level 1 to 3)的直接連結" title="Crisis Escalation Protocol (Level 1 to 3)的直接連結" translate="no">​</a></h4>
<table><thead><tr><th style="text-align:center">Risk Classification</th><th style="text-align:left">Clinical Indicators</th><th style="text-align:left">System Response Action</th><th style="text-align:center">Latency Target</th></tr></thead><tbody><tr><td style="text-align:center"><strong>Level 1 (Mild)</strong></td><td style="text-align:left">Fatigue, loneliness, academic/work stress</td><td style="text-align:left">Empathetic listening, active validation</td><td style="text-align:center">&lt; 800 ms</td></tr><tr><td style="text-align:center"><strong>Level 2 (Moderate)</strong></td><td style="text-align:left">Chronic hopelessness, self-worth collapse</td><td style="text-align:left">Cognitive reframing, counseling clinic recommendation</td><td style="text-align:center">&lt; 800 ms</td></tr><tr><td style="text-align:center"><strong>Level 3 (Crisis)</strong></td><td style="text-align:left">Explicit suicidal ideation, intent, or self-harm</td><td style="text-align:left"><strong>Instant LLM bypass: Display 24/7 Lifeline (109 / 1393) modal with one-tap emergency call</strong></td><td style="text-align:center"><strong>&lt; 150 ms</strong></td></tr></tbody></table>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-guardrail-middleware-implementation">2. Guardrail Middleware Implementation<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23#2-guardrail-middleware-implementation" class="hash-link" aria-label="2. Guardrail Middleware Implementation的直接連結" title="2. Guardrail Middleware Implementation的直接連結" translate="no">​</a></h3>
<p>The Safety Wrapper operates as an asynchronous FastAPI middleware, validating payloads prior to dispatching GPU inference requests:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Safety Wrapper core logic: safety_wrapper.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> fastapi </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> HTTPException</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">CRISIS_PATTERNS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">r"(죽고\s*싶|자살|목숨을\s*끊|살기\s*싫|뛰어내리)"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">r"(suicide|kill\s*myself|end\s*my\s*life)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">SafetyWrapper</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> emergency_hotline</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"109"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compiled_regex </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">IGNORECASE</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> CRISIS_PATTERNS</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hotline </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> emergency_hotline</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">evaluate_input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> user_text</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> pattern </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compiled_regex</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> pattern</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">user_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"is_crisis"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"action"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ESCALATE_IMMEDIATELY"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"override_response"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token string-interpolation string" style="color:#e3116c">f"당신의 고통을 깊이 공감합니다. 지금 즉시 전문가의 도움을 받으실 수 있습니다. "</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token string-interpolation string" style="color:#e3116c">f"24시간 자살예방 상담전화 ☎ </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">self</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">hotline</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">번으로 연락해 주세요. 당신은 혼자가 아닙니다."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"is_crisis"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"action"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"PROCEED_TO_LLM"</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-aws-ec2-gpu-production-budget--sizing">3. AWS EC2 GPU Production Budget &amp; Sizing<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23#3-aws-ec2-gpu-production-budget--sizing" class="hash-link" aria-label="3. AWS EC2 GPU Production Budget &amp; Sizing的直接連結" title="3. AWS EC2 GPU Production Budget &amp; Sizing的直接連結" translate="no">​</a></h3>
<p>To serve our validated chatbot to university students and military beta testers over the 5-month summer break, we conducted an AWS EC2 instance sizing and cost optimization study:</p>
<table><thead><tr><th style="text-align:left">Instance Family</th><th style="text-align:center">GPU Configuration</th><th style="text-align:center">vCPU / RAM</th><th style="text-align:center">On-Demand (Hourly)</th><th style="text-align:center">5-Month Estimated Cost (On-Demand)</th><th style="text-align:center">1-Year Savings Plan (Hourly)</th><th style="text-align:center">5-Month Optimized Cost</th></tr></thead><tbody><tr><td style="text-align:left"><strong>g4dn.2xlarge</strong></td><td style="text-align:center">1x NVIDIA T4 (16GB)</td><td style="text-align:center">8 vCPU / 32GB</td><td style="text-align:center">$0.752</td><td style="text-align:center">$2,707</td><td style="text-align:center">$0.481</td><td style="text-align:center">$1,731</td></tr><tr><td style="text-align:left"><strong>g5.2xlarge (Chosen)</strong></td><td style="text-align:center"><strong>1x NVIDIA A10G (24GB)</strong></td><td style="text-align:center"><strong>8 vCPU / 32GB</strong></td><td style="text-align:center"><strong>$1.212</strong></td><td style="text-align:center"><strong>$4,363</strong></td><td style="text-align:center"><strong>$0.763</strong></td><td style="text-align:center"><strong>$2,746 (Save 37%)</strong></td></tr><tr><td style="text-align:left"><strong>g6.2xlarge</strong></td><td style="text-align:center">1x NVIDIA L4 (24GB)</td><td style="text-align:center">8 vCPU / 32GB</td><td style="text-align:center">$0.978</td><td style="text-align:center">$3,520</td><td style="text-align:center">$0.626</td><td style="text-align:center">$2,253</td></tr></tbody></table>
<p><strong>Engineering Selection</strong>: While <code>g4dn.2xlarge</code> offered lower cost, its 16GB VRAM could not host our 9B model alongside vLLM PagedAttention KV-caches without extreme 4-bit quantization degradation. We selected <strong><code>g5.2xlarge</code> with a 1-year Savings Plan</strong>, unlocking 24GB high-speed GDDR6 VRAM, sub-50ms token latencies, and 37% budgetary savings!</p>
<p><img decoding="async" loading="lazy" alt="Final Presentation" src="https://globaldevlog.com/zh-Hant/assets/images/02_presentation-4d633f8f7c1e0375cfa80546d37e3291.png" width="720" height="405" class="img_ev3q"></p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-1st-semester-capstone-retrospective--defense-highlights">4. 1st Semester Capstone Retrospective &amp; Defense Highlights<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23#4-1st-semester-capstone-retrospective--defense-highlights" class="hash-link" aria-label="4. 1st Semester Capstone Retrospective &amp; Defense Highlights的直接連結" title="4. 1st Semester Capstone Retrospective &amp; Defense Highlights的直接連結" translate="no">​</a></h3>
<p>On June 18th, our engineering team presented the completed <strong>1st Semester Final Defense Presentation (PPTX)</strong> before the university evaluation panel:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">timeline</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    title Maeumjigi Semester 1 Engineering Milestones</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    March 2026 : Project Kickoff : Claude Code Adoption : Convention Scraper Pivot</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    April 2026 : 120K Corpus Preprocessing : Llama 3.1 JSONL Pipeline : Next.js 14 Frontend</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    May 2026 : 4 Rounds of Fine-Tuning : Checkpoint-4400 Locked : FastAPI PostgreSQL Hardened</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    June 2026 : Dual Safety Wrapper : AWS g5.2xlarge Sizing : 1st Semester Final Presentation Passed</span><br></div></code></pre></div></div>
<p>The review panel commended the team for solving the over-counseling trap through empirical data mixing (2:1 ratio) and praised the clinical safety guarantees provided by the deterministic Safety Wrapper.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-whats-next-in-semester-2">5. What's Next in Semester 2?<a href="https://globaldevlog.com/zh-Hant/blog/2026-06-18-maeumjigi-23#5-whats-next-in-semester-2" class="hash-link" aria-label="5. What's Next in Semester 2?的直接連結" title="5. What's Next in Semester 2?的直接連結" translate="no">​</a></h3>
<p>With Phase 1 concluded, our team enters summer deployment:</p>
<ol>
<li class="">Provisioning our AWS EC2 <code>g5.2xlarge</code> instance with automated containerized health checks.</li>
<li class="">Launching closed-beta testing across military welfare communities.</li>
<li class="">Returning in Semester 2 with live multi-turn field data and expanded knowledge graph capabilities!</li>
</ol>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="devlog" term="devlog"/>
        <category label="safety-wrapper" term="safety-wrapper"/>
        <category label="guardrail" term="guardrail"/>
        <category label="aws-ec2" term="aws-ec2"/>
        <category label="gpu-serving" term="gpu-serving"/>
        <category label="capstone-defense" term="capstone-defense"/>
        <category label="suicide-prevention" term="suicide-prevention"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 畢業設計開發日誌 #22] 第四輪微調大捷: Checkpoint-4400、黃金數據混合比與全系統架構設計]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-30-maeumjigi-22</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-30-maeumjigi-22"/>
        <updated>2026-05-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[第一階段大模型微調完美收官：破解過度心理諮詢陷阱的 2:1 黃金數據配比，鎖定 Checkpoint-4400 最優權重，確立全棧系統架構。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/zh-Hant/assets/images/01_cover-5fcc23c7f36696eb5311e706c9d9eede.png" width="2495" height="1393" class="img_ev3q"></p>
<p>The final week of May 2026 (May 27–30) proved to be the grand culmination of our experimental modeling phase. After weeks of hyperparameter tuning, GPU out-of-memory crashes, and dataset revisions, our <strong>4th Fine-Tuning cycle (fine_tuning_4)</strong> crossed the finish line.</p>
<p>This post reveals the technical breakthrough that resolved the chronic "over-counseling" trap, quantitative validation of our golden weight <strong>Checkpoint-4400</strong>, and the end-to-end system architecture designed for real-world deployment.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-over-counseling-paradox--data-mix-optimization">1. The Over-Counseling Paradox &amp; Data Mix Optimization<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-30-maeumjigi-22#1-the-over-counseling-paradox--data-mix-optimization" class="hash-link" aria-label="1. The Over-Counseling Paradox &amp; Data Mix Optimization的直接連結" title="1. The Over-Counseling Paradox &amp; Data Mix Optimization的直接連結" translate="no">​</a></h3>
<p>In our earlier fine-tuning cycles (v1 through v3), the model suffered from an unexpected behavioral pathology we coined the <strong>"Over-Counseling Trap"</strong>:</p>
<ul>
<li class="">When a user said: <em>"Good morning! What should I eat for lunch?"</em></li>
<li class="">The model unnaturally replied: <em>"I hear deep sorrow in your morning greeting. Eating alone can feel overwhelming. Please remember you are not alone; take a deep breath."</em></li>
</ul>
<p>Because the training corpus consisted exclusively of 100% clinical depression and psychological counseling transcripts, the model had overfitted into interpreting <em>every</em> casual human interaction as a critical depressive cry for help.</p>
<p>To eliminate this pathological hyper-empathy without compromising crisis intervention sensitivity, we engineered experimental <strong>Data Mix Ratios</strong> blending daily casual chit-chat (AI Hub Social Multi-Turn Corpus) with our curated counseling data:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pie title "4th Fine-Tuning Golden Mix Composition (2:1 Ratio)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "Curated Clinical &amp; Crisis Counseling (AI Hub v4)" : 66.7</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "Open-Domain Daily Chit-Chat &amp; Casual Banter" : 33.3</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-checkpoint-4400-loss-convergence--golden-weights">2. Checkpoint-4400: Loss Convergence &amp; Golden Weights<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-30-maeumjigi-22#2-checkpoint-4400-loss-convergence--golden-weights" class="hash-link" aria-label="2. Checkpoint-4400: Loss Convergence &amp; Golden Weights的直接連結" title="2. Checkpoint-4400: Loss Convergence &amp; Golden Weights的直接連結" translate="no">​</a></h3>
<p>We fine-tuned <strong>Qwen 2.5 / 3.5-9B</strong> across 6,000 steps on an enterprise A100 GPU cluster using QLoRA (<code>r=16</code>, <code>lora_alpha=32</code>, <code>learning_rate=2e-4</code> with cosine decay).</p>
<p>Analyzing intermediate checkpoints revealed that <strong>Checkpoint-4400</strong> represented the exact Pareto-optimal boundary between conversational fluidity and clinical safety:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">xychart-beta</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    title "Validation Loss &amp; Over-Counseling Metric Across Training Steps"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    x-axis ["Step 1000", "Step 2000", "Step 3000", "Step 4000", "Step 4400", "Step 5000", "Step 6000"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    y-axis "Validation Loss (Lower is Better)" 0.8 --&gt; 2.4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    line [2.35, 1.82, 1.45, 1.18, 1.04, 1.22, 1.48]</span><br></div></code></pre></div></div>
<table><thead><tr><th style="text-align:center">Checkpoint Identifier</th><th style="text-align:center">Validation Perplexity</th><th style="text-align:center">Empathy Depth Score</th><th style="text-align:center">Casual Context Score</th><th style="text-align:left">Behavioral Diagnosis</th></tr></thead><tbody><tr><td style="text-align:center"><strong>Checkpoint-2000</strong></td><td style="text-align:center">4.82</td><td style="text-align:center">64.2 / 100</td><td style="text-align:center">88.0 / 100</td><td style="text-align:left">Underfitted; replies resemble generic base model</td></tr><tr><td style="text-align:center"><strong>Checkpoint-3500</strong></td><td style="text-align:center">3.15</td><td style="text-align:center">81.4 / 100</td><td style="text-align:center">79.5 / 100</td><td style="text-align:left">Good empathy; occasional overly serious responses</td></tr><tr><td style="text-align:center"><strong>Checkpoint-4400 (Golden)</strong></td><td style="text-align:center"><strong>2.68</strong></td><td style="text-align:center"><strong>94.2 / 100</strong></td><td style="text-align:center"><strong>92.8 / 100</strong></td><td style="text-align:left"><strong>Optimal! Crisp humor in daily chat, deep empathy in crisis</strong></td></tr><tr><td style="text-align:center"><strong>Checkpoint-5500</strong></td><td style="text-align:center">3.42</td><td style="text-align:center">95.0 / 100</td><td style="text-align:center">58.2 / 100</td><td style="text-align:left">Overfitted; loss diverges, robotic therapy loops return</td></tr></tbody></table>
<p>Checkpoint-4400 demonstrated flawless contextual switching: answering casual greetings lightheartedly, while seamlessly transitioning into calm, de-escalating empathy the second emotional vulnerability was detected.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-end-to-end-system-architecture">3. End-to-End System Architecture<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-30-maeumjigi-22#3-end-to-end-system-architecture" class="hash-link" aria-label="3. End-to-End System Architecture的直接連結" title="3. End-to-End System Architecture的直接連結" translate="no">​</a></h3>
<p>With Checkpoint-4400 locked in, we designed the complete production architecture, codified in <code>chatbot_architecture.html</code>:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Client_Tier ["Client Layer (Web &amp; Mobile)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        UI1[Next.js 14 Web PWA]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        UI2[React Native Mobile App]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Gateway_Tier ["API Gateway &amp; Ingress"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Nginx[Nginx Reverse Proxy &amp; SSL Termination]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        FastAPI[FastAPI Asynchronous Gateway]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Redis[(Redis Session Buffer &amp; Rate Limiter)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Guardrail_Tier ["Safety &amp; Clinical Validation"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Guard_In[Pre-Filter: Zero-Shot Suicide &amp; Self-Harm Triage]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Guard_Out[Post-Verifier: Hallucination &amp; Emergency Hotline Injector]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Model_Tier ["Model Serving Infrastructure"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        vLLM[vLLM High-Throughput Serving Engine]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Model[(Qwen 3.5-9B + LoRA Checkpoint-4400)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Data_Tier ["Persistence &amp; Analytics"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Postgres[(PostgreSQL User &amp; Conversation History)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    UI1 &amp; UI2 &lt;--&gt;|HTTPS / WSS / SSE| Nginx</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Nginx &lt;--&gt; FastAPI</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    FastAPI &lt;--&gt; Redis</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    FastAPI --&gt; Guard_In</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Guard_In --&gt;|Passed Safe / Level 1-2| vLLM</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Guard_In --&gt;|Critical Crisis: Level 3| Guard_Out</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vLLM &lt;--&gt; Model</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vLLM --&gt; Guard_Out</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Guard_Out --&gt; FastAPI</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    FastAPI &lt;--&gt; Postgres</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-lora-adapter-configuration--automated-reporting">4. LoRA Adapter Configuration &amp; Automated Reporting<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-30-maeumjigi-22#4-lora-adapter-configuration--automated-reporting" class="hash-link" aria-label="4. LoRA Adapter Configuration &amp; Automated Reporting的直接連結" title="4. LoRA Adapter Configuration &amp; Automated Reporting的直接連結" translate="no">​</a></h3>
<p>The finalized <code>adapter_config.json</code> locked our architectural parameters:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"base_model_name_or_path"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Qwen/Qwen2.5-9B-Instruct"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"lora_alpha"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"lora_dropout"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"r"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"target_modules"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"q_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"o_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gate_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"up_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"down_proj"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"task_type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CAUSAL_LM"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>On May 30th, our autonomous documentation script compiled <code>fine_tuning_4_report.docx</code>, incorporating full qualitative inference transcripts comparing the original base Qwen model against Checkpoint-4400.</p>
<p><img decoding="async" loading="lazy" alt="System Architecture" src="https://globaldevlog.com/zh-Hant/assets/images/02_architecture-41f5095033492354eaf45a8553f49ff6.png" width="2541" height="1390" class="img_ev3q"></p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-day-22-milestones--what-lies-ahead">5. Day 22 Milestones &amp; What Lies Ahead<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-30-maeumjigi-22#5-day-22-milestones--what-lies-ahead" class="hash-link" aria-label="5. Day 22 Milestones &amp; What Lies Ahead的直接連結" title="5. Day 22 Milestones &amp; What Lies Ahead的直接連結" translate="no">​</a></h3>
<ol>
<li class=""><strong>Over-Counseling Conquered</strong>: The 2:1 data mix ratio permanently cured our model from hyper-formal, robotic therapeutic over-reactions.</li>
<li class=""><strong>Golden Weight Staged</strong>: Checkpoint-4400 achieved our project's lowest validation perplexity (2.68) and highest human evaluation scores.</li>
<li class=""><strong>Production Blueprint Published</strong>: Completed the full-stack architecture bridging Next.js, FastAPI, vLLM, and Redis.</li>
</ol>
<p>In Dev Log #23, we enter the final milestone of Semester 1: building our dual-layer Safety Wrapper guardrail and calculating cloud GPU server budgets for university deployment!</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="devlog" term="devlog"/>
        <category label="fine-tuning" term="fine-tuning"/>
        <category label="qwen" term="qwen"/>
        <category label="checkpoint-analysis" term="checkpoint-analysis"/>
        <category label="architecture" term="architecture"/>
        <category label="over-counseling" term="over-counseling"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 心語伴侶畢業專題開發日誌 #21] 語料混合配比黃金分割與第 4 輪微調全面打響]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-22-maeumjigi-21</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-22-maeumjigi-21"/>
        <updated>2026-05-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[透過情緒傾訴對話與日常閒聊語料的 2:1 黃金配比微調，打破單一情緒過度擬合，實作日常提問明晰回應與危機訊號深層同理心的絕佳平衡。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="封面圖" src="https://globaldevlog.com/zh-Hant/assets/images/01_cover-a335ed829194fe04a7f1886ca95deda9.png" width="2531" height="1378" class="img_ev3q"></p>
<p>5 月 21 日與 22 日兩天，Maeumjigi 畢業專題團隊全量投入了針對全新高品質語料的大規模批次產生，並正式拉開了<strong>第 4 輪微調實驗 (fine_tuning_4)</strong> 的帷幕。</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="擺脫情緒過敏同理心諮商與日常閒聊的博弈">擺脫「情緒過敏」：同理心諮商與日常閒聊的博弈<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-22-maeumjigi-21#%E6%93%BA%E8%84%AB%E6%83%85%E7%B7%92%E9%81%8E%E6%95%8F%E5%90%8C%E7%90%86%E5%BF%83%E8%AB%AE%E5%95%86%E8%88%87%E6%97%A5%E5%B8%B8%E9%96%92%E8%81%8A%E7%9A%84%E5%8D%9A%E5%BC%88" class="hash-link" aria-label="擺脫「情緒過敏」：同理心諮商與日常閒聊的博弈的直接連結" title="擺脫「情緒過敏」：同理心諮商與日常閒聊的博弈的直接連結" translate="no">​</a></h3>
<p>在嚴密盲測第 3 輪微調模型時，我們捕獲了一個致命缺陷：</p>
<blockquote>
<p>「使用純粹心理諮商語料微調後，模型的回覆被嚴重『情緒綁架』。如何才能在保留深層同理心的同時，捍衛模型處理日常對話的自然常識能力？」</p>
</blockquote>
<p>我們的核心初衷是打造一位「傳遞溫暖同理心力量的專業心理防範自殺介入導師」。但在全量心理語料過度擬合 (Overfitting) 後，面對使用者諸如「今天天氣如何？」或「午餐吃點什麼？」等毫無負面情緒的日常隨想，大模型依然表現出過度嚴肅、沉重且充滿心理病理化傾向的回應。</p>
<p>為徹底打破這一失衡，我們確立了按科學配比深度混合「情緒介入語料」與「日常百科/閒聊語料」的全新實驗假說。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="借助-api-批次處理高效合成-10000-筆-v6-情緒增強語料">借助 API 批次處理高效合成 10,000 筆 v6 情緒增強語料<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-22-maeumjigi-21#%E5%80%9F%E5%8A%A9-api-%E6%89%B9%E6%AC%A1%E8%99%95%E7%90%86%E9%AB%98%E6%95%88%E5%90%88%E6%88%90-10000-%E7%AD%86-v6-%E6%83%85%E7%B7%92%E5%A2%9E%E5%BC%B7%E8%AA%9E%E6%96%99" class="hash-link" aria-label="借助 API 批次處理高效合成 10,000 筆 v6 情緒增強語料的直接連結" title="借助 API 批次處理高效合成 10,000 筆 v6 情緒增強語料的直接連結" translate="no">​</a></h3>
<blockquote>
<p>「參數約束：統一定長 512 Tokens / 單筆平均有效長度 500 Tokens / 涵蓋 20 種典型心理諮商人設 / 持久化目標：v6.jsonl」</p>
</blockquote>
<p>為了鍛造最頂級的訓練集，我們呼叫 Anthropic API (Haiku) Batch 批次模式，一口氣合成了整整 10,000 筆長程心理對話資料 (<code>Emotion_train_dataset_v6.jsonl</code>)。中間雖歷經非同步批次無進度回傳的緊張焦慮，但借助補全校驗指令碼 (<code>fill_v6.py</code>) 成功補齊了所有樣本。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="探尋黃金分割率第-4-輪多比例矩陣微調">探尋黃金分割率：第 4 輪多比例矩陣微調<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-22-maeumjigi-21#%E6%8E%A2%E5%B0%8B%E9%BB%83%E9%87%91%E5%88%86%E5%89%B2%E7%8E%87%E7%AC%AC-4-%E8%BC%AA%E5%A4%9A%E6%AF%94%E4%BE%8B%E7%9F%A9%E9%99%A3%E5%BE%AE%E8%AA%BF" class="hash-link" aria-label="探尋黃金分割率：第 4 輪多比例矩陣微調的直接連結" title="探尋黃金分割率：第 4 輪多比例矩陣微調的直接連結" translate="no">​</a></h3>
<blockquote>
<p>「1 萬筆 Emotion_train_dataset_v6 已就緒。請分別按 7:3、2:1 以及 5:5 比例混合日常與情緒語料。」
「本次 fine_tuning_4 擺脫單純調整 LoRA Alpha，轉向以語料混合比例 (mixed_2_1, mixed_5_5, mixed_7_3) 作為核心自變數展開橫向對抗實驗。」</p>
</blockquote>
<p><img decoding="async" loading="lazy" alt="比例實驗設計" src="https://globaldevlog.com/zh-Hant/assets/images/02_ratio-aa49284ad0e0859f6efd82a31aa871ce.png" width="2507" height="1395" class="img_ev3q"></p>
<p>第 4 輪微調自此化身為一場驗證「日常常識」與「專業同理心」能否達成最完美動態平衡的技術競技場。三組異質資料集被注入 Qwen3.5-9B 底座，新一輪 GPU 矩陣訓練轟鳴打響！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="開發日誌" term="開發日誌"/>
        <category label="微調" term="微調"/>
        <category label="資料集" term="資料集"/>
        <category label="提示詞" term="提示詞"/>
        <category label="畢業專題" term="畢業專題"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 機器人] Sim-to-Real：透過 Arduino Uno、HC-06 藍牙與 ArUco 視覺追蹤實作硬體遷移 (#5)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5"/>
        <updated>2026-05-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[將 Webots 模擬中驗證的 648 狀態 Q-Learning 策略移植到實體雙輪差速 Arduino Uno 機器人上，結合天花板俯視攝影機單應性去畸變與 ArUco 視覺姿態追蹤，實現 Sim-to-Real 硬體自律避障實戰。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="ArUco 標記視覺追蹤畫面" src="https://globaldevlog.com/zh-Hant/assets/images/01_aruco_vision-b5bd404f0b35979c465be925a4fac943.png" width="1280" height="720" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心重點 (TL;DR)</strong></p>
<ul>
<li class=""><strong>問題 (Problem)</strong>：微控制器 (Arduino Uno ATmega328P) 僅有 2KB SRAM 的極端算力瓶頸、車輪打滑 (Wheel Slip) 與超音波感測雜訊，以及 Webots 模擬物理與真實硬體之間典型的<strong>虛實鴻溝 (Reality Gap)</strong>。</li>
<li class=""><strong>解決方案 (Solution)</strong>：採用<strong>分散式 I/O 架構 (HC-06 藍牙, 115,200 baud)</strong>，上位機 PC 負責 OpenCV 視覺處理與 Q-Table 決策，Arduino 專注馬達底層 PWM 驅動；搭配俯視攝影機棋盤格去畸變與 <strong>ArUco 標記單應性矩陣 (<code>vision.py</code>)</strong>，實現次公分級 (x, y, θ) 即時姿態追蹤。</li>
<li class=""><strong>實驗結果 (Result)</strong>：模擬訓練的 648 狀態 Q 表格零樣本遷移至物理 1.2m × 1.2m 障礙場地中，在無需真實微調的情況下達成了 <strong>86.7% 的目標抵達成功率 (26/30 次試驗)</strong>，成功跨越虛實鴻溝。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-sim-to-real-的核心挑戰跨越虛實鴻溝-reality-gap">1. Sim-to-Real 的核心挑戰：跨越虛實鴻溝 (Reality Gap)<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#1-sim-to-real-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%8C%91%E6%88%B0%E8%B7%A8%E8%B6%8A%E8%99%9B%E5%AF%A6%E9%B4%BB%E6%BA%9D-reality-gap" class="hash-link" aria-label="1. Sim-to-Real 的核心挑戰：跨越虛實鴻溝 (Reality Gap)的直接連結" title="1. Sim-to-Real 的核心挑戰：跨越虛實鴻溝 (Reality Gap)的直接連結" translate="no">​</a></h2>
<p>在 Webots 模擬環境中，地面摩擦係數恆定，車輪馬達轉速確定，超音波測距亦無雜散反射。然而在真實物理世界中，不可預測的物理擾動無處不在：</p>
<ol>
<li class=""><strong>算力與記憶體的絕對瓶頸</strong>：實體小車主控 <strong>Arduino Uno (ATmega328P)</strong> 僅具備 32KB Flash 與 2KB SRAM。在晶片上執行深度神經網路或複雜的浮點矩陣運算在物理上是不可能的。</li>
<li class=""><strong>感測器雜訊與反射死角</strong>：廉價超音波測距模組在面對壓克力牆面的大角度傾角時，聲波極易發生鏡面散射，導致讀數歸零或突變至最大測量極限。</li>
<li class=""><strong>車輪打滑與累積里程計漂移 (Odometry Drift)</strong>：地面摩擦微小差異與左右直流減速馬達的齒輪箱公差，導致即使發出筆直前進指令，小車也會逐漸偏向一側。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-分散式控制架構上位機-pc大腦-arduino肌肉">2. 分散式控制架構：上位機 PC（大腦）↔ Arduino（肌肉）<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#2-%E5%88%86%E6%95%A3%E5%BC%8F%E6%8E%A7%E5%88%B6%E6%9E%B6%E6%A7%8B%E4%B8%8A%E4%BD%8D%E6%A9%9F-pc%E5%A4%A7%E8%85%A6-arduino%E8%82%8C%E8%82%89" class="hash-link" aria-label="2. 分散式控制架構：上位機 PC（大腦）↔ Arduino（肌肉）的直接連結" title="2. 分散式控制架構：上位機 PC（大腦）↔ Arduino（肌肉）的直接連結" translate="no">​</a></h2>
<p>為了打破微控制器的算力限制，我們確立了**「PC 充當大腦，Arduino 充當肌肉」**的分散式控制策略：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph CeilingCam["天花板俯視視覺系統 (Ceiling Vision)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Cam["全高畫質網路攝影機 (30 FPS)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Aruco["ArUco 標記偵測 (DICT_4X4_50)&lt;br&gt;單應性矩陣變換 (homography.npy)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Cam --&gt; Aruco</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph PC["上位機 PC (Python 演算法引擎)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Pose["機器人平面姿態估計 (x, y, θ)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Disc["648 狀態離散化模組"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        QL["已訓練 Q 表格 (ε=0)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Host["HC-06 藍牙序列埠通訊 (115200 baud)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Aruco --&gt; Pose --&gt; Disc --&gt; QL --&gt; BT_Host</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Robot["實體差速移動機器人 (Real Robot)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Slave["HC-06 藍牙從屬模組"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        MCU["Arduino Uno (韌體)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Motors["L298N 馬達驅動板 + 直流減速馬達"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Host &lt;--&gt;|"動作位元組指令 [0..3]&lt;br&gt;確認回執/狀態"| BT_Slave --&gt; MCU --&gt; Motors</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>上位機 PC 視覺管線</strong>：安裝在天花板上的網路攝影機俯視監控整座競技場，以 30 FPS 的幀率即時偵測小車頂部的 ArUco 二維標記，計算平面絕對姿態 <code>(x, y, θ)</code>，對應進 648 個離散狀態區間，並從 Q 表格中讀取最佳動作。</li>
<li class=""><strong>HC-06 藍牙通訊最佳化</strong>：為了將藍牙序列埠 (SPP) 的往返時延 (RTT) 降至最低，傳輸鮑率由預設的 9,600 提升至 <strong>115,200 bps</strong>，維持 30~50Hz 的閉迴路控制響應。</li>
<li class=""><strong>Arduino 韌體 (<code>arduino_firmware.ino</code>)</strong>：接收到單一位元組指令（<code>0: 前進</code>, <code>1: 後退</code>, <code>2: 左轉</code>, <code>3: 右轉</code>）後，即刻控制 L298N 驅動晶片輸出精準的脈衝寬度調變 (PWM) 驅動車輪。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-俯視攝影機單應性校正與-aruco-視覺姿態追蹤">3. 俯視攝影機單應性校正與 ArUco 視覺姿態追蹤<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#3-%E4%BF%AF%E8%A6%96%E6%94%9D%E5%BD%B1%E6%A9%9F%E5%96%AE%E6%87%89%E6%80%A7%E6%A0%A1%E6%AD%A3%E8%88%87-aruco-%E8%A6%96%E8%A6%BA%E5%A7%BF%E6%85%8B%E8%BF%BD%E8%B9%A4" class="hash-link" aria-label="3. 俯視攝影機單應性校正與 ArUco 視覺姿態追蹤的直接連結" title="3. 俯視攝影機單應性校正與 ArUco 視覺姿態追蹤的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="硬體系統建置與場地標定" src="https://globaldevlog.com/zh-Hant/assets/images/02_hardware_setup-fddea6cef90bd71036b2aff5eef38f33.png" width="1707" height="1067" class="img_ev3q"></p>
<p>為了在物理場地上實現毫米級精度的姿態解算與障礙測距，我們建置了<strong>基於 OpenCV 的俯視電腦視覺管線</strong>。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-視角校正與單應性變換-calib_autopy">📐 視角校正與單應性變換 (<code>calib_auto.py</code>)<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#-%E8%A6%96%E8%A7%92%E6%A0%A1%E6%AD%A3%E8%88%87%E5%96%AE%E6%87%89%E6%80%A7%E8%AE%8A%E6%8F%9B-calib_autopy" class="hash-link" aria-label="-視角校正與單應性變換-calib_autopy的直接連結" title="-視角校正與單應性變換-calib_autopy的直接連結" translate="no">​</a></h3>
<ol>
<li class=""><strong>棋盤格去畸變標定</strong>：針對廣角鏡頭的桶形畸變 (Barrel Distortion)，利用標準 9×6 棋盤格提取相機內部參數矩陣 ($K$) 與畸變係數 ($D$) 進行反畸變映射。</li>
<li class=""><strong>透視單應性變換 (Homography)</strong>：為了將傾斜的攝影機視角投影為正向俯視的 1.2m × 1.2m 鳥瞰視角 (Bird's-eye View)，透過場地 4 個角落座標擬合出 $3 \times 3$ 單應性矩陣 (<code>homography.npy</code>)。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-aruco-標記姿態解算實作-visionpy">💻 ArUco 標記姿態解算實作 (<code>vision.py</code>)<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#-aruco-%E6%A8%99%E8%A8%98%E5%A7%BF%E6%85%8B%E8%A7%A3%E7%AE%97%E5%AF%A6%E4%BD%9C-visionpy" class="hash-link" aria-label="-aruco-標記姿態解算實作-visionpy的直接連結" title="-aruco-標記姿態解算實作-visionpy的直接連結" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 載入 ArUco 字典與偵測參數</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">aruco_dict </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">getPredefinedDictionary</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DICT_4X4_50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">parameters </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DetectorParameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">detector </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ArucoDetector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">aruco_dict</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_robot_pose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> homography_matrix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""從透視矯正影像中解算機器人的平面座標 (x, y) 與朝向角 (theta)"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    warped </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">warpPerspective</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> homography_matrix</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">600</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">600</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    corners</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> detector</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detectMarkers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">warped</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> ids </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        c </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> corners</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        center_x </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        center_y </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 利用標記前側邊向量解算朝向角 (theta)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        front_x </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        front_y </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        theta </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">arctan2</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">front_y </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> center_y</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> front_x </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> center_x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> center_x</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> center_y</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> theta</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-硬體實測避坑排查與真實部署戰報">4. 硬體實測避坑排查與真實部署戰報<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#4-%E7%A1%AC%E9%AB%94%E5%AF%A6%E6%B8%AC%E9%81%BF%E5%9D%91%E6%8E%92%E6%9F%A5%E8%88%87%E7%9C%9F%E5%AF%A6%E9%83%A8%E7%BD%B2%E6%88%B0%E5%A0%B1" class="hash-link" aria-label="4. 硬體實測避坑排查與真實部署戰報的直接連結" title="4. 硬體實測避坑排查與真實部署戰報的直接連結" translate="no">​</a></h2>
<p>在實體小車聯調測試期間，面對突發的硬體邊緣狀況，我們實施了針對性的工程應對方案：</p>
<table><thead><tr><th style="text-align:left">故障現象 (Troubleshooting)</th><th style="text-align:left">根本原因剖析</th><th style="text-align:left">針對性工程對策</th></tr></thead><tbody><tr><td style="text-align:left"><strong>前進指令發出後小車明顯偏航</strong></td><td style="text-align:left">左右直流減速馬達的內阻公差與齒輪摩擦不均</td><td style="text-align:left">撰寫標定指令碼 (<code>ble_vision_turn_calib.py</code>)，動態引入左右輪獨立 PWM 微調偏移量</td></tr><tr><td style="text-align:left"><strong>藍牙偶爾斷線導致小車失控衝撞</strong></td><td style="text-align:left">射頻干擾引發短暫封包遺失，微控制器維持前序動作</td><td style="text-align:left">在韌體中引入 200ms 通訊看門狗 (Watchdog)，逾時無數據立即煞車全停</td></tr><tr><td style="text-align:left"><strong>日光燈反光引起標記辨識偶發性遺失</strong></td><td style="text-align:left">壓克力反光膜在強光照射下產生高光死區</td><td style="text-align:left">最佳化自適應二值化 (Adaptive Thresholding) 參數，並引入一維卡爾曼濾波進行平滑內插</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-真實物理場地最終基準測試">🏆 真實物理場地最終基準測試<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#-%E7%9C%9F%E5%AF%A6%E7%89%A9%E7%90%86%E5%A0%B4%E5%9C%B0%E6%9C%80%E7%B5%82%E5%9F%BA%E6%BA%96%E6%B8%AC%E8%A9%A6" class="hash-link" aria-label="🏆 真實物理場地最終基準測試的直接連結" title="🏆 真實物理場地最終基準測試的直接連結" translate="no">​</a></h3>
<ul>
<li class=""><strong>自律導航抵達成功率</strong>：在物理 1.2m × 1.2m 障礙競技場中展開 30 輪獨立盲測，最終錄得 <strong>86.7% 的成功率 (26/30 次成功抵達)</strong>。</li>
<li class="">僅依托事先離散化的 <strong>2,592 個 Q 表格參數（體積僅約 2.5 KB）</strong>，小車未經過任何真實環境梯度微調便成功克服了物理摩擦與感測器雜訊。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-webots-強化學習系列全篇結語">5. Webots 強化學習系列全篇結語<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-21-rl-qlearning-5#5-webots-%E5%BC%B7%E5%8C%96%E5%AD%B8%E7%BF%92%E7%B3%BB%E5%88%97%E5%85%A8%E7%AF%87%E7%B5%90%E8%AA%9E" class="hash-link" aria-label="5. Webots 強化學習系列全篇結語的直接連結" title="5. Webots 強化學習系列全篇結語的直接連結" translate="no">​</a></h2>
<p>至此，歷經 5 篇深度記錄的 <strong>Webots 強化學習機器人開發日誌</strong> 圓滿收官：</p>
<ol>
<li class=""><strong>#1</strong>：648 狀態空間離散化與 Webots TCP Socket IPC 通訊架構</li>
<li class=""><strong>#2</strong>：表格型 Q-Learning 指數衰減探索與多目標稠密獎勵塑造</li>
<li class=""><strong>#3</strong>：攻克 12 牆障礙地圖中的局部最佳解與 Headless 自動化模擬</li>
<li class=""><strong>#4</strong>：<code>evaluate_qlearning</code> 零探索確定性評測及與 DQN、SARSA 的三方基準測試</li>
<li class=""><strong>#5</strong>：基於 Arduino Uno、藍牙通訊與 ArUco 視覺姿態追蹤的 Sim-to-Real 硬體落地</li>
</ol>
<p>從虛擬模擬物理建模、演算法核心推導，到大批量多隨機種子自動化測試，最終成功落地於低成本嵌入式實體機器人，本系列完整呈現了機器人強化學習研發的全生命週期。感謝大家的關注與支持，後續敬請期待我們全新的前沿開發實錄！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="強化學習" term="強化學習"/>
        <category label="Sim-to-Real" term="Sim-to-Real"/>
        <category label="Arduino" term="Arduino"/>
        <category label="機器人學" term="機器人學"/>
        <category label="電腦視覺" term="電腦視覺"/>
        <category label="ArUco" term="ArUco"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 心語伴侶畢業專題開發日誌 #20] 第三輪微調收官與全端功能 (DB 持久化、社群看板) 深度整合]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-20-maeumjigi-20</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-maeumjigi-20"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[透過精細校準日常閒聊與危機介入比例完成第 3 輪微調，打通對話持久化資料庫與前端社群分頁及情緒日記篩選元件。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="封面圖" src="https://globaldevlog.com/zh-Hant/assets/images/01_cover-ac5e0126cfa930b87e044dbc1b82cc03.png" width="2519" height="1373" class="img_ev3q"></p>
<p>5 月 19 日至 20 日的兩天時間裡，第三輪微調 (<code>fine_tuning_3</code>) 的評估自動化與面向生產級 App 服務的全端功能深度打磨實作了全面齊頭並進。</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第三輪微調收尾自動化提取最佳-checkpoint-與平行推論">第三輪微調收尾：自動化提取最佳 Checkpoint 與平行推論<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-maeumjigi-20#%E7%AC%AC%E4%B8%89%E8%BC%AA%E5%BE%AE%E8%AA%BF%E6%94%B6%E5%B0%BE%E8%87%AA%E5%8B%95%E5%8C%96%E6%8F%90%E5%8F%96%E6%9C%80%E4%BD%B3-checkpoint-%E8%88%87%E5%B9%B3%E8%A1%8C%E6%8E%A8%E8%AB%96" class="hash-link" aria-label="第三輪微調收尾：自動化提取最佳 Checkpoint 與平行推論的直接連結" title="第三輪微調收尾：自動化提取最佳 Checkpoint 與平行推論的直接連結" translate="no">​</a></h3>
<p>汲取前序微調經驗，旨在兼顧知識圖譜確定性規則與自然同理心引導的第三輪微調實驗順利完成訓練：</p>
<blockquote>
<p>「自動遍歷 fine_tuning_3 目錄下所有實驗模型，精準篩選 Eval Loss 表現最佳的最佳檢查點 (Best Checkpoint)，替換至對應 adapter 目錄並回報指標。」
「編寫全新的 run_test_20_30.sh 自動化指令碼，在背景針對 fine_tuning_3 展開多卡平行推論與自動評測。」</p>
</blockquote>
<p>告別以往繁瑣的人工跨資料夾複製，AI 編寫的高可用指令碼自主提煉最佳權重並配置至推論服務，將 4 組別名候選模型一次性掛在 GPU 伺服器背景展開全自動化基準盲測。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="全端貫通大模型對話-db-持久化與社群互動功能落地">全端貫通：大模型對話 DB 持久化與社群互動功能落地<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-maeumjigi-20#%E5%85%A8%E7%AB%AF%E8%B2%AB%E9%80%9A%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%B0%8D%E8%A9%B1-db-%E6%8C%81%E4%B9%85%E5%8C%96%E8%88%87%E7%A4%BE%E7%BE%A4%E4%BA%92%E5%8B%95%E5%8A%9F%E8%83%BD%E8%90%BD%E5%9C%B0" class="hash-link" aria-label="全端貫通：大模型對話 DB 持久化與社群互動功能落地的直接連結" title="全端貫通：大模型對話 DB 持久化與社群互動功能落地的直接連結" translate="no">​</a></h3>
<p>在伺服器叢集高速運算的同時，本地全端整合測試也迎來了實質性突破：</p>
<blockquote>
<p>「深度梳理 seed_data.sql 初始化認證資訊。預設測試使用者初始密碼究竟是 Test1234! 嗎？當前指令碼目標寫入的目標 DB 架構為何？」</p>
</blockquote>
<p>為了徹底殲滅此前導致 <code>401 Unauthorized</code> 鑑權死結的根本誘因，我們逆向審查了資料種子的雜湊散列格式，成功修復了驗證鏈路。</p>
<p>5 月 20 日下午，我們提交了一個里程碑意義的 Git Commit：
<code>feat: 챗봇 DB 저장, 게시판 페이지네이션, 감정일기 필터 추가 (支援大模型對話持久化、社群看板分頁及情緒日記多維篩選)</code></p>
<p><img decoding="async" loading="lazy" alt="行動端功能截圖" src="https://globaldevlog.com/zh-Hant/assets/images/02_app_feature-26d976bed8544fa566bd187f86d90617.png" width="2522" height="1384" class="img_ev3q"></p>
<p>至此，微調大型語言模型的互動上下文終於正式打通資料庫實作了安全落盤儲存！社群看板分頁瀏覽元件以及記錄每日心情起伏的日記多維過濾面板也全部在行動端高品質渲染上線。</p>
<p>無形而龐大的前沿大模型，終於被穩穩裝入了精美的行動應用程式容器之中！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="開發日誌" term="開發日誌"/>
        <category label="微調" term="微調"/>
        <category label="後端" term="後端"/>
        <category label="前端" term="前端"/>
        <category label="資料庫" term="資料庫"/>
        <category label="畢業專題" term="畢業專題"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 機器人] evaluate_qlearning：確定性導航定量評估與 DQN·SARSA 對比基準 (#4)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度解析針對已訓練 Q-Table 權重的零探索率 (ε=0) 確定性評估管線 (evaluate_qlearning.py)、路徑效率、近距離避碰 (Near-miss) 指標，以及與 DQN、SARSA 模型的全面橫向基準測試。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="成功率與步數基準測試" src="https://globaldevlog.com/zh-Hant/assets/images/01_eval_success_steps-1434c5fba2793b6877d335bf289ef59b.png" width="1170" height="650" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心重點 (TL;DR)</strong></p>
<ul>
<li class=""><strong>目的 (Objective)</strong>：在完全關閉訓練探索雜訊 (ε=0) 的純確定性策略 (Greedy Policy) 下，驗證策略在真實部署環境中的自律導航可靠性與路徑品質。</li>
<li class=""><strong>評估方法 (Evaluation)</strong>：透過 <code>evaluate_qlearning.py</code> 指令碼，在 5 組獨立測試隨機種子 (Eval Seeds 0~4) 上執行共計 30 輪評估，嚴密監測成功率、理論最短路徑比率 (Path Efficiency) 及接近障礙險情 (Near-miss) 頻次。</li>
<li class=""><strong>實驗結果 (Result)</strong>：表格型 Q-Learning 在 12 牆障礙地圖中斬獲 93.3% 的確定性抵達成功率，且單步推論延遲僅為 0.02ms，在執行效率與輕量化上顯著優於沉重的深度神經網路 DQN 與保守的在線學習 SARSA。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-定量評估管線設計-evaluate_qlearningpy">1. 定量評估管線設計 (<code>evaluate_qlearning.py</code>)<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4#1-%E5%AE%9A%E9%87%8F%E8%A9%95%E4%BC%B0%E7%AE%A1%E7%B7%9A%E8%A8%AD%E8%A8%88-evaluate_qlearningpy" class="hash-link" aria-label="1-定量評估管線設計-evaluate_qlearningpy的直接連結" title="1-定量評估管線設計-evaluate_qlearningpy的直接連結" translate="no">​</a></h2>
<p>訓練過程中記錄的獎勵曲線混雜了 ε-貪婪探索的隨機雜訊，無法真實反映即將在實體機器人上執行的<strong>確定性貪婪策略 (Deterministic Greedy Policy)</strong> 的真實水準。</p>
<p>為此，我們建構了專門的評估引擎，載入訓練保存好的權重檔案 (<code>models/&lt;scenario&gt;/seed_&lt;seed&gt;/qlearning_qtable.npy</code>)，並將探索率嚴格固定為 $\epsilon = 0$：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart LR</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Model["已訓練 Q 表格&lt;br&gt;(qlearning_qtable.npy)"] --&gt; Agent["貪婪代理人&lt;br&gt;(ε = 0 固定)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    EvalSeeds["5 組評估隨機種子&lt;br&gt;(EVAL_SEEDS 0..4)"] --&gt; Webots["Webots 模擬器&lt;br&gt;(30 次獨立測試)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Agent &lt;--&gt; Webots</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Webots --&gt; CSV["軌跡與指標記錄&lt;br&gt;(eval_log.csv / traces)"]</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-核心評估迴圈程式碼">💻 核心評估迴圈程式碼<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4#-%E6%A0%B8%E5%BF%83%E8%A9%95%E4%BC%B0%E8%BF%B4%E5%9C%88%E7%A8%8B%E5%BC%8F%E7%A2%BC" class="hash-link" aria-label="💻 核心評估迴圈程式碼的直接連結" title="💻 核心評估迴圈程式碼的直接連結" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># evaluate_qlearning.py 核心邏輯摘錄</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">agent </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> QLearningAgent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">agent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 載入已訓練的 Q-Table</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> ep </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n_episodes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    env_seed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> EVAL_SEEDS</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">ep </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">EVAL_SEEDS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    raw</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> info </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">reset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">env_seed</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_info</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> discretize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_steps </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        a </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> agent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">act</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> deterministic</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 純粹貪婪決策 argmax Q(s, a)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        raw_next</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> done</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> info </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">step</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> discretize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw_next</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 記錄軌跡座標與近距擦撞險情 (dist &lt;= 0.15m)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        record_metrics</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> done</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">break</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-核心評估指標與度量準則">2. 核心評估指標與度量準則<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4#2-%E6%A0%B8%E5%BF%83%E8%A9%95%E4%BC%B0%E6%8C%87%E6%A8%99%E8%88%87%E5%BA%A6%E9%87%8F%E6%BA%96%E5%89%87" class="hash-link" aria-label="2. 核心評估指標與度量準則的直接連結" title="2. 核心評估指標與度量準則的直接連結" translate="no">​</a></h2>
<p>除了基礎的「是否抵達目標」之外，我們從<strong>平滑度與安全性</strong>等多維度進行了深入度量：</p>
<ol>
<li class=""><strong>抵達成功率 (Success Rate)</strong>：在 150 步以內無碰撞且順利進入目標半徑 (0.10m) 範圍的回合比例。</li>
<li class=""><strong>路徑效率 (Path Efficiency)</strong>：理論最短幾何距離 (L_shortest) 與機器人實際行駛累積距離 (L_actual) 的百分比比值：
<strong>Path Efficiency (%) = (Shortest Path / Actual Path) × 100%</strong></li>
<li class=""><strong>近距危險頻次 (Near-Miss Count)</strong>：機器人超音波測距與牆體距離低於 0.15m 的危險步數。</li>
<li class=""><strong>振盪與打轉指標 (Oscillation &amp; Spin)</strong>：連續左右搖頭抖動與原地無效打轉的步數統計。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-三大演算法橫向基準測試q-learning-vs-sarsa-vs-dqn">3. 三大演算法橫向基準測試：Q-Learning vs. SARSA vs. DQN<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4#3-%E4%B8%89%E5%A4%A7%E6%BC%94%E7%AE%97%E6%B3%95%E6%A9%AB%E5%90%91%E5%9F%BA%E6%BA%96%E6%B8%AC%E8%A9%A6q-learning-vs-sarsa-vs-dqn" class="hash-link" aria-label="3. 三大演算法橫向基準測試：Q-Learning vs. SARSA vs. DQN的直接連結" title="3. 三大演算法橫向基準測試：Q-Learning vs. SARSA vs. DQN的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="行駛品質與對比基準" src="https://globaldevlog.com/zh-Hant/assets/images/02_eval_quality-42dd1fc5e31c5e63d48c26ece24e8936.png" width="1625" height="585" class="img_ev3q"></p>
<p>在完全一致的 Webots 12 牆障礙競技場中，對三種經典強化學習演算法展開了對齊測試，揭示了各自的技術權衡：</p>
<table><thead><tr><th style="text-align:left">評估指標</th><th style="text-align:center">Tabular Q-Learning (本專案)</th><th style="text-align:center">SARSA (在線策略 On-policy)</th><th style="text-align:center">Deep Q-Network (DQN)</th></tr></thead><tbody><tr><td style="text-align:left"><strong>學習範式</strong></td><td style="text-align:center">離策略時序差分 TD(0)</td><td style="text-align:center">在線策略時序差分 TD(0)</td><td style="text-align:center">神經網路深度強化學習 (Deep RL)</td></tr><tr><td style="text-align:left"><strong>參數體積</strong></td><td style="text-align:center"><strong>2,592 個浮點數 (~2.5 KB)</strong></td><td style="text-align:center">2,592 個浮点數 (~2.5 KB)</td><td style="text-align:center">約 150,000 個權重參數 (~600 KB)</td></tr><tr><td style="text-align:left"><strong>確定性成功率 (<code>obstacle12</code>)</strong></td><td style="text-align:center"><strong>93.3%</strong></td><td style="text-align:center">86.7%</td><td style="text-align:center">90.0%</td></tr><tr><td style="text-align:left"><strong>單回合平均步數</strong></td><td style="text-align:center"><strong>41.2 步</strong></td><td style="text-align:center">48.5 步</td><td style="text-align:center">44.7 步</td></tr><tr><td style="text-align:left"><strong>單步推論延遲 (Latency)</strong></td><td style="text-align:center"><strong>0.02 ms</strong> (NumPy 陣列尋址)</td><td style="text-align:center">0.02 ms</td><td style="text-align:center">2.15 ms (PyTorch 前向傳播)</td></tr><tr><td style="text-align:left"><strong>近距擦撞危險 (Near-miss)</strong></td><td style="text-align:center">較低 (游刃有餘的繞行)</td><td style="text-align:center">極低 (過度避碰，極端保守)</td><td style="text-align:center">中等</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-演算法行為深度剖析">🔍 演算法行為深度剖析<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4#-%E6%BC%94%E7%AE%97%E6%B3%95%E8%A1%8C%E7%82%BA%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90" class="hash-link" aria-label="🔍 演算法行為深度剖析的直接連結" title="🔍 演算法行為深度剖析的直接連結" translate="no">​</a></h3>
<ul>
<li class=""><strong>Q-Learning 的優勢</strong>：得益於 <code>max_a' Q(s', a')</code> 的離策略特性，Q-Learning 在反向傳播時剔除了探索階段遭遇的隨機碰撞懲罰，能夠精確收斂至<strong>最短可行迂迴路徑</strong>。</li>
<li class=""><strong>SARSA 的局限性</strong>：由於根據實際取樣的下一步動作更新價值，SARSA 過度畏懼探索期的碰撞，導致策略<strong>大幅度繞開牆體，走出過於冗長、效率低下的迂迴航線</strong>。</li>
<li class=""><strong>DQN 的權衡</strong>：雖然能精細表達連續狀態，但訓練穩定性高度依賴經驗回放池與目標網路，且 2.15ms 的推論開銷與 GPU 依賴對於 Arduino 等超低功耗微控制器帶來了不可承受的負擔。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-定量評估結論">4. 定量評估結論<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4#4-%E5%AE%9A%E9%87%8F%E8%A9%95%E4%BC%B0%E7%B5%90%E8%AB%96" class="hash-link" aria-label="4. 定量評估結論的直接連結" title="4. 定量評估結論的直接連結" translate="no">​</a></h2>
<p>定量基準測試表明，壓縮至 648 個狀態區間的<strong>表格型 Q-Learning 僅憑 2.5 KB 的極小記憶體開銷</strong>，不僅在成功率 (93.3%) 上超越了複雜的深度神經網路 (DQN)，更在即時推論吞吐與路徑經濟性上表現優異。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下一步研究方向">5. 下一步研究方向<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-20-rl-qlearning-4#5-%E4%B8%8B%E4%B8%80%E6%AD%A5%E7%A0%94%E7%A9%B6%E6%96%B9%E5%90%91" class="hash-link" aria-label="5. 下一步研究方向的直接連結" title="5. 下一步研究方向的直接連結" translate="no">​</a></h2>
<p>至此，在模擬環境中的全套定量評估已圓滿收官。強化學習最具挑戰性的最終門檻在於<strong>向現實物理世界遷移 (Sim-to-Real)</strong>。</p>
<p>在最後的第 5 篇中，我們將深入硬體實戰：將訓練好的 Q-Table 部署至 <strong>2 輪差速 Arduino Uno 小車，透過 HC-06 藍牙通訊連動，並結合俯視攝影機棋盤格去畸變標定與 ArUco 視覺標記追蹤</strong>，揭秘軟硬體聯調與 Sim-to-Real 的關鍵避坑指南！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="強化學習" term="強化學習"/>
        <category label="Q-Learning" term="Q-Learning"/>
        <category label="性能評估" term="性能評估"/>
        <category label="基準測試" term="基準測試"/>
        <category label="DQN" term="DQN"/>
        <category label="SARSA" term="SARSA"/>
        <category label="機器人學" term="機器人學"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 機器人] 克服 12 面牆體障礙與 Local Minima 陷阱：多隨機種子 Headless 模擬 (#3)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3"/>
        <updated>2026-05-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度剖析在 12 面牆體內建障礙環境 (obstacle12) 及高難度地圖中面臨的局部最佳解 (Local Minima) 瓶頸，以及基於 Webots Headless 無渲染加速管線實作的多隨機種子平行強化學習訓練實戰。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="obstacle12 模擬環境" src="https://globaldevlog.com/zh-Hant/assets/images/01_obstacle12_arena-bc2bc496b7ff2cdfe32dd9630f39bd68.png" width="1438" height="966" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心重點 (TL;DR)</strong></p>
<ul>
<li class=""><strong>問題 (Problem)</strong>：當目標位於牆體正後方時，由於距離增量獎勵 (+0.1 × Δdist) 固有的貪婪特性，機器人無法主動繞開障礙，而是反覆正面撞牆，深陷嚴重的<strong>局部最佳解 (Local Minima)</strong> 死結。</li>
<li class=""><strong>解決方案 (Solution)</strong>：將 3 方向超音波搖頭掃描（前/左/右）狀態感知與長遠視野貝爾曼最佳價值反向傳播 (γ = 0.99) 相結合，並建構基於 PowerShell 的 <strong>Webots Headless 無渲染加速管線 (<code>headless_run.ps1</code>)</strong>，對種子 0、1、2 進行自動化重複實驗。</li>
<li class=""><strong>實驗結果 (Result)</strong>：在 12 面牆體障礙環境下，代理人成功習得穩健的側向繞行策略 (Detour Policy)，訓練 600 個回合後目標抵達成功率穩定維持在 90% 以上。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-障礙環境面臨的嚴峻挑戰local-minima局部最佳解陷阱">1. 障礙環境面臨的嚴峻挑戰：Local Minima（局部最佳解）陷阱<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3#1-%E9%9A%9C%E7%A4%99%E7%92%B0%E5%A2%83%E9%9D%A2%E8%87%A8%E7%9A%84%E5%9A%B4%E5%B3%BB%E6%8C%91%E6%88%B0local-minima%E5%B1%80%E9%83%A8%E6%9C%80%E4%BD%B3%E8%A7%A3%E9%99%B7%E9%98%B1" class="hash-link" aria-label="1. 障礙環境面臨的嚴峻挑戰：Local Minima（局部最佳解）陷阱的直接連結" title="1. 障礙環境面臨的嚴峻挑戰：Local Minima（局部最佳解）陷阱的直接連結" translate="no">​</a></h2>
<p>在無障礙的開闊競技場 (<code>no_obstacle</code>) 中，代理人只需調整朝向直面目標即可。然而，當在室內場地引入 <strong>12 面內建隔斷牆 (<code>obstacle12</code>)</strong> 時，獎勵函數的局限性暴露出了致命瓶頸：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Start["起點 (Robot)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Wall["牆體障礙 (Wall)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Goal["目標位置 (Goal)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Start --&gt;|"直線前進（貪婪距離縮短獎勵）"| Wall</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Wall -.-&gt;|"碰撞並陷入停滯 (Local Minima)"| Wall</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Start ==&gt;|"側向繞行策略 (Detour Path)&lt;br&gt;初期承受距離變遠扣分"| Goal</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>直觀困境</strong>：當目標位於牆壁正後方時，朝向目標前行將直接誘發牆面碰撞。</li>
<li class=""><strong>獎勵衝突與死結</strong>：要成功繞過障礙，代理人必須暫時背離目標行駛（導致即時距離增量獎勵為負）。在缺乏充分探索的情況下，貪婪策略拒絕接受即時懲罰，導致機器人<strong>在牆前頻繁打轉、不斷發生輕微擦撞或耗盡 150 步逾時上限</strong>。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-破局之道搖頭掃描感知與時序差分價值反向傳播">2. 破局之道：搖頭掃描感知與時序差分價值反向傳播<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3#2-%E7%A0%B4%E5%B1%80%E4%B9%8B%E9%81%93%E6%90%96%E9%A0%AD%E6%8E%83%E6%8F%8F%E6%84%9F%E7%9F%A5%E8%88%87%E6%99%82%E5%BA%8F%E5%B7%AE%E5%88%86%E5%83%B9%E5%80%BC%E5%8F%8D%E5%90%91%E5%82%B3%E6%92%AD" class="hash-link" aria-label="2. 破局之道：搖頭掃描感知與時序差分價值反向傳播的直接連結" title="2. 破局之道：搖頭掃描感知與時序差分價值反向傳播的直接連結" translate="no">​</a></h2>
<p>攻克該局部最佳解陷阱的關鍵在於將 <strong>648 狀態空間下的 3 方向測距感知（前方、左側、右側）</strong> 與 <strong>高折扣因子 (γ = 0.99)</strong> 深度結合：</p>
<ol>
<li class=""><strong>多維度狀態感知</strong>：<!-- -->
<ul>
<li class="">當機器人處於前方障礙危險 (<code>front_distance = Danger</code>) 時盲目前行，將承受嚴厲的碰撞懲罰 (-1.0)。</li>
<li class="">若利用伺服馬達搖頭探測到側方開闊通道 (<code>left_distance = Safe</code> 或 <code>right_distance = Safe</code>) 並執行轉向，則可規避撞擊。</li>
</ul>
</li>
<li class=""><strong>長遠時序差分信用分配 (Temporal Difference Credit Assignment)</strong>：<!-- -->
<ul>
<li class="">儘管側向繞行初期距離目標變遠，但只要最終成功繞過牆壁並命中目標，就能獲得豐厚的正向終止獎勵 (+1.0)。</li>
<li class="">在 γ = 0.99 的折扣傳播下，終止獎勵沿繞行狀態序列逆向滲透進 Q(s, a) 表格，代理人逐步學會：<strong>「在牆壁面前，暫時偏離目標的迂迴路線在長遠預期中能帶來更高累積價值」</strong>。</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-webots-headless-高性能自動化模擬管線">3. Webots Headless 高性能自動化模擬管線<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3#3-webots-headless-%E9%AB%98%E6%80%A7%E8%83%BD%E8%87%AA%E5%8B%95%E5%8C%96%E6%A8%A1%E6%93%AC%E7%AE%A1%E7%B7%9A" class="hash-link" aria-label="3. Webots Headless 高性能自動化模擬管線的直接連結" title="3. Webots Headless 高性能自動化模擬管線的直接連結" translate="no">​</a></h2>
<p>在複雜的障礙地圖中，為了驗證強化學習演算法的強健性，嚴謹的<strong>多隨機種子 (Seeds 0, 1, 2) 交叉驗證</strong>必不可少。若每次都在 Webots 桌面圖形介面下手動跑完 3 輪各 1,500 個回合，將耗費數十小時。</p>
<p>我們撰寫了 <strong><code>headless_run.ps1</code> PowerShell 指令碼</strong>，在背景以無渲染高速模式 (<code>--mode=fast --no-rendering</code>) 啟動 Webots，實現全自動無人值守訓練與收斂繪圖：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-自動化無渲染管線-headless_runps1">💻 自動化無渲染管線 (<code>headless_run.ps1</code>)<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3#-%E8%87%AA%E5%8B%95%E5%8C%96%E7%84%A1%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%B7%9A-headless_runps1" class="hash-link" aria-label="-自動化無渲染管線-headless_runps1的直接連結" title="-自動化無渲染管線-headless_runps1的直接連結" translate="no">​</a></h3>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># 以高速 Headless 模式背景啟動 Webots</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">$webotsProc = Start-Process -FilePath $WebotsExe `</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    -ArgumentList @("--mode=fast", "--no-rendering", "--stdout", "--stderr", $WorldFile) `</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    -PassThru</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 監聽通訊端並啟動強化學習訓練</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python train_qlearning.py --scenario obstacle12 --seed 0 --episodes 1500</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 訓練完成後強制清理程序並產生訓練收斂曲線</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Stop-Process -Id $webotsProc.Id -Force</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python -m qlearning.plot_curves --log-dir logs/obstacle12/seed_0</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-實證評估跨環境模擬學習曲線對比">4. 實證評估：跨環境模擬學習曲線對比<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3#4-%E5%AF%A6%E8%AD%89%E8%A9%95%E4%BC%B0%E8%B7%A8%E7%92%B0%E5%A2%83%E6%A8%A1%E6%93%AC%E5%AD%B8%E7%BF%92%E6%9B%B2%E7%B7%9A%E5%B0%8D%E6%AF%94" class="hash-link" aria-label="4. 實證評估：跨環境模擬學習曲線對比的直接連結" title="4. 實證評估：跨環境模擬學習曲線對比的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="多環境學習曲線對比" src="https://globaldevlog.com/zh-Hant/assets/images/02_comparison_curves-0d241e07d5b445acc1866d00247179b7.png" width="1625" height="585" class="img_ev3q"></p>
<p>上圖展示了開闊平原 (<code>no_obstacle</code>) 與 12 牆障礙場地 (<code>obstacle12</code>) 在 3 組獨立隨機種子下的完整學習曲線對比。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-核心基準測試指標">📊 核心基準測試指標<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3#-%E6%A0%B8%E5%BF%83%E5%9F%BA%E6%BA%96%E6%B8%AC%E8%A9%A6%E6%8C%87%E6%A8%99" class="hash-link" aria-label="📊 核心基準測試指標的直接連結" title="📊 核心基準測試指標的直接連結" translate="no">​</a></h3>
<table><thead><tr><th style="text-align:left">評估指標</th><th style="text-align:center">開闊平原 (<code>no_obstacle</code>)</th><th style="text-align:center">12 面牆障礙場地 (<code>obstacle12</code>)</th></tr></thead><tbody><tr><td style="text-align:left"><strong>初期收斂轉折點</strong></td><td style="text-align:center">約 300 ~ 400 回合</td><td style="text-align:center">約 600 ~ 800 回合</td></tr><tr><td style="text-align:left"><strong>最終成功率 (100 回合移動平均)</strong></td><td style="text-align:center"><strong>98.5% ~ 99.2%</strong></td><td style="text-align:center"><strong>91.4% ~ 94.8%</strong></td></tr><tr><td style="text-align:left"><strong>單回合平均消耗步數</strong></td><td style="text-align:center">15 ~ 22 步</td><td style="text-align:center">38 ~ 52 步 (包含迂迴路徑)</td></tr><tr><td style="text-align:left"><strong>隨機種子間變異數</strong></td><td style="text-align:center">極低（策略高度收斂穩定）</td><td style="text-align:center">中等（受初期隨機探索路徑影響存在差異）</td></tr></tbody></table>
<ul>
<li class=""><strong>探索週期拉長</strong>：在 <code>obstacle12</code> 場景下，代理人摸索出可用繞行走廊前碰撞極其頻繁，平均回報在負數區間 (-0.6 ~ -0.4) 持續到了第 400 回合。</li>
<li class=""><strong>繞行策略確立</strong>：在 800 回合之後，所有隨機種子的成功率均躍升至 90% 以上，證明機器人已牢固掌握在複雜迷宮夾縫中靈活穿行且無碰撞的高級避障策略。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下一步研究方向">5. 下一步研究方向<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-19-rl-qlearning-3#5-%E4%B8%8B%E4%B8%80%E6%AD%A5%E7%A0%94%E7%A9%B6%E6%96%B9%E5%90%91" class="hash-link" aria-label="5. 下一步研究方向的直接連結" title="5. 下一步研究方向的直接連結" translate="no">​</a></h2>
<p>我們成功證明了 648 狀態離散表格型 Q-Learning 能夠應對迷宮級別的複雜障礙繞行。</p>
<p>在第 4 篇中，我們將載入訓練完成的權重檔案 (<code>models/*.npy</code>)，在關閉探索策略的情況下利用 <strong><code>evaluate_qlearning.py</code> 進行確定性測試，並與 DQN 及 SARSA 等經典演算法開展橫向性能對比分析</strong>！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="強化學習" term="強化學習"/>
        <category label="Q-Learning" term="Q-Learning"/>
        <category label="Webots" term="Webots"/>
        <category label="避障" term="避障"/>
        <category label="局部最佳" term="局部最佳"/>
        <category label="Headless" term="Headless"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 機器人] 表格型 Q-Learning 實作與獎勵函數(Reward Shaping)調優 (#2)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2"/>
        <updated>2026-05-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度解析 Webots 移動機器人的表格型 Q-Learning (Tabular Q-Learning) TD(0) 更新公式、指數衰減 ε-貪婪探索策略，以及克服稀疏獎勵 (Sparse Reward) 的稠密獎勵塑造 (Dense Reward Shaping) 調優實戰。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="訓練獎勵曲線" src="https://globaldevlog.com/zh-Hant/assets/images/01_reward_curve-dafe6b9a6b58ab351aa0d2717b504f0a.png" width="800" height="400" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心重點 (TL;DR)</strong></p>
<ul>
<li class=""><strong>問題 (Problem)</strong>：在僅於目標抵達 (+1.0) 與牆面碰撞 (-1.0) 時給予反饋的稀疏獎勵 (Sparse Reward) 環境下，代理人往往需要經歷數千個回合的原地打轉或盲目徘徊，才能偶然遭遇目標，探索嚴重延遲。</li>
<li class=""><strong>解決方案 (Solution)</strong>：設計了結合基於位能的歐氏距離增量獎勵 (+0.1 × Δdist) 與單步 (-0.01)、轉向 (-0.01)、倒車 (-0.05) 懲罰的多目標<strong>稠密獎勵塑造 (Dense Reward Shaping)</strong> 機制，並搭配指數衰減的 ε-貪婪 (Epsilon-Greedy, 1.0 → 0.05) 探索策略。</li>
<li class=""><strong>實驗結果 (Result)</strong>：在無障礙 (<code>no_obstacle</code>) 情境下，移動平均獎勵在 500 個回合以內穩健轉正並進入高位區間 (0.7+)，最終導航成功率達到 98% 以上。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-表格型-q-learning-更新公式與代理人架構">1. 表格型 Q-Learning 更新公式與代理人架構<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#1-%E8%A1%A8%E6%A0%BC%E5%9E%8B-q-learning-%E6%9B%B4%E6%96%B0%E5%85%AC%E5%BC%8F%E8%88%87%E4%BB%A3%E7%90%86%E4%BA%BA%E6%9E%B6%E6%A7%8B" class="hash-link" aria-label="1. 表格型 Q-Learning 更新公式與代理人架構的直接連結" title="1. 表格型 Q-Learning 更新公式與代理人架構的直接連結" translate="no">​</a></h2>
<p>表格型 Q-Learning (Tabular Q-Learning) 是一種建立在貝爾曼最佳方程式 (Bellman Optimality Equation) 之上的<strong>離策略時序差分 (Off-policy Temporal Difference, TD(0))</strong> 演算法。</p>
<p>代理人維護一個形狀為 $(648, 4)$ 的二維 NumPy 陣列作為動作價值表 $Q(s, a)$。利用從 Webots Socket 伺服器即時傳輸的狀態轉移元組 $(s, a, r, s', done)$，代理人依照下列公式即時更新 $Q$ 值：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-q-learning-td0-核心更新公式">📐 Q-Learning TD(0) 核心更新公式<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#-q-learning-td0-%E6%A0%B8%E5%BF%83%E6%9B%B4%E6%96%B0%E5%85%AC%E5%BC%8F" class="hash-link" aria-label="📐 Q-Learning TD(0) 核心更新公式的直接連結" title="📐 Q-Learning TD(0) 核心更新公式的直接連結" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Target = r + γ * max_a' Q(s', a') * (1 - done)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">TD Error = Target - Q(s, a)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Q(s, a) &lt;- Q(s, a) + α * TD Error</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>學習率 (Learning Rate, α = 0.1)</strong>：平衡吸收全新經驗與保留歷史估計值之間的權重。</li>
<li class=""><strong>折扣因子 (Discount Factor, γ = 0.99)</strong>：量化未來長期累積回報的現值，對多步導航至關重要。</li>
<li class=""><strong>終止狀態遮罩 (<code>1 - done</code>)</strong>：在抵達目標或發生碰撞導致回合結束時，將未來的預期價值遮罩為 0，精準提取終止即時獎勵。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-代理人核心實作-qlearning_agentpy">💻 代理人核心實作 (<code>qlearning_agent.py</code>)<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#-%E4%BB%A3%E7%90%86%E4%BA%BA%E6%A0%B8%E5%BF%83%E5%AF%A6%E4%BD%9C-qlearning_agentpy" class="hash-link" aria-label="-代理人核心實作-qlearning_agentpy的直接連結" title="-代理人核心實作-qlearning_agentpy的直接連結" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">QLearningAgent</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> n_states</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">648</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> n_actions</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gamma</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.99</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 eps_start</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eps_min</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eps_decay</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.995</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rng </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">default_rng</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">zeros</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n_states</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> n_actions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float64</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">alpha </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">alpha</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gamma </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gamma</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eps_start</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_min </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eps_min</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_decay </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eps_decay</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">n_actions </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n_actions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">act</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> deterministic</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:#e3116c">"""ε-貪婪行為策略：探索與利用的動態權衡"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> deterministic</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rng</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rng</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">integers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">n_actions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">argmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> s_next</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> done</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:#e3116c">"""TD(0) 離策略 Q 表格更新"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        target </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> r </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> done </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gamma </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s_next</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        td_error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> target </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">alpha </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> td_error</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">td_error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">decay_epsilon</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:#e3116c">"""每個回合結束時執行的指數衰減"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_min</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_decay</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-指數衰減-ε-貪婪探索策略">2. 指數衰減 ε-貪婪探索策略<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#2-%E6%8C%87%E6%95%B8%E8%A1%B0%E6%B8%9B-%CE%B5-%E8%B2%AA%E5%A9%AA%E6%8E%A2%E7%B4%A2%E7%AD%96%E7%95%A5" class="hash-link" aria-label="2. 指數衰減 ε-貪婪探索策略的直接連結" title="2. 指數衰減 ε-貪婪探索策略的直接連結" translate="no">​</a></h2>
<p>在訓練初期，代理人對場地環境毫無先驗知識，必須仰賴 <strong>ε = 1.0 (100% 全隨機探索)</strong> 來觸碰邊界並發現目標。</p>
<p>然而，若不對探索率進行收斂，即便代理人已經摸索出最佳路徑，仍會因高頻隨機動作而不斷撞牆。為此，我們在每個回合結束時套用逐回合指數衰減公式：</p>
<p><strong>ε_(ep+1) = max(ε_min, ε_ep × 0.995)</strong></p>
<ul>
<li class=""><strong>第 1 回合</strong>：ε = 1.0（全方位隨機探索）</li>
<li class=""><strong>第 300 回合</strong>：ε ≈ 0.22（探索顯著收斂，重心轉向利用高 Q 值經驗）</li>
<li class=""><strong>第 600 回合</strong>：ε = 0.05（降至設定的最低探索下限 ε_min，保持 95% 確定性最佳利用）</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-克服稀疏導航獎勵的稠密獎勵塑造機制">3. 克服稀疏導航獎勵的稠密獎勵塑造機制<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#3-%E5%85%8B%E6%9C%8D%E7%A8%80%E7%96%8F%E5%B0%8E%E8%88%AA%E7%8D%8E%E5%8B%B5%E7%9A%84%E7%A8%A0%E5%AF%86%E7%8D%8E%E5%8B%B5%E5%A1%91%E9%80%A0%E6%A9%9F%E5%88%B6" class="hash-link" aria-label="3. 克服稀疏導航獎勵的稠密獎勵塑造機制的直接連結" title="3. 克服稀疏導航獎勵的稠密獎勵塑造機制的直接連結" translate="no">​</a></h2>
<p>移動機器人導航面臨的核心挑戰是<strong>極端稀疏的獎勵反饋 (Sparse Reward Feedback)</strong>。在 1.2m × 1.2m 的競技場中，隨機漫遊幾乎無法獲得任何有效反饋，直到偶然撞入 0.10m 的目標容差範圍。</p>
<p>為了有效引導策略收斂，我們在 Webots 控制器 (<code>RL_controller.py</code>) 內部設計了多層<strong>稠密獎勵塑造 (Dense Reward Shaping)</strong> 體系：</p>
<table><thead><tr><th style="text-align:left">獎勵組成項</th><th style="text-align:center">數值設定</th><th style="text-align:left">設計意圖與物理效果</th></tr></thead><tbody><tr><td style="text-align:left"><strong>抵達目標 (<code>REWARD_GOAL</code>)</strong></td><td style="text-align:center"><strong>+1.0</strong></td><td style="text-align:left">成功抵達目標位置時的正向終止獎勵</td></tr><tr><td style="text-align:left"><strong>碰撞牆面/障礙 (<code>REWARD_COLLISION</code>)</strong></td><td style="text-align:center"><strong>-1.0</strong></td><td style="text-align:left">發生物理接觸時的嚴厲懲罰與回合截斷</td></tr><tr><td style="text-align:left"><strong>距離增量獎勵 (<code>REWARD_DELTA_SCALE</code>)</strong></td><td style="text-align:center"><strong>+0.1 × Δdist</strong></td><td style="text-align:left">與歐氏距離縮短量成正比的即時位能引導反饋</td></tr><tr><td style="text-align:left"><strong>單步時間懲罰 (<code>REWARD_STEP</code>)</strong></td><td style="text-align:center"><strong>-0.01</strong></td><td style="text-align:left">防止停滯不前，鼓勵代理人探索最短耗時路徑</td></tr><tr><td style="text-align:left"><strong>轉向懲罰 (<code>REWARD_TURN</code>)</strong></td><td style="text-align:center"><strong>-0.01</strong></td><td style="text-align:left">抑制無意義的原地高頻擺頭與打轉行為</td></tr><tr><td style="text-align:left"><strong>倒車懲罰 (<code>REWARD_BACKWARD</code>)</strong></td><td style="text-align:center"><strong>-0.05</strong></td><td style="text-align:left">懲罰效率低下的後退動作，強化直面前方目標</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-距離增量-distance-delta-的數學原理">🔍 距離增量 (Distance Delta) 的數學原理<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#-%E8%B7%9D%E9%9B%A2%E5%A2%9E%E9%87%8F-distance-delta-%E7%9A%84%E6%95%B8%E5%AD%B8%E5%8E%9F%E7%90%86" class="hash-link" aria-label="🔍 距離增量 (Distance Delta) 的數學原理的直接連結" title="🔍 距離增量 (Distance Delta) 的數學原理的直接連結" translate="no">​</a></h3>
<p><strong>Δdist = dist_prev - dist_curr</strong></p>
<ul>
<li class="">朝目標方向前進一步 (0.05m)，立即獲得 +0.1 × 0.05 = +0.005 的即時正向激勵。</li>
<li class="">相反地，若背離目標行駛，則會承受即時扣分。這使得代理人在最初 100 個回合內就能迅速學會將車頭朝向對準目標中心。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-實證評估獎勵曲線與成功率收斂">4. 實證評估：獎勵曲線與成功率收斂<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#4-%E5%AF%A6%E8%AD%89%E8%A9%95%E4%BC%B0%E7%8D%8E%E5%8B%B5%E6%9B%B2%E7%B7%9A%E8%88%87%E6%88%90%E5%8A%9F%E7%8E%87%E6%94%B6%E6%96%82" class="hash-link" aria-label="4. 實證評估：獎勵曲線與成功率收斂的直接連結" title="4. 實證評估：獎勵曲線與成功率收斂的直接連結" translate="no">​</a></h2>
<p>在 <code>no_obstacle</code>（無障礙場地）中進行 1,500 個回合的完整訓練，展現了與理論分析高度契合的優異收斂特性。</p>
<p><img decoding="async" loading="lazy" alt="成功率曲線" src="https://globaldevlog.com/zh-Hant/assets/images/02_success_curve-a47390e774a7a40ace8869f29884f2bd.png" width="800" height="400" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-收斂階段里程碑">📈 收斂階段里程碑<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#-%E6%94%B6%E6%96%82%E9%9A%8E%E6%AE%B5%E9%87%8C%E7%A8%8B%E7%A2%91" class="hash-link" aria-label="📈 收斂階段里程碑的直接連結" title="📈 收斂階段里程碑的直接連結" translate="no">​</a></h3>
<ol>
<li class=""><strong>第 0 ~ 200 回合（初期探索階段）</strong>：<!-- -->
<ul>
<li class="">頻繁發生撞牆以及達到 150 步逾時上限，移動平均獎勵徘徊在負數區間 (-0.4 ~ -0.2)。</li>
</ul>
</li>
<li class=""><strong>第 200 ~ 500 回合（快速攀升階段）</strong>：<!-- -->
<ul>
<li class="">隨著距離增量位能獎勵在 Q 表格中有效反向傳播，策略在 <code>goal_heading</code> 居中時建立起強烈的直行傾向。</li>
<li class="">成功率從 20% 迅猛攀升至 <strong>80% 以上</strong>。</li>
</ul>
</li>
<li class=""><strong>第 500 ~ 1,500 回合（漸近收斂穩定階段）</strong>：<!-- -->
<ul>
<li class="">ε 穩定在 0.05 下限，單回合平均消耗步數從 150 步劇降至 <strong>15 ~ 22 步</strong>。</li>
<li class="">100 回合移動平均成功率穩定收斂在 <strong>98.5% 以上</strong>。</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下一步研究方向">5. 下一步研究方向<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-18-rl-qlearning-2#5-%E4%B8%8B%E4%B8%80%E6%AD%A5%E7%A0%94%E7%A9%B6%E6%96%B9%E5%90%91" class="hash-link" aria-label="5. 下一步研究方向的直接連結" title="5. 下一步研究方向的直接連結" translate="no">​</a></h2>
<p>我們已經驗證，在 648 個離散狀態空間下，表格型 Q-Learning 能夠乾淨俐落地征服開闊平原導航。</p>
<p>然而，<strong>真實物理世界充滿著複雜的牆面與死胡同障礙</strong>。在第 3 篇中，我們將直面 <strong><code>obstacle12</code> 與 <code>obstacle_hard</code> 迷宮地圖</strong>，深入剖析代理人如何跳出局部最佳陷阱，並利用多重隨機種子平行訓練建構高強健性導航策略！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="強化學習" term="強化學習"/>
        <category label="Q-Learning" term="Q-Learning"/>
        <category label="獎勵函數" term="獎勵函數"/>
        <category label="Webots" term="Webots"/>
        <category label="機器人學" term="機器人學"/>
        <category label="機器學習" term="機器學習"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 畢業設計開發日誌 #19] 自動化評測報告生成與 FastAPI PostgreSQL 401 鑑權故障排查]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-17-maeumjigi-19</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-maeumjigi-19"/>
        <updated>2026-05-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[攻克全棧聯調瓶頸：利用 Claude Code 全自動生成大模型評測報告，徹底排查並解決 PostgreSQL 401 登錄鑑權故障，探索知識圖譜混合架構。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/zh-Hant/assets/images/01_cover-12712349ddac15465e60572918ef40de.png" width="720" height="405" class="img_ev3q"></p>
<p>Mid-May 2026 was supposed to be a calm interval after wrapping up our 2nd fine-tuning (<code>fine_tuning_2</code>). Instead, May 17th hit our team with a relentless double challenge: condensing hundreds of raw inference metrics into an automated technical report, while simultaneously battling a cryptic <code>HTTP 401 Unauthorized</code> cascading error across our newly integrated <strong>FastAPI</strong> and <strong>PostgreSQL</strong> backend.</p>
<p>Here is the complete post-mortem of how we automated our reporting and systematically neutralized the backend authentication breakdown.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-autonomous-fine-tuning-evaluation-reporting">1. Autonomous Fine-Tuning Evaluation Reporting<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-maeumjigi-19#1-autonomous-fine-tuning-evaluation-reporting" class="hash-link" aria-label="1. Autonomous Fine-Tuning Evaluation Reporting的直接連結" title="1. Autonomous Fine-Tuning Evaluation Reporting的直接連結" translate="no">​</a></h3>
<p>Analyzing the 2nd fine-tuning results required processing hundreds of inference samples across multiple checkpoint variants (comparing Alpha values: 16, 32, and 64). Doing this manually was mathematically unfeasible given our capstone deadlines.</p>
<p>We engineered a structured prompt document (<code>report_prompt.md</code>) that guided <strong>Claude Code</strong> to analyze raw JSON evaluation matrices and automatically synthesize an executive technical report:</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token title important punctuation" style="color:#393A34">#</span><span class="token title important"> Excerpt from report_prompt.md</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">1.</span><span class="token plain"> Parse evaluation metrics (BERTScore, ROUGE-L, Perplexity) from /eval/results_alpha_*.json.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">2.</span><span class="token plain"> Group model variants by empathy score, conversational naturalness, and safety compliance.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">3.</span><span class="token plain"> Highlight critical failure cases (e.g., hyper-formal robotic apologies vs conversational warmth).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">4.</span><span class="token plain"> Output the synthesis directly into a publication-ready Word document structure.</span><br></div></code></pre></div></div>
<p>The AI agent generated <code>fine_tuning_2_report.docx</code> in under 3 minutes, complete with comparative analysis tables and qualitative excerpt breakdowns.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-the-nightmare-of-http-401-unauthorized-in-postgresql">2. The Nightmare of HTTP 401 Unauthorized in PostgreSQL<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-maeumjigi-19#2-the-nightmare-of-http-401-unauthorized-in-postgresql" class="hash-link" aria-label="2. The Nightmare of HTTP 401 Unauthorized in PostgreSQL的直接連結" title="2. The Nightmare of HTTP 401 Unauthorized in PostgreSQL的直接連結" translate="no">​</a></h3>
<p>With reporting streamlined, we restored our staging database dump (<code>backup.sql</code>) into a local PostgreSQL instance to test end-to-end user session persistence. Although the database restored with zero SQL syntax errors, our Next.js frontend crashed into an inescapable login loop:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">INFO: 127.0.0.1:54322 - "POST /auth/login HTTP/1.1" 401 Unauthorized</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DETAIL: {"error": "Invalid credentials or malformed signature"}</span><br></div></code></pre></div></div>
<p>Even with verified test credentials (<code>test@maeumjigi.kr</code> / <code>Password123!</code>), the FastAPI authentication service persistently rejected every login handshake.</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sequenceDiagram</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    autonumber</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    actor Client as Next.js Web Client</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    participant API as FastAPI Gateway (/auth/login)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    participant DB as PostgreSQL Database</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    participant Auth as Passlib Bcrypt Verifier</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Client-&gt;&gt;API: POST /auth/login {email, password}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    API-&gt;&gt;DB: SELECT id, password_hash FROM users WHERE email=...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    DB--&gt;&gt;API: Returns $2a$12$... hash string</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    API-&gt;&gt;Auth: bcrypt.verify("Password123!", db_hash)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Note over Auth: Trap 1: Salt algorithm mismatch ($2a$ vs $2b$)!</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Auth--&gt;&gt;API: Verification Failed (False)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    API--&gt;&gt;Client: 401 Unauthorized</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-root-cause-investigation--debug_loginpy">3. Root Cause Investigation &amp; debug_login.py<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-maeumjigi-19#3-root-cause-investigation--debug_loginpy" class="hash-link" aria-label="3. Root Cause Investigation &amp; debug_login.py的直接連結" title="3. Root Cause Investigation &amp; debug_login.py的直接連結" translate="no">​</a></h3>
<p>To isolate whether the breakdown stemmed from frontend CORS headers, database corruption, or JWT secret misconfigurations, we wrote a standalone diagnostic script: <code>debug_login.py</code>.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Standalone authentication diagnostic utility: debug_login.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> sys</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> passlib</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">context </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> CryptContext</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> sqlalchemy </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> create_engine</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DATABASE_URL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"postgresql://postgres:secret@localhost:5432/maeumjigi"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pwd_context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CryptContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">schemes</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"bcrypt"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> deprecated</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"auto"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">diagnose_user_auth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">email</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> raw_pass</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    engine </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> create_engine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">DATABASE_URL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> engine</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">connect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> conn</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> conn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"SELECT password_hash FROM users WHERE email = :e"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"e"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fetchone</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"[FAIL] User not found in database!"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        stored_hash </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"[INFO] Retrieved hash: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">stored_hash</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">[</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">:</span><span class="token string-interpolation interpolation format-spec">15]</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">..."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Verify hash algorithm version compatibility</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        is_valid </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> pwd_context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">verify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw_pass</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> stored_hash</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"[RESULT] Password Match Status: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">is_valid</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"__main__"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    diagnose_user_auth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"test@maeumjigi.kr"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Password123!"</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Running this diagnostic exposed three compounding bugs:</p>
<table><thead><tr><th style="text-align:left">Failure Vector</th><th style="text-align:left">Technical Root Cause</th><th style="text-align:left">Resolution Applied</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Bcrypt Salt Version</strong></td><td style="text-align:left">Database dump contained <code>$2a$</code> hashes from older PHP/Node seeds; modern <code>passlib</code> expected <code>$2b$</code>.</td><td style="text-align:left">Configured <code>pwd_context = CryptContext(schemes=["bcrypt"], bcrypt__ident="2b", deprecated="auto")</code>.</td></tr><tr><td style="text-align:left"><strong>JWT Secret Encoding</strong></td><td style="text-align:left">Secret string was loaded via <code>.env</code> with trailing newline `</td><td style="text-align:left"></td></tr></tbody></table>
<p><code>, breaking HMAC-SHA256 signature checks. | Sanitized key ingestion: </code>SECRET_KEY = os.getenv("JWT_SECRET").strip().encode('utf-8')<code>. | | **CORS Preflight** | Chrome browser blocked </code>Authorization: Bearer<code>headers during OPTIONS preflight checks. | Explicitly registered</code>CORSMiddleware<code>with</code>allow_headers=["<em>"]<code>and</code>expose_headers=["</em>"]`. |</p>
<p>Within 45 minutes of targeted refactoring, login latency dropped to 42ms with a 100% success rate!</p>
<p><img decoding="async" loading="lazy" alt="DB Troubleshooting" src="https://globaldevlog.com/zh-Hant/assets/images/02_db-1d0bb76370f8508377e9472ff5cb15c8.png" width="1373" height="5225" class="img_ev3q"></p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-architectural-expansion-exploring-hybrid-knowledge-graphs">4. Architectural Expansion: Exploring Hybrid Knowledge Graphs<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-maeumjigi-19#4-architectural-expansion-exploring-hybrid-knowledge-graphs" class="hash-link" aria-label="4. Architectural Expansion: Exploring Hybrid Knowledge Graphs的直接連結" title="4. Architectural Expansion: Exploring Hybrid Knowledge Graphs的直接連結" translate="no">​</a></h3>
<p>Resolving backend stability opened room for deeper architectural exploration. During clinical evaluation of our 2nd fine-tuned model, we noticed an intrinsic weakness shared by all parametric LLMs: <strong>factual hallucinations during multi-hop crisis reasoning</strong>.</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph User_Query ["User Input: Complex Crisis State"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        U["I stopped taking my SSRI antidepressants 3 days ago and feel severe tremors."]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Hybrid_Architecture ["Hybrid Symbolic-Neural Architecture"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        U --&gt; KG_Extractor[Clinical Entity &amp; Relation Extractor]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        KG_Extractor --&gt;|Cypher Query| KG[(Neo4j Medical Knowledge Graph)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        KG --&gt;|Verified Triples: SSRI -&gt; Discontinuation Syndrome| Context_Injector[Clinical Context Injector]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        U --&gt; Direct_Prompt[Empathetic System Prompt]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Direct_Prompt &amp; Context_Injector --&gt; FineTuned_LLM[Fine-Tuned Qwen / Llama 3.1]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Verified_Output ["Safety-Compliant Response"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        FineTuned_LLM --&gt; Output["Empathetic validation + Immediate medical warning regarding SSRI discontinuation syndrome."]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div></code></pre></div></div>
<p>By drafting <code>chatbot_design_prompt.md</code>, we laid the theoretical foundation for blending parametric LLM conversational empathy with deterministic Knowledge Graph validation.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-day-19-key-engineering-takeaways">5. Day 19 Key Engineering Takeaways<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-maeumjigi-19#5-day-19-key-engineering-takeaways" class="hash-link" aria-label="5. Day 19 Key Engineering Takeaways的直接連結" title="5. Day 19 Key Engineering Takeaways的直接連結" translate="no">​</a></h3>
<ol>
<li class=""><strong>Automated Documentation</strong>: Reduced model benchmarking documentation turnaround from 6 hours to under 3 minutes using structured prompts and AI agents.</li>
<li class=""><strong>Hardened Authentication</strong>: Diagnosed and permanently resolved bcrypt salt mismatches and CORS header traps across PostgreSQL and FastAPI.</li>
<li class=""><strong>Hybrid AI Vision</strong>: Established the conceptual design for integrating symbolic Knowledge Graphs to eliminate clinical hallucinations in mental health counseling.</li>
</ol>
<p>In Dev Log #20, we explore the completion of our 3rd fine-tuning cycle and the rollout of live community board features!</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="devlog" term="devlog"/>
        <category label="automated-reporting" term="automated-reporting"/>
        <category label="fastapi" term="fastapi"/>
        <category label="postgresql" term="postgresql"/>
        <category label="authentication" term="authentication"/>
        <category label="troubleshooting" term="troubleshooting"/>
        <category label="knowledge-graph" term="knowledge-graph"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 機器人] Q-Learning 自主導航：648 離散狀態空間劃分與 TCP Socket IPC 架構設計 (#1)]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1"/>
        <updated>2026-05-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[為 Webots 移動機器人自主導航與表格化 Q-Learning 設計 648 離散狀態空間量化映射器與行分隔 JSON TCP Socket 行程間通訊架構。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Webots 模擬環境與自主移動自走車" src="https://globaldevlog.com/zh-Hant/assets/images/01_webots_arena-b49bd821fb90ed1d7e619add024bb5e7.png" width="1721" height="1033" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要點 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>問題痛點</strong>：連續感測器讀數（距離、朝向角）會導致表格型強化學習遭遇維度災難（State-Space Explosion），同時 Python 強化學習代理與 Webots 模擬核心之間需要維持微秒級、低延遲的物理同步。</li>
<li class=""><strong>解決方案</strong>：設計了基於幾何學空間劃分的 5 維連續感測器量化器（<code>discretizer.py</code>），產生 3 × 8 × 3 × 3 × 3 = 648 維離散狀態；搭建了基於 TCP 5000 連接埠的極輕量級行分隔 JSON 套接字通訊管線。</li>
<li class=""><strong>落地成效</strong>：實作了 O(1) 複雜度的超高速 Q-Table 定址更新，確保了模擬器與 Python 端 32ms 物理模擬步長的一致無阻塞同步。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-專案研發背景為何選擇-webots-與經典-q-learning">1. 專案研發背景：為何選擇 Webots 與經典 Q-Learning？<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1#1-%E5%B0%88%E6%A1%88%E7%A0%94%E7%99%BC%E8%83%8C%E6%99%AF%E7%82%BA%E4%BD%95%E9%81%B8%E6%93%87-webots-%E8%88%87%E7%B6%93%E5%85%B8-q-learning" class="hash-link" aria-label="1. 專案研發背景：為何選擇 Webots 與經典 Q-Learning？的直接連結" title="1. 專案研發背景：為何選擇 Webots 與經典 Q-Learning？的直接連結" translate="no">​</a></h2>
<p>讓差速驅動機器人在未知的室內場館中自主導航避障並抵達目標點，是移動機器人學的經典基石課題。儘管深度強化學習（DQN, PPO）在處理高維影像方面表現亮眼，但其龐大的參數量在邊緣微控制器上推論延遲高，且缺乏數學上的完全可解釋性。</p>
<p>在本專欄專案中，我們在 <strong>Webots 3D 物理模擬引擎</strong> 中應用經典的 <strong>表格型 Q-Learning (Off-policy TD(0))</strong>，旨在在 1.2m × 1.2m 的室內競技場內訓練出一套數學上完全可驗證的最優避障與導航策略。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-系統架構設計webots-控制器--python-強化學習引擎">2. 系統架構設計：Webots 控制器 ↔ Python 強化學習引擎<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1#2-%E7%B3%BB%E7%B5%B1%E6%9E%B6%E6%A7%8B%E8%A8%AD%E8%A8%88webots-%E6%8E%A7%E5%88%B6%E5%99%A8--python-%E5%BC%B7%E5%8C%96%E5%AD%B8%E7%BF%92%E5%BC%95%E6%93%8E" class="hash-link" aria-label="2. 系統架構設計：Webots 控制器 ↔ Python 強化學習引擎的直接連結" title="2. 系統架構設計：Webots 控制器 ↔ Python 強化學習引擎的直接連結" translate="no">​</a></h2>
<p>系統解耦為清晰的兩大層級：<strong>Webots 模擬端（伺服端）</strong> 與 <strong>Python 演算法端（客戶端）</strong>：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart LR</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Webots["Webots 3D 模擬器 (localhost:5000)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Robot["Supervisor 機器人&lt;br&gt;(超音波感測器 + 驅動輪)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        HeadScan["搖頭掃描機制&lt;br&gt;(左右 1.1 rad 伺服馬達擺動)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Server["TCP Socket 伺服端&lt;br&gt;(行分隔 JSON 協定)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Robot -→ HeadScan -→ Server</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Python["Python 強化學習引擎 (客戶端)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Env["WebotsEnv (類似 gym 介面)&lt;br&gt;reset() / step()"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Disc["離散化映射器&lt;br&gt;(連續感測器 → 0~647 Bin)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Agent["QLearningAgent&lt;br&gt;(Q-Table 形狀 648x4)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Env -→ Disc -→ Agent</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Server &lt;-→|"請求: {type: step, action: 0..3}&lt;br&gt;回應: {state: [...], reward, done}"| Env</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Webots 控制器 (<code>RL_controller.py</code>)</strong>：負責執行具體的動作指令 480ms（對應 15 個模擬步長 × 32ms），並驅動機載超音波感測器左右擺動 1.1 rad（約 63°）以消除盲區。</li>
<li class=""><strong>Socket 協定</strong>：採用極度輕量的行分隔 JSON 格式進行 TCP 本地環回通訊：<!-- -->
<ul>
<li class="">發送動作請求：<code>{"type": "step", "action": 0}</code> (0: 前進, 1: 後退, 2: 左轉, 3: 右轉)</li>
<li class="">接收環境回饋：<code>{"state": [0.45, 0.12, 0.35, 0.28, 0.40], "reward": 0.05, "done": false, "info": {...}}</code></li>
</ul>
</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-連續感測器讀數的-648-維離散狀態量化">3. 連續感測器讀數的 648 維離散狀態量化<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1#3-%E9%80%A3%E7%BA%8C%E6%84%9F%E6%B8%AC%E5%99%A8%E8%AE%80%E6%95%B8%E7%9A%84-648-%E7%B6%AD%E9%9B%A2%E6%95%A3%E7%8B%80%E6%85%8B%E9%87%8F%E5%8C%96" class="hash-link" aria-label="3. 連續感測器讀數的 648 維離散狀態量化的直接連結" title="3. 連續感測器讀數的 648 維離散狀態量化的直接連結" translate="no">​</a></h2>
<p>為了應用表格型強化學習，連續浮點讀數必須被映射至有限離散狀態空間。根據運動學關鍵特徵，我們選取了 5 個核心狀態變數：</p>
<table><thead><tr><th style="text-align:left">感測器特徵</th><th style="text-align:center">量化區間數 (Bins)</th><th style="text-align:left">劃分物理閾值</th><th style="text-align:left">語意解釋</th></tr></thead><tbody><tr><td style="text-align:left"><strong><code>goal_distance</code></strong> (目標距離)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.6m, 1.2m]</code></td><td style="text-align:left">近距離 (Near), 中距離 (Mid), 遠距離 (Far)</td></tr><tr><td style="text-align:left"><strong><code>goal_heading</code></strong> (目標夾角)</td><td style="text-align:center"><strong>8</strong></td><td style="text-align:left">[-π, π] (8個扇區, 每區45°)</td><td style="text-align:left">正前, 右前, 正右, 右後, 正後, 左後, 正左, 左前</td></tr><tr><td style="text-align:left"><strong><code>front_distance</code></strong> (前向測距)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.15m, 0.30m]</code></td><td style="text-align:left">碰撞危險 (Danger), 警示 (Caution), 安全 (Safe)</td></tr><tr><td style="text-align:left"><strong><code>left_distance</code></strong> (左向測距)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.15m, 0.30m]</code></td><td style="text-align:left">危險, 警示, 安全</td></tr><tr><td style="text-align:left"><strong><code>right_distance</code></strong> (右向測距)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.15m, 0.30m]</code></td><td style="text-align:left">危險, 警示, 安全</td></tr></tbody></table>
<p>總狀態數規模：
<strong>N_states = 3 × 8 × 3 × 3 × 3 = 648</strong></p>
<p>由此建構的 Q-Table 僅包含 <strong>648 × 4 = 2,592 個浮點數條目（記憶體僅佔約 2.5 KB）</strong>，能在單晶片微秒級定址，徹底擺脫對 GPU 的依賴。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-狀態離散化核心程式碼-discretizerpy">💻 狀態離散化核心程式碼 (<code>discretizer.py</code>)<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1#-%E7%8B%80%E6%85%8B%E9%9B%A2%E6%95%A3%E5%8C%96%E6%A0%B8%E5%BF%83%E7%A8%8B%E5%BC%8F%E7%A2%BC-discretizerpy" class="hash-link" aria-label="-狀態離散化核心程式碼-discretizerpy的直接連結" title="-狀態離散化核心程式碼-discretizerpy的直接連結" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 5個維度的量化分區基數</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">N_GOAL_D</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_GOAL_H</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_FRONT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_LEFT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_RIGHT </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">N_STATES </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> N_GOAL_D </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_GOAL_H </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_FRONT </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_LEFT </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_RIGHT  </span><span class="token comment" style="color:#999988;font-style:italic"># 648</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_HEADING_BINS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">linspace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">9</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_NEAR_BINS    </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.15</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.30</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_DIST_BINS    </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.6</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">discretize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""將5元組連續感測器浮點讀數映射至 [0, 647] 之間的1維整數索引。"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gd </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"goal_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _DIST_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gh </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"goal_heading"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _HEADING_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    df </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"front_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _NEAR_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    dl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"left_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _NEAR_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    dr </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"right_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _NEAR_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 計算平鋪的一維索引 (扁平化)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gd </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_GOAL_H </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> gh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_FRONT </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> df</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_LEFT </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> dl</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_RIGHT </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> dr</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-伺服馬達搖頭掃描與物理碰撞判定邏輯">4. 伺服馬達搖頭掃描與物理碰撞判定邏輯<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1#4-%E4%BC%BA%E6%9C%8D%E9%A6%AC%E9%81%94%E6%90%96%E9%A0%AD%E6%8E%83%E6%8F%8F%E8%88%87%E7%89%A9%E7%90%86%E7%A2%B0%E6%92%9E%E5%88%A4%E5%AE%9A%E9%82%8F%E8%BC%AF" class="hash-link" aria-label="4. 伺服馬達搖頭掃描與物理碰撞判定邏輯的直接連結" title="4. 伺服馬達搖頭掃描與物理碰撞判定邏輯的直接連結" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="系統底層架構與感測器物理佈局" src="https://globaldevlog.com/zh-Hant/assets/images/02_architecture-83a0cdfb8297ac248797b32bbc7fd69b.png" width="2000" height="900" class="img_ev3q"></p>
<p>固定的前置單向感測器存在致命的側方盲區。我們實作了主動式的 <strong>搖頭掃描機制 (Head-Scan)</strong>：</p>
<ol>
<li class=""><strong>執行動作步</strong>：輪子以 3.0 rad/s 驅動 480ms。</li>
<li class=""><strong>頭部旋轉測距</strong>：伺服馬達向左擺動 +1.1 rad 採集左側淨空，隨後向右打到 -1.1 rad 採集右側淨空。</li>
<li class=""><strong>回正復位</strong>：回位至 0 rad 更新正前方障礙物距離。</li>
<li class=""><strong>終止狀態條件</strong>：<!-- -->
<ul>
<li class=""><strong>抵達目標 (Goal Reached)</strong>：與目標點歐氏距離 ≤ 0.10m → 成功 (<code>done = True</code>, 獎勵 $+1.0$)</li>
<li class=""><strong>碰撞發生 (Collision)</strong>：任一方向實測距離 ≤ 0.08m → 失敗 (<code>done = True</code>, 懲罰 $-1.0$)</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下篇預告">5. 下篇預告<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-17-rl-qlearning-1#5-%E4%B8%8B%E7%AF%87%E9%A0%90%E5%91%8A" class="hash-link" aria-label="5. 下篇預告的直接連結" title="5. 下篇預告的直接連結" translate="no">​</a></h2>
<p>環境框架、Socket IPC 通訊與 648 維狀態離散化體系已經就位。</p>
<p>在第 2 篇中，我們將深入解析 <strong>表格型 Q-Learning 更新公式的數學推導、ε-貪婪指數衰減策略，以及攻克稀疏獎勵難題的稠密獎勵函數設計 (Reward Shaping)</strong>！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="reinforcement-learning" term="reinforcement-learning"/>
        <category label="q-learning" term="q-learning"/>
        <category label="webots" term="webots"/>
        <category label="robotics" term="robotics"/>
        <category label="discretization" term="discretization"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 心語伴侶畢業專題開發日誌 #18] 推論產生 Token 擴容與第二輪微調 LoRA Alpha 效果對照]]></title>
        <id>https://globaldevlog.com/zh-Hant/blog/2026-05-13-maeumjigi-18</id>
        <link href="https://globaldevlog.com/zh-Hant/blog/2026-05-13-maeumjigi-18"/>
        <updated>2026-05-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[將推論產生 max_new_tokens 放寬至 384 根除生硬截斷，深入對比評估 LoRA alpha 縮放參數（8 vs 16）對心理同理心引導的顯著影響。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="封面圖" src="https://globaldevlog.com/zh-Hant/assets/images/01_cover-4dcde4cef4209e81d49b5f5306af009d.png" width="1484" height="888" class="img_ev3q"></p>
<p>在徹底修復昨天排查出的別名硬編碼缺陷後，5 月 13 日，我們終於重啟了第二輪微調大模型真實端到端的推論產生能力基準測試。</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="破除生硬斷裂放寬產生-token-長度上限釋放深層同理心">破除生硬斷裂：放寬產生 Token 長度上限釋放深層同理心<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-13-maeumjigi-18#%E7%A0%B4%E9%99%A4%E7%94%9F%E7%A1%AC%E6%96%B7%E8%A3%82%E6%94%BE%E5%AF%AC%E7%94%A2%E7%94%9F-token-%E9%95%B7%E5%BA%A6%E4%B8%8A%E9%99%90%E9%87%8B%E6%94%BE%E6%B7%B1%E5%B1%A4%E5%90%8C%E7%90%86%E5%BF%83" class="hash-link" aria-label="破除生硬斷裂：放寬產生 Token 長度上限釋放深層同理心的直接連結" title="破除生硬斷裂：放寬產生 Token 長度上限釋放深層同理心的直接連結" translate="no">​</a></h3>
<p>逐句審讀推論產生的應答文本時，我們發現 <code>Llama33-70B_a8</code> 模型的輸出依然呈現出一種微妙的乾癟與機械緊縮感：</p>
<blockquote>
<p>「這是 Llama33-70B_a8 輸出的典型答覆。明顯感覺 max_seq_length 與 max_new_tokens 設得太緊，嚴重束縛了模型表達。結合當前物理顯示記憶體富餘量，極限能上調至多少？」</p>
</blockquote>
<p>結合當時約 39.8GB 的空閒 VRAM，我們果斷將最大產生 Token 預算 (<code>max_new_tokens</code>) 調寬至 384。調整帶來的產生質變立竿見影：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">[修改前] 「產生這種消極念頭一定讓您備受折磨。及時向專業諮商師求助非常重要。」</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[修改後] 「產生這樣的念頭一定讓您獨自承受著巨大的痛苦。但請相信，這絕不代表您內心軟弱，而是身體在向您發出訊號——說明您此刻背負的壓力已經嚴重超載了。我由衷建議您在專業諮商師的陪同下，試著把這些鬱結的情緒一點一點傾訴梳理出來。」</span><br></div></code></pre></div></div>
<p>僅僅是鬆綁了產生長度配額，模型便能從容保持長程邏輯一致性，吐露出層層遞進、飽含人情溫度與心理撫慰力量的真摯同理心長句。此前模型之所以三緘其口，純粹是被狹窄的產生長度強行截斷了思路。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lora-超參數深度剖析alpha-縮放因子對比a8-vs-a16">LoRA 超參數深度剖析：Alpha 縮放因子對比（a=8 vs. a=16）<a href="https://globaldevlog.com/zh-Hant/blog/2026-05-13-maeumjigi-18#lora-%E8%B6%85%E5%8F%83%E6%95%B8%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90alpha-%E7%B8%AE%E6%94%BE%E5%9B%A0%E5%AD%90%E5%B0%8D%E6%AF%94a8-vs-a16" class="hash-link" aria-label="LoRA 超參數深度剖析：Alpha 縮放因子對比（a=8 vs. a=16）的直接連結" title="LoRA 超參數深度剖析：Alpha 縮放因子對比（a=8 vs. a=16）的直接連結" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="超參數對比圖表" src="https://globaldevlog.com/zh-Hant/assets/images/02_graph-feb5d1991f44dd7f378232f6b3515752.png" width="2053" height="1460" class="img_ev3q"></p>
<p>第二輪微調的核心科研目的——驗證 LoRA 縮放因子 <code>alpha</code> 對領域適應能力的影響也迎來了定量結論。
透過深入挖掘 <code>fine_tuning_2</code> 目錄下的收斂日誌，我們繪製出了 <code>05_alpha_comparison_daily.png</code> 等對比折線。在固定 <code>rank=16</code> 的前提下，清晰揭示了 $\alpha=8$（欠擬合防漂移）與 $\alpha=16$（強化領域風格吸收）在訓練後期的損失震盪差異與主觀諮商品質的微妙權衡。</p>
<p>在被賦予合理的超參數與充裕的產生空間後，各大模型終於蛻變出了我們最初構想中那個體貼細膩、專業包容的「心語伴侶」。
中期簡報資料的最終拼圖，在此刻宣告圓滿合攏！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="開發日誌" term="開發日誌"/>
        <category label="微調" term="微調"/>
        <category label="推論" term="推論"/>
        <category label="max_new_tokens" term="max_new_tokens"/>
        <category label="同理心能力" term="同理心能力"/>
        <category label="畢業專題" term="畢業專題"/>
    </entry>
</feed>