<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>紙魚ㄉ部落格</title>
<link>https://paperfishblog.netlify.app/real-index.html</link>
<atom:link href="https://paperfishblog.netlify.app/real-index.xml" rel="self" type="application/rss+xml"/>
<description></description>
<generator>quarto-1.7.32</generator>
<lastBuildDate>Sun, 22 Mar 2026 16:00:00 GMT</lastBuildDate>
<item>
  <title>Open WebUI + Ollama 的本機安裝流程( Windows )</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/note-install-webui-ollama/</link>
  <description><![CDATA[ 






<section id="webui" class="level1">
<h1>WebUI</h1>
<blockquote class="blockquote">
<p>前置要求：安裝 python 3.11</p>
</blockquote>
<ol type="1">
<li>要確認 pip install 可以在 Powershell 顯示</li>
</ol>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install open-webui</span></code></pre></div>
<ol start="2" type="1">
<li>如果不行但是 python 指令可，可以換</li>
</ol>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">python</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-m</span> pip install open-webui</span></code></pre></div>
<ol start="3" type="1">
<li>試試在一個不需要系統管理員權限的地方建資料夾</li>
</ol>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> %USERPROFILE%</span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mkdir</span> openwebui</span>
<span id="cb3-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> openwebui</span>
<span id="cb3-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">open-webui</span> serve</span></code></pre></div>
<p>其中</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">open-webui</span> serve</span></code></pre></div>
<p>執行成功的話開啟 http://localhost:8080 可以進入首頁</p>
</section>
<section id="ollama" class="level1">
<h1>Ollama</h1>
<ol type="1">
<li>開 Powershell</li>
</ol>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">irm</span> https://ollama.com/install.ps1 <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">iex</span></span></code></pre></div>
<ol start="2" type="1">
<li>下載一個簡單的模型</li>
</ol>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ollama</span> pull tinyllama</span></code></pre></div>
</section>
<section id="開啟測試" class="level1">
<h1>開啟測試</h1>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">open-webui</span> serve</span></code></pre></div>
<p>成功的話開啟 http://localhost:8080 可以進入首頁</p>
<ol start="2" type="1">
<li><p>建立管理員帳密(可以亂設)</p></li>
<li><p>找到左上角，找到剛 pull 的模型</p></li>
<li><p>對話測試</p></li>
</ol>
<p><img src="https://paperfishblog.netlify.app/posts/tech/note-install-webui-ollama/scuess.png" class="img-fluid"></p>
</section>
<section id="官方連結" class="level1">
<h1>官方連結</h1>
<ol type="1">
<li><p><a href="https://github.com/open-webui/open-webui">Open WebUI 的 Github</a></p></li>
<li><p><a href="https://ollama.com/">Ollama 官方網站</a></p></li>
</ol>



</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>LLM</category>
  <category>Ollama</category>
  <category>Open WebUI</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/note-install-webui-ollama/</guid>
  <pubDate>Sun, 22 Mar 2026 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/note-install-webui-ollama/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>懶人包－快速在 Vscode 建立 Python 跟 R 環境</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/setting-py-r-in-vscode/</link>
  <description><![CDATA[ 






<p>最近會在學校用電腦的次數可能會相對變多，所以寫一下這個筆記~~。</p>
<section id="設定-python-在-vscode" class="level1">
<h1>設定 Python 在 Vscode</h1>
<section id="在-vsocde-中跑-python" class="level2">
<h2 class="anchored" data-anchor-id="在-vsocde-中跑-python">在 Vsocde 中跑 Python</h2>
<section id="執行步驟" class="level3">
<h3 class="anchored" data-anchor-id="執行步驟">執行步驟</h3>
<ol type="1">
<li><p>下載 Python &amp; Vscode</p></li>
<li><p>開啟 Vscode，選左側 Extensions，搜尋官方 Python (有藍勾勾)並安裝</p></li>
<li><p>在 Vscode 建立 <code>test.py</code>測試 code</p></li>
</ol>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hello world"</span>)</span></code></pre></div>
<p>按右上角的播放鍵，下方會彈出 Terminal 顯示 output。</p>
</section>
</section>
<section id="在-vscode-跑-jupyter-notebook" class="level2">
<h2 class="anchored" data-anchor-id="在-vscode-跑-jupyter-notebook">在 Vscode 跑 Jupyter Notebook</h2>
<p>如果前面順利執行，代表原生 Python 的全域環境已經可以在 Vscode 執行。Jupyter Notebook 只要再多裝一個 Jupyter (有藍勾勾) 的 Extension 就好。</p>
<p>要測試的話也是一樣， 在 Vscode 建立 <code>test.ipynb</code>測試 code， <code>Crtl</code>+ <code>Enter</code> 執行 Code cell 。 首次執行時，會跳出需要安裝 ipykernel 的提示，選安裝。<strong>安裝完成後，建議先關閉 Vscode 後再重新開啟，不然可能會出現跑了 Cell 卻沒反應的情況</strong>。</p>
</section>
<section id="在-vscode-安裝-指定-venv-虛擬環境" class="level2">
<h2 class="anchored" data-anchor-id="在-vscode-安裝-指定-venv-虛擬環境">在 Vscode 安裝 &amp; 指定 venv 虛擬環境</h2>
<p>其實還有其他類型的虛擬環境可以裝(例如 conda)，但我習慣用 venv，所以寫這個：）。最快速懶人的做法如下</p>
<section id="在專案資料夾建立-venv-虛擬環境" class="level3">
<h3 class="anchored" data-anchor-id="在專案資料夾建立-venv-虛擬環境">在專案資料夾建立 venv 虛擬環境</h3>
<ol type="1">
<li><p>建立專案資料夾，並用 Vscode 開啟它。（或是開　Vscode 後選 Open Floder）</p></li>
<li><p>在 Vscode 的搜尋視窗內，輸入</p></li>
</ol>
<pre><code>&gt; Python: Select Interpreter</code></pre>
<ol start="3" type="1">
<li><p>然後選 “Create Virtual Environment…” / “<strong>Venv</strong> Creates a .venv virtual environment in the current workspace”</p></li>
<li><p>等待許久後，專案資料夾會出現 <code>.venv</code> 的虛擬環境資料夾。</p></li>
</ol>
<p>在 Jupyter Notebook 指定虛擬環境非常簡單，如果<code>.venv</code>跟剛剛設好的 <code>test.ipynb</code>在同一個專案資料夾下，只要在 <code>test.ipynb</code> 點選 Select Kernel，就會出現剛剛建立的 <code>.venv</code>。</p>
</section>
</section>
</section>
<section id="設定-r-在-vscode" class="level1">
<h1>設定 R 在 Vscode</h1>
<section id="在-vsocde-中跑-r" class="level2">
<h2 class="anchored" data-anchor-id="在-vsocde-中跑-r">在 Vsocde 中跑 R</h2>
<section id="執行步驟-1" class="level3">
<h3 class="anchored" data-anchor-id="執行步驟-1">執行步驟</h3>
<ol type="1">
<li><p>下載 R &amp; Vscode</p></li>
<li><p>開啟 R 原生 IDE，輸入以下code 安裝 Package:</p></li>
</ol>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"languageserver"</span>)</span></code></pre></div>
<p><img src="https://paperfishblog.netlify.app/posts/tech/setting-py-r-in-vscode/r-lang.png" class="img-fluid"></p>
<ol start="3" type="1">
<li><p>開啟 Vscode，選左側 Extensions，搜尋官方 R (有藍勾勾)並安裝</p></li>
<li><p>在 Vscode search 視窗輸入</p></li>
</ol>
<pre><code>&gt; Preferences: Open Settings</code></pre>
<p>開啟 Preferences: Open Settings (UI) 視窗，在左側 Commend 面板找到 Extension 下 R 的部分，並找到 Rpaths 部份</p>
<p><a href="setting-r-in-vscode.md"></a></p>
<p>依據自己的作業系統輸入包含 R.exe 所在的 Path，以 Windows 為例，預設安裝 Path 為 <code>C:\Program Files\R\R-XXX(version)\bin\R.exe</code>，建議自己實際找一次以免出錯。</p>
</section>
<section id="測試" class="level3">
<h3 class="anchored" data-anchor-id="測試">測試</h3>
<p>在 Vscode 新增 <code>test.R</code> 輸入</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hello World!"</span>)</span></code></pre></div>
<p>按右上角的播放鍵，按第一下會喚醒 Terminal 裡的 R ，第二下後就會有結果囉！</p>
<p>這就是最基本的設定。</p>
</section>
</section>
<section id="在-vsocde-中跑-rmarkdown" class="level2">
<h2 class="anchored" data-anchor-id="在-vsocde-中跑-rmarkdown">在 Vsocde 中跑 Rmarkdown</h2>
<p>Rmarkdown 可以幫助我們在同一個中同時撰寫研究報告跟跑 R 程式的結果，非常的 useful！</p>
<section id="執行步驟-2" class="level3">
<h3 class="anchored" data-anchor-id="執行步驟-2">執行步驟</h3>
<ol type="1">
<li>安裝 pandoc: 因為 Rmarkdown 是基於 pandoc 設計的 package，所以需要先裝它。</li>
</ol>
<ul>
<li><p>前往 pandoc 官網下載最新版，依據作業系統下載對應執行檔</p></li>
<li><p>在 Terminal (Windows: cmd / Powershell 均可) 輸入</p></li>
</ul>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pandoc</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--version</span></span></code></pre></div>
<ul>
<li>有跑出對應版本代表有成功，沒有可能是需要將 Vscode 以及所有指令視窗關掉重開，或是環境變數問題。以 Windows 為例：
<ul>
<li>前者首先可以先嘗試關掉所有會用到指令操作的視窗，再重新開啟 cmd 或 powershell 重新測試，再去開啟 Vscode 的 Terminal 測試，都有跑出對應版本即可。</li>
<li>後者需要確定 <code>pandoc.exe</code> 的所在目錄，最好直接在檔案總管裡的C槽搜尋，因為在 Terminal 輸入的指令可能會找不到拒絕 User 存取的資料夾，再來去 Win 圖示&gt;右鍵&gt;設定&gt;搜尋環境變數&gt;點選編輯系統環境變數&gt;環境變數，在跳出視窗內點選”XXX(使用者名稱)的使用者變數”裡的 PATH，按”編輯”，會跳出目前已經設定的環境變數，檢查<code>pandoc.exe</code> 的所在目錄是在其中且路徑正確，如果沒有則修正，有的話把它上移看看，最後關掉所有視窗重新測試。</li>
</ul></li>
</ul>
<ol start="2" type="1">
<li>在 R 裡安裝 package rmarkdown</li>
</ol>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rmarkdown"</span>)</span></code></pre></div>
</section>
<section id="測試-1" class="level3">
<h3 class="anchored" data-anchor-id="測試-1">測試</h3>
<p>在 Vscode 新增 <code>test.rmd</code> 輸入 test code <a href="./test.txt" download="">點此下載 txt 版</a>。</p>
<!-- <a href="./test.rmd" download>點此下載 rmd 版(可能壞掉)</a> -->
<p>按右上角的播放鍵 ( Knit Rmd )，會跳出 output 顯示產製過程的 log，成功會像這樣：</p>
<pre><code>rmarkdown::render(...)


processing file: test.rmd

1/2                  

2/2 [unnamed-chunk-1]

output file: test.knit.md


... +RTS -K512m -RTS test.knit.md --to html4 --from markdown+autolink_bare_uris+tex_math_single_backslash --output test.html --lua-filter 
...
...
...
--embed-resources --standalone --variable bs3=TRUE --section-divs --template ...
...
...--include-in-header 

...


Output created: test.html

&lt;&lt;&lt;vsc&gt;&gt;&gt;...(outputfilename &amp; Path)&lt;&lt;&lt;vsc&gt;&gt;&gt;
[VSC-R] test.rmd process exited with exit code null</code></pre>
<p>並且可以在跟<code>test.rmd</code>同資料夾下找到<code>test.html</code></p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/setting-py-r-in-vscode/rmd-ex.png" class="img-fluid"></p>
<p>沒記錯的話，設定中文並且直接轉 PDF 需要另行設定，有點麻煩，改天有機會繼續寫。目前的設定可以產出 html 後用瀏覽器的列印功能(<code>Ctrl</code>+<code>P</code>)轉 PDF，非常簡單。</p>
</section>
</section>
</section>
<section id="reference" class="level1">
<h1>Reference</h1>
<section id="python" class="level2">
<h2 class="anchored" data-anchor-id="python">Python</h2>
<p>軟軟的官方文件基本上夠用了~~</p>
<ul>
<li><a href="https://code.visualstudio.com/docs/datascience/jupyter-notebooks">https://code.visualstudio.com/docs/datascience/jupyter-notebooks</a></li>
</ul>
</section>
<section id="r" class="level2">
<h2 class="anchored" data-anchor-id="r">R</h2>
<ul>
<li><p><a href="https://kemushi54.github.io/2019/09/07/%E7%AD%86%E8%A8%98-R-with-Visual-Studio-Code-VS-Code/">https://kemushi54.github.io/2019/09/07/%E7%AD%86%E8%A8%98-R-with-Visual-Studio-Code-VS-Code/</a></p></li>
<li><p><a href="https://www.datanovia.com/learn/tools/r-in-vscode/r-markdown-and-report-generation-in-vscode.html">https://www.datanovia.com/learn/tools/r-in-vscode/r-markdown-and-report-generation-in-vscode.html</a></p></li>
</ul>



</section>
</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>note</category>
  <category>Vscode</category>
  <category>python</category>
  <category>R</category>
  <category>rmarkdown</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/setting-py-r-in-vscode/</guid>
  <pubDate>Thu, 26 Feb 2026 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/setting-py-r-in-vscode/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>讓 AI 教我製作簡易 RAG 網頁服務(下)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/llm-course-note2/llm-course-note2.html</link>
  <description><![CDATA[ 






<!-- 使用 AI -->
<div data-ai-tag="2">

</div>
<script src="../../../../asserts/box.js" defer=""></script>
<p>拖到開學才寫完：P。</p>
<hr>
<section id="讓使用者上傳-pdf-並即時學習" class="level1">
<h1>讓使用者上傳 PDF 並即時學習</h1>
<p>要實作的功能是：使用者在網頁上丟一個 PDF 檔案，系統自動讀取、切割文字、轉成向量，然後 AI 下一秒就能回答裡面的內容。</p>
<section id="裝套件" class="level2">
<h2 class="anchored" data-anchor-id="裝套件">裝套件</h2>
<p>要裝兩個東西</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install pypdf python-multipart</span></code></pre></div>
<ul>
<li><p><code>pypdf</code>: 用來讀取 PDF 檔案的文字。</p></li>
<li><p><code>python-multipart</code>: 讓 FastAPI 可以接收檔案上傳。</p></li>
</ul>
</section>
<section id="改-rag-服務的函數" class="level2">
<h2 class="anchored" data-anchor-id="改-rag-服務的函數">改 RAG 服務的函數</h2>
<p>再來要調整 <code>rag_service.py</code>，讓它可以吃 PDF。</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sentence_transformers <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> SentenceTransformer</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sklearn.metrics.pairwise <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> cosine_similarity</span>
<span id="cb2-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pypdf <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> PdfReader <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW]</span></span>
<span id="cb2-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb2-5"></span>
<span id="cb2-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"正在載入 Embedding 模型..."</span>)</span>
<span id="cb2-7">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> SentenceTransformer(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'paraphrase-multilingual-MiniLM-L12-v2'</span>)</span>
<span id="cb2-8"></span>
<span id="cb2-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 這次我們不預設資料，改用一個空的列表，等使用者上傳</span></span>
<span id="cb2-10">knowledge_base <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb2-11">knowledge_embeddings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb2-12"></span>
<span id="cb2-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> add_pdf_to_knowledge_base(file_stream):</span>
<span id="cb2-14">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb2-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    讀取 PDF -&gt; 抓文字 -&gt; 切割 -&gt; 轉向量 -&gt; 存入知識庫</span></span>
<span id="cb2-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb2-17">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">global</span> knowledge_base, knowledge_embeddings</span>
<span id="cb2-18">    </span>
<span id="cb2-19">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. 讀取 PDF 文字</span></span>
<span id="cb2-20">    reader <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> PdfReader(file_stream)</span>
<span id="cb2-21">    text <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span></span>
<span id="cb2-22">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> page <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> reader.pages:</span>
<span id="cb2-23">        text <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> page.extract_text() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb2-24">    </span>
<span id="cb2-25">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. 文字切割 (Chunking)</span></span>
<span id="cb2-26">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># LLM 一次讀不完這麽多字，我們要切成小塊 (例如每 200 字一塊)</span></span>
<span id="cb2-27">    chunk_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span></span>
<span id="cb2-28">    chunks <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [text[i:i<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>chunk_size] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(text), chunk_size)]</span>
<span id="cb2-29">    </span>
<span id="cb2-30">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"PDF 處理完畢，切成 </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(chunks)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> 個片段"</span>)</span>
<span id="cb2-31"></span>
<span id="cb2-32">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. 更新知識庫</span></span>
<span id="cb2-33">    new_items <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"id"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(knowledge_base) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: chunk} <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, chunk <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(chunks)]</span>
<span id="cb2-34">    knowledge_base.extend(new_items)</span>
<span id="cb2-35">    </span>
<span id="cb2-36">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. 重新計算向量索引 (簡單暴力法：全部重算)</span></span>
<span id="cb2-37">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 在真實系統中，我們會用 Vector DB (如 ChromaDB) 來處理，不用每次重算</span></span>
<span id="cb2-38">    texts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [item[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> item <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> knowledge_base]</span>
<span id="cb2-39">    knowledge_embeddings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.encode(texts)</span>
<span id="cb2-40">    </span>
<span id="cb2-41">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(chunks)</span>
<span id="cb2-42"></span>
<span id="cb2-43"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> search_knowledge_base(query, top_k<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>):</span>
<span id="cb2-44">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb2-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    搜尋功能 (跟之前一樣，但加了防呆機制)</span></span>
<span id="cb2-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb2-47">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">global</span> knowledge_embeddings</span>
<span id="cb2-48">    </span>
<span id="cb2-49">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 如果知識庫是空的，直接回傳空</span></span>
<span id="cb2-50">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> knowledge_base <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> knowledge_embeddings <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>:</span>
<span id="cb2-51">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> []</span>
<span id="cb2-52"></span>
<span id="cb2-53">    query_embedding <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.encode([query])</span>
<span id="cb2-54">    similarities <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> cosine_similarity(query_embedding, knowledge_embeddings)</span>
<span id="cb2-55">    top_indices <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> similarities[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].argsort()[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>top_k:][::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb2-56">    </span>
<span id="cb2-57">    results <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb2-58">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> top_indices:</span>
<span id="cb2-59">        score <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> similarities[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][idx]</span>
<span id="cb2-60">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> score <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.3</span>: </span>
<span id="cb2-61">            results.append(knowledge_base[idx][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>])</span>
<span id="cb2-62">            </span>
<span id="cb2-63">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> results</span></code></pre></div>
</section>
<section id="改後端" class="level2">
<h2 class="anchored" data-anchor-id="改後端">改後端</h2>
<p>使用者上傳的檔案要用 API 才能傳到後端，因此要修改 <code>server.py</code></p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ... (前面的 import 不變)</span></span>
<span id="cb3-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> fastapi <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> UploadFile, File <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 引入檔案處理模組</span></span>
<span id="cb3-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> rag_service <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> search_knowledge_base, add_pdf_to_knowledge_base <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 引入新函式</span></span>
<span id="cb3-4"></span>
<span id="cb3-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ... (app = FastAPI... 中間設定不變) ...</span></span>
<span id="cb3-6"></span>
<span id="cb3-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 上傳檔案的 API</span></span>
<span id="cb3-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@app.post</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/upload"</span>)</span>
<span id="cb3-9"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> upload_file(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>: UploadFile <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> File(...)):</span>
<span id="cb3-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb3-11">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 把檔案內容傳給 rag_service 處理</span></span>
<span id="cb3-12">        chunk_count <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> add_pdf_to_knowledge_base(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>)</span>
<span id="cb3-13">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"message"</span>: <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"成功讀取 </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>filename<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">，新增了 </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>chunk_count<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> 個知識片段！"</span>}</span>
<span id="cb3-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb3-15">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"error"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(e)}</span>
<span id="cb3-16"></span>
<span id="cb3-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ... (chat_with_ai 函式保持不變) ...</span></span>
<span id="cb3-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 注意：記得確認 chat_with_ai 裡面有呼叫 search_knowledge_base</span></span></code></pre></div>
</section>
<section id="改前端" class="level2">
<h2 class="anchored" data-anchor-id="改前端">改前端</h2>
<p>最後修改前端，先在 HTML 區建一個讓使用者可以按下「📚 知識庫上傳」的按鈕，上傳 PDF</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode html code-with-copy"><code class="sourceCode html"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;!-- 在標題下方 --&gt;</span></span>
<span id="cb4-2"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"margin-bottom: 10px; padding: 10px; background: #fff3cd; border: 1px solid #ffeeba; border-radius: 5px;"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-3">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">h3</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span>📚 知識庫上傳<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">h3</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-4">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">input</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> type</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pdf-upload"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> accept</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".pdf"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-5">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">button</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> onclick</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"uploadPDF()"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background-color: #ffc107; color: black;"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span>上傳 PDF<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">button</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-6">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">span</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"upload-status"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"margin-left: 10px; font-size: 0.9em; color: #666;"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">span</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-7"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-8"></span>
<span id="cb4-9"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-10">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div>
<p>再到 js 區新增接收檔案傳進後端的函數</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode javascript code-with-copy"><code class="sourceCode javascript"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uploadPDF</span>() {</span>
<span id="cb5-2">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> fileInput <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pdf-upload"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-3">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> statusText <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"upload-status"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-4">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fileInput<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">files</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-5"></span>
<span id="cb5-6">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>file) {</span>
<span id="cb5-7">                <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">alert</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"請先選擇 PDF 檔案！"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-8">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-9">            }</span>
<span id="cb5-10"></span>
<span id="cb5-11">            statusText<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"正在處理中... (請稍候)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-12"></span>
<span id="cb5-13">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> formData <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">new</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">FormData</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-14">            formData<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">append</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> file)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-15"></span>
<span id="cb5-16">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span> {</span>
<span id="cb5-17">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fetch</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"http://127.0.0.1:8000/upload"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> {</span>
<span id="cb5-18">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">method</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"POST"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-19">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> formData</span>
<span id="cb5-20">                })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-21">                </span>
<span id="cb5-22">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> response<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">json</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-23">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">message</span>) {</span>
<span id="cb5-24">                    statusText<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"✅ "</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">message</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-25">                    statusText<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"green"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-26">                } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> {</span>
<span id="cb5-27">                    statusText<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"❌ 上傳失敗："</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-28">                    statusText<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-29">                }</span>
<span id="cb5-30">            } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">catch</span> (error) {</span>
<span id="cb5-31">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">console</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">error</span>(error)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-32">                statusText<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"❌ 連線錯誤"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-33">            }</span>
<span id="cb5-34">        }</span></code></pre></div>
</section>
<section id="測試" class="level2">
<h2 class="anchored" data-anchor-id="測試">測試</h2>
<ol type="1">
<li><p>準備好一個有專屬資訊的PDF (例如課綱)</p></li>
<li><p>啟動後端 <code>uvicorn server:app --reload</code>，確認正常運作</p></li>
<li><p>開啟網站</p></li>
<li><p>先輸入一個只有 PDF 有寫的問題，預期輸出為不知道</p></li>
<li><p>上傳檔案，再問一次，預期輸出要能回答相關問題</p></li>
</ol>
</section>
</section>
<section id="將系統打包成-docker-容器" class="level1">
<h1>將系統打包成 Docker 容器</h1>
<p>讓系統無論在哪都可以跑。</p>
<section id="安裝-docker" class="level2">
<h2 class="anchored" data-anchor-id="安裝-docker">安裝 Docker</h2>
<p>首次使用需要此步，Windows 安裝前提條件：</p>
<ul>
<li>Windows 10 64-bit：版本 1903 或更新版</li>
<li>啟用 <strong>WSL 2（Windows Subsystem for Linux）</strong></li>
</ul>
<section id="安裝步驟" class="level3">
<h3 class="anchored" data-anchor-id="安裝步驟">安裝步驟：</h3>
<ol type="1">
<li><p><strong>安裝 WSL 2（如尚未安裝）</strong></p>
<ul>
<li>開啟 PowerShell（以系統管理員身份）</li>
</ul>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode powershell code-with-copy"><code class="sourceCode powershell"><span id="cb6-1">wsl <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>install</span></code></pre></div>
<ul>
<li>安裝完成後重新啟動電腦。</li>
</ul></li>
<li><p><strong>下載 Docker Desktop for Windows</strong></p>
<ul>
<li>官方下載連結：<a href="https://www.docker.com/products/docker-desktop">https://www.docker.com/products/docker-desktop</a></li>
</ul></li>
<li><p><strong>安裝並啟動 Docker Desktop</strong></p>
<ul>
<li>執行安裝檔</li>
<li>勾選「使用 WSL 2」作為後端</li>
<li>安裝完成後啟動 Docker Desktop 並登入 Docker Hub（可註冊免費帳號）</li>
</ul></li>
<li><p><strong>確認 Docker 安裝是否成功</strong></p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--version</span></span>
<span id="cb7-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker</span> run hello-world</span></code></pre></div></li>
</ol>
</section>
</section>
<section id="產出-requirements.txt" class="level2">
<h2 class="anchored" data-anchor-id="產出-requirements.txt">產出 <code>requirements.txt</code></h2>
<p>用來告訴 Docker 內的乾淨環境：需要安裝哪些東西。</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb8-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> freeze <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> requirements.txt</span></code></pre></div>
<p>會看到資料夾多了一個 <code>requirements.txt</code> 檔案，打開來檢查，裡面應該有 fastapi, uvicorn, openai, sentence-transformers 等等。</p>
</section>
<section id="寫-dockerfile" class="level2">
<h2 class="anchored" data-anchor-id="寫-dockerfile">寫 Dockerfile</h2>
<p>寫一個腳本，告訴 Docker 怎麼打包。</p>
<ol type="1">
<li><p>在專案根目錄建立一個新檔案，檔名就叫 <code>Dockerfile</code> (沒有副檔名)。</p></li>
<li><p>貼上以下內容：</p></li>
</ol>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode dockerfile code-with-copy"><code class="sourceCode dockerfile"><span id="cb9-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. 選擇基底映像檔 (就像選擇要灌什麼作業系統)</span></span>
<span id="cb9-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 我們選一個輕量級的 Python 3.12 環境</span></span>
<span id="cb9-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">FROM</span> python:3.12-slim</span>
<span id="cb9-4"></span>
<span id="cb9-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. 設定工作目錄 (在箱子裡建立一個 /app 資料夾)</span></span>
<span id="cb9-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">WORKDIR</span> /app</span>
<span id="cb9-7"></span>
<span id="cb9-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. 把你電腦裡的 requirements.txt 複製進箱子</span></span>
<span id="cb9-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">COPY</span> requirements.txt .</span>
<span id="cb9-10"></span>
<span id="cb9-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. 在箱子裡安裝套件</span></span>
<span id="cb9-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --no-cache-dir 可以讓箱子小一點</span></span>
<span id="cb9-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">RUN</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--no-cache-dir</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-r</span> requirements.txt</span>
<span id="cb9-14"></span>
<span id="cb9-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 5. 把你電腦裡的所有程式碼 (main.py, server.py...) 複製進箱子</span></span>
<span id="cb9-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">COPY</span> . .</span>
<span id="cb9-17"></span>
<span id="cb9-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 6. 告訴 Docker，這個程式會開 8000 port</span></span>
<span id="cb9-19"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">EXPOSE</span> 8000</span>
<span id="cb9-20"></span>
<span id="cb9-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 7. 當箱子啟動時，要執行的指令</span></span>
<span id="cb9-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 注意：這裡的 --host 0.0.0.0 是關鍵，一定要設成這樣外部才連得進去</span></span>
<span id="cb9-23"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">CMD</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"uvicorn"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"server:app"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--host"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.0.0.0"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--port"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"8000"</span>]</span></code></pre></div>
<p>用指令開始 build</p>
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb10-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker</span> build <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-t</span> my-ai-system .</span></code></pre></div>
<p><code>my-ai-system</code> 就是會在 Docker 桌面版的 images 頁面裡看到的名稱。</p>
<section id="build-常見問題" class="level3 call-out.tips">
<h3 class="anchored" data-anchor-id="build-常見問題">Build 常見問題</h3>
<section id="安裝不了套件" class="level4">
<h4 class="anchored" data-anchor-id="安裝不了套件">安裝不了套件</h4>
<p>有時候前面都很正常但裝到某些套件時突然說裝不了，因為套件的版本沒有這麼新的。這時只要先確認目前使用的虛擬環境的 python 版本，將對應 version 的內容修改成與虛擬環境一致，通常就沒問題了。</p>
<p>如果還有問題可以上<a href="https://hub.docker.com/_/python">DockerHub 上 Ptyhon 官方 image</a>找出對應的版本，除非安裝的版本太新，不然應該都會有。</p>
</section>
</section>
</section>
</section>
<section id="目前的問題" class="level1">
<h1>目前的問題</h1>
<section id="docker-包起來的容量很大" class="level2">
<h2 class="anchored" data-anchor-id="docker-包起來的容量很大">Docker 包起來的容量很大</h2>
<p>最後一次包起來在 Docker images page 上居然顯示 13 GB ，好扯==。因為沒有任何向量資料庫，大成這樣其實很不尋常。</p>
</section>
<section id="要如何測試-rag-效能" class="level2">
<h2 class="anchored" data-anchor-id="要如何測試-rag-效能">要如何測試 RAG 效能</h2>
<p>目前這個的玩具的設定對稍微長一點的文本檢索機制不佳，回應也有幻覺問題。本來想說既然 AI 幫我生出這個小玩具，那我再加入 LangChain 或 Huggingface 的生態系、改一改模型、加入不同的檢索策略跟 Reranker 機制，以及針對特定需求作客製化後，就會是個實用小玩具了，可是要怎麼好好評估這些機制實在很困擾我。雖然之前有寫過製作 RAG 的作業，也跑過 kaggle 上的競賽，但這些都有提供一定數量的測試集，裡面都會附上問題與解答，但現在這個小玩具的情境裡：</p>
<ol type="1">
<li><p>我需要針對我落地的情境設計問題與確定解答，但我沒有很多時間製作測試集，可能只能針對提問的種類設計幾個問題與解答，再就這些小樣本推論回應品質。</p></li>
<li><p>文本要自行上傳，且資料格式是沒有標準儲存格式的 PDF，<sup>1</sup>如果讓給其他使用者使用、上傳其他來源的資料，回應的品質會變得難以掌握。除非我能列出所有可能面臨的使用情境，並且設計出好的解決策略，讓使用體驗良好，但這對初次設計的我來說太不切實際了。</p></li>
</ol>
<p>我覺得第二個問題是最棘手的，所以我後來決定重新做一個不能上傳資料的封閉式 RAG 系統，先解決資料格式與來源不一的問題。這個小玩具就先放著，以後再想想看要怎麼調整。</p>
</section>
</section>
<section id="後續" class="level1">
<h1>後續</h1>
<p>後來我就爬了一些舊聞，做了網路考古學家的小玩具，<del>目標是滿足當歷史學家的慾望</del>。不過還是有遇到一些問題，<del>加上後來過年了就偷懶</del>，但還是希望能趕快解決，順利上線後再分享。</p>
<p>然後雖然最後沒辦法上生成式AI應用系統與工程，不過昨天透過學校的 Email 提供的連結旁聽了一下，想不到居然是著重跟 AI Agent 協作的課，跟我(還有 Geimei)之前想的完全不一樣 XD，滿有趣的。雖然我猜這要應用在職場上還需要一段時間(取決於公司)，不過有機會還是看看有沒有甚麼小撇步可以偷學XD</p>



</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>舉例來說，有的 PDF 是圖片轉成的，沒有辦法擷取出文字；也有的 PDF 會有反爬蟲機制，試圖用程式開啟並擷取PDF會爬到一堆亂碼。這些都會影響模型生成回答的正確性。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>toys</category>
  <category>LLM</category>
  <category>RAG</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/llm-course-note2/llm-course-note2.html</guid>
  <pubDate>Wed, 25 Feb 2026 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/llm-course-note2/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>酷酷的時間序列(1)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/</link>
  <description><![CDATA[ 






<!-- 使用 AI -->
<div data-ai-tag="1,2">

</div>
<script src="../../../../asserts/box.js" defer=""></script>
<p>頻譜分析 (Spectral Analysis，以下想簡稱的時候會叫 SA) 是過去沒學過的東西，此外也意外發現 Singular Spectrum Analysis(簡稱 SSA )，紀錄一下。</p>
<hr>
<div class="callout callout-style-default callout-caution callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
注意
</div>
</div>
<div class="callout-body-container callout-body">
<p>文很長，尤其是 SSA 的部分，超長。</p>
</div>
</div>
<section id="sa-概念" class="level1">
<h1>SA 概念</h1>
<p>Spectral Analysis 的目標是藉由將具有週期性(seasonal)的時間序列資料從 time domain 轉換為 frequency domain 的形式，擷取出訊號的特徵，再就這些特徵做出更好的預測或解釋。</p>
<p>從一個簡單的例子開始：</p>
<div id="cell-1" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-outputid="2a286076-0a26-484e-e839-28ded97056d5" data-execution_count="2">
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1">y1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>),<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">80</span>)</span>
<span id="cb1-2">y2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>)</span>
<span id="cb1-3">y3 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>),<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>)</span>
<span id="cb1-4">y <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> y1<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>y2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>y3</span>
<span id="cb1-5">ts_len <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span></span>
<span id="cb1-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>, y1[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"h"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Value"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y1, y2, y3 time series(head 20)"</span>)</span>
<span id="cb1-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(y2[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>)</span>
<span id="cb1-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(y3[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"green"</span>)</span>
<span id="cb1-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(ts_len, y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"h"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Value"</span>,  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y time series"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-2-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-2-output-2.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p><code>y1</code>、<code>y2</code>、<code>y3</code>各表示一個振動幅度的訊號，結合出來的<code>y</code>是這三種不同週期波的疊加，3個週期及幅度如下表：</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th style="text-align: center;">變數</th>
<th style="text-align: center;">週期</th>
<th style="text-align: center;">幅度</th>
<th style="text-align: center;">重複次數</th>
<th style="text-align: center;">元素數</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">y1</td>
<td style="text-align: center;">3</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">80</td>
<td style="text-align: center;">240</td>
</tr>
<tr class="even">
<td style="text-align: center;">y2</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">2</td>
<td style="text-align: center;">60</td>
<td style="text-align: center;">240</td>
</tr>
<tr class="odd">
<td style="text-align: center;">y3</td>
<td style="text-align: center;">6</td>
<td style="text-align: center;">5</td>
<td style="text-align: center;">40</td>
<td style="text-align: center;">240</td>
</tr>
</tbody>
</table>
<p>實務上會看到的資料可能就是<code>y</code>的長相，那麼，我們該如何辨識出潛藏在<code>y</code>後面的訊號呢? 可以使用 Fast Fourier Transform ，並且畫出功率譜（power spectrum）圖：</p>
<div id="cell-3" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-outputid="f418d580-da07-42ea-e2f8-f49027de2a00" data-execution_count="3">
<div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">I <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fft</span>(y))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">^</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span></span>
<span id="cb2-2">P <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span>)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>I[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">120</span>]</span>
<span id="cb2-3">f <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">119</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span></span>
<span id="cb2-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(f[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],P[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Frequency"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Power"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-3-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<div class="callout callout-style-default callout-note no-icon callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon no-icon"></i>
</div>
<div class="callout-title-container flex-fill">
code 分析
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>這段程式碼是在對前面建立的時間序列 <code>y</code> 進行 <strong>頻譜分析（spectral analysis）</strong>，也就是把時間序列轉換成<strong>頻率域（frequency domain）</strong>，並畫出它的<strong>功率譜（power spectrum）</strong>。</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">I <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fft</span>(y))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">^</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span></span></code></pre></div>
<p>這裡做的事情是：</p>
<ol type="1">
<li><p><code>fft(y)</code>： 對時間序列 <code>y</code> 做 <strong>快速傅立葉轉換（Fast Fourier Transform, FFT）</strong>。</p>
<ul>
<li>這會把時間序列從「時間域」轉換成「頻率域」的表示。</li>
<li>輸出是一個複數向量，每個元素代表一個頻率成分（振幅和相位）。</li>
</ul></li>
<li><p><code>abs(fft(y))^2</code>：</p>
<ul>
<li>取絕對值的平方（即<strong>振幅平方</strong>），表示各頻率成分的<strong>能量或功率（power）</strong>。</li>
</ul></li>
<li><p><code>/240</code>：</p>
<ul>
<li>除以樣本長度 ( n = 240 )，是為了做標準化（normalization）。</li>
</ul></li>
</ol>
<p>所以 <code>I</code> 是一個<strong>periodogram（週期圖）</strong>的原始估計結果。</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1">P <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> I[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">120</span>]</span></code></pre></div>
<p>是在取出頻譜中「有意義的一半」：</p>
<ul>
<li>傅立葉轉換結果的頻譜是<strong>對稱的</strong>（因為原始資料是實數），所以只需要前一半的頻率資訊。</li>
<li><code>I[1:120]</code>：取前 120 個頻率成分（因為一共有 240 個）。</li>
<li><code>(4/240)</code>：再做縮放，讓功率的大小更符合理論上對稱頻譜的能量分配。</li>
</ul>
<p><code>P</code> 就是功率譜（Power Spectrum）的估計值，也可視為 <code>periodogram</code>。</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">f <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">119</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span></span></code></pre></div>
<p>建立頻率軸：</p>
<ul>
<li>產生從 <code>0</code> 到 <code>119</code> 的整數序列。</li>
<li>除以 <code>240</code> 之後，代表對應的<strong>頻率（cycles per time unit）</strong>。</li>
</ul>
<p><code>f</code> 對應每個頻率成分的位置。</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(f[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], P[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Frequency"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Power"</span>)</span></code></pre></div>
<ul>
<li><code>f[-1]</code> 與 <code>P[-1]</code>：去掉第 1 個元素（對應頻率 0 的直流成分，通常不感興趣）。</li>
<li><code>type="l"</code>：畫線圖。</li>
<li>標上座標軸標籤。</li>
</ul>
<section id="這會畫出一張功率-vs-頻率的圖也就是功率譜power-spectrum圖" class="level2">
<h2 class="anchored" data-anchor-id="這會畫出一張功率-vs-頻率的圖也就是功率譜power-spectrum圖">這會畫出一張「功率 vs 頻率」的圖，也就是<strong>功率譜（power spectrum）圖</strong>。</h2>
<p>這張圖的<strong>橫軸</strong>是頻率，<strong>縱軸</strong>是對應的能量（或功率）。可以看到幾個明顯的尖峰（peaks），它們對應到原始資料 <code>y</code> 裡的週期成分：</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th style="text-align: center;">週期</th>
<th style="text-align: center;">對應頻率 ( f = 1/ )</th>
<th style="text-align: center;">來源</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">3</td>
<td style="text-align: center;">≈ 0.333</td>
<td style="text-align: center;">來自 <code>y1</code></td>
</tr>
<tr class="even">
<td style="text-align: center;">4</td>
<td style="text-align: center;">0.25</td>
<td style="text-align: center;">來自 <code>y2</code></td>
</tr>
<tr class="odd">
<td style="text-align: center;">6</td>
<td style="text-align: center;">≈ 0.167</td>
<td style="text-align: center;">來自 <code>y3</code></td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
</div>
<p>尖峰越高表示該頻率的能量(power)越強，週期性越明顯。以這個例子來說是週期為 3 的部分較明顯。</p>
<section id="平滑處理" class="level2">
<h2 class="anchored" data-anchor-id="平滑處理">平滑處理</h2>
<p>以上的動作在 <code>R</code> 可以只用一列 code 完成，不過這是經過平滑的版本，採用更高階的 Power Spectrum Analysis 方法處理:</p>
<div id="cell-5" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-outputid="c7de02db-0ff2-42b1-b6d3-5025512ad2d7" data-execution_count="4">
<div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">spectrum</span>(y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">spans =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">log =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"no"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-4-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<div class="callout callout-style-default callout-note no-icon callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-3-contents" aria-controls="callout-3" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon no-icon"></i>
</div>
<div class="callout-title-container flex-fill">
code 解釋
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-3" class="callout-3-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<ol type="1">
<li><code>spectrum(y, ...)</code></li>
</ol>
<ul>
<li><code>spectrum()</code> 是 R 內建函數，用來估計時間序列的 <strong>頻譜（spectral density）</strong>。</li>
<li>輸入是一個向量或 ts 物件。</li>
<li>預設會使用 <strong>periodogram</strong> 作為基礎，但內建支援 <strong>平滑化（smoothing）</strong>，讓結果更穩定，不會像手算 FFT 的 periodogram 那麼 noisy。</li>
</ul>
<hr>
<ol start="2" type="1">
<li><code>spans = 5</code></li>
</ol>
<ul>
<li><code>spans</code> 用來指定 <strong>平滑窗口大小</strong>，也就是對 periodogram 做「移動平均」的程度。</li>
<li>值越大 → 平滑越強 → 尖峰變低，但波動噪聲也減少。</li>
<li>如果是單一數字 <code>k</code> → 實際做 <strong>k 個點平滑</strong>。</li>
<li>如果是向量 → 代表使用加權移動平均（Tukey window）。</li>
</ul>
<p>例子：</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1">spans <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span></code></pre></div>
<p>→ 代表用 2 點移動平均，平滑頻譜，數字越大越平滑，可降低隨機波動。</p>
<hr>
<section id="log-no" class="level2">
<h2 class="anchored" data-anchor-id="log-no">3. <code>log = "no"</code></h2>
<ul>
<li><p>控制功率譜的 <strong>縱軸刻度</strong>：</p>
<ul>
<li><code>"yes"</code> → 將功率取對數 (log scale)，常用於功率跨越很大範圍的情況</li>
<li><code>"no"</code> → 不取對數，直接畫原始功率（線性尺度）</li>
</ul></li>
</ul>
<p>此處程式指定 <code>"no"</code>，所以縱軸是原始功率。</p>
<hr>
<ol start="4" type="1">
<li>預設輸出</li>
</ol>
<p><code>spectrum()</code> 會返回一個列表（list）：</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>元素</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>spec</code></td>
<td>平滑後的功率譜數值</td>
</tr>
<tr class="even">
<td><code>freq</code></td>
<td>對應的頻率（cycles per unit time）</td>
</tr>
<tr class="odd">
<td><code>coh</code></td>
<td>交叉頻譜（如果有兩個序列）</td>
</tr>
<tr class="even">
<td><code>order</code></td>
<td>用於 AR 模型估計的順序</td>
</tr>
<tr class="odd">
<td><code>window</code></td>
<td>使用的平滑窗口資訊</td>
</tr>
</tbody>
</table>
<p>同時會 <strong>畫出圖形</strong>，X 軸是頻率、Y 軸是功率。</p>
<hr>
<ol start="5" type="1">
<li>功用和優點</li>
</ol>
<p>比手動 FFT 更好的地方：</p>
<ol type="1">
<li><strong>平滑 periodogram</strong> → 對 noisy 資料更穩定</li>
<li>自動處理對稱性，只畫前一半頻率</li>
<li>可以方便地與 log 軸、AR 模型估計結合</li>
<li>不需要手動算 FFT、取平方、縮放</li>
</ol>
<hr>
<section id="補充示意" class="level3">
<h3 class="anchored" data-anchor-id="補充示意">補充示意</h3>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1">s <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">spectrum</span>(y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">spans=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">log=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"no"</span>)</span>
<span id="cb9-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># s$freq -&gt; 頻率</span></span>
<span id="cb9-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># s$spec -&gt; 功率</span></span></code></pre></div>
<p>可以用這兩個來做自訂圖：</p>
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb10-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>freq, s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>spec, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Frequency"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Power"</span>)</span></code></pre></div>
<p>這樣就能自訂顯示，而不只是內建圖。</p>
</section>
</section>
</div>
</div>
</div>
</section>
</section>
<section id="section" class="level1">
<h1></h1>
</section>
<section id="spectral-analysis-應用在實務上的小小缺點" class="level1">
<h1>Spectral Analysis 應用在實務上的小小缺點</h1>
<p>與經典的統計模型 ARIMA 家族應用時可能會出現的問題類似，Spectral Analysis 在由固定的頻率訊號組成的序列可以做出良好的預測，不過一旦實際序列中出現新的頻率的訊號，預測效果就會變差，因為模型根本沒有在訓練階段看過它。因此 Spectral Analysis 只能在具有穩定的週期且不會被突發事件影響的時間序列上才會有良好的表現。</p>
<p>(雖然我個人覺得這是預測長時間的時間序列都會面臨的問題</p>
<p><a href="https://otexts.com/fpp3/long-short-ts.html">Forecasting: Principles and Practice</a>也提出類似的解法：將最早的資料丟掉，只用近期的資料，避免模型僵化。雖然這主要針對 ARIMA 家族，但也適用於 Spectral Analysis。</p>
</section>
<section id="sa-的-nonparamatric-version-singular-spectrum-analysis" class="level1">
<h1>SA 的 nonparamatric version : Singular spectrum analysis</h1>
<p>Singular spectrum analysis (SSA) 是一種無母數方法(non-parametric technique)，這種方法的特色是不需要滿足統計假設即可使用，而且 SSA 除了擁有 decompensation 的能力外，搭配 linear recurrent formula 可以拿來做時間序列預測。</p>
<section id="ssa-的概念" class="level2">
<h2 class="anchored" data-anchor-id="ssa-的概念">SSA 的概念</h2>
<p>從 Singular spectrum analysis 的名詞顧名思義，它會把時間序列做 SVD 分解，不過時間序列都是一維的資料，要如何達成呢？其實 SSA 會經過以下步驟：</p>
<ol type="1">
<li><p>Embedding</p></li>
<li><p>Singular Value Decomposition</p></li>
<li><p>Grouping</p></li>
<li><p>Diagonal Averaging</p></li>
</ol>
<p>讓我們來一步步理解這些是甚麼。</p>
<section id="embedding" class="level3">
<h3 class="anchored" data-anchor-id="embedding">Embedding</h3>
<p>講到 Embedding ，會先想到的是修 NLP 時看到的 word embedding，但兩者的概念完全無關！它的概念為：把一維時間序列，轉成一個高維的矩陣，使後續可以執行 SVD 分解。具體來說：</p>
<p>給定一個一維且長度為 <img src="https://latex.codecogs.com/png.latex?n">時間序列<img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7By%7D%20=%20(y_1,%20y_2,%20%5Cdots,%20y_n)'">，首先要先選擇 window length <img src="https://latex.codecogs.com/png.latex?L">，代表「一次要看多長的一段歷史」，所以<img src="https://latex.codecogs.com/png.latex?L">的數學條件是：</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?L"> 是一個研究者要設定的整數</li>
<li><img src="https://latex.codecogs.com/png.latex?1%20%3C%20L%20%3C%20n"></li>
</ul>
<p>選擇 <img src="https://latex.codecogs.com/png.latex?L"> 的大小效果為：</p>
<ul>
<li>小 <img src="https://latex.codecogs.com/png.latex?L">：只看短期結構</li>
<li>大 <img src="https://latex.codecogs.com/png.latex?L">：可以抓到長期趨勢或週期</li>
</ul>
<p>再來，要建立延遲向量（lagged vectors），意思是從原始序列中，切出 <img src="https://latex.codecogs.com/png.latex?K"> 個長度為 <img src="https://latex.codecogs.com/png.latex?L"> 的小序列，其中 <img src="https://latex.codecogs.com/png.latex?K%20=%20n%20-%20L%20+%201"></p>
<p>然後對每個 <img src="https://latex.codecogs.com/png.latex?i%20=%201,%20%5Cdots,%20K">，定義：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BY%7D_i%20=%0A%5Cbegin%7Bpmatrix%7D%0Ay_i%20%5C%5C%0Ay_%7Bi+1%7D%20%5C%5C%0A%5Cvdots%20%5C%5C%0Ay_%7Bi+L-1%7D%0A%5Cend%7Bpmatrix%7D"></p>
<p>這其實是一個 Moving Window 的概念：</p>
<ul>
<li>第 1 個視窗：<img src="https://latex.codecogs.com/png.latex?(y_1,%20y_2,%20%5Cdots,%20y_L)"></li>
<li>第 2 個視窗：<img src="https://latex.codecogs.com/png.latex?(y_2,%20y_3,%20%5Cdots,%20y_%7BL+1%7D)"></li>
<li>…</li>
<li>一直到最後一個</li>
</ul>
<p>最後排成一個 trajectory matrix：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7BY%7D_%7BL%20%5Ctimes%20K%7D%20=%0A%5Cbegin%7Bpmatrix%7D%0A%7C%20&amp;%20%7C%20&amp;%20&amp;%20%7C%20%5C%5C%0A%5Cmathbf%7BY%7D_1%20&amp;%20%5Cmathbf%7BY%7D_2%20&amp;%20%5Ccdots%20&amp;%20%5Cmathbf%7BY%7D_K%20%5C%5C%0A%7C%20&amp;%20%7C%20&amp;%20&amp;%20%7C%0A%5Cend%7Bpmatrix%7D%0A"></p>
<p>寫成元素形式：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7BY%7D_%7BL%20%5Ctimes%20K%7D%20=%0A%5Cbegin%7Bpmatrix%7D%0Ay_1%20&amp;%20y_2%20&amp;%20%5Ccdots%20&amp;%20y_K%20%5C%5C%0Ay_2%20&amp;%20y_3%20&amp;%20%5Ccdots%20&amp;%20y_%7BK+1%7D%20%5C%5C%0A%5Cvdots%20&amp;%20%5Cvdots%20&amp;%20%5Cddots%20&amp;%20%5Cvdots%20%5C%5C%0Ay_L%20&amp;%20y_%7BL+1%7D%20&amp;%20%5Ccdots%20&amp;%20y_n%0A%5Cend%7Bpmatrix%7D%0A"></p>
<p>這個矩陣是一個Hankel matrix，有一個很重要的特性是它<strong>每條反對角線上的元素都一樣</strong>。</p>
</section>
<section id="singular-value-decomposition" class="level3">
<h3 class="anchored" data-anchor-id="singular-value-decomposition">Singular Value Decomposition</h3>
<p>這個步驟比較簡單，就是對 trajectory matrix 做 SVD 分解：</p>
<p><img src="https://latex.codecogs.com/png.latex?Y=UDV'"></p>
<p>其中 <img src="https://latex.codecogs.com/png.latex?U"> 和 <img src="https://latex.codecogs.com/png.latex?V"> 有 orthonormal columns ，也就是說它的數學性質是：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BU%7D_i'%20%5Cmathbf%7BU%7D_j%20=%200,%5Cquad%5Cmathbf%7BV%7D_i'%20%5Cmathbf%7BV%7D_j%20=%200"></p>
<p>與 PCA 類似，SVD 拆解出來的東西統稱為 Components，運用在時間序列上就是有包含(signal)與雜訊(noise)的集合，從訊號再細分則有趨勢( trend )與季節性 ( seasonal )的部分，雜訊則是胡亂抖動、不規則的部份。而 <img src="https://latex.codecogs.com/png.latex?U"> 簡單來說就是判斷這些 Components 屬於哪一種類型( pattern )。</p>
<p><img src="https://latex.codecogs.com/png.latex?D"> 是 diagonal matrix，<img src="https://latex.codecogs.com/png.latex?D">，即只有特徵值( eigenvalue )在對角元素的矩陣，且對角元素按大至小排序：<img src="https://latex.codecogs.com/png.latex?d_1%20%5Cge%20d_2%20%5Cge%20%5Cdots%20d_p">。</p>
<p><img src="https://latex.codecogs.com/png.latex?V">則是表示<img src="https://latex.codecogs.com/png.latex?U">在時間軸上是怎麼被啟動的，代表某個 component 在每個時間點的強弱。</p>
<p>這個 SVD 分解也可以改寫成 sum of rank-one bi-dimensional matrices 的形式</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7BY%7D%0A=%20%5Csum_%7Bi=1%7D%5E%7Bd%7D%20%5Cmathbf%7BY%7D_i%0A=%20%5Csum_%7Bi=1%7D%5E%7Bd%7D%20%5Csqrt%7B%5Clambda_i%7D%20%5Cmathbf%7BU%7D_i%20%5Cmathbf%7BV%7D_i%5E%7B%5Cprime%7D%0A"></p>
<p>文字說明：</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?d">：Y 的 rank</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Clambda_i">：第 <img src="https://latex.codecogs.com/png.latex?i"> 個特徵值</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BU%7D_i">：左特徵向量</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BV%7D_i%5E%7B%5Cprime%7D">：右特徵向量的轉置</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BY%7D_i%20=%20%5Clambda_i%20%5Cmathbf%7BU%7D_i%20%5Cmathbf%7BV%7D_i%5E%7B%5Cprime%7D">：第 <img src="https://latex.codecogs.com/png.latex?i"> 個分量矩陣</p></li>
</ul>
<p>考慮到 <img src="https://latex.codecogs.com/png.latex?Y"> 的對稱結構，取 rank 時可以根據以下性質</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathrm%7Brank%7D(Y)%20=%20%5Cmathrm%7Brank%7D(YY')%20=%20%5Cmathrm%7Brank%7D(Y'Y)"></p>
<p>改寫成以下：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BS%7D%20=%20YY'"></p>
<p>這樣 <img src="https://latex.codecogs.com/png.latex?S"> 的 rank-one bi-dimensional matrices 會變成</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7BS%7D%20=%20%5Cmathbf%7BYY'%7D%0A=%20%5Csum_%7Bi=1%7D%5E%7Bd%7D%20%5Clambda_i%20%5Cmathbf%7BU%7D_i%20%5Cmathbf%7BV%7D_i%5E%7B%5Cprime%7D%5Cmathbf%7BV%7D_i%5Cmathbf%7BU%7D_i%5E%7B%5Cprime%7D%0A=%20%5Csum_%7Bi=1%7D%5E%7Bd%7D%20%5Clambda_i"></p>
<p>就是特徵值的總和，也是 components 的<strong>權重(weight)</strong>總和！</p>
</section>
<section id="grouping" class="level3">
<h3 class="anchored" data-anchor-id="grouping">Grouping</h3>
<p>Grouping 目的是要從所有特徵值中，挑出具代表性的訊號(signal)，其他則是雜訊(noise)。換句話說，我們可以把<img src="https://latex.codecogs.com/png.latex?Y">寫成</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7BY%7D%0A=%20%5Csum_%7Bi=1%7D%5E%7Bd%7D%20%5Csqrt%7B%5Clambda_i%7D%20%5Cmathbf%7BU%7D_i%20%5Cmathbf%7BV%7D_i%5E%7B%5Cprime%7D%20+%20%5Cvarepsilon%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%5Csqrt%7B%5Clambda_i%7D%20%5Cmathbf%7BU%7D_i%20%5Cmathbf%7BV%7D_i%5E%7B%5Cprime%7D">就是訊號+權重。</p>
</section>
<section id="diagonal-averaging" class="level3">
<h3 class="anchored" data-anchor-id="diagonal-averaging">Diagonal Averaging</h3>
<p>這一步的意思是把重建好的訊號矩陣，還原成一條序列，具體來說是要前述提取的訊號+權重重建成新的 Hankel matrix。要達成這個目標，有時也會用到 Smoothing 的方法讓重建出來的訊號更加平滑(沒有多餘的雜訊)，常見的作法是將所有多個反對角線的元素進行平均值化。</p>
</section>
</section>
<section id="選擇參數" class="level2">
<h2 class="anchored" data-anchor-id="選擇參數">選擇參數</h2>
<p>SSA 是一種無母數方法(Nonparametric method)，研究者需要控制 2 參數：Window length <img src="https://latex.codecogs.com/png.latex?L">以及選擇<img src="https://latex.codecogs.com/png.latex?r">個特徵作為訊號。那該怎麼設定才算是好的呢？以下有一個簡單的方法－看相關係數，用數學說，對每一群重建的<img src="https://latex.codecogs.com/png.latex?%F0%9D%91%8C%5E%7B(1)%7D_T">與<img src="https://latex.codecogs.com/png.latex?%F0%9D%91%8C%5E%7B(2)%7D_T">，他們的相關係數可以寫成：</p>
<p><img src="https://latex.codecogs.com/png.latex?%F0%9D%9C%8C_%7B(12)%7D%5Ew%20=%20%5Cfrac%7B(%F0%9D%91%8C%5E%7B(1)%7D_T,%F0%9D%91%8C%5E%7B(2)%7D_T)%7D%7B%20%5C%7C%F0%9D%91%8C%5E%7B(1)%7D_T%5C%7C_w%20%5Ctimes%5C%7C%F0%9D%91%8C%5E%7B(2)%7D_T%5C%7C_w%7D"></p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?%5C%7C%F0%9D%91%8C%5E%7B(i)%7D_T%5C%7C=%5Csqrt%7B(%F0%9D%91%8C%5E%7B(1)%7D_T,%F0%9D%91%8C%5E%7B(2)%7D_T)_w%7D"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?(%F0%9D%91%8C%5E%7B(1)%7D_T,%F0%9D%91%8C%5E%7B(2)%7D_T)_w=%5Csum%20%5Climits%20_%7Bk=1%7D%5E%7Bt%7Dw_ky%5Ei_ky%5Ej_k,%20i=1,j=2"></p></li>
</ul>
<p>如果兩者接近 0 相關，表示兩者可以好好分離，如果很高，表示兩群訊號有不少個訊號應該要被分在同一群。我們可以將其應用在分離訊號部分與雜訊部分的結果。這些<img src="https://latex.codecogs.com/png.latex?Y">兩兩相關的程度可以組成一個 W-correlation matrix (這裡的 w= 相關度) ，想要一目了然的查看這些<img src="https://latex.codecogs.com/png.latex?Y">兩兩相關的程度，可以用 W-correlation matrix 繪製的 heatmap。</p>
<p>根據文獻提供的說法，最適<img src="https://latex.codecogs.com/png.latex?L">值接近時間序列長度的一半，並且與每個時期的觀察數成比例，如此可以擁有較好的預測效果。不過如果時間許可，多加嘗試不同組合會是較好的選擇。</p>
</section>
<section id="ssa-怎麼拿來做時間序列預測" class="level2">
<h2 class="anchored" data-anchor-id="ssa-怎麼拿來做時間序列預測">SSA 怎麼拿來做時間序列預測?</h2>
<p>前述介紹的 SA 與 SSA 本質上都是一種對時間序列做分解，進而提取特徵的技術，沒有辦法拿來直接進行預測。要進行預測需要搭配一些演算法。最 basic 的方法是搭配 linear recurrent formula (LRF)，它看起來就像是 <img src="https://latex.codecogs.com/png.latex?AR(d)"> 模型：</p>
<p><img src="https://latex.codecogs.com/png.latex?y_t=a_1y_%7Bt-1%7D+a_2%7Bt-1%7D+%5Cdots+a_dy_%7Bt-d%7D%20%5C%5C%20t=d+1,%5Cdots%20,T"></p>
<p>其中<img src="https://latex.codecogs.com/png.latex?a_i">系列就是賦予不同時間步的<img src="https://latex.codecogs.com/png.latex?y">的權重，它們可以經由前面分解出的 components 算出：</p>
<p><img src="https://latex.codecogs.com/png.latex?a%5ET=(a_%7BL-1%7D,%20%5Cdots%20,%20a_1%20)=%20%5Cfrac%7B1%7D%7B1-v%5E2%7D%5Csum%20%5Climits%20_%7Bj=1%7D%5E%7Br%7D%5Cpi_j%20U_j%5E%7B%5Cbigtriangledown%7D"></p>
<p>其中：</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?U_j%5E%7B%5Cbigtriangledown%7D">：前 <img src="https://latex.codecogs.com/png.latex?L-1"> 個 components of <img src="https://latex.codecogs.com/png.latex?U_j">，<img src="https://latex.codecogs.com/png.latex?U_j"> 來自 <img src="https://latex.codecogs.com/png.latex?U"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Cpi_j">：<img src="https://latex.codecogs.com/png.latex?U_j">最後一個 component</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?v%5E2=%5Csum%20%5Climits%20_%7Bj=1%7D%5E%7Br%7D%5Cpi_j%5E2"></p></li>
</ul>
<p>這個方法實務上比較常用，R 也有對應的 package 可用。</p>
</section>
<section id="ssa-簡單實作" class="level2">
<h2 class="anchored" data-anchor-id="ssa-簡單實作">SSA 簡單實作</h2>
<p>在 R 裡，想要使用 SSA 可以使用 <code>RSSA</code> package 來處理。</p>
<section id="回到第一個設的簡單例子" class="level3">
<h3 class="anchored" data-anchor-id="回到第一個設的簡單例子">回到第一個設的簡單例子</h3>
<div id="cell-12" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-execution_count="11">
<div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># install.packages("Rssa")</span></span>
<span id="cb11-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(Rssa)</span>
<span id="cb11-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 沿用 y 當例子</span></span>
<span id="cb11-4">s <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ssa</span>(y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">L =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">110</span>)     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># L = window length</span></span>
<span id="cb11-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s)                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 看 eigenvalues</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-5-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>可以觀察到在　window length 設置為 110 的情況下，約莫有 8 個左右的特徵有權重(eigenvalues)，進一步去看看這些特徵的波動：</p>
<div id="cell-14" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-execution_count="6">
<div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb12-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定subplot繪圖參數</span></span>
<span id="cb12-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>))</span>
<span id="cb12-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 繪製特徵向量 (Eigenvectors)</span></span>
<span id="cb12-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vectors"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">idx =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Top 10 Eigenvectors"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-6-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>可以觀察到，前 8 個 components 的波型很規律，可以設為訊號，後面佔比 0% 的 components 都是不規則狀，屬於雜訊。</p>
<p>觀察分群結果可以看看熱度圖：</p>
<div id="cell-16" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-execution_count="7">
<div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"wcor"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 看熱度圖</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-7-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>顏色越深越一致就越代表是同一群，可以觀察到整體區塊可以分成　2　個：前1-8個 components 圖片看起來很乾淨，第 9 個 lag 以後出現很多在不同區域的相關，這些即為雜訊，呼應雜訊的長相都有點不規則。</p>
<p>我們來利用 1-8 個 components 重建時間序列。</p>
<div id="cell-18" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-execution_count="32">
<div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 重建時間序列</span></span>
<span id="cb14-2">r <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reconstruct</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">groups =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend1=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend2=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend3=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend4=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend5=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>))</span>
<span id="cb14-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 畫重建後的序列</span></span>
<span id="cb14-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb14-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend1"</span>)</span>
<span id="cb14-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend2"</span>)</span>
<span id="cb14-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend3, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend3"</span>)</span>
<span id="cb14-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend4, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend4"</span>)</span>
<span id="cb14-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend5, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend5"</span>)</span>
<span id="cb14-10">combined_series_ex1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend1<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend3<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend4<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend5</span>
<span id="cb14-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(combined_series_ex1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trends"</span>)</span>
<span id="cb14-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb14-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(combined_series_ex1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trends and Original Time series"</span>)</span>
<span id="cb14-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 疊加原始序列</span></span>
<span id="cb14-15"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>)</span>
<span id="cb14-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 加上圖例</span></span>
<span id="cb14-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">legend</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"topright"</span>,</span>
<span id="cb14-18">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Reconstructed"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Original"</span>),</span>
<span id="cb14-19">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-8-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-8-output-2.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>因為選擇效果太好，直接用 8 個components 100%還原了，太強了！不過這是可能因為這個例子沒有真正雜訊擾亂的緣故。</p>
<p>不過這個例子有點太簡單了，再換一個。</p>
</section>
<section id="換一個複雜的例子試試" class="level3">
<h3 class="anchored" data-anchor-id="換一個複雜的例子試試">換一個複雜的例子試試</h3>
<p>先換成一個組成更複雜的例子：</p>
<blockquote class="blockquote">
<p>改寫自 <a href="https://github.com/fabsta/interesting_notebooks/blob/master/introducing-ssa-for-time-series-decomposition.ipynb">github 上分享的 ipynb</a>，也是用 python 處理的版本。</p>
</blockquote>
<div id="cell-21" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}" data-execution_count="1">
<div class="sourceCode cell-code" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb15-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># The number of time 'moments'</span></span>
<span id="cb15-2">N <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">240</span></span>
<span id="cb15-3"></span>
<span id="cb15-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Time index</span></span>
<span id="cb15-5">t <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(N <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb15-6"></span>
<span id="cb15-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Components</span></span>
<span id="cb15-8">trend <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.001</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (t <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">120</span>)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">^</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb15-9">p1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span></span>
<span id="cb15-10">p2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">70</span></span>
<span id="cb15-11"></span>
<span id="cb15-12">periodic1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sin</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> pi <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> p1)</span>
<span id="cb15-13">periodic2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.75</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sin</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> pi <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> p2)</span>
<span id="cb15-14"></span>
<span id="cb15-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set seed for reproducibility</span></span>
<span id="cb15-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>)</span>
<span id="cb15-17"></span>
<span id="cb15-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate noise</span></span>
<span id="cb15-19">noise <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runif</span>(N) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>)</span>
<span id="cb15-20"></span>
<span id="cb15-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Final time series</span></span>
<span id="cb15-22">y_two <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> trend <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> periodic1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> periodic2 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> noise</span>
<span id="cb15-23"></span>
<span id="cb15-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Plot</span></span>
<span id="cb15-25"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(t, y_two, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.5</span>,</span>
<span id="cb15-26">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"t"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"F(t)"</span>,</span>
<span id="cb15-27">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The  Time Series y_tow and its Components"</span>)</span>
<span id="cb15-28"></span>
<span id="cb15-29"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(t, trend, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb15-30"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(t, periodic1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb15-31"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(t, periodic2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"green"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb15-32"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(t, noise, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"purple"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb15-33"></span>
<span id="cb15-34"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">legend</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"topright"</span>,</span>
<span id="cb15-35">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time Series (y_two)"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend"</span>, </span>
<span id="cb15-36">                  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Periodic #1"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Periodic #2"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Noise"</span>),</span>
<span id="cb15-37">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"green"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"purple"</span>),</span>
<span id="cb15-38">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>),</span>
<span id="cb15-39">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-9-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>在這個例子裡，<code>y_two</code> 包含了趨勢、2種週期性波動(或稱季節性波動)，以及雜訊，結構如下：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AY_2%20=%20%5Cunderbrace%7B0.001%20(t-100)%5E2%7D_%7B%5Ctext%7Btrend%7D%7D%0A+%20%5Cunderbrace%7B2%5Csin%5Cleft(%5Cfrac%7B2%5Cpi%20t%7D%7B20%7D%5Cright)%7D_%7B%5Ctext%7Bperiodic%201%7D%7D%0A+%20%5Cunderbrace%7B0.75%5Csin%5Cleft(%5Cfrac%7B2%5Cpi%20t%7D%7B30%7D%5Cright)%7D_%7B%5Ctext%7Bperiodic%202%7D%7D%0A+%20%5Cunderbrace%7B%5Cvarepsilon_t%7D_%7B%5Ctext%7Bnoise%7D%7D%0A"></p>
<p>其中<img src="https://latex.codecogs.com/png.latex?%5Cvarepsilon_t%20%5Csim%20%5Ctext%7BUniform%7D(-1,1)">，為 white noise。但是 trend 是非線性(non-linear)，非一般的 SA 與 ARMA 等線性模型可正確估計。<sup>1</sup></p>
<p>2種週期性波動則是：</p>
<table class="caption-top table">
<colgroup>
<col style="width: 3%">
<col style="width: 48%">
<col style="width: 13%">
<col style="width: 17%">
<col style="width: 17%">
</colgroup>
<thead>
<tr class="header">
<th></th>
<th>數學表示</th>
<th>週期 (Period)</th>
<th>振幅 (Amplitude)</th>
<th>頻率 (Frequency)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>第一個</td>
<td><img src="https://latex.codecogs.com/png.latex?2%5Csin%5Cleft(%5Cfrac%7B2%5Cpi%20t%7D%7B20%7D%5Cright)"></td>
<td>20</td>
<td>2</td>
<td><img src="https://latex.codecogs.com/png.latex?1/20"></td>
</tr>
<tr class="even">
<td>第二個</td>
<td><img src="https://latex.codecogs.com/png.latex?0.75%5Csin%5Cleft(%5Cfrac%7B2%5Cpi%20t%7D%7B30%7D%5Cright)"></td>
<td>30</td>
<td>0.75</td>
<td><img src="https://latex.codecogs.com/png.latex?1/30"></td>
</tr>
</tbody>
</table>
<p>因為 20 和 30 的最小公倍數是 60，所以疊加後整體 pattern 每 60 單位會重複一次。</p>
<div id="cell-23" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb16-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># install.packages("Rssa")</span></span>
<span id="cb16-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># library(Rssa)</span></span>
<span id="cb16-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 用 y_two 當例子</span></span>
<span id="cb16-4">s <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ssa</span>(y_two, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">L =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">110</span>)     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># L = window length</span></span>
<span id="cb16-5"></span>
<span id="cb16-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s)                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 看 eigenvalues</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-10-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>可以觀察到在 window length 設置為 110 的情況下，共有 50 個 eigenvalues，約莫有 8 個左右的特徵權重較高，後面逐漸逼近 0 ，進一步去看看這些特徵的占比與波動：</p>
<div id="cell-25" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb17-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定subplot繪圖參數</span></span>
<span id="cb17-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>))</span>
<span id="cb17-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 繪製特徵向量 (Eigenvectors)</span></span>
<span id="cb17-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vectors"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">idx =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Top 20 Eigenvectors"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-11-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>這是 <img src="https://latex.codecogs.com/png.latex?U"> 裡，前 20 個 Eigenvectors (即<img src="https://latex.codecogs.com/png.latex?U_j,%5C%20j=1,%5Cdots%2020">)的波形圖，可以依照這些特徵數的權重占比去決定訊號與雜訊，此外，也可以藉由 <img src="https://latex.codecogs.com/png.latex?U_j"> 的波形來判斷，如果波型不規則，極有可能是雜訊。</p>
<p>這裡可以先猜：第1、2個占比較多，設為趨勢；第3、4次之，為週期性波動；其他占比較少，且不少波形不規則，設定為雜訊。</p>
<p>也可以用 w-correlation 繪製的 heatmap 決定，並觀察目前選擇的 <img src="https://latex.codecogs.com/png.latex?L"> 能不能有效分解：</p>
<div id="cell-27" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb18-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"wcor"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 看熱度圖</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-12-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>方塊顏色越深表示越有相關，如果 window length <img src="https://latex.codecogs.com/png.latex?L"> 設得較好，理想情況屬於訊號的部分應該要盡量只有自己跟自己(如 F1 跟 F1)顏色最深，其他都是淺的 。以這裡來說，可以看到 F1 與 F2那裡只有自己顏色最深，可以選為 trend；F3 與 F4 顏色一樣深且最深，可以視做同一個 component；F5 與 F6 類似 F1 與 F2 的情況，但他們之間的關係度稍高，考慮到前面還有 1% 左右的占比，視做同一個 component，列入訊號中；之後的東西除了因為占比少，不同區域的方塊顏色也是不規則分布。</p>
<p>因此這樣猜測：</p>
<ul>
<li><p>F1 與 F2 為trend</p></li>
<li><p>F3 與 F4 為週期 1</p></li>
<li><p>F5 與 F6 為週期 2</p></li>
<li><p>其他視作雜訊</p></li>
</ul>
<p>我們利用 <code>reconstruct()</code> 執行前面提過的 LRF 演算法，重建時間序列。</p>
<div id="cell-29" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb19-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 重建時間序列</span></span>
<span id="cb19-2">r <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reconstruct</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">groups =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Period1 =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Period2 =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>))</span>
<span id="cb19-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># r 裡面會有Trend, Period1, Period2 , original time series 等多維度資料，不建議直接用plot(r)</span></span>
<span id="cb19-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 分開畫</span></span>
<span id="cb19-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb19-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend"</span>)</span>
<span id="cb19-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Period1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Period1"</span>)</span>
<span id="cb19-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Period2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Period2"</span>)</span>
<span id="cb19-9">combined_series <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Period1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Period2</span>
<span id="cb19-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(combined_series, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Reconstructed Trend + Period"</span>)</span>
<span id="cb19-11"></span>
<span id="cb19-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># plot(r$Rest, type = "l",</span></span>
<span id="cb19-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#      main = "Remain")</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-13-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>右下角即是重建後的時間序列，跟原本的資料相比：</p>
<div id="cell-31" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb20-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(combined_series, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Reconstructed Signal"</span>)</span>
<span id="cb20-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(y_two,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 紅線為原始資料</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-14-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>顯然效果不錯，擷取訊號部分重建的時間序列可視作原本時間序列的平滑版本。</p>
</section>
</section>
<section id="ssa-的優缺點" class="level2">
<h2 class="anchored" data-anchor-id="ssa-的優缺點">SSA 的優缺點</h2>
<p>在預測方面，網路上的討論幾乎沒有看到 SSA 的缺點，都是讚美。這主要是是因為 SSA 用上了 SVD 的技術，使得它不需要嚴格的統計假設，研究者只需調整 <img src="https://latex.codecogs.com/png.latex?L"> 的個數、選擇多少個特徵作為訊號重建跟使用什麼樣的平滑技巧。不過我在去年(2025/11月)的演講訪問到的教授 Paulo Canas Rodrigues 倒是說明了 SSA 本身可能會出現的問題，<sup>2</sup>原文翻譯如下：</p>
<blockquote class="blockquote">
<p>單獨使用 SSA 並不會導致更好的預測準確性。原因是某些時間序列的訊號可能無法被我們選擇的 components 捕捉（如果我們選擇太少的components），或者該訊號可能包括一些噪音（如果我們選擇太多的 components ）。</p>
<p>SSA by itself does not result in better forecasting accuracy. The reason is that some signal from the time series might not be captured by the components that we select (if we select too few components), or that the signal might include some noise (if we select too many components).</p>
</blockquote>
<p>這點在第一個例子中不太明顯，但第二個例子確實感覺得到選擇 components 需要一點感覺跟猜測，而且 SSA 是按照資料本身去分解，在不知道原資料有哪些訊號的情況不好處理。</p>
<p>我們來就第二個例子來驗證老師的說法。</p>
<div id="cell-34" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb21-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 重建時間序列</span></span>
<span id="cb21-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 只選一個 component</span></span>
<span id="cb21-3">r <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reconstruct</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">groups =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb21-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 分開畫</span></span>
<span id="cb21-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb21-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend(only 1 component )"</span>)</span>
<span id="cb21-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend and Original time series"</span>)</span>
<span id="cb21-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(y_two, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-15-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>這個例子驗證了 component 選太少作為訊號的問題。如果是選太多：</p>
<div id="cell-36" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb22-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 重建時間序列</span></span>
<span id="cb22-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 選30個 component</span></span>
<span id="cb22-3">r_30 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reconstruct</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">groups =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Guess1=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Guess2=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Guess3=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Guess3=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Guess4=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">30</span> ))</span>
<span id="cb22-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 分開畫</span></span>
<span id="cb22-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb22-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend"</span>)</span>
<span id="cb22-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Guess1"</span>)</span>
<span id="cb22-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Guess2"</span>)</span>
<span id="cb22-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess3, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Guess3"</span>)</span>
<span id="cb22-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess4, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Guess4"</span>)</span>
<span id="cb22-11">combined_series_30 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess2 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess3<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> r_30<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Guess4</span>
<span id="cb22-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(combined_series_30 , <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Signal and Original time series"</span>)</span>
<span id="cb22-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(y_two, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-16-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>明顯可以看出<code>Guess3</code>與<code>Guess4</code>是雜訊，因為它有不規則的波形，誤把雜訊加入的結果是估計出來的序列變得很不平滑，這種情況像是迴歸裡的 overfitting，模型對未來新資料的預測能力很可能會不佳，解釋性也不高。</p>
</section>
<section id="提外話假如去動l" class="level2">
<h2 class="anchored" data-anchor-id="提外話假如去動l">提外話：假如去動<img src="https://latex.codecogs.com/png.latex?L">?</h2>
<section id="l-設超小" class="level3">
<h3 class="anchored" data-anchor-id="l-設超小"><img src="https://latex.codecogs.com/png.latex?L"> 設超小</h3>
<div id="cell-39" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb23-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 用 y_two 當例子</span></span>
<span id="cb23-2">s2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ssa</span>(y_two, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">L =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># L = window length</span></span>
<span id="cb23-3">s2</span>
<span id="cb23-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定subplot繪圖參數</span></span>
<span id="cb23-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb23-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 繪製特徵向量 (Eigenvectors)</span></span>
<span id="cb23-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vectors"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">idx =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2 Eigenvectors"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<pre><code>
Call:
ssa(x = y_two, L = 2)

Series length: 240, Window length: 2,   SVD method: eigen
Special triples:  0

Computed:
Eigenvalues: 2, Eigenvectors: 2,    Factor vectors: 0

Precached: 0 elementary series (0 MiB)

Overall memory consumption (estimate): 0.004471 MiB</code></pre>
</div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-17-output-2.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<div id="cell-40" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb25-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(s2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>U)</span></code></pre></div>
<div class="cell-output cell-output-display">
<style>
.list-inline {list-style: none; margin:0; padding: 0}
.list-inline>li {display: inline-block}
.list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}
</style>
<ol class="list-inline"><li>2</li><li>2</li></ol>
</div>
</div>
<p>只剩下 2 個 components 可以用啦 XDD 因為選擇 <img src="https://latex.codecogs.com/png.latex?L"> 便超短的代價是SSA 最多只能有 L 個非零特徵值，<img src="https://latex.codecogs.com/png.latex?U">的維度也變成<img src="https://latex.codecogs.com/png.latex?2%20%5Ctimes%202">。看起來第 1 個 component 占比很大，可以當趨勢，第 2 個 component 當雜訊。</p>
<!-- $K= \text{length of y}-L$會變小。 -->
<p>看一下 W-correlation</p>
<div id="cell-42" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb26-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s2,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"wcor"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 看熱度圖</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-19-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>看起來分得很好 XDD ，再來看看重建會發生甚麼事</p>
<div id="cell-44" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb27-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 重建時間序列</span></span>
<span id="cb27-2">r2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reconstruct</span>(s2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">groups =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Trend =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb27-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># r 裡面會有Trend, Period1, Period2 , original time series 等多維度資料，不建議直接用plot(r)</span></span>
<span id="cb27-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 分開畫</span></span>
<span id="cb27-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb27-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trend"</span>)</span>
<span id="cb27-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(combined_series, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Reconstructed Signal(L=110)"</span>)</span>
<span id="cb27-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(r2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>Trend, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"l"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trends and Original Time series"</span>)</span>
<span id="cb27-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(y_two,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 紅線為原始資料</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-20-output-1.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>問題變得比較明顯了，SSA 的目標是要有效分離出訊號與雜訊，但是因為 components 只有兩個，即使挑其中一個占比大的，重建時資料也變得會捕捉到雜訊，不像 <img src="https://latex.codecogs.com/png.latex?L=110"> 的版本要平滑。這種情況像是迴歸裡的 overfitting，模型對未來新資料的預測能力會不佳。</p>
</section>
<section id="l-設超大" class="level3">
<h3 class="anchored" data-anchor-id="l-設超大"><img src="https://latex.codecogs.com/png.latex?L"> 設超大</h3>
<p>再來看看 <img src="https://latex.codecogs.com/png.latex?L"> 設超大會怎樣：</p>
<div id="cell-46" class="cell" data-quarto-private-1="{&quot;key&quot;:&quot;vscode&quot;,&quot;value&quot;:{&quot;languageId&quot;:&quot;r&quot;}}">
<div class="sourceCode cell-code" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb28-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 用 y_two 當例子</span></span>
<span id="cb28-2">s2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ssa</span>(y_two, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">L =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">239</span>)     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># L = window length</span></span>
<span id="cb28-3">s2</span>
<span id="cb28-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定subplot繪圖參數</span></span>
<span id="cb28-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb28-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 繪製特徵向量 (Eigenvectors)</span></span>
<span id="cb28-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(s, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vectors"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">idx =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2 Eigenvectors"</span>)</span></code></pre></div>
<div class="cell-output cell-output-display">
<pre><code>
Call:
ssa(x = y_two, L = 239)

Series length: 240, Window length: 239, SVD method: eigen
Special triples:  0

Computed:
Eigenvalues: 2, Eigenvectors: 2,    Factor vectors: 0

Precached: 0 elementary series (0 MiB)

Overall memory consumption (estimate): 0.008087 MiB</code></pre>
</div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/index_files/figure-html/cell-21-output-2.png" width="420" height="420" class="figure-img"></p>
</figure>
</div>
</div>
</div>
<p>還是只剩兩個 components ，看來問題會與 <img src="https://latex.codecogs.com/png.latex?L"> 設超小一樣。</p>
</section>
</section>
</section>
<section id="reference" class="level1">
<h1>Reference</h1>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0" data-line-spacing="2">
<div id="ref-wang2025lecture" class="csl-entry">
Rodrigues, P. C. (2025). <em>Time series forecasting: Exploring hybrid strategies with singular spectrum analysis</em>.
</div>
<div id="ref-stanford_series_notes" class="csl-entry">
Stanford University. (n.d.). <em>SA Time Series Notes</em>. <a href="https://web.stanford.edu/class/earthsys214/notes/series.html">https://web.stanford.edu/class/earthsys214/notes/series.html</a>
</div>
<div id="ref-arxiv_2511_05619" class="csl-entry">
Tianze Wang, Sofiane Ennadir, John Pertoft, Gabriela Zarzar Gandler, Lele Cao, Zineb Senane, Styliani Katsarou, Sahar Asadi, Axel Karlsson, Oleg Smirnov. (2025). <em>Frequency Matters: When Time Series Foundation Models Fail Under Spectral Shift</em>. <a href="https://arxiv.org/abs/2511.05619">https://arxiv.org/abs/2511.05619</a>
</div>
</div>
<section id="延伸閱讀" class="level2">
<h2 class="anchored" data-anchor-id="延伸閱讀">延伸閱讀</h2>
<p><strong>SA 部分：</strong></p>
<ul>
<li><p><a href="https://bookdown.org/rdpeng/timeseriesbook/spectral-analysis.html">A Very Short Course on Time Series Analysis-Spectral Analysis</a></p></li>
<li><p><a href="https://unit8co.github.io/darts/examples/03-FFT-examples.html">FFT 用於 Machine learning</a></p></li>
</ul>
<p><strong>SSA 部分</strong></p>
<ul>
<li><p><a href="https://www.kaggle.com/code/jdarcy/introducing-ssa-for-time-series-decomposition">SSA_kaggle</a></p></li>
<li><p><a href="https://en.wikipedia.org/wiki/Singular_spectrum_analysis">SSA_WIKI</a></p></li>
<li><p><a href="https://intlpress.com/site/pub/files/_fulltext/journals/sii/2010/0003/0003/SII-2010-0003-0003-a001.pdf">SSA_推薦讀物(pdf)</a></p></li>
</ul>



</section>
</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>主要因為未滿足穩態(stationarity)的數學假設，如果有做 differencing 使其變回穩態，這個例子是能用的。↩︎</p></li>
<li id="fn2"><p>教授名字及演講主文獻在參考文獻的第一項中。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>note</category>
  <category>R</category>
  <category>Time series</category>
  <category> Spectral Analysis</category>
  <category>Singular Spectrum Analysis</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/</guid>
  <pubDate>Tue, 10 Feb 2026 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/ts-spectral-periodogram/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>特展「南無警察大菩薩：臺北北警察署歷史特展」觀後記錄</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/hist/lecture-2025-08-10/lecture-2025-08-10.html</link>
  <description><![CDATA[ 






<section id="事後註記20260204" class="level1">
<h1>事後註記(2026/02/04)</h1>
<p>去年跟朋友去逛萬華時意外逛到的特展，幸運的是當時是特展的最後一天，更幸運的是當時還遇到策展人親自講解。可以聽到一些策展角度的解說，像是為什麼要擺這張圖、為什麼要放一件藝術品在門口，對從末修過策展相關課程的我來說是滿有趣的角度。</p>
</section>
<section id="展覽資訊" class="level1">
<h1>展覽資訊</h1>
<p>取自 <a href="https://tncmmm.gov.taipei/Content_List.aspx?n=15FADF76F7B2F3EE">新文化紀念館網站</a></p>
<p>日期：2024年9月28日（六）-2025年8月10日（日）</p>
<p>日治時期，警察權力包山包海，從戶口到衛生、從治安到思想，樣樣都能管。位在大稻埕的「北署」是管理臺北市的重要警政機關之一，特別與臺灣人的政治社會運動、民俗信仰活動有關。本次特展以北署為起點，透過沉浸的氛圍營造、警察歷史回顧，讓民眾了解在日治時期要成為一位警察大人的過程，以及北署長、執勤員警的面貌。從古蹟建築的歷史脈絡切入策展，從而展現文化資產再利用的精神底蘊，也呈現更完整的時代視角。</p>
<p>雖然特展已過，但可以在<a href="https://www-ws.gov.taipei/Download.ashx?u=LzAwMS9VcGxvYWQvNjgxL3JlbGZpbGUvMC8xMzA0OTUvMDUyYjkzODQtMWU2OS00YzU2LWIwODQtYWIyZDRiMmE5ZjYwLnBkZg%3D%3D&amp;n=5Lit5paHLnBkZg%3D%3D&amp;icon=..pdf">這個官方連結</a>查看導覽手冊，引用的史料及圖片與實際逛展的內容幾乎一致，。</p>
</section>
<section id="展覽內容心得" class="level1">
<h1>展覽內容&amp;心得</h1>
<p>2025年8月10日，我跟朋友來到了新文化紀念館，這是由台北市文化局直營的博物館，前身是日治時期的北署，曾經拘留過林獻堂、蔣渭水等有名的知識份子，目前一樓保存了監牢、水牢等遺跡。原本只是想聽聽常駐導覽，結果意外遇到這個特展，來寫一下逛展心得~</p>
<p>在完全沒做功課的前提下，只逛這個特展可能會搞不清楚為什麼要以北署為中心，但只要知道新文化紀念館的前身是日治時期的北署，我想大概就能理解了。<sup>1</sup></p>
<p>整體來說，策展人感覺有點像把這個題目化成歷史學論文，然後再轉化成展覽的形式，而且我們去的時候還意外碰到策展人親自導覽，聽到不少策展擺放圖片跟文物的想法，再外加一點點小小的題外話，是非常特別的體驗。</p>
<section id="第一展區" class="level2">
<h2 class="anchored" data-anchor-id="第一展區">第一展區</h2>
<p>展覽首先討論「警察」的概念，以及它是如何引入日本。歐洲的警察依職能分類可分成英國型跟歐洲大陸型，1872年明治維新時期日本派川路利良向當時的法蘭西、普魯士等地方考察，回來後主張在日本東京設置首府警察，各府縣則由其長官管理，是警察組織發源之始，且後續也有請普魯士的上尉指導。因為主要借鑑國家來源，日本警察繼承了歐洲大陸型的職能。</p>
<p>介紹歐洲警察職能時，展覽現場放了一張很大張的世界地圖，是萬國全圖。<sup>2</sup>一開始完全摸不著頭緒放這張要幹嘛，策展人講解後才知道，原來只是因為它夠大張、夠清楚、有整個19世紀的歐洲、有國旗(雖然不太明顯)，還有年代接近XD。地圖左邊放川路利良照片講話(歷史圖片+AI生成動嘴及聲音)，他說出來的那段話其實不是 100% 出自他之口，而是等價意思，實際的論點來自他的著作，論述更加冗長。雖感覺沒什麼必要（因為不是他真的講過的），不過只要成本不高，增加大眾的興趣也不是壞事。</p>
<p>接著陳述警察組織在台的職務及組織演變，職務有以下：</p>
<ul>
<li><p>司法警察：主責犯罪搜查</p></li>
<li><p>保安警察：主責預防犯罪</p></li>
<li><p>衛生警察：主責衛生工作、藥物檢查（含鴉片）</p></li>
<li><p>風俗警察：主責管理風月場所及相關事務</p></li>
<li><p>交通警察：主責交通管理工作，涵蓋交通管制、宣導講習等</p></li>
</ul>
<p>此外沿用清代的保甲制度協助治理，到這裡都是歷史課本上可見內容，比較有趣的是有列出北署參與過的案件或政令實行。</p>
<p>組織演變按時期即重要年代區分：</p>
<ol type="1">
<li>1895-1909 <img src="https://latex.codecogs.com/png.latex?%5C%5C"> 建立期</li>
</ol>
<p>此時期警察的工作以衛生、戶口為主，且需配合軍隊活動。中央會將地方性的警察管理事項，交由地方視情況調整，並開始制定一系列的法令，任用臺人執行警務。</p>
<ol start="2" type="1">
<li>1910 年，權力擴張</li>
</ol>
<p>後藤新平成立了臺灣特有的「 警察本署」，並透過制度設計使警察成為地方行政的主體，職權包含治安、行政、戶口調查、法令傳達、衛生事務、道路交通等，權力也隨之擴張，正式確立「警察萬能」的制度精神。</p>
<ol start="3" type="1">
<li>1920 年代 組織改制</li>
</ol>
<p>以 1920 年北署成立為始，此時期總督府修正地方官官制，在「市」設置警察署、在「郡」設警察課，並由郡守掌有警察權，曾引起警界異議。這項改動主要是因為此時期政治社會運動蓬勃發展，高等警察、特高警察也愈發重要，豎立警察「大人」的形象。</p>
<p>可以觀察到，2. 3. 點都是台灣有但日本沒有的組織調整，應該也是六三法的威力影響。</p>
<p>1925 年， 臺北州警察衛生展覽會在在會場展示「南無警察大菩薩」海報，引發各界討論。這個展覽係由臺北州警務部主辦，在短短的 4 日內(11 月 21 日至 25 日)吸引 20 萬人次參觀。其中本土知識份子的反應多是負面的，日本警察界多是讚賞，前者可以從台灣日日新報觀察，後者則是會出現在回憶錄。</p>
<p>北署方面，自成立以來，因應臺北市地名改變、廳舍搬遷等因素，北署的位置與管轄範圍也有所變化，相較於日本時代初期，這時的警察職權劃分更細緻。由於警察需要處理司法、保安、戶口、防疫等各項地方行政事務，北署在交通要道、機關單位附近設立「派出所」進行更全面的管理。</p>
<ol start="4" type="1">
<li>1930 年後，勤務更趨複雜</li>
</ol>
<p>在既有的組織架構又衍生出更多的專責單位，反映職務日趨複雜的現象。</p>
<p>也因此，北署的人力需求逐年增加。手冊中列出 1920 年改制後的北署人力增長，在總督府統計書中也有自 1901 開始的紀錄，不過沒有細到北署的。<sup>3</sup></p>
<p>當時的現場還放了靈感源自南無警察大菩薩的裝置藝術，沒拍到但外表是一個頭部是機器人身體式木造人體骨架擺出海報的姿勢，外型很酷炫。聽說是借來的，胸部那裏還有一顆假心臟其實會動，據說藝術家擔心會壞掉，所以非必要就不會讓它動。</p>
<p>第一展區的後半部，介紹警察在 1920 改制前的官階，以及訓練課程，其中也有收錄到後來成為二二八事件的受難者之一湯德章在訓練時期的照片。策展人提到，在選擇湯德章的照片時他盡量選擇比較日常的、放鬆的照片，希望能給觀眾留下不同於「二二八事件的受難者」的印象。很欣賞這樣的想法跟做法。</p>
<p>另外還有一面牆貼著北署新廳舍落成時的新聞照片，以及滿滿的長官人頭像，這裡的布置巧思與前面的世界地圖類似，有圖片就盡量放。</p>
</section>
<section id="第二展區" class="level2">
<h2 class="anchored" data-anchor-id="第二展區">第二展區</h2>
<p>第二展區的位置在原本北署的署長室(沒記錯的話)，放了歷代北署署長的照片與畫像立牌。畫像的部分是為了策展委託人畫的，沒記錯策展人好像自由讓畫家發揮，所以畫像版的署長手上拿的東西有時會跟出身有關係，有時沒有特殊意涵。</p>
<p>這裡開始我因為從早上連續站到下午而體力不支、逐漸失神，但其他遊客還是非常專心地在聽。</p>
</section>
<section id="第三展區" class="level2">
<h2 class="anchored" data-anchor-id="第三展區">第三展區</h2>
<p>這個展區有策展精選的刑事案件，案件內容都很精彩。我猜這些資料可能是從漢珍的日日新報資料庫找來的(？)，<del>老實說覺得那個資料庫有點難用</del>，能夠找到這麼多曲折離奇的案件我是非常佩服。</p>
</section>
<section id="第四展區" class="level2">
<h2 class="anchored" data-anchor-id="第四展區">第四展區</h2>
<p>我看到中間看起來像是原本是一個水池的東西，裡面放滿了小石子、展示的文物…跟藝術品，同樣也是為了策展而做的，靠牆的地方有非常多的椅子，我當時像是找到綠洲一樣如釋重負地坐下來。這一區展示的主要是警察與知識份子的交鋒，以及戰爭時期警察的工作以及呈現在文物裡的意象。這些文物主要是一些文宣、手冊、火柴盒…等等，跟各方借來的。</p>
<p>聽策展人說，這一塊區域的布置也是苦思許久，跟室內設計公司討論後才決定跟一起放小石子，再用耙子整理，用出一條條溝紋跟小山丘，再借一顆某種樹點綴，好像有某種目的，但是我忘記是甚麼了QQ</p>
<p>最後，在靠近後門的牆上，有滿滿的日式燈籠擺飾，上面的題字區寫了 3 個問題：</p>
<ol type="1">
<li><p>從「警察者其保傅也」、「一手提劍、一手持經典」、「野性老虎」到當代常聽到的「人民保母」，都是對警察形象的描述，你認為警察應該是什麼樣子呢？</p></li>
<li><p>許多人對日本時代的印象是治安良好，但這可能與警察幾近「萬能」的權力和嚴格的執法有關，你認為擴大警察權力等於提升治安狀況嗎？如果是的話，是否有什麼會被犧牲。</p></li>
<li><p>警察職勤除了服從上級命令指派，過程中是否「應該」或者「可以」有自我價值的判斷？</p></li>
</ol>
<p>策展人提到策畫這個展覽也是為了啟發民眾重新思考當代警察的角色與權能，另外也提到備展時間有限，有些地方沒有校正好，展覽期間也收到有能之士的考據與指正，感謝他們幫忙修正一些有誤的地方。</p>
<p>許多遊客在一旁的便條紙牆上寫下他們的對問題看法跟展覽心得，包含日文跟韓文的留言。我跟朋友雖然沒留言，但閱讀牆上的滿滿的留言就已經饒富趣味。</p>



</section>
</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>即使沒有特展，聽一聽一樓的志工常駐導覽也是非常不錯的選擇。↩︎</p></li>
<li id="fn2"><p><a href="https://digicoll.lib.berkeley.edu/record/56079?v=uv#?xywh=713%2C588%2C7380%2C3821">這裡可以看原圖跟誕生年代</a>↩︎</p></li>
<li id="fn3"><p>詳，年代看起來從，電子資源收錄於臺灣日治時期統計資料庫。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>展覽心得</category>
  <category>台灣史</category>
  <guid>https://paperfishblog.netlify.app/posts/hist/lecture-2025-08-10/lecture-2025-08-10.html</guid>
  <pubDate>Tue, 03 Feb 2026 16:00:00 GMT</pubDate>
</item>
<item>
  <title>讓 AI 教我製作簡易 RAG 網頁服務(上)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/llm-course-note1/llm-course-note1.html</link>
  <description><![CDATA[ 






<!-- 使用 AI -->
<div data-ai-tag="2">

</div>
<script src="../../../../asserts/box.js" defer=""></script>
<section id="緣起" class="level1">
<h1>緣起</h1>
<p>最近發現 AI 聯盟開了「生成式AI應用系統與工程」，課綱很吸引我，但跟統研的必修衝堂了 QQ ，所以叫 Gemini 根據課綱教我，看看它能教到甚麼程度。</p>
<p>目前發現它教的都是最簡單的部分XD，比較複雜的東西以前端為例，課綱裡列的 Next.js / React / Tailwind 都被自刻 Html/CSS/JavaScript 取代，然後還吹得我好像會了甚麼很厲害的東西一樣 == 。不過它也有教我沒接觸過的 FastAPI，雖然講得超級淺，但能 Build 出自己的 API 服務滿有成就感的，除此之外還可以觀察有使用 LLM 的系統服務是怎麼串的，所以有了這篇文章。</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
建議前置步驟
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<ol type="1">
<li>建立新的專案資料夾</li>
<li>建立新的乾淨虛擬環境</li>
</ol>
</div>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
環境與套件版本
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<ul>
<li><p>IDE: Vscode</p></li>
<li><p>Python version: 3.12.5 (venv 虛擬環境)</p></li>
</ul>
<p>另外，也記錄本次 <code>requirement.txt</code>，實際上我是跟著 Gemini 的指示做 pip install，所以我覺得沒必要先安裝好這些，但可以參考。</p>
<pre><code>annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.12.1
certifi==2026.1.4
charset-normalizer==3.4.4
click==8.3.1
colorama==0.4.6
distro==1.9.0
fastapi==0.128.0
filelock==3.20.3
fsspec==2026.1.0
groq==1.0.0
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
huggingface-hub==0.36.0
idna==3.11
Jinja2==3.1.6
jiter==0.12.0
joblib==1.5.3
MarkupSafe==3.0.3
mpmath==1.3.0
networkx==3.6.1
numpy==2.4.1
openai==2.15.0
packaging==25.0
pydantic==2.12.5
pydantic_core==2.41.5
PyYAML==6.0.3
regex==2026.1.15
requests==2.32.5
safetensors==0.7.0
scikit-learn==1.8.0
scipy==1.17.0
sentence-transformers==5.2.0
setuptools==80.9.0
sniffio==1.3.1
starlette==0.50.0
sympy==1.14.0
threadpoolctl==3.6.0
tokenizers==0.22.2
torch==2.9.1
tqdm==4.67.1
transformers==4.57.6
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.6.3
uvicorn==0.40.0</code></pre>
</div>
</div>
</div>
</section>
<section id="申請-groq-免費-api-key-並測試" class="level1">
<h1>申請 Groq 免費 API Key 並測試</h1>
<p>因為 OpenAI API 要 $$，所以我叫它改用免費的 Groq :P。</p>
<section id="申請步驟" class="level2">
<h2 class="anchored" data-anchor-id="申請步驟">申請步驟</h2>
<p>完整申請步驟如下：<sup>1</sup></p>
<ol type="1">
<li><p>上去<a href="https://groq.com/">官網</a>，點選右上角的 Start Building</p></li>
<li><p>建立帳號登入</p></li>
<li><p>點選右上方的 API Keys，進入後選擇大大的「+ Create API Key」</p></li>
<li><p>給 API 命名後 Submit，copy API。</p></li>
</ol>
</section>
<section id="測試" class="level2">
<h2 class="anchored" data-anchor-id="測試">測試</h2>
<p>接著在乾淨的 venv 環境下安裝套件</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install openai</span></code></pre></div>
<p>然後在專案資料夾下建立 <code>main.py</code> 或其他 py 檔，輸入以下程式（記得修改 API KEY）：</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"></span>
<span id="cb3-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> openai <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> OpenAI</span>
<span id="cb3-3"></span>
<span id="cb3-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. 設定連線資訊</span></span>
<span id="cb3-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># base_url 是關鍵，這行告訴程式：「不要連去 OpenAI，改連去 Groq」</span></span>
<span id="cb3-6">client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> OpenAI(</span>
<span id="cb3-7">    api_key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"剛剛申請的API KEY"</span>, </span>
<span id="cb3-8">    base_url<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://api.groq.com/openai/v1"</span></span>
<span id="cb3-9">)</span>
<span id="cb3-10"></span>
<span id="cb3-11"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--- 免費 AI 聊天機器人 (Groq Llama3) 啟動 ---"</span>)</span>
<span id="cb3-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--- 輸入 'quit' 可以離開程式 ---"</span>)</span>
<span id="cb3-13"></span>
<span id="cb3-14"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>:</span>
<span id="cb3-15">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. 讓使用者輸入</span></span>
<span id="cb3-16">    user_input <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">你："</span>)</span>
<span id="cb3-17">    </span>
<span id="cb3-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> user_input.lower() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quit"</span>:</span>
<span id="cb3-19">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"再見！"</span>)</span>
<span id="cb3-20">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb3-21"></span>
<span id="cb3-22">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. 呼叫 AI (這裡使用開源模型 Llama3-8b)</span></span>
<span id="cb3-23">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb3-24">        completion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> client.chat.completions.create(</span>
<span id="cb3-25">            model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"llama-3.3-70b-versatile"</span>,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 指定模型名稱</span></span>
<span id="cb3-26">            messages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[</span>
<span id="cb3-27">                {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"system"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"你是一個繁體中文的 AI 助教，回答簡潔有力。"</span>},</span>
<span id="cb3-28">                {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: user_input}</span>
<span id="cb3-29">            ]</span>
<span id="cb3-30">        )</span>
<span id="cb3-31"></span>
<span id="cb3-32">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. 取得回答</span></span>
<span id="cb3-33">        ai_response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> completion.choices[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].message.content</span>
<span id="cb3-34">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"AI：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>ai_response<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb3-35">        </span>
<span id="cb3-36">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb3-37">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"發生錯誤：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div>
<p>然後跑跑看腳本，如果能在 Terminal 跟模型對話就成功了，如果失敗，則有可能是 API Key 錯誤或是模型不支援要更換。這裡我們用的模型是<code>llama-3.3-70b-versatile</code>。</p>
<p>如果點進去 Groq 官網，會發現它們用的是 <code>Groq()</code>而非 <code>OpenAI()</code>建立 Client，這主要是因為：</p>
<ol type="1">
<li><p>時序上 OpenAI 較早提供 API 服務，在使用者增加後其他公司才後續跟進</p></li>
<li><p><code>OpenAI()</code>也支援連接其他 LLM 的 API 服務，泛用性較高</p></li>
<li><p>觀察網路上的常見的 LLM 服務課程，開發程式碼也以<code>OpenAI()</code>為主</p></li>
</ol>
<p>所以就用<code>OpenAI()</code>囉！</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-3-contents" aria-controls="callout-3" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
其他問題：<code>base_url</code> 是啥
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-3" class="callout-3-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p><code>base_url</code> 就是伺服器的地址。想像你在寄信（發送請求）：</p>
<ul>
<li><p>信的內容 (Payload)： 你的問題（例如：“你好”）。</p></li>
<li><p>信的格式 (Schema)： OpenAI 規定的格式（JSON 格式）。</p></li>
<li><p>收件地址 (base_url)： 這就是關鍵所在。</p></li>
</ul>
<p>預設情況 (不寫 base_url)：當你只寫 client = OpenAI(api_key=“…”) 時，程式預設的 base_url 是　https://api.openai.com/v1 (直接寄去 OpenAI 總部)。</p>
<p>我們的情況 (修改 base_url)：當你加上 base_url=“https://api.groq.com/openai/v1” 時，發生了這件事：你的程式依然使用 OpenAI 的格式打包信件，但是信件被攔截，轉送到了 Groq 的伺服器。Groq 的伺服器看懂了這個格式 (因為它相容)，於是處理後回傳結果。</p>
<p>同理，假設我們今天要改成在本地跑 Ollama 模型，可以把 <code>base_url</code>改成自己的電腦地址，ex <code>base_url="http://localhost:11111/v1"</code>。</p>
</div>
</div>
</div>
</section>
</section>
<section id="建立後端-api-服務" class="level1">
<h1>建立後端 API 服務</h1>
<p>首先，在乾淨的 venv 環境下安裝套件</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install fastapi uvicorn</span></code></pre></div>
<p>說明：</p>
<ol type="1">
<li><p>FastAPI: 用來快速建立網頁伺服器的框架。</p></li>
<li><p>Uvicorn: 用來啟動這個伺服器的工具。</p></li>
</ol>
<p>在專案資料夾下建立 <code>server.py</code>，內容打一下（一樣要修改API KEY）：</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> fastapi <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> FastAPI</span>
<span id="cb5-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pydantic <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> BaseModel</span>
<span id="cb5-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> openai <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> OpenAI</span>
<span id="cb5-4"></span>
<span id="cb5-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. 初始化 FastAPI APP</span></span>
<span id="cb5-6">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> FastAPI()</span>
<span id="cb5-7"></span>
<span id="cb5-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. 設定 Groq 連線 (跟上一步一樣)</span></span>
<span id="cb5-9">client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> OpenAI(</span>
<span id="cb5-10">    api_key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"你申請的API Key"</span>, </span>
<span id="cb5-11">    base_url<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://api.groq.com/openai/v1"</span></span>
<span id="cb5-12">)</span>
<span id="cb5-13"></span>
<span id="cb5-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. 定義資料格式：告訴程式，別人傳進來的資料應該長什麼樣子</span></span>
<span id="cb5-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 這裡規定：一定要有一個欄位叫做 "message" 且是文字 (str)</span></span>
<span id="cb5-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> UserInput(BaseModel):</span>
<span id="cb5-17">    message: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span></span>
<span id="cb5-18"></span>
<span id="cb5-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. 建立一個 "路徑" (API Endpoint)</span></span>
<span id="cb5-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 當有人對網址 /chat 發送 POST 請求時，會執行這個函式</span></span>
<span id="cb5-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@app.post</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/chat"</span>)</span>
<span id="cb5-22"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> chat_with_ai(data: UserInput):</span>
<span id="cb5-23">    user_message <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> data.message</span>
<span id="cb5-24">    </span>
<span id="cb5-25">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"收到訊息：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>user_message<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 讓你在後台看得到</span></span>
<span id="cb5-26"></span>
<span id="cb5-27">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb5-28">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 呼叫 Groq</span></span>
<span id="cb5-29">        completion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> client.chat.completions.create(</span>
<span id="cb5-30">            model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"llama-3.3-70b-versatile"</span>,</span>
<span id="cb5-31">            messages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[</span>
<span id="cb5-32">                {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"system"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"你是一個繁體中文 AI 助教。"</span>},</span>
<span id="cb5-33">                {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: user_message}</span>
<span id="cb5-34">            ]</span>
<span id="cb5-35">        )</span>
<span id="cb5-36">        ai_reply <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> completion.choices[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].message.content</span>
<span id="cb5-37">        </span>
<span id="cb5-38">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 回傳結果給前端</span></span>
<span id="cb5-39">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"reply"</span>: ai_reply}</span>
<span id="cb5-40">        </span>
<span id="cb5-41">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb5-42">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"error"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(e)}</span>
<span id="cb5-43"></span>
<span id="cb5-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 5. 測試首頁 (確認伺服器活著)</span></span>
<span id="cb5-45"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@app.get</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/"</span>)</span>
<span id="cb5-46"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> read_root():</span>
<span id="cb5-47">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"status"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Server is running!"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"course"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Generative AI Engineering"</span>}</span></code></pre></div>
<p>這裡使用的測試模型一樣是<code>llama-3.3-70b-versatile</code>。然後用</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">uvicorn</span> server:app <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--reload</span></span></code></pre></div>
<p>啟動伺服器，成功時 Terminal 會顯示的訊息</p>
<pre><code>INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
...
...
...
INFO:     ... - "GET / HTTP/1.1" 200 OK
INFO:     ... - "GET /docs HTTP/1.1" 200 OK
INFO:     ... - "GET /openapi.json HTTP/1.1" 200 OK</code></pre>
<p>且到 <a href="http://127.0.0.1:8000">http://127.0.0.1:8000</a>時，畫面會像這樣</p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/llm-course-note1/scuess.png" class="img-fluid"></p>
<p>這個成功畫面顯示的就是第五步的 code，但其實第五步不是必須，只是可以直接確認伺服器可正常運作，完全不寫的話 INFO 會跑出 404 Not Found 的訊息，打開網站也會跑出 404 的訊息，但我們可以用一個簡單的方式確認：</p>
<ol type="1">
<li><p>首先打開 API 文件 <a href="http://127.0.0.1:8000/docs">http://127.0.0.1:8000/docs</a>。</p></li>
<li><p>依序點擊</p></li>
</ol>
<p><img src="https://paperfishblog.netlify.app/posts/tech/llm-course-note1/post.png" class="img-fluid"> 3. 修改 “string”部分(模仿使用者輸入問題)</p>
<pre><code>{
  "message": "string"
}</code></pre>
<ol start="4" type="1">
<li><p>按下 Execute 按鈕</p></li>
<li><p>查看下方 Response 部分， Code 200 代表正常運作，且可以看到 Response Body 有模型回應跟 Response Header 紀錄回應時間等資訊。</p></li>
</ol>
<p>實務上 API 文件在開發跟檢查問題時都很需要，學起來不是壞事：）</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-4-contents" aria-controls="callout-4" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
AI 沒教的事：隱藏 API KEY
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-4" class="callout-4-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>如果連 API KEY 都不能隱藏的話，要怎麼上線！首先再來安裝一個套件</p>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb9-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install dotenv</span></code></pre></div>
<p>接著在同專案資料夾下建立名為 <code>.env</code> 的檔案，建立變數：</p>
<pre class="env"><code>API_KEY="你申請的API KEY"</code></pre>
<p>可以用以下腳本試跑看看 Terminal 可不可以成功顯示 <code>API_KEY</code>裡的參數。</p>
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> os</span>
<span id="cb11-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb11-3"></span>
<span id="cb11-4">load_dotenv() <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 讀取 .env</span></span>
<span id="cb11-5"></span>
<span id="cb11-6">API_KEY <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.getenv(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"API_KEY"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 取得 API_KEY 這個參書內容</span></span>
<span id="cb11-7"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(API_KEY)</span></code></pre></div>
<p>成功的話再把這段程式貼入 <code>server.py</code> 裡。</p>
</div>
</div>
</div>
</section>
<section id="網頁服務的建立" class="level1">
<h1>網頁服務的建立</h1>
<p>上一個 part 的程式只能在後端運作，使用者用不了，所以需要前端讓使用者輸入東西，傳給後端處理。這裡使用比較常見的網頁服務來實作，而且是用最簡單的 HTML 包 CSS 跟 JS 的形式。</p>
<p>不過首先要先讓網頁跟伺服器可以溝通，就要先解決瀏覽器的安全機制 (CORS)。</p>
<p>所謂的 CORS 就是，網頁檔案 (HTML) 和伺服器 (FastAPI) 雖然都在同一台電腦，但在瀏覽器眼中它們是「不同的來源」。如果沒有特別允許，瀏覽器會禁止網頁去讀取伺服器的資料。所以要修改 <code>server.py</code>變成:</p>
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 建立 web</span></span>
<span id="cb12-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> fastapi <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> FastAPI</span>
<span id="cb12-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> fastapi.middleware.cors <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> CORSMiddleware  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 引入 CORS 套件</span></span>
<span id="cb12-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pydantic <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> BaseModel</span>
<span id="cb12-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> openai <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> OpenAI</span>
<span id="cb12-6"></span>
<span id="cb12-7">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> FastAPI()</span>
<span id="cb12-8"></span>
<span id="cb12-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 設定 CORS，允許所有來源 (為了開發方便，我們先全開)</span></span>
<span id="cb12-10">app.add_middleware(</span>
<span id="cb12-11">    CORSMiddleware,</span>
<span id="cb12-12">    allow_origins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span>],  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 允許任何網址呼叫這個 API</span></span>
<span id="cb12-13">    allow_credentials<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb12-14">    allow_methods<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span>],  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 允許任何方法 (GET, POST...)</span></span>
<span id="cb12-15">    allow_headers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span>],</span>
<span id="cb12-16">)</span>
<span id="cb12-17"></span>
<span id="cb12-18">client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> OpenAI(</span>
<span id="cb12-19">    api_key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"APIKEY"</span>, </span>
<span id="cb12-20">    base_url<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://api.groq.com/openai/v1"</span></span>
<span id="cb12-21">)</span>
<span id="cb12-22"></span>
<span id="cb12-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 後面都一樣</span></span></code></pre></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-5-contents" aria-controls="callout-5" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
CORS 各設定詳解
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-5" class="callout-5-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<table class="caption-top table">
<colgroup>
<col style="width: 44%">
<col style="width: 33%">
<col style="width: 22%">
</colgroup>
<thead>
<tr class="header">
<th>參數名稱</th>
<th>設定值</th>
<th>功能說明</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>allow_origins</td>
<td>[“*”]</td>
<td>允許的來源網域。[“*”] 代表「允許所有網站」存取你的 API。在開發階段很方便，但在正式上線（Production）時，建議指定具體的網址（例如 [“https://www.your-app.com”]）以策安全。</td>
</tr>
<tr class="even">
<td>allow_credentials</td>
<td>True</td>
<td>是否允許攜帶憑證。當設為 True 時，跨來源請求可以包含 Cookies、HTTP 認證（Authentication）或 TLS 用戶端憑證。注意：如果此項為 True，allow_origins 最好不要設為 [“*”]，有些瀏覽器會因為安全限制而報錯。</td>
</tr>
<tr class="odd">
<td>allow_methods</td>
<td>[“*”]</td>
<td>允許的 HTTP 方法。[“*”] 代表允許所有方法，包括 GET、POST、PUT、DELETE 等。你目前的程式碼中使用了 POST 方法來處理 <code>/chat</code> 和 <code>/upload</code>。</td>
</tr>
<tr class="even">
<td>allow_headers</td>
<td>[“*”]</td>
<td>允許的 HTTP 標頭。[“*”] 代表允許請求攜帶任何自定義標頭（Headers），例如 Content-Type、Authorization 等。</td>
</tr>
</tbody>
</table>
<p>AI 目前給的設定都是安全最低的選擇，有部署需要時需要調整(尤其是<code>allow_origins</code>)。</p>
</div>
</div>
</div>
<p>然後建立網頁<code>index.html</code></p>
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode html code-with-copy"><code class="sourceCode html"><span id="cb13-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;!DOCTYPE</span> html<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-2"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">html</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> lang</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"zh-TW"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-3"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">head</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-4">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">meta</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> charset</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"UTF-8"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-5">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">meta</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> name</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"viewport"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"width=device-width, initial-scale=1.0"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-6">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">title</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span>我的 AI 聊天室<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">title</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-7">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">style</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-8">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/* 簡單的 CSS 樣式，讓畫面好看一點 */</span></span>
<span id="cb13-9">        body { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">font-family</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">sans-serif</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">max-width</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">600</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">margin</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">auto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">padding</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-10">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.chat-box</span> { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">solid</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#ccc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">height</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">overflow-y</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">scroll</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">padding</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">margin-bottom</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border-radius</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#f9f9f9</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-11">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.message</span> { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">margin</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">padding</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border-radius</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">width</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">fit-content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">max-width</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">80</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-12">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.user</span> { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background-color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#007bff</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">white</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">margin-left</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">auto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">text-align</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">right</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> } <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/* 使用者訊息靠右 */</span></span>
<span id="cb13-13">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.ai</span> { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background-color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#e9ecef</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">black</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">margin-right</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">auto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> } <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/* AI 訊息靠左 */</span></span>
<span id="cb13-14">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.input-area</span> { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">display</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">flex</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">gap</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-15">        input { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">flex</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">padding</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border-radius</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">solid</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#ddd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-16">        button { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">padding</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background-color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#28a745</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">white</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">border-radius</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">cursor</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">pointer</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-17">        button<span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:hover</span> { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background-color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#218838</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-18">        button<span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:disabled</span> { <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">background-color</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">#ccc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> }</span>
<span id="cb13-19">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">style</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-20"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">head</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-21"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">body</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-22"></span>
<span id="cb13-23">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">h2</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span>🤖 我的生成式 AI 助手<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">h2</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-24">    </span>
<span id="cb13-25">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-26">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"message ai"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span>你好！我是你的 AI 助教，有什麼我可以幫你的嗎？<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-27">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-28"></span>
<span id="cb13-29">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"input-area"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-30">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">input</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> type</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user-input"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> placeholder</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"輸入你的問題..."</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> onkeypress</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"handleEnter(event)"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-31">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">button</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> onclick</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sendMessage()"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"send-btn"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span>發送<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">button</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-32">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">div</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-33"></span>
<span id="cb13-34">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-35">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 3. JavaScript 邏輯區：負責跟後端講話</span></span>
<span id="cb13-36">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sendMessage</span>() {</span>
<span id="cb13-37">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> inputField <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user-input"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-38">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> sendBtn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"send-btn"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-39">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chatBox <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-40">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> message <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> inputField<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trim</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-41"></span>
<span id="cb13-42">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>message) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 如果沒輸入字就不理會</span></span>
<span id="cb13-43"></span>
<span id="cb13-44">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 顯示使用者的訊息</span></span>
<span id="cb13-45">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(message<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-46">            inputField<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 清空輸入框</span></span>
<span id="cb13-47">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">disabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 發送中禁止按按鈕</span></span>
<span id="cb13-48">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"思考中..."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-49"></span>
<span id="cb13-50">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span> {</span>
<span id="cb13-51">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// --- 關鍵步驟：呼叫你的 FastAPI 後端 ---</span></span>
<span id="cb13-52">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fetch</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"http://127.0.0.1:8000/chat"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> {</span>
<span id="cb13-53">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">method</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"POST"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-54">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">headers</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> { <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Content-Type"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"application/json"</span> }<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-55">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">JSON</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stringify</span>({ <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">message</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> message })</span>
<span id="cb13-56">                })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-57"></span>
<span id="cb13-58">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> response<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">json</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-59">                </span>
<span id="cb13-60">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 顯示 AI 的回答</span></span>
<span id="cb13-61">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">reply</span>) {</span>
<span id="cb13-62">                    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">reply</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ai"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-63">                } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> {</span>
<span id="cb13-64">                    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"錯誤："</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">JSON</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stringify</span>(data)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ai"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-65">                }</span>
<span id="cb13-66"></span>
<span id="cb13-67">            } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">catch</span> (error) {</span>
<span id="cb13-68">                <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"連線失敗，請確認後端伺服器有開啟。"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ai"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-69">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">console</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">error</span>(error)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-70">            }</span>
<span id="cb13-71"></span>
<span id="cb13-72">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 恢復按鈕狀態</span></span>
<span id="cb13-73">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">disabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-74">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"發送"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-75">        }</span>
<span id="cb13-76"></span>
<span id="cb13-77">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 輔助功能：把訊息加到畫面上</span></span>
<span id="cb13-78">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> sender) {</span>
<span id="cb13-79">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chatBox <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-80">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> div <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">createElement</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"div"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-81">            div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">add</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"message"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> sender)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-82">            div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-83">            chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendChild</span>(div)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-84">            chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollTop</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollHeight</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 自動捲動到底部</span></span>
<span id="cb13-85">        }</span>
<span id="cb13-86"></span>
<span id="cb13-87">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 輔助功能：按 Enter 鍵也能發送</span></span>
<span id="cb13-88">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">handleEnter</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span>) {</span>
<span id="cb13-89">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">key</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">===</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Enter"</span>) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sendMessage</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-90">        }</span>
<span id="cb13-91">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-92"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">body</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb13-93"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">html</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div>
<p>然後在伺服器已啟動的前提下用瀏覽器開啟網頁，成功時可以正常跟它對話，正常流程為：</p>
<ol type="1">
<li><p>Frontend (HTML/JS): 瀏覽器抓取你輸入的文字。</p></li>
<li><p>API Call: 瀏覽器發送請求給 http://127.0.0.1:8000/chat。</p></li>
<li><p>Backend (FastAPI): 你的 Python 程式收到請求，轉頭去問 Groq。</p></li>
<li><p>LLM: Groq 運算完，把結果傳回 Python。</p></li>
<li><p>Render: Python 把結果傳回瀏覽器，JavaScript 把文字貼在畫面上。</p></li>
</ol>
<p>但它還不具備逐字載入與上下文記憶功能。</p>
</section>
<section id="細節微調" class="level1">
<h1>細節微調</h1>
<section id="讓網頁逐字載入回應" class="level2">
<h2 class="anchored" data-anchor-id="讓網頁逐字載入回應">讓網頁逐字載入回應</h2>
<p>先修改 <code>serve.py</code>，首先在套件部分加入</p>
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> fastapi.responses <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> StreamingResponse <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 引入串流回應</span></span></code></pre></div>
<p>再去修改回傳函數</p>
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 這裡我們不直接回傳，而是寫一個 "產生器 (Generator)"</span></span>
<span id="cb15-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 它的功能是：一邊收 Groq 的資料，一邊吐給前端</span></span>
<span id="cb15-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> get_ai_response_stream(user_message: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb15-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb15-5">        completion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> client.chat.completions.create(</span>
<span id="cb15-6">            model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"llama-3.3-70b-versatile"</span>,</span>
<span id="cb15-7">            messages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[</span>
<span id="cb15-8">                {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"system"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"你是一個繁體中文 AI 助教。"</span>},</span>
<span id="cb15-9">                {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: user_message}</span>
<span id="cb15-10">            ],</span>
<span id="cb15-11">            stream<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [關鍵] 告訴 Groq 我們要用串流模式</span></span>
<span id="cb15-12">        )</span>
<span id="cb15-13">        </span>
<span id="cb15-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> chunk <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> completion:</span>
<span id="cb15-15">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 檢查這個碎片有沒有內容</span></span>
<span id="cb15-16">            content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chunk.choices[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].delta.content</span>
<span id="cb15-17">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> content:</span>
<span id="cb15-18">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">yield</span> content  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># "yield" 意思是產出一個碎片就馬上送出去</span></span>
<span id="cb15-19">                </span>
<span id="cb15-20">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb15-21">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">yield</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Error: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(e)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb15-22"></span>
<span id="cb15-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@app.post</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/chat"</span>)</span>
<span id="cb15-24"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> chat_with_ai(data: UserInput):</span>
<span id="cb15-25">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"收到訊息 (串流模式)：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>message<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb15-26">    </span>
<span id="cb15-27">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 回傳一個 "串流回應"，而不是一般的 JSON</span></span>
<span id="cb15-28">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> StreamingResponse(</span>
<span id="cb15-29">        get_ai_response_stream(data.message), </span>
<span id="cb15-30">        media_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text/plain"</span></span>
<span id="cb15-31">    )</span></code></pre></div>
<p>再來改前端部分，主要是要修改 Javascripts 部分，讓回應可以產生逐字載入的動畫效果：</p>
<div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode html code-with-copy"><code class="sourceCode html"><span id="cb16-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb16-2">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sendMessage</span>() {</span>
<span id="cb16-3">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> inputField <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user-input"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-4">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> sendBtn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"send-btn"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-5">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chatBox <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-6">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> message <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> inputField<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trim</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-7"></span>
<span id="cb16-8">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>message) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-9"></span>
<span id="cb16-10">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 1. 顯示使用者訊息</span></span>
<span id="cb16-11">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(message<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-12">            inputField<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-13">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">disabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-14">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"思考中..."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-15"></span>
<span id="cb16-16">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 2. 準備接收 AI 的回答 (先建立一個空的對話框)</span></span>
<span id="cb16-17">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> aiMessageDiv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ai"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> </span>
<span id="cb16-18"></span>
<span id="cb16-19">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span> {</span>
<span id="cb16-20">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fetch</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"http://127.0.0.1:8000/chat"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> {</span>
<span id="cb16-21">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">method</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"POST"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb16-22">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">headers</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> { <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Content-Type"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"application/json"</span> }<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb16-23">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">JSON</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stringify</span>({ <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">message</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> message })</span>
<span id="cb16-24">                })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-25"></span>
<span id="cb16-26">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// [NEW] 處理串流資料的關鍵邏輯</span></span>
<span id="cb16-27">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> reader <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> response<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getReader</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-28">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> decoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">new</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">TextDecoder</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-29"></span>
<span id="cb16-30">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span>) {</span>
<span id="cb16-31">                    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> { done<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> value } <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> reader<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-32">                    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (done) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 如果讀完了就跳出迴圈</span></span>
<span id="cb16-33"></span>
<span id="cb16-34">                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 把讀到的二進位資料轉成文字</span></span>
<span id="cb16-35">                    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chunk <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> decoder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">decode</span>(value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> { <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">stream</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-36">                    </span>
<span id="cb16-37">                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 把文字 "追加" 到目前的對話框裡，而不是覆蓋</span></span>
<span id="cb16-38">                    aiMessageDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> chunk<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-39">                    </span>
<span id="cb16-40">                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 自動捲動到底部</span></span>
<span id="cb16-41">                    chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollTop</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollHeight</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-42">                }</span>
<span id="cb16-43"></span>
<span id="cb16-44">            } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">catch</span> (error) {</span>
<span id="cb16-45">                aiMessageDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"連線發生錯誤。"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-46">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">console</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">error</span>(error)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-47">            }</span>
<span id="cb16-48"></span>
<span id="cb16-49">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">disabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-50">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"發送"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-51">        }</span>
<span id="cb16-52"></span>
<span id="cb16-53">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 輔助功能：建立訊息框並回傳該元素 (讓我們可以持續更新它)</span></span>
<span id="cb16-54">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> sender) {</span>
<span id="cb16-55">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chatBox <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-56">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> div <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">createElement</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"div"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-57">            div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">add</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"message"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> sender)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-58">            div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-59">            chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendChild</span>(div)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-60">            chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollTop</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollHeight</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-61">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 回傳這個 div，讓外面的程式可以繼續塞字進去</span></span>
<span id="cb16-62">        }</span>
<span id="cb16-63"></span>
<span id="cb16-64">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">handleEnter</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span>) {</span>
<span id="cb16-65">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">key</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">===</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Enter"</span>) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sendMessage</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb16-66">        }</span>
<span id="cb16-67">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div>
<p><del>不意外的超級長，根本可以拆成獨立檔案了</del></p>
<p>測試有沒有成功跟前面一樣，只要確定伺服器有再跑，網頁重整後輸入 Prompt 觀察即可。</p>
</section>
<section id="上下文記憶" class="level2">
<h2 class="anchored" data-anchor-id="上下文記憶">上下文記憶</h2>
<p>LLM 本質上是 「無狀態 (Stateless)」 的，意思就是說：每一次呼叫 API，對它來說都是全新的開始，它完全不記得上一秒發生了什麼。所以我們需要把之前的對話紀錄，全部打包再一次寄給它，讓它看起來好像具備了記憶能力。</p>
<p>一樣先修改 <code>serve.py</code>，首先在套件部分加入</p>
<div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> typing <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> List, Dict <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 引入型別定義</span></span></code></pre></div>
<p>再去修改資料模型與回傳函數：</p>
<div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb18-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 修改資料模型：不再只是單一字串，而是接受一個列表 (List)</span></span>
<span id="cb18-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 格式會像這樣：[{"role": "user", "content": "hi"}, {"role": "assistant", "content": "hello"}]</span></span>
<span id="cb18-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> UserInput(BaseModel):</span>
<span id="cb18-4">    messages: List[Dict[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>]]</span>
<span id="cb18-5"></span>
<span id="cb18-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> get_ai_response_stream(messages_history: List[Dict[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>]]):</span>
<span id="cb18-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb18-8">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 這裡不再是寫死的 system prompt + user message</span></span>
<span id="cb18-9">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 而是直接把前端傳來的 "整包歷史紀錄" 丟給 Groq</span></span>
<span id="cb18-10">        </span>
<span id="cb18-11">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 我們可以在最前面偷偷加一個 System Prompt 設定 AI 人設</span></span>
<span id="cb18-12">        system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"system"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"你是一個繁體中文 AI 助教，記憶力很好。"</span>}]</span>
<span id="cb18-13">        full_context <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> messages_history</span>
<span id="cb18-14"></span>
<span id="cb18-15">        completion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> client.chat.completions.create(</span>
<span id="cb18-16">            model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"llama-3.3-70b-versatile"</span>,</span>
<span id="cb18-17">            messages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>full_context, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 這裡放入完整的對話歷史</span></span>
<span id="cb18-18">            stream<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb18-19">        )</span>
<span id="cb18-20">        </span>
<span id="cb18-21">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> chunk <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> completion:</span>
<span id="cb18-22">            content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chunk.choices[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].delta.content</span>
<span id="cb18-23">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> content:</span>
<span id="cb18-24">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">yield</span> content</span>
<span id="cb18-25"></span>
<span id="cb18-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb18-27">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">yield</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Error: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(e)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb18-28"></span>
<span id="cb18-29"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@app.post</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/chat"</span>)</span>
<span id="cb18-30"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> chat_with_ai(data: UserInput):</span>
<span id="cb18-31">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># print(f"收到歷史對話，長度：{len(data.messages)}") # 除錯用</span></span>
<span id="cb18-32">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> StreamingResponse(</span>
<span id="cb18-33">        get_ai_response_stream(data.messages), </span>
<span id="cb18-34">        media_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text/plain"</span></span>
<span id="cb18-35">    )</span></code></pre></div>
<p>可以觀察到<del>除了程式越來越長以外</del>，送給模型的不再只有 System Prompt，也有歷史對話訊息。</p>
<p>再來需要修改前端，讓它可以記錄之前的對話訊息，送入後端，一樣改<code>index.html</code>的 JavaScripts 部分：</p>
<div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode html code-with-copy"><code class="sourceCode html"><span id="cb19-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb19-2">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// [NEW] 用來暫存對話紀錄的變數</span></span>
<span id="cb19-3">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> conversationHistory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-4"></span>
<span id="cb19-5">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sendMessage</span>() {</span>
<span id="cb19-6">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> inputField <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user-input"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-7">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> sendBtn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"send-btn"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-8">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chatBox <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-9">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> message <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> inputField<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trim</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-10"></span>
<span id="cb19-11">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>message) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-12"></span>
<span id="cb19-13">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 1. 顯示並紀錄使用者的訊息</span></span>
<span id="cb19-14">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(message<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-15">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// [NEW] 把使用者的話加入歷史紀錄</span></span>
<span id="cb19-16">            conversationHistory<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span>({ <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"user"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> message })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-17"></span>
<span id="cb19-18">            inputField<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-19">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">disabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-20">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"思考中..."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-21"></span>
<span id="cb19-22">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> aiMessageDiv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ai"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> </span>
<span id="cb19-23">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> fullAiResponse <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 用來收集 AI 的完整回答</span></span>
<span id="cb19-24"></span>
<span id="cb19-25">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span> {</span>
<span id="cb19-26">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// [NEW] 傳送 "整包歷史紀錄" 給後端</span></span>
<span id="cb19-27">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fetch</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"http://127.0.0.1:8000/chat"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> {</span>
<span id="cb19-28">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">method</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"POST"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb19-29">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">headers</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> { <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Content-Type"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"application/json"</span> }<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb19-30">                    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">JSON</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stringify</span>({ <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">messages</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> conversationHistory }) </span>
<span id="cb19-31">                })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-32"></span>
<span id="cb19-33">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> reader <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> response<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getReader</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-34">                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> decoder <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">new</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">TextDecoder</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-35"></span>
<span id="cb19-36">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span>) {</span>
<span id="cb19-37">                    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> { done<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> value } <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">await</span> reader<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-38">                    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (done) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-39"></span>
<span id="cb19-40">                    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chunk <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> decoder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">decode</span>(value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> { <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">stream</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-41">                    aiMessageDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> chunk<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-42">                    fullAiResponse <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> chunk<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// 收集碎片</span></span>
<span id="cb19-43">                    chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollTop</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollHeight</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-44">                }</span>
<span id="cb19-45">                </span>
<span id="cb19-46">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// [NEW] AI 講完後，把它的話也加入歷史紀錄</span></span>
<span id="cb19-47">                conversationHistory<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span>({ <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"assistant"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> fullAiResponse })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-48"></span>
<span id="cb19-49">            } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">catch</span> (error) {</span>
<span id="cb19-50">                aiMessageDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"連線發生錯誤。"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-51">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">console</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">error</span>(error)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-52">            }</span>
<span id="cb19-53"></span>
<span id="cb19-54">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">disabled</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-55">            sendBtn<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"發送"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-56">        }</span>
<span id="cb19-57"></span>
<span id="cb19-58">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendMessage</span>(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> sender) {</span>
<span id="cb19-59">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> chatBox <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementById</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat-box"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-60">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> div <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">createElement</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"div"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-61">            div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">add</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"message"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> sender)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-62">            div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">innerText</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-63">            chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendChild</span>(div)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-64">            chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollTop</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chatBox<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scrollHeight</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-65">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-66">        }</span>
<span id="cb19-67"></span>
<span id="cb19-68">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">handleEnter</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span>) {</span>
<span id="cb19-69">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">key</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">===</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Enter"</span>) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sendMessage</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb19-70">        }</span>
<span id="cb19-71">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div>
<p>最後重整網頁，輸入需要上下文記憶的問題，看它能不能正確回應、順順的跟你聊下去。</p>
</section>
</section>
<section id="rag-系統" class="level1">
<h1>RAG 系統</h1>
<p>重點終於來啦，RAG 的概念因為我已經學過，就不贅述，直接進入程式部分。</p>
<section id="先建立簡單的-rag" class="level2">
<h2 class="anchored" data-anchor-id="先建立簡單的-rag">先建立簡單的 RAG</h2>
<p>安裝套件</p>
<div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb20-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install sentence-transformers scikit-learn</span></code></pre></div>
<p>然後建立一個簡單的資料來源<code>faq_data.py</code></p>
<div class="sourceCode" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 這只是一個簡單的模擬資料庫 (Dictionary)</span></span>
<span id="cb21-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 實務上這裡會是 PDF 檔案或資料庫</span></span>
<span id="cb21-3">knowledge_base <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [</span>
<span id="cb21-4">    {</span>
<span id="cb21-5">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"id"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb21-6">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"關於請假：本公司員工每年享有 14 天特休，請假需提前 3 天在 HR 系統申請。"</span></span>
<span id="cb21-7">    },</span>
<span id="cb21-8">    {</span>
<span id="cb21-9">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"id"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb21-10">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"關於加班費：平日加班費為 1.33 倍，假日加班費為 1.66 倍，需經理核准。"</span></span>
<span id="cb21-11">    },</span>
<span id="cb21-12">    {</span>
<span id="cb21-13">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"id"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,</span>
<span id="cb21-14">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"關於午餐：公司每日中午 12:00 提供免費便當，素食者需在 10:00 前登記。"</span></span>
<span id="cb21-15">    },</span>
<span id="cb21-16">    {</span>
<span id="cb21-17">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"id"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb21-18">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"關於在家工作 (WFH)：每週三為固定 WFH 日，其餘時間需進辦公室。"</span></span>
<span id="cb21-19">    }</span>
<span id="cb21-20">]</span></code></pre></div>
<p>建立一個新的 <code>rag_service.py</code></p>
<div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb22-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sentence_transformers <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> SentenceTransformer</span>
<span id="cb22-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sklearn.metrics.pairwise <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> cosine_similarity</span>
<span id="cb22-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> faq_data <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> knowledge_base</span>
<span id="cb22-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb22-5"></span>
<span id="cb22-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"正在載入 Embedding 模型 (第一次會比較久)..."</span>)</span>
<span id="cb22-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 下載一個輕量級的中文模型 (這會存在你的電腦裡)</span></span>
<span id="cb22-8">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> SentenceTransformer(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'paraphrase-multilingual-MiniLM-L12-v2'</span>)</span>
<span id="cb22-9"></span>
<span id="cb22-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. 先把知識庫裡的每一條資料，都轉換成「向量 (Vector)」並存起來</span></span>
<span id="cb22-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 這樣待會搜尋速度才會快</span></span>
<span id="cb22-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"正在建立索引..."</span>)</span>
<span id="cb22-13">knowledge_texts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [item[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> item <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> knowledge_base]</span>
<span id="cb22-14">knowledge_embeddings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.encode(knowledge_texts)</span>
<span id="cb22-15"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"索引建立完成！"</span>)</span>
<span id="cb22-16"></span>
<span id="cb22-17"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> search_knowledge_base(query, top_k<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb22-18">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb22-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    輸入使用者的問題 (query)，回傳最相似的 k 筆資料</span></span>
<span id="cb22-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb22-21">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. 把使用者的問題也變成向量</span></span>
<span id="cb22-22">    query_embedding <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.encode([query])</span>
<span id="cb22-23">    </span>
<span id="cb22-24">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. 計算相似度 (Cosine Similarity)</span></span>
<span id="cb22-25">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 比較「問題向量」跟所有「知識庫向量」的距離</span></span>
<span id="cb22-26">    similarities <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> cosine_similarity(query_embedding, knowledge_embeddings)</span>
<span id="cb22-27">    </span>
<span id="cb22-28">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. 找出分數最高的 top_k 個結果</span></span>
<span id="cb22-29">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># argsort 會回傳排序後的索引，我們取最後 top_k 個 (分數最高的)</span></span>
<span id="cb22-30">    top_indices <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> similarities[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].argsort()[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>top_k:][::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb22-31">    </span>
<span id="cb22-32">    results <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb22-33">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> top_indices:</span>
<span id="cb22-34">        score <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> similarities[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][idx]</span>
<span id="cb22-35">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> score <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.3</span>: <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定一個門檻，相關度太低就不採用 (避免誤導)</span></span>
<span id="cb22-36">            results.append(knowledge_base[idx][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>])</span>
<span id="cb22-37">            </span>
<span id="cb22-38">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> results</span>
<span id="cb22-39"></span>
<span id="cb22-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 測試用 (當你直接執行這個檔案時會跑這段)</span></span>
<span id="cb22-41"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__name__</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"__main__"</span>:</span>
<span id="cb22-42">    test_query <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"請問加班有錢拿嗎？"</span></span>
<span id="cb22-43">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">測試搜尋：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>test_query<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb22-44">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(search_knowledge_base(test_query))</span></code></pre></div>
<p>跑這支腳本，成功的話會出現資料庫裡，加班費的計算方式。</p>
</section>
<section id="整合進後端" class="level2">
<h2 class="anchored" data-anchor-id="整合進後端">整合進後端</h2>
<p>修改 <code>server.py</code> 的套件部分跟<code>get_ai_response_stream()</code>：</p>
<div class="sourceCode" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> rag_service <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> search_knowledge_base <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 引入搜尋功能</span></span></code></pre></div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-6-contents" aria-controls="callout-6" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
提示
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-6" class="callout-6-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>這裡抓的是同資料夾下，<code>rag_service.py</code> 的 <code>search_knowledge_base()</code> 函數，如果是放在專案資料夾下的另一個資料夾(假設叫 <code>another_folder</code> )則需這樣讀取</p>
<div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb24-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> another_folder.rag_service <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> search_knowledge_base <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [NEW] 引入搜尋功能</span></span></code></pre></div>
</div>
</div>
</div>
<div class="sourceCode" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ... (中間設定不變)</span></span>
<span id="cb25-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> get_ai_response_stream(messages_history: List[Dict[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>]]):</span>
<span id="cb25-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb25-4">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. 抓出使用者最新的一句話</span></span>
<span id="cb25-5">        latest_user_message <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> messages_history[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>]</span>
<span id="cb25-6">        </span>
<span id="cb25-7">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. [RAG 核心] 先去知識庫搜尋有沒有相關資料</span></span>
<span id="cb25-8">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"正在搜尋知識庫：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>latest_user_message<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb25-9">        retrieved_info <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> search_knowledge_base(latest_user_message)</span>
<span id="cb25-10">        </span>
<span id="cb25-11">        system_instruction <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"你是一個繁體中文 AI 助教。"</span></span>
<span id="cb25-12">        </span>
<span id="cb25-13">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. 如果有搜到資料，就把它塞進 System Prompt 裡</span></span>
<span id="cb25-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> retrieved_info:</span>
<span id="cb25-15">            <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"搜到資料：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>retrieved_info<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb25-16">            context_str <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>.join(retrieved_info)</span>
<span id="cb25-17">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 這裡就是 RAG 的精隨：把資料貼給 AI 看</span></span>
<span id="cb25-18">            system_instruction <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">【參考資料】：</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>context_str<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">請根據上述【參考資料】回答使用者的問題。如果資料裡沒有答案，請說不知道，不要瞎掰。"</span></span>
<span id="cb25-19">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb25-20">            <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"沒搜到相關資料"</span>)</span>
<span id="cb25-21"></span>
<span id="cb25-22">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 重新組裝 messages</span></span>
<span id="cb25-23">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 我們把原本的 messages_history 保留，但把第一句 system prompt 換掉</span></span>
<span id="cb25-24">        current_messages <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"system"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>: system_instruction}] </span>
<span id="cb25-25">        </span>
<span id="cb25-26">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 把使用者之前的對話紀錄接在後面 (除了第一句 system prompt)</span></span>
<span id="cb25-27">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 注意：這裡邏輯要小心，我們假設 messages_history 都是 user/assistant 的對話</span></span>
<span id="cb25-28">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> msg <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> messages_history:</span>
<span id="cb25-29">             <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> msg[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"role"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"system"</span>:</span>
<span id="cb25-30">                 current_messages.append(msg)</span>
<span id="cb25-31"></span>
<span id="cb25-32">        completion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> client.chat.completions.create(</span>
<span id="cb25-33">            model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"llama-3.3-70b-versatile"</span>,</span>
<span id="cb25-34">            messages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>current_messages,</span>
<span id="cb25-35">            stream<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb25-36">        )</span>
<span id="cb25-37">        </span>
<span id="cb25-38">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> chunk <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> completion:</span>
<span id="cb25-39">            content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chunk.choices[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].delta.content</span>
<span id="cb25-40">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> content:</span>
<span id="cb25-41">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">yield</span> content</span>
<span id="cb25-42"></span>
<span id="cb25-43">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb25-44">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">yield</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Error: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(e)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb25-45"></span>
<span id="cb25-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ... (後面不變)</span></span></code></pre></div>
<p>前端不用改，直接進入網頁測試</p>
<ol type="1">
<li><p>測試 1 (基本題): 問它：「你好。」(它應該正常回答)</p></li>
<li><p>測試 2 (RAG 題): 問它：「請問公司中午有便當吃嗎？」</p></li>
<li><p>測試 3 (RAG 題): 問它：「我想申請 WFH，星期幾可以？」</p></li>
</ol>
<p>若有成功按照資料庫回答問題表示成功。</p>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
警告
</div>
</div>
<div class="callout-body-container callout-body">
<p>啟動伺服器時，首次執行會進行 Embedding 步驟，耗時較長，需耐心等候，如果要加快速度，實務上會用 Chrome DB 儲存切好 chunk的資料。</p>
</div>
</div>
</section>
</section>
<section id="暫時性結論" class="level1">
<h1>暫時性結論</h1>
<p>其實它後面還介紹了上傳 PDF 、包成 Docker 之類的服務，<del>但我累惹先停在這</del>，不過還是沒有介紹到課綱裡的前端框架跟服務，感覺它完全自己走自己的==。</p>
<p>後面的東西有空再來繼續，希望可以實現包 Docker 成功初體驗~~</p>
</section>
<section id="有用的參考資料" class="level1">
<h1>有用的參考資料</h1>
<p>官方文件絕對是最好的一手資料來源：</p>
<ul>
<li><p><a href="https://console.groq.com/docs/overview">Groq</a></p></li>
<li><p><a href="https://fastapi.tiangolo.com/#create-it">FastAPI</a></p></li>
</ul>
<p>其次是其他工程師寫過的文章</p>
<ul>
<li><p><a href="https://ithelp.ithome.com.tw/m/articles/10320028">IT鐵人幫-FastAPI介紹</a></p></li>
<li><p><a href="https://realnewbie.com/posts/complete-guide-to-managing-environment-variables-with-python-dotenv">讀取 .env 的重要性+實作</a></p></li>
</ul>



</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>AI 多半會直接給，不過我還是列一下從搜尋結果進入官網的步驟。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>toys</category>
  <category>LLM</category>
  <category>RAG</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/llm-course-note1/llm-course-note1.html</guid>
  <pubDate>Sun, 25 Jan 2026 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/llm-course-note1/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>初探存活分析(3)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/survival-analysis3/</link>
  <description><![CDATA[ 






<section id="緣起" class="level1">
<h1>緣起</h1>
<p>本來這學期的課程加分作業有一個手刻 Cox model 係數，但是我卡在刻不出來QQ。<sup>1</sup>當然最後也沒交，但是又覺得這個題目有趣<del>加上都寫一半了，不要浪費任何可以交 kpi 的機會</del>，所以只好事後叫 AI 刻刻來觀察，順便看看能不能偷學幾招刻函數的技巧。</p>
<p><del>還好寫了其他的加分作業有過，存活大成功</del></p>
</section>
<section id="cox-model-概念" class="level1">
<h1>Cox model 概念</h1>
<p>先來看看 Cox model 的概念，它假設每個個體<img src="https://latex.codecogs.com/png.latex?i">的 hazard function如下：</p>
<p><img src="https://latex.codecogs.com/png.latex?h(t%7CZ_i)%20=%20h_0(t)%20e%5E%7B%5Cbeta%5E%5Ctop%20Z_i%7D"></p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?h(t%7CZ_i)">: 就是在時間點 <img src="https://latex.codecogs.com/png.latex?t"> 且事件尚未發生的時候，發生事件的機率，例如：如果這裡的事件發生是指死亡，那就是特定時間點<img src="https://latex.codecogs.com/png.latex?t">的死亡率。</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?h_0(t)">: 單一個體的 base-line hazar function，白話來說就是個體身上其他影響發生事件與否的風險因子。Cox model 的理念是剃除時間及其他變因，以便觀察主要風險因子的影響，所以這個<img src="https://latex.codecogs.com/png.latex?h_0(t)">在估計的過程中會被消除。</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?Z_i">: 是個向量，包含<img src="https://latex.codecogs.com/png.latex?p">個影響發生事件與否的主要風險因子，也是研究者收集回來的資料們。</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Cbeta">: 估計係數<img src="https://latex.codecogs.com/png.latex?(%5Cbeta_0,%5Cbeta_1,%5Cdots,p)%5ET">，是<img src="https://latex.codecogs.com/png.latex?p%5Ctimes1">的矩陣，影響主要風險因子的權重(weight)，是研究者最關心的東西，也是本文的目標。</p></li>
</ul>
<p>對第<img src="https://latex.codecogs.com/png.latex?i">個事件發生的個體<img src="https://latex.codecogs.com/png.latex?r_i">（即第<img src="https://latex.codecogs.com/png.latex?i">個死亡的個體），給定已經發生的過去事件，這名個體上事件發生的機率是：</p>
<p><img src="https://latex.codecogs.com/png.latex?p(r_i%20%7C%20%5Cmathcal%7BH%7D_i)%20=%20%5Cfrac%7Be%5E%7B%5Cbeta%5E%5Ctop%20Z_%7B(i)%7D%7D%7D%7B%5Csum_%7Bj%20%5Cin%20R(t_%7B(i)%7D)%7D%20e%5E%7B%5Cbeta%5E%5Ctop%20Z_j%7D%7D"></p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?R(t_%7B(i)%7D)"> 是在 <img src="https://latex.codecogs.com/png.latex?t_%7B(i)%7D"> 時仍在風險集的個體集合（未發生事件或 censor）。白話來說就是還在時間點<img src="https://latex.codecogs.com/png.latex?t_%7B(i)%7D">中，資料中的存活個數。</li>
</ul>
<p>這個東西才是我們真正要拿來估計<img src="https://latex.codecogs.com/png.latex?%5Cbeta">時會用到的式子。</p>
<section id="cox-model-係數數學上是如何得出的" class="level2">
<h2 class="anchored" data-anchor-id="cox-model-係數數學上是如何得出的">Cox model 係數數學上是如何得出的</h2>
<p>Cox model 是用 likelihood method 得出係數，先來導一下：</p>
<p><img src="https://latex.codecogs.com/png.latex?L(%5Cbeta%20)=%20%5Cprod%20%5Climits_%20%7Bi=1%7D%5En%20p(r_i%7C%5Cmathcal%7BH%7D%20_i)=%20%5Cprod_%7Bi=1%7D%5En%20%5Cleft(%20%5Cfrac%7Be%5E%7B%5Cbeta%5E%5Ctop%20Z_%7B(i)%7D%7D%7D%7B%5Csum_%7Bj%20%5Cin%20R(t_%7B(i)%7D)%7D%20e%5E%7B%5Cbeta%5E%5Ctop%20Z_j%7D%7D%20%5Cright)%5E%7B%5Cdelta_%7B(i)%7D%7D"></p>
<p>取 log</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Clog%20L(%5Cbeta)%20=%20l(%5Cbeta)=%20%5Csum_%7Bi=1%7D%5E%7Bn%7D%20%5Cleft%5B%0A%5Cbeta%5E%7BT%7D%20z_%7B(i)%7D%20-%20%5Clog%20%5Cleft%5C%7B%0A%5Csum_%7Bj%20%5Cin%20R(t_%7B(i)%7D)%7D%20e%5E%7B%5Cbeta%5E%7BT%7D%20z_j%7D%0A%5Cright%5C%7D%0A%5Cright%5D%20%5Cdelta_%7B(i)%7D%0A"></p>
<p>到這裡都是非常正規的 likelihood method 流程，接下來就是比較不一樣的地方了！</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AU(%5Cbeta)%0A=%20%5Cfrac%7B%5Cpartial%20l(%5Cbeta)%7D%7B%5Cpartial%20%5Cbeta%7D%0A=%20%5Csum_%7Bi=1%7D%5E%7Bn%7D%0A%5Cleft%5B%0Az_%7B(i)%7D%20-%0A%5Cfrac%7B%0A%5Csum_%7Bj%20%5Cin%20R(t_%7B(i)%7D)%7D%20e%5E%7B%5Cbeta%5E%7BT%7D%20z_j%20%7D%20z_j%0A%7D%7B%0A%5Csum_%7Bj%20%5Cin%20R(t_%7B(i)%7D)%7D%20e%5E%7B%5Cbeta%5E%7BT%7D%20z_j%7D%0A%7D%0A%5Cright%5D%0A%5Cdelta_%7B(i)%7D%0A"></p>
<p>這個<img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)">是甚麼東東呢？數學上是對 log 化後的 likelihood 偏微，是一種 score function，但它也可寫成：</p>
<p><img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)%20=%20%5Csum_%7Bi=1%7D%5En%20%5Cdelta_%7B(i)%7D%20%5Cleft%5B%20Z_%7B(i)%7D%20-%20%5Cbar%20Z_%7B(i)%7D%20%5Cright%5D"></p>
<p>看起來很像什麼東西？當第 i 個的事件發生時，<img src="https://latex.codecogs.com/png.latex?%5Cdelta_%7B(i)%7D">會標記為 1 ，這個式子便是在衡量，當實際發生事件者的共變數(covariates)與平均的差距。</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
共變數(covariates)的用語變化
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>共變數也叫做共變量，直觀就是餵進去模型的資料，不過常常會看到定義相同，但是術語不同的狀況，例如：在計量經濟學裡會稱呼為獨立變數(independent variable)；機器學習會叫做 inputs 。這是因為統計是各種領域交會的領域，所以有些用語不一定會統一，如果要再算上中文翻譯，可能就會更加多樣了…。</p>
<p>順便一提，這個用語最早是由統計學的奠基者—費雪(R.A Fisher)於 1940 年代發明。</p>
</div>
</div>
</div>
<p>要求得<img src="https://latex.codecogs.com/png.latex?%5Cbeta">，就要求 <img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)"> 的 MLE，也就是利用一皆微分</p>
<p><img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)%20%5Coverset%7B%5Ctext%7Bset%7D%7D%7B=%7D%200"></p>
<p>並且估出 Fisher information matrix，為了簡化式子，定義以下：</p>
<p>令 <img src="https://latex.codecogs.com/png.latex?Z_j"> 為 p 維的共變數。</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?%5CGamma_0(%5Cbeta)%20=%20%5Csum_%7Bj%20%5Cin%20R(t_i)%7D%20e%5E%7B%5Cbeta%5E%5Ctop%20Z_j%7D,"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5CGamma_1(%5Cbeta)%20=%20%5Csum_%7Bj%20%5Cin%20R(t_i)%7D%20e%5E%7B%5Cbeta%5E%5Ctop%20Z_j%7D%20Z_j,"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5CGamma_2(%5Cbeta)%20=%20%5Csum_%7Bj%20%5Cin%20R(t_i)%7D%20e%5E%7B%5Cbeta%5E%5Ctop%20Z_j%7D%20Z_j%5E%7B%5Cotimes%202%7D,"></p>
<ul>
<li>其中<img src="https://latex.codecogs.com/png.latex?Z_j%5E%7B%5Cotimes%202%7D%20=%20Z_j%20Z_j%5E%5Ctop"></li>
</ul></li>
</ul>
<p>代入 <img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)">：</p>
<p><img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)%20=%20%5Csum_%7Bi=1%7D%5En%20%5Cleft%5Bz_%7B(i)%7D-%5Cfrac%7B%5CGamma_1(%5Cbeta)%7D%7B%5CGamma_0(%5Cbeta)%7D%5Cright%5D%20%5Cdelta_%7B(i)%7D"></p>
<p>因此 Fisher information matrix 為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathcal%7BI%7D(%5Cbeta)%0A=%20-%20%5Cfrac%7B%5Cpartial%5E2%20%5Cell(%5Cbeta)%7D%7B%5Cpartial%20%5Cbeta%5E2%7D%0A=%20%5Csum_%7Bi=1%7D%5En%0A%5Cleft%5B%0A%5Cfrac%7B%5CGamma_2(%5Cbeta)%7D%7B%5CGamma_0(%5Cbeta)%7D%0A-%20%5Cfrac%7B%5CGamma_1(%5Cbeta)%5CGamma_1(%5Cbeta)%5E%5Ctop%7D%7B%5CGamma_0(%5Cbeta)%5E2%7D%0A%5Cright%5D%0A%5Cdelta(i)"></p>
<p>不過很遺憾，努力導出來的這些東西是沒有辦法直接算出來的QQ，因此，我們需要牛頓法來迭代，求出近似值。</p>
</section>
</section>
<section id="牛頓法的運用" class="level1">
<h1>牛頓法的運用</h1>
<p>回顧微積分牛頓法的概念，設定初始值<img src="https://latex.codecogs.com/png.latex?x_0">，在可微函數<img src="https://latex.codecogs.com/png.latex?f(x)">上求以下：</p>
<p><img src="https://latex.codecogs.com/png.latex?0=(x_%7Bn+1%7D-x_n)f'(x_n)+f(x_n)%20~~~~~~~%20n=0,1,%5Cdots"></p>
<p>也就是</p>
<p><img src="https://latex.codecogs.com/png.latex?x_%7Bn+1%7D=x_n-%5Cfrac%7Bf(x_n)%7D%7Bf'(x_n)%7D"></p>
<p>以上是微的變數只有一個的版本，要微很多個變數是用泰勒展開式表示。</p>
<p>牛頓法也相當於泰特展開式的只取到一階微分的版本，運用在這裡的話就是把<img src="https://latex.codecogs.com/png.latex?x_0">換成<img src="https://latex.codecogs.com/png.latex?%5Cbeta">，理想的正確答案為<img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cbeta%7D">：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cbegin%7Baligned%7DU(%5Chat%7B%5Cbeta%7D)-U(%5Cbeta)&amp;%20%5Capprox%20%5Cfrac%7B%5Cpartial%20U(%5Cbeta)%7D%7B%5Cpartial%20%5Cbeta%7D(%5Chat%7B%5Cbeta%7D-%5Cbeta)%5C%5C%0A&amp;%20=%20-%5Cmathcal%7BI%7D(%5Cbeta)(%5Chat%7B%5Cbeta%7D-%5Cbeta)%0A%5Cend%7Baligned%7D"></p>
<p>因為<img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cbeta%7D">是理想的正確答案(a.k.a MLE)，所以<img src="https://latex.codecogs.com/png.latex?U(%5Chat%7B%5Cbeta%7D)=0">，然後再經過一點小小的線性代數運算，求得<img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cbeta%7D">：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cbeta%7D%5Capprox%20%5Cbeta%20+U(%5Cbeta)%5Cmathcal%7BI%7D%5E%7B-1%7D(%5Cbeta)"></p>
<p>這就是牛頓法的運用。</p>
</section>
<section id="實作" class="level1">
<h1>實作</h1>
<section id="模擬係數" class="level2">
<h2 class="anchored" data-anchor-id="模擬係數">模擬係數</h2>
<p>先模擬一份假資料：</p>
<ul>
<li><p>存活分配來自 <img src="https://latex.codecogs.com/png.latex?%5Cexp(%5Clambda=0.1)"></p></li>
<li><p>含有來自 Bernoulli <img src="https://latex.codecogs.com/png.latex?Ber(0.5)">的 <img src="https://latex.codecogs.com/png.latex?X_1">(類別變數) 及來自常態分配<img src="https://latex.codecogs.com/png.latex?N(0,1)">的 <img src="https://latex.codecogs.com/png.latex?X_2">(連續變數)</p></li>
<li><p>樣本為 1000，censor的機率來自<img src="https://latex.codecogs.com/png.latex?U(10,20)"></p></li>
<li><p>分別給予 <img src="https://latex.codecogs.com/png.latex?X_1"> 跟 <img src="https://latex.codecogs.com/png.latex?X_2">初始的<img src="https://latex.codecogs.com/png.latex?%5Cbeta_1=0.7">與<img src="https://latex.codecogs.com/png.latex?%5Cbeta_1=0.5"></p></li>
</ul>
<div class="cell">
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20260116</span>)</span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 模擬參數</span></span>
<span id="cb1-4">n <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span></span>
<span id="cb1-5">lambda <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 基礎事件率</span></span>
<span id="cb1-6">beta1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span>        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># X1 的 log-hazard 影響</span></span>
<span id="cb1-7">beta2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># X2 的 log-hazard 影響</span></span>
<span id="cb1-8"></span>
<span id="cb1-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 模擬自變量 X1、X2</span></span>
<span id="cb1-10">X1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rbinom</span>(n, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>)   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 50%機率</span></span>
<span id="cb1-11">X2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rnorm</span>(n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mean =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 標準常態分布</span></span>
<span id="cb1-12"></span>
<span id="cb1-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 存活時間，考慮 X1 與 X2 的影響</span></span>
<span id="cb1-14">true_surv_time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rexp</span>(n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rate =</span> lambda <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exp</span>(beta1<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>X1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> beta2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>X2))</span>
<span id="cb1-15"></span>
<span id="cb1-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># censor 時間</span></span>
<span id="cb1-17">censor_time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runif</span>(n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">min =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">max =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>)</span>
<span id="cb1-18"></span>
<span id="cb1-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 觀察時間與事件狀態</span></span>
<span id="cb1-20">obs_time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pmin</span>(true_surv_time, censor_time)</span>
<span id="cb1-21">status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ifelse</span>(true_surv_time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> censor_time, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-22"></span>
<span id="cb1-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 整理成 data frame</span></span>
<span id="cb1-24">data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb1-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">id =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>n,</span>
<span id="cb1-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">X1 =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(X1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb1-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">X2 =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(X2, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb1-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(obs_time, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb1-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">status =</span> status,</span>
<span id="cb1-30">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">true_surv =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(true_surv_time, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb1-31">)</span>
<span id="cb1-32"></span>
<span id="cb1-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 查看資料結構</span></span>
<span id="cb1-34"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str</span>(data)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>'data.frame':   1000 obs. of  6 variables:
 $ id       : int  1 2 3 4 5 6 7 8 9 10 ...
 $ X1       : num  1 0 1 1 1 1 1 1 0 1 ...
 $ X2       : num  -0.09 -0.5 1.03 0.25 -0.31 -0.53 -0.73 1.18 0.91 0.67 ...
 $ time     : num  0.01 6.49 1.05 1.8 0.83 0.39 8.74 2.33 0.42 3.62 ...
 $ status   : num  1 1 1 1 1 1 1 1 1 1 ...
 $ true_surv: num  0.01 6.49 1.05 1.8 0.83 0.39 8.74 2.33 0.42 3.62 ...</code></pre>
</div>
</div>
<p>基本資料</p>
<div class="cell">
<div class="cell-output cell-output-stdout">
<pre><code>[1] "資料收集部分："</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "censor 數: 183"</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "event 數 : 817"</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "----------------"</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "  total  : 1000"</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "變數部分："</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "X1=0 數: 533"</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "X1=1 數: 467"</code></pre>
</div>
</div>
<p><code>X2</code>是連續變數，且已知放入的平均值跟標準差，故省略。</p>
<p>我們可以用 R 裡面的 package <code>survival</code>估計正確答案。</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb11-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(survival)</span>
<span id="cb11-2">cox.ph <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coxph</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Surv</span>(time,status) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.factor</span>(X1)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>X2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data=</span>data)</span>
<span id="cb11-3">s2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summary</span>(cox.ph)</span>
<span id="cb11-4"></span>
<span id="cb11-5">s2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>coefficients</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>                    coef exp(coef)   se(coef)        z     Pr(&gt;|z|)
as.factor(X1)1 0.9017075  2.463806 0.07247418 12.44178 1.550194e-35
X2             0.4892881  1.631155 0.03712873 13.17815 1.172262e-39</code></pre>
</div>
</div>
<p>裡面的0.9017075跟0.4892881分別為<code>X1</code>與<code>X2</code>正確係數答案。</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
為什麼手動指定了係數但 <code>survival</code> 估計出來的東西還是不一樣
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>這是因為存活資料多了 censor 的狀態，資料不齊全的情況注定無法給出完全正確的答案，在樣本不夠多但 censor 的比例高的情況下就會影響真正估計的係數；反之樣本夠多或censor 的比例夠低，估計出來的係數就會非常接近正確答案。</p>
<p>為了加快網頁的渲染，本例設1000，有興趣的人可以把程式碼複製起來把樣本數調大，就會很接近設定的</p>
</div>
</div>
</div>
</section>
<section id="ai-給的手刻程式" class="level2">
<h2 class="anchored" data-anchor-id="ai-給的手刻程式">AI 給的手刻程式</h2>
<p>AI 總共給了 2 個手刻程式解，第一個是比較長，但是比較直觀的版本：</p>
</section>
<section id="牛頓法版本" class="level2">
<h2 class="anchored" data-anchor-id="牛頓法版本">牛頓法版本</h2>
<p>先將資料按時間先後排序以及轉矩陣跟設變數等，以便進行後面的刻算法。</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1">data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> data[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">order</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time), ]</span>
<span id="cb13-2">X <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(data[, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X1"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X2"</span>)]) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 大矩陣，裡面的其中一行就是 Z_j</span></span>
<span id="cb13-3">time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time</span>
<span id="cb13-4">status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status</span>
<span id="cb13-5">n <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(X)</span>
<span id="cb13-6">p <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(X)</span></code></pre></div>
</div>
<p><img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)">的函數版</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1">score_cox <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(beta, X, time, status) {</span>
<span id="cb14-2">  eta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>(X <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%*%</span> beta) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 注意 beta在這裡是個向量，所以(beta^TZ)^T=Xbeta</span></span>
<span id="cb14-3">  U <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(beta))　<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 初始係數矩陣(U(beta))</span></span>
<span id="cb14-4">  </span>
<span id="cb14-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sum from i=1 to n  且 delta =1 式子才能成立</span></span>
<span id="cb14-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which</span>(status <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) {</span>
<span id="cb14-7">    risk <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which</span>(time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> time[i]) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># risk set 數量</span></span>
<span id="cb14-8">    w <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exp</span>(eta[risk]) </span>
<span id="cb14-9">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 算Z bar</span></span>
<span id="cb14-10">    X_bar <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">colSums</span>(X[risk, , <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">drop =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> w) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(w)</span>
<span id="cb14-11">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># U 是 sum 起來的所以要加上</span></span>
<span id="cb14-12">    U <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> U <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> X[i, ] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> X_bar</span>
<span id="cb14-13">  }</span>
<span id="cb14-14">  </span>
<span id="cb14-15">  U</span>
<span id="cb14-16">}</span></code></pre></div>
</div>
<p>就是直白的把<img src="https://latex.codecogs.com/png.latex?U(%5Cbeta)=%5Csum_%7Bi=1%7D%5En%20%5Cdelta_%7B(i)%7D%20%5Cleft%5B%20Z_%7B(i)%7D%20-%20%5Cbar%20Z_%7B(i)%7D%20%5Cright%5D">做出來。</p>
<p>再來是<img src="https://latex.codecogs.com/png.latex?I(%5Cbeta)">的函數版</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb15-1">info_cox <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(beta, X, time, status) {</span>
<span id="cb15-2">  eta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>(X <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%*%</span> beta)</span>
<span id="cb15-3">  I <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">matrix</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(X), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(X))</span>
<span id="cb15-4">  </span>
<span id="cb15-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sum form i = 1 to n 且 delta =1 式子才能成立</span></span>
<span id="cb15-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which</span>(status <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) {</span>
<span id="cb15-7">    risk <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which</span>(time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> time[i])</span>
<span id="cb15-8">    w <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exp</span>(eta[risk])</span>
<span id="cb15-9">    </span>
<span id="cb15-10">    X_bar <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">colSums</span>(X[risk, , <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">drop =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> w) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(w)</span>
<span id="cb15-11">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 相當於算 bar Z _{(i)}</span></span>
<span id="cb15-12">    X_centered <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sweep</span>(X[risk, , <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">drop =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, X_bar)</span>
<span id="cb15-13">    I <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> I <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(X_centered) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%*%</span> (X_centered <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> w) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(w)</span>
<span id="cb15-14">  }</span>
<span id="cb15-15">  </span>
<span id="cb15-16">  I</span>
<span id="cb15-17">}</span></code></pre></div>
</div>
<div class="callout callout-style-simple callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
語法字典
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li><p><code>colSums()</code>：對矩陣中每一欄進行加總</p></li>
<li><p><code>sweep()</code>：用一個向量掃過矩陣的某個方向，做逐元素運算，塞入的input 為：<code>sweep(x, MARGIN, STATS, FUN = "-")</code></p></li>
</ul>
<table class="caption-top table">
<thead>
<tr class="header">
<th>參數</th>
<th>意思</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>x</code></td>
<td>矩陣</td>
</tr>
<tr class="even">
<td><code>MARGIN</code></td>
<td>1 = row，2 = column</td>
</tr>
<tr class="odd">
<td><code>STATS</code></td>
<td>要套用的向量</td>
</tr>
<tr class="even">
<td><code>FUN</code></td>
<td>運算（預設是減法）</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>再來刻牛頓法：</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb16-1">newton_cox <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(X, time, status,</span>
<span id="cb16-2">                       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">beta_init =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>),</span>
<span id="cb16-3">                       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tol =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-6</span>,</span>
<span id="cb16-4">                       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">maxit =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>) {</span>
<span id="cb16-5">  </span>
<span id="cb16-6">  beta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> beta_init</span>
<span id="cb16-7">  </span>
<span id="cb16-8">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (iter <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>maxit) {</span>
<span id="cb16-9">    U <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">score_cox</span>(beta, X, time, status)</span>
<span id="cb16-10">    I <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">info_cox</span>(beta, X, time, status)</span>
<span id="cb16-11">    </span>
<span id="cb16-12">    step <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">solve</span>(I, U) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#求UI^{-1}</span></span>
<span id="cb16-13">    beta_new <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> beta <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> step</span>
<span id="cb16-14">    </span>
<span id="cb16-15">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 如果係數幾乎沒有改變就停止迭代</span></span>
<span id="cb16-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(beta_new <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> beta)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> tol) {</span>
<span id="cb16-17">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Converged at iteration"</span>, iter, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb16-18">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">beta =</span> beta_new, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">I =</span> I))</span>
<span id="cb16-19">    }</span>
<span id="cb16-20">    </span>
<span id="cb16-21">    beta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> beta_new</span>
<span id="cb16-22">  }</span>
<span id="cb16-23">  </span>
<span id="cb16-24">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Did not converge"</span>)</span>
<span id="cb16-25">}</span></code></pre></div>
</div>
<p>刻完了！來算算看</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb17-1">fit_newton <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">newton_cox</span>(X, time, status)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>Converged at iteration 4 </code></pre>
</div>
<div class="sourceCode cell-code" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb19-1">fit_newton<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>beta</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>       X1        X2 
0.9009551 0.4887990 </code></pre>
</div>
</div>
<p>對照正確答案</p>
<div class="cell">
<div class="cell-output cell-output-stdout">
<pre><code>                    coef exp(coef)   se(coef)        z     Pr(&gt;|z|)
as.factor(X1)1 0.9017075  2.463806 0.07247418 12.44178 1.550194e-35
X2             0.4892881  1.631155 0.03712873 13.17815 1.172262e-39</code></pre>
</div>
</div>
<p>非常接近，不錯不錯。</p>
</section>
<section id="ai-給的第二種答案" class="level2">
<h2 class="anchored" data-anchor-id="ai-給的第二種答案">AI 給的第二種答案</h2>
<p>這個答案的寫法比較精簡</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb22-1">cox_ploglik <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(beta, time, status, X) {</span>
<span id="cb22-2">  eta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>(X <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%*%</span> beta)</span>
<span id="cb22-3">  loglik <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb22-4">  </span>
<span id="cb22-5">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which</span>(status <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) {</span>
<span id="cb22-6">    risk_set <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which</span>(time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> time[i])</span>
<span id="cb22-7">    loglik <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> loglik <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb22-8">      eta[i] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span></span>
<span id="cb22-9">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">log</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exp</span>(eta[risk_set])))</span>
<span id="cb22-10">  }</span>
<span id="cb22-11">  </span>
<span id="cb22-12">  loglik</span>
<span id="cb22-13">}</span></code></pre></div>
</div>
<p>就這樣，因為它使用時用了一個現成的<code>optim()</code>來求解，就不用手刻牛頓法。</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb23-1">X <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(data[, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X1"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X2"</span>)])</span>
<span id="cb23-2"></span>
<span id="cb23-3">neg_pl <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(beta) {</span>
<span id="cb23-4">  <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cox_ploglik</span>(beta, data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time, data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status, X)</span>
<span id="cb23-5">}</span>
<span id="cb23-6"></span>
<span id="cb23-7">fit <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">optim</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">par =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>),</span>
<span id="cb23-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fn =</span> neg_pl,</span>
<span id="cb23-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">method =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"BFGS"</span>,</span>
<span id="cb23-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hessian =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb23-11"></span>
<span id="cb23-12">fit<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>par</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] 0.9009556 0.4887988</code></pre>
</div>
</div>
<p>也是相當接近。</p>
</section>
</section>
<section id="結論" class="level1">
<h1>結論</h1>
<p>刻函數還是好難TT，不過 AI 裡面用到的語法跟順序結構滿值得一學的，只是最難的應該還是初始矩陣跟變數要怎麼設、跟數學式的差異，還有迴圈的應用時機吧。</p>
</section>
<section id="參考資料" class="level1">
<h1>參考資料</h1>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0" data-line-spacing="2">
<div id="ref-Lin2024Lecture" class="csl-entry">
余日彰. (2025). <em>存活分析</em>. 未公開課堂講義.
</div>
<div id="ref-lin2008survival" class="csl-entry">
林建甫. (2008). <em>存活分析</em> (初版). 雙葉書局.
</div>
</div>



</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>老師甚至在課堂上表演了一次，結果回家還是刻不出來，我要哭了…。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>Survial Analysis</category>
  <category>R</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/survival-analysis3/</guid>
  <pubDate>Thu, 15 Jan 2026 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/survival-analysis3/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>「乙未戰爭浮世繪的多重面向」觀後心得</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/hist/lecture-2025-11-01/lecture-2025-11-01.html</link>
  <description><![CDATA[ 






<blockquote class="blockquote">
<p>這是去年 ( 2025/11/1 ) 的演講，但是因為大概還記得內容，所以用心得的方式呈現。</p>
</blockquote>
<section id="演講資訊" class="level1">
<h1>演講資訊</h1>
<p>取自 <a href="https://www.facebook.com/hchcc.gov/posts/%E4%B9%99%E6%9C%AA%E4%B8%AD%E7%9A%84%E7%AB%B9%E5%A1%B9%E7%B3%BB%E5%88%97%E8%AC%9B%E5%BA%A7%E5%9B%9E%E4%BE%86%E5%95%A6-%E5%9C%8B%E7%AB%8B%E6%B8%85%E8%8F%AF%E5%A4%A7%E5%AD%B8%E6%96%87%E7%89%A9%E9%A4%A8-x-%E6%96%B0%E7%AB%B9%E7%B8%A3%E7%B8%A3%E5%8F%B2%E9%A4%A8-%E4%BB%8A%E5%B9%B4%E7%A7%8B%E5%A4%A9%E6%B8%85%E5%A4%A7%E6%96%87%E7%89%A9%E9%A4%A8%E5%86%8D%E5%BA%A6%E8%88%87-%E6%96%B0%E7%AB%B9%E7%B8%A3%E6%94%BF%E5%BA%9C%E6%96%87%E5%8C%96%E5%B1%80-%E5%90%88%E4%BD%9C%E5%B8%B6%E4%BE%86%E5%85%A9%E5%A0%B4%E6%A9%AB%E8%B7%A8%E6%AD%B7%E5%8F%B2%E8%88%87%E5%9C%A8%E5%9C%B0%E8%A8%98%E6%86%B6%E7%9A%84%E6%B7%B1%E5%BA%A6%E8%AC%9B%E5%BA%A7%E9%82%80%E8%AB%8B%E4%BD%A0%E4%B8%80%E5%90%8C/1220344430128620/">清大文物館 FB</a></p>
<p>2025/11/1（六）14:00–16:00</p>
<ul>
<li><p>地點：清大總圖書館（旺宏館）1樓 清沙龍</p></li>
<li><p>講題：歷史・藝術・媒體——甲午乙未戰爭浮世繪的多重面向</p></li>
<li><p>講者：馬孟晶 副教授（國立清華大學通識中心暨歷史研究所）</p></li>
</ul>
<p>藝術史這塊我一直是門外漢等級，但看到馬老師的名字的時候卻有股莫名的熟悉感，查了清大歷研所才發現馬老師的名字有出現在上面，看起來是專攻東方藝術史，非常少見的領域，難怪我會有印象 XD</p>
<p>這個演講雖然隸屬「乙未中的竹塹」系列講座，但其實沒怎麼提到竹塹，更多的是從圖像史的視角切入，說明浮世繪的創作部分與有紀錄到史實的地方。<sup>1</sup></p>
</section>
<section id="演講摘要與心得" class="level1">
<h1>演講摘要與心得</h1>
<p>創作與記錄史實的區分一直是歷史學極度重視的課題，但當研究對象是藝術或是圖像作品時，這會變成棘手的難題。乙末戰爭圖像在日本是由當代流行的浮世繪所繪製而成，傳統史學都大多認定其帶有官方的宣傳意義，除此之外沒有更多延伸。不過馬老師指出，如果從圖像史的角度切入，可以觀察到浮世繪具有藝術與媒體的雙重性格，同時也在日本歷史發展甚至當代西方藝術中留下了不少痕跡。</p>
<p>在西方藝術方面，老師以梵谷的 1887 年的作品〈唐吉老爺〉為例，說明背景裡的仿畫的一幅浮世繪〈花魁〉，原畫最早於 1820 年完成，經過 1886 年被巴黎雜誌刊登後，才被梵谷所知。</p>
<p>再來是浮世繪技術與媒材的演變，這部分是最 hardcore 的地方，非常多專業術語，粗鄙如我只能記錄以下：</p>
<ul>
<li><p>17 世紀，刻板畫(單色)</p></li>
<li><p>18 世紀，出現多版彩色套印</p></li>
</ul>
<blockquote class="blockquote">
<p>這個時期的藝術家主要在木板上刻印 ( 應該是刻板畫的概念 )，好處是可以進行大量印刷，因為浮世繪實際上是很庶民的文化，小件的作品可以出現在書籍封面，甚至後來還有明信片的玩法！</p>
</blockquote>
<ul>
<li>19 世紀前半，西方的透視技法傳入</li>
</ul>
<blockquote class="blockquote">
<p>這個時期的某些藝術家透過自學的方式學習西方的透視技法，並且將其運用在作品裡。<sup>2</sup></p>
</blockquote>
<ul>
<li>19 世紀後半，石印版畫傳入</li>
</ul>
<blockquote class="blockquote">
<p>石印版畫的好處有點忘記，印象中是可以改善木板保存不易的問題，壞處則是成本較高。</p>
</blockquote>
<p>繪畫方式方面，在西方影響之前，浮世繪主要有影響力的是歌川派，他們不會親臨現場創作。在明治時期，受到西方影響，藝術家開始會以寫生( 親臨現場 )的方式創作，有些人會在創作中留下這方面的線索，這類型作品也可為後世歷史研究增添幾分可信度。<sup>3</sup>不過，當時還是有些作品是用想像的方式畫出。</p>
<p>浮世繪在早期有身兼傳遞訊息的媒體性質，但在西方技術傳入後，多了畫報這種媒介，畫報主要是由寫真或插畫所組成，是浮世繪的競爭者，也為浮世繪的衰落埋下伏筆。<sup>4</sup></p>
<p>乙末戰爭發生時期恰好是浮世繪發展的最後巔峰，集結了浮世繪的發展集大成：創作風格多元、尺寸多元與材質多元。老師也介紹了多位藝術家的創作。我比較有印象的是小林清親的作品，他受到西方技法的影響，因此他的作品會出現光影、寫真意識等手法，也會用銅板畫。<sup>5</sup>同時他也是位很有想法的藝術家，會畫漫畫來諷刺時事或是官員。<sup>6</sup></p>
<p>浮世繪最終在 19 世紀末，也就是日俄戰爭時期迎來尾聲，後來逐漸被成本更低的小型寫真取代。</p>
<p>可能因為這是面向一般民眾的講座，提問環節的時候有人問了我覺得對研究藝術史的人來說會有點雷的問題 XD ，提問的人有點誤把圖像的呈現都當成了歷史。不過也多虧他，我才回想起修文藝復興史時，老師的種種叮嚀，對我來說不是壞事，但就辛苦馬老師了。</p>
</section>
<section id="延伸閱讀" class="level1">
<h1>延伸閱讀</h1>
<ul>
<li><a href="https://kamatiam.org/%E6%9C%89%E5%9C%96%E6%9C%89%E7%9C%9F%E7%9B%B8%EF%BC%8D%E6%B5%AE%E4%B8%96%E7%B9%AA%E8%A3%A1%E7%9A%84%E7%94%B2%E5%8D%88%E6%88%B0%E7%88%AD%E6%96%B0%E8%81%9E/">有圖有真相?－浮世繪裡的甲午戰爭新聞</a></li>
</ul>
<blockquote class="blockquote">
<p>老師寫的文章</p>
</blockquote>
<ul>
<li><p><a href="https://arthistorystroll.wpcomstaging.com/2022/06/24/%E3%80%90%E7%89%88%E5%8D%B0%E6%B5%AE%E7%94%9F%E3%80%91%E6%B5%AE%E4%B8%96%E7%B9%AA%E5%A6%82%E4%BD%95%E8%A3%BD%E4%BD%9C%EF%BC%9F%E4%B8%80%E7%AA%BA%E7%B9%81%E8%A4%87%E7%B2%BE%E6%B9%9B%E7%9A%84%E7%89%88/">【版印浮生】浮世繪如何製作？一窺繁複精湛的版印工藝</a></p></li>
<li><p><a href="https://kentaarts.com/vangogh-and-ukiyoe/">梵谷與浮世繪的邂逅——從臨摹到星空</a></p></li>
</ul>



</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>就我個人粗淺的理解，創作與記錄歷史的區隔一直是歷史學極度重視的課題，但經常無法適切的區分，所以我這裡的寫法稍嫌簡化了些。↩︎</p></li>
<li id="fn2"><p>說某些是因為真的忘記確切人名，老師對不起QQ↩︎</p></li>
<li id="fn3"><p>反之，沒有用親臨現場方式創作的作品都需要保持存疑。↩︎</p></li>
<li id="fn4"><p>很久很久以前的日本書籍《畫報近代百年史》以繪畫、照片等視覺資料回顧1850至1863年，西洋文明到日本的情勢，變相解說畫報的角色，<a href="https://collections.nmth.gov.tw/CollectionContent.aspx?a=132&amp;rno=2017.024.0013.0001">連結在此</a>。↩︎</p></li>
<li id="fn5"><p>銅板畫印象中也是某個時期傳入或被發明，但我的筆記沒寫到。↩︎</p></li>
<li id="fn6"><p>跟今日不同，當時的漫畫定位主要是來諷刺之用，五官會畫得非常扭曲及誇張，同時大多沒有連環畫而是單一畫作。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>講座心得</category>
  <category>藝術史</category>
  <category>圖像史</category>
  <category>日本史</category>
  <guid>https://paperfishblog.netlify.app/posts/hist/lecture-2025-11-01/lecture-2025-11-01.html</guid>
  <pubDate>Tue, 06 Jan 2026 16:00:00 GMT</pubDate>
</item>
<item>
  <title>解決無法 update R package 問題: xfun</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/rmd-problem/rmd-problem.html</link>
  <description><![CDATA[ 






<p>寫作業寫到一半突然遇到問題，真得嚇死，趕快紀錄一下，<del>順便遲交每月文章kpi</del>。</p>
<section id="問題敘述" class="level1">
<h1>問題敘述</h1>
<p>在寫 rmarkdown 文件時突然遇到 YMAL 部分(—處)跑出紅線:</p>
<pre><code>Failed to run diagnostics: ! in callr subprocess.
Caused by error in `loadNamespace(j &lt;- i[[1L]], c(lib.loc, .libPaths()), versionCheck = vI[[j]])`:
! namespace 'xfun' 0.47 is being loaded, but &gt;= 0.52 is requiredlintr</code></pre>
<p>查了一下解法很簡單，只要用 R 安裝套件</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xfun"</span>)</span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">packageVersion</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xfun"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#確認版本</span></span></code></pre></div>
<p>即可，但我偏偏還碰到了結尾是 <code>Permission denied</code> 的錯誤，必須要關掉 Vscode 重開。此外還試了另一個(AI推薦的方法):</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"R_LIBS_USER"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 查看你的使用者套件路徑</span></span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xfun"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lib =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"R_LIBS_USER"</span>)) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 塞使用者套件路徑</span></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xfun"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lib =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"...../R/win-library/4.4"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 或是 .libPaths() 裡的第一個路徑直接貼上</span></span></code></pre></div>
<p>但無效，現在想想可能是因為沒有徹底重開？總之，第三次重開後，再輸入</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xfun"</span>)</span></code></pre></div>
<p>這時跳出來的 error 居然不同了，大概像這樣：</p>
<pre><code>Error in unpackPkgZip(foundpkgs[okp, 2L], foundpkgs[okp, 1L], lib, libs_only,  :
  ERROR: failed to lock directory 'R\win-library\4.4' for modifying
Try removing '\R\win-library\4.4/00LOCK'</code></pre>
<p>真的是太神奇，如果不是 AI 推薦方法成功一半就是重開大法有成吧？總之這個訊息的用意是要找出對應路徑下的資料夾(<code>00LOCK</code>)並刪除它，之後再來</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xfun"</span>)</span>
<span id="cb6-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">packageVersion</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xfun"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#確認版本</span></span></code></pre></div>
<p>即可成功消除紅線， rmarkdown 也能順利編譯成 PDF了，可喜可賀可口可樂。</p>
</section>
<section id="其他苦主與討論" class="level1">
<h1>其他苦主與討論</h1>
<ul>
<li><a href="https://forum.posit.co/t/cannot-update-package-xfun-to-0-19/86877">Cannot update Package “xfun” to 0.19</a></li>
</ul>



</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>problem solver</category>
  <category>rmarkdown</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/rmd-problem/rmd-problem.html</guid>
  <pubDate>Fri, 26 Dec 2025 16:00:00 GMT</pubDate>
</item>
<item>
  <title>近況&amp;未來規劃(1)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/dairy1/</link>
  <description><![CDATA[ 






<p>因為這個月作業、報告、論文進度各種夾殺，雖然我有一些筆記存稿，但因為都是理論類，內容感覺還是理解太淺，所以這個月應該來不及生出一篇QQ。另一方面，最近也想把我的網站跟 side project 做改版，但是可能到寒假才能動工，所以來寫一篇當作許願(?。最後，最近也有看到一些 AI 用在部落格文章上的一些討論，讓我開始思考有沒有更好的呈現內容的方式。</p>
<p>所以簡單來說，這是一篇想到甚麼東西就很隨意地記錄下來的文章，<del>用來水部落格文章數ㄉ</del></p>
<hr>
<section id="許願清單的部分" class="level1">
<h1>許願清單的部分~</h1>
<section id="我的部落格" class="level2">
<h2 class="anchored" data-anchor-id="我的部落格">我的部落格</h2>
<section id="新增歷史文章的板塊" class="level3">
<h3 class="anchored" data-anchor-id="新增歷史文章的板塊">新增歷史文章的板塊</h3>
<p>我有一個小小的興趣，就是看跟經濟史有關的書籍，大學時歷史是我一半的主修，時間允許也會去參加我感興趣的歷史講座。我有自己的手寫講座筆記，但還沒有很系統的整理過。現在因為歷史不再是我的主修，有些東西會隨時間忘卻，實在很可惜，所以很想把這些東西整理一下丟上來。</p>
<p>不過，考慮到 Quarto 的模板限制，直接丟文章列表旁邊的標籤( 原本是 Categories，我硬把它的名字改掉ㄌㄎㄎ)會混在一起。所以我開始思考要怎麼規劃，讓統計/資料科學的文章跟歷史分開，目前想到的解法就是在右上角做一個連結，點進去會顯示歷史類的文章類表跟文章標籤(就跟目前的主頁顯示一樣!)。不過這樣好像也需要新的 home page ，正在苦惱中~</p>
</section>
<section id="新增文章留言" class="level3">
<h3 class="anchored" data-anchor-id="新增文章留言">新增文章留言</h3>
<p>其實這個超級簡單，至少比前面簡單多了，只是 Quarto 跟一般 CMS 系統相比有個缺點是沒有在做 SEO 的話文章根本不會有曝光機會，因為搜尋系統根本爬不到XD 之前研究了下，如果有交 site map 給瀏覽器的話似乎有機會讓讀者用關鍵字找到，不過我目前只有交 Chrome 的，之前用 Edge 測試就無法。所以覺得這功能可有可無，感覺要先處理 SEO 的部份再新增才有意義。</p>
</section>
<section id="製作新史學的-csl-格式" class="level3">
<h3 class="anchored" data-anchor-id="製作新史學的-csl-格式">製作新史學的 csl 格式</h3>
<p><del>這個需求的起源是我覺得 apa 太醜</del>，唯一有在進行中的需求，不過因為我對製作 csl 檔案實在一竅不通，叫AI寫也是各種問題，我也搞不清楚到底是 quarto 渲染的問題還是 pandoc 不行，又或是我的語法要調整，就，很討厭的卡住了。這個需求也是我認為最需要先解決的，解完新增歷史文章時的 bib 列出來會好看很多。</p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/dairy1/where-is-my-author.png" class="img-fluid"> <em>目前測試結果，啊我的作者呢QAQ</em></p>
</section>
<section id="部落格的封面圖" class="level3">
<h3 class="anchored" data-anchor-id="部落格的封面圖">部落格的封面圖</h3>
<p>本著極簡風格(?)的理念，部落格的文章封面一直都是用小畫家+鼠繪使用，因為這樣風格最簡單，用AI生成的圖片有我不需要的 style。缺點就是圖片size無法統一，這是我困擾的地方，最近想到可以用 Canva 將以後生成的圖片統一，還可以用手畫(更換裝置的話)，整體來說只有優點沒有缺點。</p>
<p><del>不過我這篇還是小畫家就是了，因為簡單(逃</del></p>
</section>
</section>
<section id="side-project" class="level2">
<h2 class="anchored" data-anchor-id="side-project">side project</h2>
<section id="要大幅改版" class="level3">
<h3 class="anchored" data-anchor-id="要大幅改版">要!大!幅!改!版!</h3>
<p>因為真的設計得很不行，聽了業界人士講座才發現我設計得太差，沒辦法每隔一段時間就自動更新資料，也沒有類似 fine-tuning 的機制，導致模型的預測會隨時間拉長正確度降低。</p>
<p>要做到這些，首先需要自動爬資料、清洗資料的腳本，再來是可能會需要一個資料庫存資料，不能再用 csv 混過去，然後是 CICD 的環境，還有可能會需要 api 串接之類的。都是之前沒碰過的東西，之後要好好研究了。</p>
</section>
</section>
</section>
<section id="關於-ai-使用" class="level1">
<h1>關於 AI 使用</h1>
<p>最近看到 AI 使用方面的討論，主要是看到其他部落格文章作者跟 AI 互動方式，還有用 AI 建立工作流的的演講，讓我重新思考要不要重新註明我使用 AI 的地方。</p>
<p>目前這篇部落格的文章絕大多數都是我撰寫的，不過像數學式、流程圖，跟 code 產生與解說會經過 AI 處理，再經過我微調後再丟上來。<sup>1</sup>依據撰寫主題的不同，有些部落客先跟 AI 討論要寫的內容再下筆，或是將草稿交給 AI 審閱，但每一個字都是本人撰寫；有些部落客則是像我一樣有限度的將一部分的內容交給 AI 處理；也有些部落客則是完全把自己的部落格當成 AI 生成說明文件的放置區，一點進去就會知道這篇文章 100% 由 AI 生成。老實說，點到最後一種的時候我的心情都會不太好，因為通常我會想搜尋網路而不是跟 AI 討論的時候，都是我已經不相信 AI 能直接給我正確的 solution 的時候，結果滿心期待的點進去居然還是 AI 生成的文章，真的是喔氣氣氣氣氣氣。</p>
<p>看了一些關於 AI 使用方向的討論後，我認為把文章標註清楚不是壞事，只是目前關於部分使用 AI 生成文章還沒看到一個比較好的標註機制，反倒是完全不使用任何 AI 的作者因為通常本身就反對使用AI，反而會清楚標示他的文章以及授權範圍，這塊需要向他們好好學習。只是要如何把他們的做法應用在我的案例上，就需要好好想想了。</p>
</section>
<section id="結語或者該說許願" class="level1">
<h1>結語(或者該說許願?)</h1>
<p>希望我能平安度過期末、期末歐趴、12月可以生文章，以及以上願望盡早成真，以上~~</p>



</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>如果看到文句不通順，那就是我沒改到:P↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>dairy</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/dairy1/</guid>
  <pubDate>Tue, 18 Nov 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/dairy1/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>初探存活分析(2)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/survival-analysis2/</link>
  <description><![CDATA[ 






<p>上一篇文章中，藉由比較存活分析與時間序列之間的差異，了解存活分析關心的課題。這次，終於要進入估計的部分了。</p>
<p>一般的教科書或是存活分析的課程，大多會從有母數方法(Parametric methods)開始教起，也就是從介紹存活分析常見的機率分布開始，推導它們的存活函數或危險函數。因為它們的模型可解釋性強，而且不需要大量樣本，只要能滿足它們的統計假設，實務上的表現會很優異。</p>
<p>不過本次文章略過它，想要從不需統計假設的無母數方法(Nonparametric method)開始，雖然教授的順序比有母數還要後面，但其實有些無母數方法由來已久。本次介紹的生命量表(life table)最早可以追溯到17世紀<sup>1</sup>，使用的技術也不複雜，會數數就可以了。另一個方法 Kaplan-Meier 就比較晚，20 世紀上半才由 Kaplan 提出。</p>
<section id="生成資料" class="level1">
<h1>生成資料</h1>
<p>我們模擬的資料來自指數分配 <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BEXP%7D%20(0.1)"> 的資料。指數分配簡單介紹如下：</p>
<p>如果事件時間 <img src="https://latex.codecogs.com/png.latex?T%20%5Csim%20%5Ctext%7BExponential%7D(%5Clambda)">，其中<img src="https://latex.codecogs.com/png.latex?%5Clambda">為每單位時間發生該事件的次數，那麼：</p>
<ul>
<li><strong>機率密度函數：</strong> <img src="https://latex.codecogs.com/png.latex?f(t)%20=%20%5Clambda%20e%5E%7B-%5Clambda%20t%7D"></li>
<li><strong>存活函數：</strong> <img src="https://latex.codecogs.com/png.latex?S(t)%20=%20P(T%20%3E%20t)%20=%20e%5E%7B-%5Clambda%20t%7D"></li>
<li><strong>hazard function：</strong> <img src="https://latex.codecogs.com/png.latex?h(t)%20=%20%5Clambda">（</li>
</ul>
<p>這次我們鎖定 <strong>存活函數</strong>，將<img src="https://latex.codecogs.com/png.latex?%5Clambda">設為0.1/年，樣本設定為 100 生成資料：</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1005</span>)</span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 模擬參數</span></span>
<span id="cb1-4">n <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>           <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 模擬個體數量</span></span>
<span id="cb1-5">lambda <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 平均存活時間 = 1/lambda = 10</span></span>
<span id="cb1-6"></span>
<span id="cb1-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 模擬真實存活時間 (from exponential)</span></span>
<span id="cb1-8">true_surv_time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rexp</span>(n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rate =</span> lambda)</span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 模擬隨機截尾時間（假設最大觀察時間是 12）</span></span>
<span id="cb1-11">censor_time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runif</span>(n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">min =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">max =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span>
<span id="cb1-12"></span>
<span id="cb1-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 觀察到的時間和事件狀態</span></span>
<span id="cb1-14">obs_time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pmin</span>(true_surv_time, censor_time)</span>
<span id="cb1-15">status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ifelse</span>(true_surv_time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> censor_time, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-16"></span>
<span id="cb1-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 整理成資料表</span></span>
<span id="cb1-18">data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb1-19">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">id =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>n,</span>
<span id="cb1-20">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(obs_time, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb1-21">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">status =</span> status,</span>
<span id="cb1-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">true_surv =</span> true_surv_time</span>
<span id="cb1-23">)</span>
<span id="cb1-24"> </span>
<span id="cb1-25"></span>
<span id="cb1-26"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">head</span>(data)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>  id  time status  true_surv
1  1  9.88      0 16.0519022
2  2  1.57      1  1.5700031
3  3 10.50      0 14.5478572
4  4  0.93      1  0.9324826
5  5  1.20      1  1.1950362
6  6  2.60      1  2.6003750</code></pre>
</div>
</div>
<p>來看看資料的長相：</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/index_files/figure-html/unnamed-chunk-3-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>  Statistic Obs_Time  True_Surv
1      Min.   0.2400  0.2364795
2   1st Qu.   2.2725  2.2699674
3    Median   5.1300  5.8076985
4      Mean   5.4698 10.6284341
5   3rd Qu.   8.0175 14.2577409
6      Max.  11.9300 69.9463918</code></pre>
</div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/index_files/figure-html/unnamed-chunk-3-2.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/index_files/figure-html/unnamed-chunk-4-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
</section>
<section id="life-table" class="level1">
<h1>Life table</h1>
<p>Life table 的先決條件是要為時間切出一樣等長的單位(<img src="https://latex.codecogs.com/png.latex?t_1,%20t_2,%20,%20%5Cdots%20,t_j">)，用這些做出時間間隔(區間)。再來是在每個個時間區間裡的位於時間段落裡的人數、死亡人數、存活人數，然後就可以推算出存活函數，具體來說，Life table 關心的統計圖是上次提到，原始資料按實際時間排列的圖：</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/index_files/figure-html/unnamed-chunk-5-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>也因此符號的定義會跟上次提供的有點不同，這裡列出 :</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?t_i">: 為時間點，區間則為 <img src="https://latex.codecogs.com/png.latex?I_i%20=%20%5Bt_%7Bi-1%7D,t_i)"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?z_i">: 第 <img src="https://latex.codecogs.com/png.latex?i"> 個區間長度</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?n_i">: 第 <img src="https://latex.codecogs.com/png.latex?i"> 個區間內可追蹤的樣本數，等於下面的<img src="https://latex.codecogs.com/png.latex?d_i">跟<img src="https://latex.codecogs.com/png.latex?l_i">的總和</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?d_i">: 第 <img src="https://latex.codecogs.com/png.latex?i"> 個區間內死亡的樣本數</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?l_i">: 第 <img src="https://latex.codecogs.com/png.latex?i"> 個區間內 censer (活著離開研究)的樣本數</p></li>
</ul>
<blockquote class="blockquote">
<p>這裡有時會再細分，例如用 <img src="https://latex.codecogs.com/png.latex?l_i">表示中途離開研究，不知後續生死的樣本數，<img src="https://latex.codecogs.com/png.latex?w_i">表示第 <img src="https://latex.codecogs.com/png.latex?i"> 個區間內確定活著離開研究的樣本數。</p>
</blockquote>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?w_i=n_i-l_i*0.5">: 第 <img src="https://latex.codecogs.com/png.latex?i"> 個區間具有死亡風險的的平均個體數，概念是：因為不確定將活著離開的人未來是否會在符合研究條件下去世，因此假設 censer 時間服從均勻分配，讓活著離開的人加權值為0.5扣除。化簡後得到<img src="https://latex.codecogs.com/png.latex?d_i+l_i*0.5"></li>
</ul>
<blockquote class="blockquote">
<p>其中，<img src="https://latex.codecogs.com/png.latex?w_i">的計算顯然是<em>估計</em>出來的，這也是會導致後續估計存活函數時會有偏差的原因，繼續看下去就知道了。</p>
</blockquote>
<p>然後我們就可以求出：</p>
<!-- 先列出上次定義的符號:

- $T_i$ : 對象$i$的存活時間，此時確定該對象已經死亡
- $c$ :研究開始到研究結束的時間
- $\delta_i$ : 研究結束時是否過世/死亡
- $U_i = \left\{
\begin{array}{r}
   T_i \quad \text{if }T_i \leq C_r \\
C_r  \quad \text{if }T_i > C_r
\end{array} \right .$ -->
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?d_%7Bt_j%7D">: 在時間點 <img src="https://latex.codecogs.com/png.latex?t_j"> 時的死亡人數，即<img src="https://latex.codecogs.com/png.latex?P(T%20%5Cin%20I_i%7CT%3EI_%7Bi-1%7D)"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?n_%7Bt_j%7D">: 在 <img src="https://latex.codecogs.com/png.latex?t_j"> 時的還活著的人數，即<img src="https://latex.codecogs.com/png.latex?P"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Chat%20q_j%20=%20%5Cfrac%7Bd_j%7D%7Bn_j%7D">: 至特定時間點<img src="https://latex.codecogs.com/png.latex?t_j">的死亡率。</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Chat%20p_j=%201-%20%5Chat%20q_j">: 至特定時間點<img src="https://latex.codecogs.com/png.latex?t_j">的存活率 <strong>(<img src="https://latex.codecogs.com/png.latex?%5Cto">這是我們的目標之一</strong></p></li>
</ul>
<p>為什麼 <img src="https://latex.codecogs.com/png.latex?%5Chat%20p_j"> 是我們的目標 ? 這是因為存活函數<img src="https://latex.codecogs.com/png.latex?S(t)">可以拆解成:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cbegin%7Barray%7D%7Blll%7D%0AS(t)%20&amp;%20=%20&amp;%20%20P(T%3Et_i)%20%5C%5C%0A&amp;%20=%20&amp;%20P(T%3Et_1)P(T%3Et_2%7CT%3Et_1)%5Cdots%20P(T%3Et_j%7CT%3Et_%7Bj-1%7D)%0A&amp;%20=%20%20%5Cprod%20_%7Bi=1%7D%5Ej%20p_i%0A%5Cend%7Barray%7D"></p>
<p>如此一來，我們會需要每個時間點的 <img src="https://latex.codecogs.com/png.latex?p_j"> ，才能湊出 <img src="https://latex.codecogs.com/png.latex?S(t)">，而估計出來的 <img src="https://latex.codecogs.com/png.latex?%5Chat%20p_j"> 因為是<u>至某個時間點產生的東西</u>，所以隨之估計出來的 <img src="https://latex.codecogs.com/png.latex?%5Chat%20S(t)"> 會是一個階梯形狀的函數。</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 分區間（每年）</span></span>
<span id="cb4-2">breaks <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">13</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb4-3">data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>interval <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cut</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> breaks, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">right =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb4-4"></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 初始化生命表資料框</span></span>
<span id="cb4-6">life_table <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb4-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">interval =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(breaks[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks)], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-"</span>, breaks[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]),</span>
<span id="cb4-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb4-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">deaths =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb4-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">censored =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb4-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">w =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb4-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">q =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb4-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">p =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb4-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lx =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb4-15">)</span>
<span id="cb4-16"></span>
<span id="cb4-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 初始存活人數（lx）設為 1，代表比例生命表</span></span>
<span id="cb4-18">lx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb4-19">censored <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> n</span>
<span id="cb4-20"></span>
<span id="cb4-21"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(breaks)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) {</span>
<span id="cb4-22">  in_interval <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>interval <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> i</span>
<span id="cb4-23">  deaths <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status[in_interval] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb4-24">  </span>
<span id="cb4-25">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status[in_interval] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>){</span>
<span id="cb4-26">  censored <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status[in_interval] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb4-27">  }</span>
<span id="cb4-28">  w <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> deaths <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> censored</span>
<span id="cb4-29">  </span>
<span id="cb4-30">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># q &lt;- ifelse(w &gt; 0, deaths / w, 0) # 死亡率(單點)</span></span>
<span id="cb4-31">  q <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span>  deaths<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>w</span>
<span id="cb4-32">  p <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> q <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 存活率(單點)</span></span>
<span id="cb4-33">  </span>
<span id="cb4-34">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># if(round(p,1) == 0){</span></span>
<span id="cb4-35">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   lx &lt;- 1</span></span>
<span id="cb4-36">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># }else {</span></span>
<span id="cb4-37">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    lx &lt;- lx*p</span></span>
<span id="cb4-38">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># }</span></span>
<span id="cb4-39">  lx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> lx <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> p</span>
<span id="cb4-40"></span>
<span id="cb4-41"></span>
<span id="cb4-42">  life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(in_interval, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb4-43">  life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>deaths[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> deaths</span>
<span id="cb4-44">  life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>censored[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> censored</span>
<span id="cb4-45">  life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>w[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(w, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb4-46">  life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>q[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(q, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb4-47">  life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>p[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(p, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb4-48">  life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>lx[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(lx, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb4-49">  </span>
<span id="cb4-50">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># lx &lt;- lx * p  # 更新下一期的 lx</span></span>
<span id="cb4-51">}</span></code></pre></div>
</div>
<div class="callout callout-style-default callout-note no-icon callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon no-icon"></i>
</div>
<div class="callout-title-container flex-fill">
點此查看 life table
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<div class="cell">
<div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">life_table</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>   interval  n deaths censored    w      q      p     lx
1       0-1  8      8       10 13.0 0.6154 0.3846 0.3846
2       1-2 14     14       10 19.0 0.7368 0.2632 0.1012
3       2-3  9      9       10 14.0 0.6429 0.3571 0.0361
4       3-4  8      8       10 13.0 0.6154 0.3846 0.0139
5       4-5  8      8       10 13.0 0.6154 0.3846 0.0053
6       5-6 12      5        7  8.5 0.5882 0.4118 0.0022
7       6-7  5      2        3  3.5 0.5714 0.4286 0.0009
8       7-8  9      3        6  6.0 0.5000 0.5000 0.0005
9       8-9  6      2        4  4.0 0.5000 0.5000 0.0002
10     9-10  7      0        7  3.5 0.0000 1.0000 0.0002
11    10-11  7      0        7  3.5 0.0000 1.0000 0.0002
12    11-12  7      0        7  3.5 0.0000 1.0000 0.0002
13    12-13  0      0        7  3.5 0.0000 1.0000 0.0002</code></pre>
</div>
</div>
<section id="欄位解說" class="level4">
<h4 class="anchored" data-anchor-id="欄位解說">欄位解說</h4>
<table class="caption-top table">
<colgroup>
<col style="width: 16%">
<col style="width: 9%">
<col style="width: 74%">
</colgroup>
<thead>
<tr class="header">
<th>欄位名稱</th>
<th>中文名稱</th>
<th>簡單說明</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>interval</code></td>
<td>區間（年份）</td>
<td>觀察時間的分段，例如 <code>0–1</code> 年、<code>1–2</code> 年…等。</td>
</tr>
<tr class="even">
<td><code>n</code></td>
<td>區間樣本數</td>
<td>落在該時間區間的個體數量（包含死亡與審查）。</td>
</tr>
<tr class="odd">
<td><code>deaths</code></td>
<td>死亡人數</td>
<td>在該區間內發生事件（如死亡）的個體數。</td>
</tr>
<tr class="even">
<td><code>censored</code></td>
<td>審查人數</td>
<td>在該區間內未發生事件，但中途退出觀察的個體數（例如失聯、試驗結束）。</td>
</tr>
<tr class="odd">
<td><code>w</code></td>
<td>平均風險人數</td>
<td>用於估計死亡率的有效樣本數，通常計算為 <code>deaths + 0.5 × censored</code>。</td>
</tr>
<tr class="even">
<td><code>q</code></td>
<td>區間死亡率</td>
<td>在該區間內死亡的機率，公式為 <code>q = deaths / w</code>。</td>
</tr>
<tr class="odd">
<td><code>p</code></td>
<td>區間存活率</td>
<td>存活機率，<code>p = 1 - q</code>，表示該區間內個體存活下來的機率。</td>
</tr>
<tr class="even">
<td><code>lx</code></td>
<td>存活比例</td>
<td>到該時間區間結束時，仍然存活的比例（累積存活率），初始為 1（或 100%）。</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
</div>
<p>圖為：</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1">time <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>lx)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(time, life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>lx, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"s"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"觀察時間"</span>,<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"存活函數"</span>)</span>
<span id="cb7-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#  加入點</span></span>
<span id="cb7-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">points</span>(time, life_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>lx, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pch =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>)</span>
<span id="cb7-5"></span>
<span id="cb7-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># # 加入真實模擬存活曲線（指數分布）</span></span>
<span id="cb7-7">t_seq <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(time), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>)</span>
<span id="cb7-8">true_surv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exp</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> t_seq) </span>
<span id="cb7-9"></span>
<span id="cb7-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(t_seq, true_surv, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb7-11"></span>
<span id="cb7-12"></span>
<span id="cb7-13"></span>
<span id="cb7-14"></span>
<span id="cb7-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 加上圖例</span></span>
<span id="cb7-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">legend</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"topright"</span>,</span>
<span id="cb7-17">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"生命表 (手刻)"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"真實指數分布"</span>),</span>
<span id="cb7-18">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>),</span>
<span id="cb7-19">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb7-20">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb7-21">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bty =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"n"</span></span>
<span id="cb7-22">)</span></code></pre></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/index_files/figure-html/unnamed-chunk-8-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>可以看到距離真實值偏離了不少，實務上會試著調整<img src="https://latex.codecogs.com/png.latex?w_i">的計算方式，不過下一個介紹的 Kaplan-Meier 可以很好的改進這點。</p>
</section>
<section id="kaplan-meier" class="level1">
<h1>Kaplan-Meier</h1>
<p>Kaplan-Meier 跟 Life table 最大的差異在於，<strong>Life table 的時間區段是事先定義，Kaplan-Meier 的時間區段會發生在<img src="https://latex.codecogs.com/png.latex?%5B%5Ctext%7B%E7%99%BC%E7%94%9F%E4%BA%8B%E4%BB%B6%E7%9A%84%E6%99%82%E9%96%93%E9%BB%9E%7D,%5Ctext%7B%E7%99%BC%E7%94%9F%E4%BA%8B%E4%BB%B6%E7%9A%84%E4%B8%8B%E4%B8%80%E5%80%8B%E6%99%82%E9%96%93%E9%BB%9E%7D)"></strong>，這裡的事件指的即是病患死亡或離開研究。也就是說，每次的時間間隔不一定會一樣，產生的 <img src="https://latex.codecogs.com/png.latex?%5Chat%20S(t)"> 會是大小不一的階梯狀函數。</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/index_files/figure-html/unnamed-chunk-9-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p><em>Kaplan-Meier 所關心的資料畫成圖就像這樣，已經專注在存活長度本身上</em></p>
<p>實務上， Life table 常用於政府或研究機構定期發布的調查資料，而 Kaplan-Meier 則用在可以具體知道事件發生時間點的臨床研究資料上。</p>
<p>為了與前面的 Life table 法區隔，我們改用<img src="https://latex.codecogs.com/png.latex?(v_1,%20%5Cdots%20,v_j)">標示Kaplan-Meier的時間間隔。</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?T_i"> : 按資料存活時間/截尾時間排序後，第<img src="https://latex.codecogs.com/png.latex?i">位對象的存活時間/截尾時間，是個隨機變數。</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?c"> :研究開始到研究結束的時間</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Cdelta_i"> : 研究結束時是否過世/死亡</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?U_i%20=%20%5Cleft%5C%7B%0A%5Cbegin%7Barray%7D%7Br%7D%0AT_i%20%5Cquad%20%5Ctext%7Bif%20%7DT_i%20%5Cleq%20C_r%20%5C%5C%0AC_r%20%20%5Cquad%20%5Ctext%7Bif%20%7DT_i%20%3E%20C_r%0A%5Cend%7Barray%7D%20%5Cright%20."></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?d_%7Bv_j%7D">: 在時間點 <img src="https://latex.codecogs.com/png.latex?v_j"> 時的死亡人數，即<img src="https://latex.codecogs.com/png.latex?P(T%20%5Cin%20%5Bv_%7Bj-1%7D,v_j)%7CT%3Ev_%7Bj-1%7D)"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?n_%7Bv_j%7D">: 在 <img src="https://latex.codecogs.com/png.latex?v_j"> 時的還活著的人數，即<img src="https://latex.codecogs.com/png.latex?P"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Chat%20q_j%20=%20%5Cfrac%7Bd_%7Bv_j%7D%7D%7Bn_%7Bv_j%7D%7D">: 至特定時間點<img src="https://latex.codecogs.com/png.latex?v_j">的死亡率。</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Chat%20p_j=%201-%20%5Chat%20q_j">: 至特定時間點<img src="https://latex.codecogs.com/png.latex?v_j">的存活率 <strong>(<img src="https://latex.codecogs.com/png.latex?%5Cto">這是我們的目標之一</strong></p></li>
</ul>
<p>而我們的目標 <img src="https://latex.codecogs.com/png.latex?%5Chat%20S(t)%20=%20%5Cprod%20_%7Bi=1%7D%5Ej%20%5Chat%20p_i"> ， 原因前面已提過，就不再贅述。</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sort data</span></span>
<span id="cb8-2">data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> data[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">order</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time), ]</span>
<span id="cb8-3"></span>
<span id="cb8-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Step 1: 只取有事件的時間</span></span>
<span id="cb8-5">event_times <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time[data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb8-6">unique_event_times <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sort</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unique</span>(event_times))</span>
<span id="cb8-7"></span>
<span id="cb8-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Step 2: 建立表格</span></span>
<span id="cb8-9">km_table <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb8-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time =</span> unique_event_times,</span>
<span id="cb8-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_risk =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>,</span>
<span id="cb8-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_event =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>,</span>
<span id="cb8-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">survival_prob =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>,</span>
<span id="cb8-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cumulative_survival =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span></span>
<span id="cb8-15">)</span>
<span id="cb8-16"></span>
<span id="cb8-17">n <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(data)</span>
<span id="cb8-18">cum_surv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb8-19"></span>
<span id="cb8-20"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(km_table)) {</span>
<span id="cb8-21">  t <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time[i]</span>
<span id="cb8-22">  at_risk <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> t)</span>
<span id="cb8-23">  events <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> t <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb8-24">  </span>
<span id="cb8-25">  p <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> events <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> at_risk <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 計算存活機率</span></span>
<span id="cb8-26">  cum_surv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> cum_surv <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> p <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 計算累積存活機率</span></span>
<span id="cb8-27">  </span>
<span id="cb8-28">  km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_risk[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> at_risk</span>
<span id="cb8-29">  km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_event[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> events</span>
<span id="cb8-30">  km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>survival_prob[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(p, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb8-31">  km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cumulative_survival[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(cum_surv, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb8-32">}</span>
<span id="cb8-33"></span>
<span id="cb8-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 加入真實值</span></span>
<span id="cb8-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 求出"某個時間點 t 的「還活著的機率」"</span></span>
<span id="cb8-36">lambda_t <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exp</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>lambda <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time)</span>
<span id="cb8-37">km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>true_survival <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(lambda_t, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span></code></pre></div>
</div>
<div class="callout callout-style-default callout-note no-icon callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon no-icon"></i>
</div>
<div class="callout-title-container flex-fill">
點此查看 Kaplan-Meier 表
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<div class="cell">
<div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1">km_table</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>   time n_risk n_event survival_prob cumulative_survival true_survival
1  0.24    100       1         0.990               0.990         0.976
2  0.31     99       1         0.990               0.980         0.969
3  0.35     98       1         0.990               0.970         0.966
4  0.37     97       1         0.990               0.960         0.964
5  0.52     96       1         0.990               0.950         0.949
6  0.81     95       1         0.989               0.940         0.922
7  0.93     94       2         0.979               0.920         0.911
8  1.06     92       1         0.989               0.910         0.899
9  1.08     91       1         0.989               0.900         0.898
10 1.15     90       1         0.989               0.890         0.891
11 1.20     89       1         0.989               0.880         0.887
12 1.25     88       1         0.989               0.870         0.882
13 1.27     87       2         0.977               0.850         0.881
14 1.57     85       2         0.976               0.830         0.855
15 1.62     83       1         0.988               0.820         0.850
16 1.79     82       1         0.988               0.810         0.836
17 1.89     81       1         0.988               0.800         0.828
18 1.90     80       1         0.988               0.790         0.827
19 1.93     79       1         0.987               0.780         0.824
20 2.03     78       1         0.987               0.770         0.816
21 2.06     77       1         0.987               0.760         0.814
22 2.19     76       1         0.987               0.750         0.803
23 2.30     75       1         0.987               0.740         0.795
24 2.37     74       1         0.986               0.730         0.789
25 2.47     73       1         0.986               0.720         0.781
26 2.60     72       1         0.986               0.710         0.771
27 2.71     71       1         0.986               0.700         0.763
28 2.94     70       1         0.986               0.690         0.745
29 3.10     69       1         0.986               0.680         0.733
30 3.13     68       1         0.985               0.670         0.731
31 3.29     67       1         0.985               0.660         0.720
32 3.41     66       1         0.985               0.650         0.711
33 3.42     65       2         0.969               0.630         0.710
34 3.52     63       1         0.984               0.620         0.703
35 3.88     62       1         0.984               0.610         0.678
36 4.25     61       1         0.984               0.600         0.654
37 4.28     60       1         0.983               0.590         0.652
38 4.49     59       1         0.983               0.580         0.638
39 4.72     58       1         0.983               0.570         0.624
40 4.75     57       1         0.982               0.560         0.622
41 4.91     56       1         0.982               0.550         0.612
42 4.92     55       1         0.982               0.540         0.611
43 4.99     54       1         0.981               0.530         0.607
44 5.01     53       1         0.981               0.520         0.606
45 5.19     49       1         0.980               0.509         0.595
46 5.80     44       1         0.977               0.498         0.560
47 5.81     43       1         0.977               0.486         0.559
48 5.85     42       1         0.976               0.475         0.557
49 6.38     40       1         0.975               0.463         0.528
50 6.88     37       1         0.973               0.450         0.503
51 7.11     36       1         0.972               0.438         0.491
52 7.30     32       1         0.969               0.424         0.482
53 7.68     29       1         0.966               0.409         0.464
54 8.31     23       1         0.957               0.392         0.436
55 8.99     22       1         0.955               0.374         0.407</code></pre>
</div>
</div>
<section id="欄位解釋" class="level5">
<h5 class="anchored" data-anchor-id="欄位解釋">欄位解釋</h5>
<table class="caption-top table">
<colgroup>
<col style="width: 62%">
<col style="width: 37%">
</colgroup>
<thead>
<tr class="header">
<th>欄位名稱</th>
<th>意義</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>time</code></td>
<td>發生事件的時間點（如死亡、復發）</td>
</tr>
<tr class="even">
<td><code>n_risk</code></td>
<td>在該時間點仍處於風險中的人數（尚未發生事件或被截尾）</td>
</tr>
<tr class="odd">
<td><code>n_event</code></td>
<td>在該時間點發生事件的人數</td>
</tr>
<tr class="even">
<td><code>survival_prob</code></td>
<td>單一時間點的存活機率（即 (1 - )）</td>
</tr>
<tr class="odd">
<td><code>cumulative_survival</code></td>
<td>累積存活率，表示從起始時間到該時間點的整體存活機率</td>
</tr>
<tr class="even">
<td><code>true_survival</code></td>
<td>真實存活率（按照模擬資料指定分布 <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BEXP%7D(0.1)"> 反推）</td>
</tr>
</tbody>
</table>
</section>
<section id="表格解讀" class="level5">
<h5 class="anchored" data-anchor-id="表格解讀">表格解讀</h5>
<p>Kaplan-Meier 曲線是階梯狀的，每次事件發生時下降，未發生事件的時間點則維持不變。這份表格展示了：</p>
<ul>
<li><strong>初始時刻（time = 0.24）</strong>：有 100 人在風險中，發生 1 件事件，存活機率為 0.990，累積存活率也為 0.990。</li>
<li><strong>隨時間推移</strong>：每次事件發生都會使累積存活率下降。例如：
<ul>
<li>在 time = 1.27，有 2 件事件，累積存活率從 0.870 降至 0.850。</li>
<li>到 time = 5.01，累積存活率已降至 0.520，表示只有 52% 的人存活至該時間點。</li>
</ul></li>
<li><strong><code>true_survival</code></strong> 欄位：用來與 Kaplan-Meier 所估計的 <code>cumulative_survival</code> 做比較。</li>
</ul>
</section>
</div>
</div>
</div>
<section id="畫圖觀察" class="level2">
<h2 class="anchored" data-anchor-id="畫圖觀察">畫圖觀察</h2>
<div class="cell">
<div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 畫 Kaplan-Meier 估計的存活機率（step line）</span></span>
<span id="cb11-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time, km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cumulative_survival, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"s"</span>, </span>
<span id="cb11-3">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylim =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb11-4">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ylab =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Survival Probability"</span>,</span>
<span id="cb11-5">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kaplan-Meier vs True Survival Function"</span>)</span>
<span id="cb11-6"></span>
<span id="cb11-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 加上真實存活函數的線（用對應時間點計算的真實值）</span></span>
<span id="cb11-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lines</span>(km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>time, km_table<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>true_survival, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb11-9"></span>
<span id="cb11-10"></span>
<span id="cb11-11"></span>
<span id="cb11-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 加圖例</span></span>
<span id="cb11-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">legend</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"topright"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"KM Estimate"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"True S(t)"</span>),</span>
<span id="cb11-14">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span></code></pre></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/index_files/figure-html/unnamed-chunk-12-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>畫圖觀察後可以發現，Kaplan-Meier 在樣本數為100下，比起 life table ， Kaplan-Meier fiting 得更好。</p>
</section>
</section>
<section id="參考資料與延伸閱讀" class="level1">
<h1>參考資料與延伸閱讀</h1>
<div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-liu2020lifetime_km" class="csl-entry">
劉嘉樺. <em><a href="https://cchia.kmu.edu.tw/images/%E6%96%87%E7%AB%A0/1-Life_table_method%E8%88%87Kaplan-Meier_method%E7%9A%84%E4%B8%8D%E5%90%8C%E4%B9%8B%E8%99%95.pdf">Life‑table method 與 Kaplan‑Meier method 的不同之處</a></em>. PDF on the internet. 2020.
</div>
<div id="ref-lin2008survival" class="csl-entry">
林建甫. <em>存活分析</em>. 雙葉書局. 2008.
</div>
</div>



</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>Greenwood Major 1938The first life tableNotes Rec. R. Soc. Lond.170–72 http://doi.org/10.1098/rsnr.1938.0017↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>Survial Analysis</category>
  <category>R</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/survival-analysis2/</guid>
  <pubDate>Sun, 19 Oct 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/survival-analysis2/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>初探存活分析(1)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/survival-analysis1/survival-analysis1.html</link>
  <description><![CDATA[ 






<section id="前言" class="level1">
<h1>前言</h1>
<p>存活分析 (Survival Analysis) 是我就讀統研所前就一直很想深入學習的科目之一，原因是它也是一門跟時間有關的一門學問。不過網路上很少人有人把它拿來跟統計學關心時間資料的代表學問-時間序列(time series)做比較，於是就來寫一篇吧！</p>
<hr>
</section>
<section id="存活分析的基本觀念" class="level1">
<h1>存活分析的基本觀念</h1>
<p>存活分析的概念雖然被廣泛應用在不同領域中，例如：生命量表(Life table)可用於保險精算；工業中的可靠度分析(Reliability)某種程度上是存活分析的非生物版本。不過這個領域主要是在醫學臨床研究下發展的，因此要了解存活分析，就要先了解學者/醫師在臨床上是怎麼調查資料，以及他們關心的是甚麼。當然，本文的企圖是從時間序列的角度切入。所以，我們從時間序列的原始資料開始。</p>
<section id="原始資料" class="level2">
<h2 class="anchored" data-anchor-id="原始資料">原始資料</h2>
<p>如果是時間序列，通常會是一組按時間順序排列的資料點，而研究者主要會關心下一個(或 n 個)的時間點資料會如何。因此一開始我們收集到的資料很可能會長這樣：</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis1/survival-analysis1_files/figure-html/unnamed-chunk-2-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p><em>上圖是一個模擬9/1~9/30的資料</em></p>
<p>可以注意到我們收集的資料是一段時間的，且不管年代更久遠的資料如何，只要有足夠的樣本我們就可以分析預測下一個時間點。</p>
<p>第一個例子是 single time series，也有多個時間序列的例子，基本上每個 time series 的時間點一定會有對應的值：</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis1/survival-analysis1_files/figure-html/unnamed-chunk-3-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p><em><del>很明顯綠線是我亂畫的</del></em></p>
<p>若資料沒有對應值，像綠線那樣，實務上就會視為缺失值，並想辦法處理。</p>
<p>不過，在存活分析裡，研究者關心的問題的是每個對象的存活時間，以及為什麼對象過世，在乎的是時間的長度而非下一個時間點的值。所以原始資料會是很多條的時間序列，此外，因為對象是活體(生物)，會因為各式各樣的原因離開研究，研究者沒有辦法、也不可能完整的收集到每個對象從出生到死亡經歷的時間，最多只能接收到<strong>在研究調查的期間，對象有沒有活著</strong>。所以原始資料會是這樣：</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis1/survival-analysis1_files/figure-html/unnamed-chunk-4-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>每個黑色圓圈是進入研究的時間，尾端則是標記在研究結束時對象是否存活。</p>
<p>這種圖示很難比較對象間的時間長度，因此會改畫成這樣：</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/survival-analysis1/survival-analysis1_files/figure-html/unnamed-chunk-5-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>研究對象研究結束的時間，稱為病患研究時間(patient time)，而對象在研究結束時仍然存活，研究者無法得知他真實過世的時間，這種資料被稱為右設限(right censor)。這是存活分析分析裡常見的資料類型。</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
censor 補充
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>讀者可能會注意到，既然有所謂的 right censor，那有沒有其他的 censor 呢？答案是有的，但要注意 censor 本來就有審查的意思在，因此這裡 <strong>censor 的定義會隨著研究者想研究的主題而改變，它代表研究者想研究卻沒有資料能證明的狀態</strong>。例如：研究者想得知不同對象的抽菸時間，需要知道他們是從甚麼時候開始抽菸及戒菸時間，但不是每個對象都能精準追蹤到甚麼時候開始抽或何時結束，這種研究者同時關心起始時間及結束時間的案例，就是 double censor。</p>
</div>
</div>
</div>
<p>如果不畫成圖表，則會標記成「<img src="https://latex.codecogs.com/png.latex?%5Ctext%7B%E5%B0%8D%E8%B1%A1%E5%AD%98%E6%B4%BB%E6%99%82%E9%96%93%7D%5E%7B%5Ctext%7B%E6%98%AF%E5%90%A6%20censor%7D%7D">」，像這樣：</p>
<p><img src="https://latex.codecogs.com/png.latex?5,5,7,7,8%5E%7B+%7D,11,12,14%5E%7B+%7D,15"></p>
<p>其中<img src="https://latex.codecogs.com/png.latex?+">的意思是：對象在研究時間結束時仍存活。對應表格為：</p>
<div class="cell">
<div class="cell-output cell-output-stdout">
<pre><code>[1] "suv_time 為存活時間"</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] "status為是否死亡，0表示研究結束時仍存活，1表示死亡"</code></pre>
</div>
<div class="cell-output cell-output-stdout">
<pre><code>   suv_time status
4         5      1
10        5      1
8         7      1
9         7      1
2         8      0
5         8      1
1        11      1
7        12      1
6        14      0
3        15      1</code></pre>
</div>
</div>
</section>
<section id="專有名詞" class="level2">
<h2 class="anchored" data-anchor-id="專有名詞">專有名詞</h2>
<p>時間序列的基本精神是：過去會影響現在。因此時間序列會用 <img src="https://latex.codecogs.com/png.latex?t"> 表示時間點，lag 表示上一個時間點到下一個時間點的間隔。建模的方向也朝這個方向發展，以<img src="https://latex.codecogs.com/png.latex?AR(1)">舉例，它的數學式為：</p>
<p><em>Suppose <img src="https://latex.codecogs.com/png.latex?%7BY_t%7D"> is a stationary time series , the <img src="https://latex.codecogs.com/png.latex?AR(1)"> model is:</em></p>
<p><img src="https://latex.codecogs.com/png.latex?y_t%20=%20a%20y_%7Bt-1%7D%20+%20%5Cepsilon%20%5Cquad,%20%5C,%20%5Cepsilon%20%5Csim%20N(0,1)"></p>
<p>決定要如何預測則是使用 acf (autocorrelation function )，也就是利用它們在不同 lag 下，與原本 times series 比較相似程度，進而決定如何預測。</p>
<p>存活分析則關心多位個體的存活時間，會用 <img src="https://latex.codecogs.com/png.latex?T_i"> 表示每位個體的實際存活時間長度(很明顯，它是隨機變數)，用<img src="https://latex.codecogs.com/png.latex?%5Cdelta_i">表示個體在研究其中是否死亡(這就不是隨機變數了，因為它是已知的資訊)，以此發展出更多不同的專有名詞跟符號。</p>
<section id="存活函數survival-function" class="level3">
<h3 class="anchored" data-anchor-id="存活函數survival-function">存活函數(Survival function)</h3>
<p>令隨機變數 <img src="https://latex.codecogs.com/png.latex?T"> 為時間點 0 到發生特殊事件的時間，其機率密度函數定義為 <img src="https://latex.codecogs.com/png.latex?f(t)">，則存活函數(Survival function)表示某一位對象的存活時間超過一段時間 t 的機率，其存活函數為：</p>
<p><img src="https://latex.codecogs.com/png.latex?S(t)%20%5Cequiv%20P(T%3Et)%20=%20%5Cint_%7Bt%7D%5E%7B%5Cinfty%7D%20f(t)dt"></p>
</section>
<section id="失敗函數" class="level3">
<h3 class="anchored" data-anchor-id="失敗函數">失敗函數</h3>
<p>與存活函數相反的概念，也是機率密度函數的 cdf。</p>
<p><img src="https://latex.codecogs.com/png.latex?F(t)=1-S(t)"></p>
</section>
<section id="危險函數hazar-function" class="level3">
<h3 class="anchored" data-anchor-id="危險函數hazar-function">危險函數(Hazar function)</h3>
<p>它的作用與存活函數類似，但它表達的是在一個時間點後死亡的機率，定義如下</p>
<p><img src="https://latex.codecogs.com/png.latex?h(t)%5Cequiv%20%5Clim%20_%7B%5CDelta%20%5Cto%200%7D%5Cfrac%7BP(t%20%5Cleq%20%20T%3Ct+%5CDelta%7CT%5Cge%20t)%7D%7B%5CDelta%7D"></p>
<p>經過推導後可以得出：</p>
<p><img src="https://latex.codecogs.com/png.latex?h(t)=%5Cfrac%7Bf(t)%7D%7BS(t)%7D=%5Cfrac%7Bd%7D%7Bdt%7D%5Clog%20%5BS(t)%5D"></p>
</section>
<section id="累積危險函數cumulative-hazar-function" class="level3">
<h3 class="anchored" data-anchor-id="累積危險函數cumulative-hazar-function">累積危險函數(Cumulative hazar function)</h3>
<p>就是 hazar function 的 cdf，經過推導後也可以用存活函數表示：</p>
<p><img src="https://latex.codecogs.com/png.latex?H(t)=%5Cint_0%5Et%20h(u)du=-%5Clog%5BS(t)%5D"></p>
</section>
</section>
<section id="統計推論" class="level2">
<h2 class="anchored" data-anchor-id="統計推論">統計推論</h2>
<p>時間序列的統計推論的架構跟傳統統計差不多，一樣分成 Type I Error、Type II Error。主要也探討研究結論與實際有出入產生的影響。</p>
<p>存活分析不同，在一開始就要考慮每個對象的存活時間與在研究的時間點是否存活，還要考慮 censor 的狀況。因此把這些關心的東西用數學表示：</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?T_i"> : 對象<img src="https://latex.codecogs.com/png.latex?i">的存活時間，此時確定該對象已經死亡</li>
<li><img src="https://latex.codecogs.com/png.latex?c"> :研究開始到研究結束的時間</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Cdelta_i"> : 研究結束時是否過世/死亡</li>
</ul>
<p>努力簡化，通常最後用兩個參數表示關心的東西：</p>
<p><img src="https://latex.codecogs.com/png.latex?U_i%20=%20%5Ctext%7Bmin%7D(T_i,%20c),%20%5Cquad%20%5Cdelta_i=I(T_i%20%5Cleq%20c)"></p>
<p>跟傳統的統計推論相比，存活分析因為多了 censor 的概念，所以推論也圍繞 censor 打轉。</p>
<section id="type-i-censoring" class="level3">
<h3 class="anchored" data-anchor-id="type-i-censoring">Type I Censoring</h3>
<p>所謂的 Type I Censoring 是指每個個體的最後追蹤時間長度不同，至於為什麼會這樣前面也有提到，研究對象是活體，容易因為各種原因無法追蹤到最後(轉院、反悔退出研究…etc)。這種資料需要設定固定的 censor 時間 <img src="https://latex.codecogs.com/png.latex?C_r"> 確保資料可以成為可用的隨機變數，即：</p>
<p><img src="https://latex.codecogs.com/png.latex?U_i%20=%20%5Cleft%5C%7B%0A%5Cbegin%7Barray%7D%7Br%7D%0A%20%20%20T_i%20%5Cquad%20%5Ctext%7Bif%20%7DT_i%20%5Cleq%20C_r%20%5C%5C%0AC_r%20%20%5Cquad%20%5Ctext%7Bif%20%7DT_i%20%3E%20C_r%0A%5Cend%7Barray%7D%20%5Cright%20."></p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cdelta_i"> 也以此類推。</p>
</section>
<section id="type-ii-censoring" class="level3">
<h3 class="anchored" data-anchor-id="type-ii-censoring">Type II Censoring</h3>
<p>Type II Censoring 的意思是在設計實驗時，以進行到第<img src="https://latex.codecogs.com/png.latex?r">個個體死亡時作為決定結束研究時間的基準，這種情況就不用原始的<img src="https://latex.codecogs.com/png.latex?U_i">而是 order statistic <img src="https://latex.codecogs.com/png.latex?U_%7B(i)%7D">表示收集到的資料。</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cbegin%7Barray%7D%7Blll%7D%0AU_%7B(1)%7D%20&amp;%20=%20&amp;%20T_%7B(1)%7D%20%5C%5C%0AU_%7B(2)%7D%20&amp;%20=%20&amp;%20C_%7B(1)%7D%20%5C%5C%0A%5Cvdots%20&amp;%20=%20&amp;%20%5Cvdots%20%5C%5C%0AU_%7B(n)%7D%20&amp;%20=%20&amp;%20T_%7B(r)%7D%0A%5Cend%7Barray%7D"></p>
</section>
</section>
</section>
<section id="結論" class="level1">
<h1>結論</h1>
<p>其實與其說是跟時間序列比較，感覺更像是跟一般商管科系認知的傳統統計做比較 XD 。雖然數學推導都跳過，不過寫完對存活分析有更進一步的了解，可以前往下一篇文章邁進了~</p>
</section>
<section id="相關文章" class="level1">
<h1>相關文章</h1>
<ul>
<li><a href="https://wangcc.me/LSHTMlearningnote/surv-intro.html">王超辰-醫學統計學</a></li>
</ul>



</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>Survial Analysis</category>
  <category>R</category>
  <category>concept</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/survival-analysis1/survival-analysis1.html</guid>
  <pubDate>Thu, 02 Oct 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/survival-analysis1/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>NLP基本概念筆記</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/nlp-note/</link>
  <description><![CDATA[ 






<section id="電腦如何理解文字" class="level1">
<h1>電腦如何理解文字?</h1>
<p>自然語言處理的主要目標為：讓電腦了解人類使用的自然語言，並且生成它，讓人有電腦在說話的錯覺。具體還可以再細分成很多目標，這次有提到的有相似詞問題，不過不管是甚麼問題，一定要經過資料前處理，也有一些指標衡量模型。</p>
</section>
<section id="專有名詞解釋" class="level1">
<h1>專有名詞解釋</h1>
<section id="tokenization-分詞" class="level2">
<h2 class="anchored" data-anchor-id="tokenization-分詞">Tokenization (分詞)</h2>
<p>將句子切成一個個獨立的符記。切分細緻程度取決於具體應用。</p>
</section>
<section id="stemming" class="level2">
<h2 class="anchored" data-anchor-id="stemming">Stemming</h2>
<p>移除單字的後綴，將其還原為詞根（Root）或詞幹。例如，“dogs” 的詞幹是 “dog”。這通常透過 rule-based的方式來實現。</p>
</section>
<section id="stop-words" class="level2">
<h2 class="anchored" data-anchor-id="stop-words">Stop words</h2>
<p>移除不重要的詞彙，例如英文中的冠詞（a, an, the），以節省儲存和運算空間。實務上會看情況決定是否執行。</p>
</section>
<section id="tf-term-frequency-詞頻" class="level2">
<h2 class="anchored" data-anchor-id="tf-term-frequency-詞頻">TF (term frequency, 詞頻)</h2>
<p>某一字詞出現的頻率。分母為<strong>在所有文件中出現的次數</strong>，分子為<strong>在目前文件中出現的次數</strong>。</p>
<p><img src="https://latex.codecogs.com/png.latex?%20tf_%7Bij%7D=%5Cfrac%7Bn_%7Bij%7D%7D%7B%5Csum_k%7Dn_%7Bkj%7D"></p>
</section>
<section id="idf-inverse-document-frequency" class="level2">
<h2 class="anchored" data-anchor-id="idf-inverse-document-frequency">IDF (inverse document frequency)</h2>
<p>有些單字的 TF 很高，但它不一定重要。為了衡量單字在整體語料中的重要程度，引入了 IDF 來減少常見單字的權重。</p>
<p>IDF 的常見公式為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathrm%7Bidf%7D_i%20=%20%5Clog%20%5Cleft(%20%5Cfrac%7B%7CD%7C%7D%7B1%20+%20df_i%7D%20%5Cright)%0A"></p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?%7CD%7C"> 表示整體文件數（document count）</li>
<li><img src="https://latex.codecogs.com/png.latex?df_i"> 表示包含單字 <img src="https://latex.codecogs.com/png.latex?i"> 的文件數（document frequency）</li>
</ul>
<p>為了避免分母為 0 所以有 + 1 的設計。</p>
</section>
<section id="language-models-語言模型" class="level2">
<h2 class="anchored" data-anchor-id="language-models-語言模型">Language Models (語言模型)</h2>
<p>將機率賦予文字序列的模型通稱為語言模型(Language Models，簡稱 LM)</p>
<p>常見的 LM 有：</p>
<ol type="1">
<li>N-gram model</li>
</ol>
<p>是一個經典的統計模型，N-gram 指的是一個連續的 N 個詞的序列。例如，“turn your homework” 的 1-gram 是 “turn”，2-gram 是 “turn your”，3-gram 是 “turn your homework”。N-gram model 利用「馬可夫假設」（Markov Assumption），即一個詞的出現機率僅取決於其前一個詞，然後透過計算詞元共現次數來預測下一個詞。</p>
<ol start="2" type="1">
<li>Neural Language model</li>
</ol>
<p>利用 Neural Network 家族計算、預測序列的 likelihood。例如 RNN 、LSTM、Transformer…等等都是一種Neural Language model。隨著深度學習的發展，目前 LM 以 Neural Language model 為主流，且與影像處理使用模型漸趨雷同。</p>
</section>
</section>
<section id="指標" class="level1">
<h1>指標</h1>
<section id="perplexity-困惑度" class="level2">
<h2 class="anchored" data-anchor-id="perplexity-困惑度">Perplexity (困惑度)</h2>
<p>Perplexity (PPL)，是用來衡量模型生成文字品質的指標。一般意義為： 困惑度越低，表示模型的語言理解和預測能力越好，能更有效地捕捉語言模式。可以將其視為模型在每個步驟中可能的選擇的「平均分支因子」。</p>
<p>這個指標有趣的是，<strong>困惑度越低，就代表這串文字越有可能是 AI 生成的</strong>，因此也被應用在識別文章是否由AI生成的判定上。</p>
<p>具體定義可能會因為使用模型不同而有計算差異，例如，用在 N-gram model 上為：</p>
<p>For the sequence of words <img src="https://latex.codecogs.com/png.latex?W=%20w_1,%20w_2,%20w_3%20,%20%5Cdots%20,%20w_N">, the PPL of the model was computed by</p>
<p><img src="https://latex.codecogs.com/png.latex?%20Perplexity%20(W)%20=%20P(w_1%20w_2%20w_3%20%20%5Cdots%20%20w_N)%5E%7B%5Cfrac%7B-1%7D%7BN%7D%7D%5C%7B%20%5Cprod%20%5En%20_%7Bk=1%7D%5Cfrac%7B1%7D%7BP(w_k%7Cw_%7Bk-n+1:k-1%7D)%7D%5C%7D%5E%7B%5Cfrac%7B1%7D%7BN%7D%7D,%E3%80%801%20%5Csim%20%5Cinfty,%201%20%5Csim%20%7CV%7C"></p>
</section>
</section>
<section id="延伸閱讀" class="level1">
<h1>延伸閱讀</h1>
<ul>
<li><p><a href="https://medium.com/@derekliao_62575/nlp%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%9F%B7%E8%A1%8C%E6%AD%A5%E9%A9%9F-ii-bag-of-words-%E8%A9%9E%E8%A2%8B%E8%AA%9E%E8%A8%80%E6%A8%A1%E5%9E%8B-3b670a0c7009">NLP的基本執行步驟(II) — Bag of Words 詞袋語言模型</a></p></li>
<li><p><a href="https://medium.com/datamixcontent-lab/%E6%96%87%E6%9C%AC%E5%88%86%E6%9E%90%E5%85%A5%E9%96%80-%E6%A6%82%E5%BF%B5%E7%AF%87-%E7%B5%A6%E6%88%91%E4%B8%80%E6%AE%B5%E8%A9%B1-%E6%88%91%E5%91%8A%E8%A8%B4%E4%BD%A0%E9%87%8D%E9%BB%9E%E5%9C%A8%E5%93%AA-%E5%B0%8D%E6%96%87%E6%9C%AC%E9%87%8D%E9%BB%9E%E5%AD%97%E8%A9%9E%E5%8A%A0%E6%AC%8A%E7%9A%84tf-idf%E6%96%B9%E6%B3%95-f6a2790b4991">對文本重點字詞加權的TF-IDF方法</a></p></li>
</ul>



</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>nlp</category>
  <category>concept</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/nlp-note/</guid>
  <pubDate>Tue, 30 Sep 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/nlp-note/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>讀書筆記-數據分析的力量 Google、Uber都在用的因果關係思考法</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/book-review/book-review.html</link>
  <description><![CDATA[ 






<section id="書籍資訊簡介" class="level1">
<h1>書籍資訊簡介</h1>
<ul>
<li>數據分析的力量 Google、Uber都在用的因果關係思考法</li>
<li><a href="https://www.books.com.tw/products/0010779908?srsltid=AfmBOorDKOOHvAhDoc-UL6Lm9pYDZ4_boShGDfCS74wQSbI4cGq2TkFK">book information</a></li>
</ul>
<section id="隨機對照實驗-rct" class="level2">
<h2 class="anchored" data-anchor-id="隨機對照實驗-rct">隨機對照實驗 (RCT)</h2>
<p>將實驗區分為介入組跟比較組，藉由探究兩者的介入效果(Treatment Effect)，即：有介入的組-沒有介入的組的效果</p>
<ul>
<li><p>介入組：有受到政策/措施影響的組別</p></li>
<li><p>比較組：沒有受到政策/措施影響的組別</p></li>
</ul>
<blockquote class="blockquote">
<p>在商業領域，RCT = A/B test</p>
</blockquote>
<section id="假設" class="level3">
<h3 class="anchored" data-anchor-id="假設">假設</h3>
<ul>
<li>如果沒有介入(政策/措施的影響)，比較組的平均 <img src="https://latex.codecogs.com/png.latex?Y_C"> 會與介入組的平均 <img src="https://latex.codecogs.com/png.latex?Y_T"> 相同</li>
</ul>
</section>
<section id="注意點" class="level3">
<h3 class="anchored" data-anchor-id="注意點">注意點</h3>
<ol type="1">
<li><p>妥善建立群組</p></li>
<li><p>一定要隨機分組</p></li>
<li><p>各組樣本數需充足</p></li>
</ol>
</section>
<section id="優缺點" class="level3">
<h3 class="anchored" data-anchor-id="優缺點">優缺點</h3>
<p>優</p>
<ol type="1">
<li><p>因果關係強</p></li>
<li><p>分析手法與結果具透明性</p></li>
</ol>
<p>缺</p>
<ol type="1">
<li>實施成本高昂</li>
</ol>
</section>
</section>
<section id="rd-設計" class="level2">
<h2 class="anchored" data-anchor-id="rd-設計">RD 設計</h2>
<ul>
<li><p>當 RCT 無法使用時使用，可將 RD 設計想成在界線附近自然發生的 RCT。</p></li>
<li><p>利用自然實驗(= 猶如人工作出實驗的自然發生情況)分析</p></li>
</ul>
<section id="假設-1" class="level3">
<h3 class="anchored" data-anchor-id="假設-1">假設</h3>
<ul>
<li>如果 <img src="https://latex.codecogs.com/png.latex?X"> 未在界線上變動，<img src="https://latex.codecogs.com/png.latex?Y"> 就不會在界線上發生跳躍</li>
</ul>
<blockquote class="blockquote">
<p>實務上是個不容易證實的假設</p>
</blockquote>
</section>
<section id="注意點-1" class="level3">
<h3 class="anchored" data-anchor-id="注意點-1">注意點</h3>
<ul>
<li><p>要先找出「界線」上，僅因一種因素(<img src="https://latex.codecogs.com/png.latex?X">)發生不連續變化的情況</p></li>
<li><p>需要檢驗其他因素是否也會導致 <img src="https://latex.codecogs.com/png.latex?Y"> 在界線上發生跳躍，如果會，那 RD 假設就不成立，結果解釋會不準確。</p></li>
</ul>
</section>
<section id="優缺" class="level3">
<h3 class="anchored" data-anchor-id="優缺">優缺</h3>
<p>優：</p>
<ol type="1">
<li><p>只要不違反假設就可以證實因果關係</p></li>
<li><p>實施成本比 RCT 低廉</p></li>
</ol>
<p>缺：</p>
<ol type="1">
<li>只能測試在界線附近的人之因果關係，離界線很遠的人，需要加上其他假設，否則無法解釋</li>
</ol>
</section>
<section id="例子" class="level3">
<h3 class="anchored" data-anchor-id="例子">例子</h3>
<ul>
<li>南北地理界線的電價差異是否導致家庭用電量變動</li>
</ul>
</section>
</section>
<section id="堆集分析bunching-analysis" class="level2">
<h2 class="anchored" data-anchor-id="堆集分析bunching-analysis">堆集分析(Bunching Analysis)</h2>
<p>一樣在 RCT 無法使用時使用，利用資料階梯狀的變化分析因果關係</p>
<section id="假設-2" class="level3">
<h3 class="anchored" data-anchor-id="假設-2">假設</h3>
<ul>
<li>如果 <img src="https://latex.codecogs.com/png.latex?X"> 並<strong>未</strong>呈階梯狀變化，<img src="https://latex.codecogs.com/png.latex?Y">分布會平滑，不會在界線上堆集。</li>
</ul>
</section>
<section id="注意點-2" class="level3">
<h3 class="anchored" data-anchor-id="注意點-2">注意點</h3>
<ul>
<li><p>評估某個呈階梯狀變化的因素是否能用於分析</p></li>
<li><p>導致資料呈階梯狀變化的因素只有一個，沒有其他人</p></li>
<li><p>需要分析誘因大幅變化的分界點上發生的堆集，檢驗人 or 企業對誘因變化有甚麼反應/因果關係</p></li>
</ul>
</section>
<section id="例子-1" class="level3">
<h3 class="anchored" data-anchor-id="例子-1">例子</h3>
<ul>
<li><p>汽車越大台，油耗規定是否越寬鬆</p></li>
<li><p>所得稅的稅率是否影響工作方式</p></li>
</ul>
</section>
<section id="優缺-1" class="level3">
<h3 class="anchored" data-anchor-id="優缺-1">優缺</h3>
<p>優：</p>
<ol type="1">
<li><p>假設成立，效果便近似 RCT</p></li>
<li><p>分析結果易於理解</p></li>
</ol>
<p>缺：</p>
<ol type="1">
<li><p>只能推定分界點附近<strong>受到呈階梯狀變化誘因影響的主體</strong></p></li>
<li><p>假設更難證明成立</p></li>
</ol>
</section>
</section>
<section id="縱橫資料分析" class="level2">
<h2 class="anchored" data-anchor-id="縱橫資料分析">縱橫資料分析</h2>
<p>一樣在 RCT 無法使用時使用，假設政府/企業實施某項政策/措施時，有的群組有受到影響，有的沒有，此時：</p>
<ul>
<li><p>有受到政策/措施影響的群組為介入組</p></li>
<li><p>沒有受到影響的為比較組</p></li>
</ul>
<p>此外也有這兩個群組實施前跟實施後不同時間點的資料，就可以做縱橫資料分析，求出介入組在沒有介入(實施某項政策/措施)時的可能效果，進而得出此項政策/措施的介入效果(=實際介入效果-沒有介入時的可能效果)</p>
<section id="假設-3" class="level3">
<h3 class="anchored" data-anchor-id="假設-3">假設</h3>
<p>平行趨勢假設：</p>
<ul>
<li>如果沒有發生介入，介入組的平均結果(<img src="https://latex.codecogs.com/png.latex?Y_T">)和比較組的的平均結果(<img src="https://latex.codecogs.com/png.latex?Y_C">)就會平行推移</li>
</ul>
<p><img src="https://paperfishblog.netlify.app/posts/tech/book-review/not_picture.png" class="img-fluid"></p>
</section>
<section id="注意點-3" class="level3">
<h3 class="anchored" data-anchor-id="注意點-3">注意點</h3>
<p>主張平行趨勢假設成立需要做：</p>
<ul>
<li><p>蒐集介入發生以前的資料，調查介入發生之前平行趨勢假設是否存在介入組跟比較組之間</p></li>
<li><p>仔細檢查介入開始後，有無其他影響因素<strong>只</strong>影響介入組跟比較組中的其中一組</p></li>
<li><p>若平行趨勢假設成立，就將雙方的平均數推移化成圖表，測定介入效果的平均數</p></li>
</ul>
</section>
<section id="例子-2" class="level3">
<h3 class="anchored" data-anchor-id="例子-2">例子</h3>
<ul>
<li><p>所得稅與移民關係的因果關係</p></li>
<li><p>實施景氣刺激政策是否只會增加搶購需求</p></li>
</ul>
</section>
<section id="優缺-2" class="level3">
<h3 class="anchored" data-anchor-id="優缺-2">優缺</h3>
<p>優：</p>
<ol type="1">
<li><p>可應用範圍比 RD 設計跟堆集分析大</p></li>
<li><p>分析結果易於理解</p></li>
<li><p>可分析對象跟範圍比 RD 設計跟堆集分析大，可分析主體效果</p></li>
</ol>
<p>缺：</p>
<ol type="1">
<li>假設很難證明是否成立，也很難成立</li>
</ol>
</section>
</section>
</section>
<section id="方法論的限制" class="level1">
<h1>方法論的限制</h1>
<ol type="1">
<li><p>若資料有問題，分析手法再好也無法解決</p></li>
<li><p>分析結果不一定能推廣到其他群體</p></li>
<li><p>研究者的主見(偏見)會影響資料選取的對象，進而影響結果的可靠性</p></li>
<li><p>理論上對介入組實施介入不影響比較組，實務上可能會被推翻</p></li>
</ol>
</section>
<section id="延伸閱讀" class="level1">
<h1>延伸閱讀</h1>
<p>Econ 大一 ~ 大三程度</p>
<ul>
<li>Introductory Econometrics： A Modern Approach , Jeffrey M. Wooldridge</li>
</ul>
<p>Econ 大三大四程度</p>
<ul>
<li>Mostly Harmless Econometrics: An Empiricist’s Companion, Angrist, Joshua D., Pischke, Jorn-Steffen</li>
</ul>
<p>Econ 研究所程度</p>
<ul>
<li>Microeconometrics: Methods and Applications, Cameron, A. Colin, Trivedi, Pravin K</li>
</ul>
</section>
<section id="小心得" class="level1">
<h1>小心得</h1>
<p>因為作者是實證經濟學背景，讀起來有熟悉又陌生的感覺，裡面的東西感覺也跟實驗設計有連結。不過因為書籍的內容是設計給高中生閱讀，因此只有通識程度，詳細介紹與細部的數學推導可能還需要靠延伸閱讀裡的書籍介紹。</p>



</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>reading note</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/book-review/book-review.html</guid>
  <pubDate>Sun, 07 Sep 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/book-review/book.png" medium="image" type="image/png" height="118" width="144"/>
</item>
<item>
  <title>Side Project - 簡易預估搭乘新竹客運假日從新竹到台中的時間</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/side-project1/side-project1.html</link>
  <description><![CDATA[ 






<section id="前言" class="level1">
<h1>前言</h1>
<p>這個 Side Project 的發想完全來自我個人生活中的需求。</p>
<p>以前在大學時認識了一些台中人，或是畢業後留在台中工作的人，加上在在大學生活期間我愛上在台中的生活，畢業後即使在台北工作，有空時我還是會跟朋友約一約，前一天回到新竹，隔天搭乘新竹客運跟著新竹的朋友一起跑到朝馬去找老朋友吃飯、聊天、唱歌。不過這種難得的假日聚會，住在台中的朋友總是會事先負責訂位，確保我們都有店可去。而我們呢，則是負責不要遲到XD</p>
<p>通常為了避免遲到，我跟朋友通常會搭早一點的客運出發。長久搭下來，發現主要影響客運的因素為會不會在國道路上塞車，經驗上來說，到台中時都會塞一小段，而在市區移動的路程時間基本上都是固定的，不會塞車。自從知道交通部有國道車流的開放資料後，我就想嘗試看看用國道的資料建模，看看能不能用神奇的 “占卜” ，讓我們成為時間精算師，再也不要搭這麼早的客運了！XD</p>
</section>
<section id="需求架構" class="level1">
<h1>需求架構</h1>
<p>我的需求架構為：</p>
<ul>
<li>建一個簡單的網站，當使用者輸入客運出發時間時，會顯示客運預計到達朝馬(台灣大道)的時間。</li>
</ul>
<p>不過因為我手上只有國道的車流資料，實際客運會在市區停留靠站，所幸花費時間基本上都是固定的，所以我<strong>只有預測客運在國道上的時間</strong>，市區停留靠站固定設定為45分鐘。</p>
<p>此外，因為設備跟預算上的限制，我的預測資料是事先準備好(預測好)，再交由網站呈現。</p>
<p>最後做出來的東西，先用 Streamlit 套件來Demo：</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/side-project1/side-project1.png" class="img-fluid figure-img"></p>
<figcaption>初版demo畫面</figcaption>
</figure>
</div>
<p><del>但其實好像可以直接用HTML+CSS+JavaScripts靜態網頁的架構，因為我的模型沒有包在裡面XD</del></p>
<p>正式版的 demo 網址 <a href="https://sideprojectapp-hwmfunbcl8sadxbpuvte4p.streamlit.app/">點此</a></p>
<p>目前覺得各方面來說有很多需要改進的地方，待會來提。</p>
</section>
<section id="建立過程" class="level1">
<h1>建立過程</h1>
<p>主要流程為：</p>
<div class="cell" data-layout-align="default">
<div class="cell-output-display">
<div>
<p></p><figure class="figure"><p></p>
<div>
<pre class="mermaid mermaid-js">graph TD
    A["下載交通部資料"] --&gt; B["清洗資料"]
    B --&gt; C["補齊缺失資料"]
    C --&gt; D["區分 training set、valid set、test set"]
    D --&gt; E["LSTM 模型建模"]
    E --&gt; F["依路段產出資料"]
    F --&gt; G["計算到達所需時間"]
    G --&gt; H["Web 呈現"]
</pre>
</div>
<p></p></figure><p></p>
</div>
</div>
</div>
<p>因為這次使用的資料類型為 single time series，<del>加上懶得去找其他變量的關係</del>，所以完全沒有做特徵工程。</p>
<section id="資料來源簡介" class="level2">
<h2 class="anchored" data-anchor-id="資料來源簡介">資料來源簡介</h2>
<p>使用來源為交通度高公局交通資料庫的開放資料，<a href="https://tisvcloud.freeway.gov.tw/history/motc20/Section/">網址</a>。<sup>1</sup></p>
<p>我使用的欄位有：</p>
<ul>
<li>SectionID : 機關發布路段代碼<sup>2</sup></li>
</ul>
<ul>
<li>Travel_Speed :依據路況資訊蒐集起始時間至結束時間區間,計算期間所蒐集資料所得之平均旅行速度。(單位:km/h)<sup>3</sup></li>
</ul>
<blockquote class="blockquote">
<p>選擇這個欄位的原因是因為從定義上來看，Travel_Speed 較能代表客運實際速度。</p>
</blockquote>
<p>以及在檔名擷取時間。</p>
<p>訓練資料為 2024 年屬於國定假日範圍的資料，其中有 10 % 的資料屬於訓練階段的 valid set ， 其餘 90% 為 training set；用來測試模型的 test set 為 2025年上半年國定假日範圍的資料，期間為 1/1 ~ 8/30 。</p>
</section>
<section id="清資料" class="level2">
<h2 class="anchored" data-anchor-id="清資料">清資料</h2>
<p>主要流程為：</p>
<ol type="1">
<li><p>將資料按2024年國定假日、符合新竹-台中的 Section 按 SectionID 爬下來</p></li>
<li><p>補齊遺失值，基本使用 Cubic Spline 補齊遺失值，如果補出來的資料太不合理，例如嚴重超過速限，則考慮那一天的資料都移除。所幸只有228那天有問題，只移除這一天。</p></li>
<li><p>最後用敘事性統計觀察資料有無異常。</p></li>
</ol>
</section>
<section id="訓練資料" class="level2">
<h2 class="anchored" data-anchor-id="訓練資料">訓練資料</h2>
<p>流程：</p>
<ol type="1">
<li><p>區分 training set, valid set，test set 為2025上半年的資料。</p></li>
<li><p>訓練模型</p></li>
<li><p>計算valid set RMSE、畫圖觀察</p></li>
<li><p>確認OK後，拿 test set 試試</p></li>
<li><p>計算test set RMSE、畫圖觀察</p></li>
</ol>
<p>確認都可以後再依 section 產出預測資料。</p>
<section id="指定-lstm-的原因" class="level3">
<h3 class="anchored" data-anchor-id="指定-lstm-的原因">指定 LSTM 的原因</h3>
<p><del>因為我想玩還有我很懶</del>，原先在指定 LSTM 之前，有先考慮過使用 ARIMA 或是 SARIMA 模型來進行預測，不過指定這種經典的統計模型的缺點是滿吃對資料的觀察能力，我太菜了，沒有人在旁邊指導我撞牆可能會撞很久。此外，一直以來有耳聞過 LSTM 的用於 series data 的強大，也有看過一些與其他模型的預測能力比較的論文，<a href="https://ieeexplore.ieee.org/abstract/document/9691669/">像是這篇</a>。</p>
<p>神經網路家族的模型雖然複雜，但沒有特殊的統計假設；而且 LSTM 模型適合預測沒有突發事件影響的資料，我的資料搜查範圍僅限假日，基本上沒有突發事件影響。因此考慮後決定用 LSTM 模型來進行預測。</p>
</section>
<section id="模型架構" class="level3">
<h3 class="anchored" data-anchor-id="模型架構">模型架構</h3>
<p>試了好幾個參數後，得出的最好的模型架構與效果為</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> LSTMModel(nn.Module):</span>
<span id="cb1-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, input_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, hidden_layer_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>, output_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, num_layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb1-3">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb1-4">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.hidden_layer_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> hidden_layer_size</span>
<span id="cb1-5">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.num_layers <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> num_layers</span>
<span id="cb1-6"></span>
<span id="cb1-7">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.lstm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.LSTM(input_size, hidden_layer_size, num_layers, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.linear <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(hidden_layer_size, output_size)</span>
<span id="cb1-9"></span>
<span id="cb1-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, input_seq):</span>
<span id="cb1-11">        batch_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> input_seq.size(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-12">        h0 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.zeros(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.num_layers, batch_size, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.hidden_layer_size).to(input_seq.device)</span>
<span id="cb1-13">        c0 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.zeros(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.num_layers, batch_size, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.hidden_layer_size).to(input_seq.device)</span>
<span id="cb1-14"></span>
<span id="cb1-15">        lstm_out, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.lstm(input_seq, (h0, c0))</span>
<span id="cb1-16">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.linear(lstm_out[:, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, :])</span>
<span id="cb1-17">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span></code></pre></div>
</section>
<section id="模型參數設定" class="level3">
<h3 class="anchored" data-anchor-id="模型參數設定">模型參數設定</h3>
<p>基本參數為</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 訓練參數</span></span>
<span id="cb2-2">seq_length <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">15</span></span>
<span id="cb2-3">predict_step <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">15</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#一次預測15分鐘</span></span>
<span id="cb2-4">epochs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">30</span></span>
<span id="cb2-5">patience <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># early stop</span></span>
<span id="cb2-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Early stopping at epoch 12.</span></span></code></pre></div>
<p>最終設定的 LSTM 模型指定參數為</p>
<pre><code>LSTMModel(
  (lstm): LSTM(4, 16, batch_first=True)
  (linear): Linear(in_features=16, out_features=1, bias=True)
)</code></pre>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">device <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.device(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cuda'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> torch.cuda.is_available() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cpu'</span>)</span>
<span id="cb4-2">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> LSTMModel().to(device)</span>
<span id="cb4-3">loss_function <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.MSELoss()</span>
<span id="cb4-4">optimizer <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.001</span>)</span></code></pre></div>
<p>使用的時間欄位來自擷取資料時，原始資料標記在資料夾及檔名的資訊，再分成多個欄位作為變數丟入模型裡，目前有：小時、月份、星期。</p>
</section>
<section id="相關訓練數據" class="level3">
<h3 class="anchored" data-anchor-id="相關訓練數據">相關訓練數據</h3>
<p>RMSE 部分 (套用到此資料集的對應單位：km/hr)：</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>set</th>
<th>RMSE</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>valid set</td>
<td>3.0401</td>
</tr>
<tr class="even">
<td>test set</td>
<td>6.0785</td>
</tr>
</tbody>
</table>
<p><img src="https://paperfishblog.netlify.app/posts/tech/side-project1/side-project2.png" class="img-fluid"> ▲ valid set 前 200 筆真實值(藍線)跟預測值(橘線)觀察圖</p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/side-project1/side-project3.png" class="img-fluid"> ▲ test set 真實值(藍線)跟預測值(橘線)觀察圖，可以看到除了部分超過速限的資料外，整體還行。</p>
<p>無論是 valid set 還是 test set 的 RMSE 跟繪圖結果，看起來都相當不錯。換算成時間與我自己搭客運的經驗，應該只有 9 分鐘的誤差。</p>
</section>
</section>
</section>
<section id="需要改進的地方" class="level1">
<h1>需要改進的地方</h1>
<section id="模型預測部分" class="level2">
<h2 class="anchored" data-anchor-id="模型預測部分">模型預測部分</h2>
<p>經過一連串的調整後，目前處於觀察預測能力到底好不好用的階段。不過因為我用的 test set 只到 2025/8/30，後續的維護 &amp; 重新訓練流程還需要再想想。此外，目前的流程為事先在本地預測好資料，再把預測好資料當成資料庫，在 Streamlit 的腳本下處理。這是因為 LSTM 預測資料需要花費很多時間，不可能讓使用者等待太久。未來可以如何調整也需要再想想。</p>
<!-- 雖然 LSTM 模型的表現從數據看起來相當不錯，不過實際預測結果的 TravelSpeed 卻經常保持在平穩的線上，這跟我實際搭車的經驗不太符合，細想後我認為有 2 個原因：

1. TravelSpeed 的定義跟搭乘客運的時速沒有關係

2. LSTM 模型適合預測沒有遭遇突發重大變化的資料，反過來說當沒有太大變化的資料時間步細分成數分鐘時，LSTM 模型的預測也不會有太大的變化

其中 1. 可能是主要原因，原先我假定一個路段間的車速也相當於客運開車的時速，但細想起來，**小型車的數量會多過包含客運在內的大型車，而他們的平均時速通常都會比大型車快**，也就是說我估出來的資料可能高估，只能說資料定義與想要探究的問題連結真的真的很重要QQ -->
</section>
<section id="uiux-設計及部署" class="level2">
<h2 class="anchored" data-anchor-id="uiux-設計及部署">UIUX 設計及部署</h2>
<p>目前將資料部署在 streamlit 平台上，雖然有不用切換程式語言、使用語言直觀簡潔、不用自己重新設計 UIUX 等優點，但實際部署後網路載入速度實在是太慢了，而且 RWD 設計也很陽春。</p>
<p>此外，streamlit could app 會將太久沒有人造訪的網站休眠，頻率大概是超過一天就不行，對我這種使用率低的app來說不是很有利。目前背後的處理資料原理也可以用 javascrip 完成，未來用 GitHub Page 部署會更好。</p>



</section>
</section>


<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">腳註</h2>

<ol>
<li id="fn1"><p>資料庫欄位解釋請參照<a href="https://drive.google.com/file/d/12GzEhuiZa7yJc5zFGVEn8U9UJR22RrGy/view">交通部官方文件</a>↩︎</p></li>
<li id="fn2"><p>交通部官方文件，頁97。↩︎</p></li>
<li id="fn3"><p>交通部官方文件，頁97。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>side project</category>
  <category>python</category>
  <category>LSTM</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/side-project1/side-project1.html</guid>
  <pubDate>Sat, 06 Sep 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/side-project1/side-project1.png" medium="image" type="image/png" height="66" width="144"/>
</item>
<item>
  <title>重新認識pytorch(3)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/pytorch3/pytorch3.html</link>
  <description><![CDATA[ 






<p>LSTM（Long Short-Term Memory）模型是 RNN 的延伸，主要目的在於改善 RNN 梯度爆炸以及長期依賴的問題。</p>
<p>具體來說，一般結構簡單的 RNN 使用的激勵函數為 Sigomid、tah，在沒有記憶性質的 NN 裡最多也只能疊6層 layers，否則在反向傳遞誤差時，<strong>誤差會隨著層數增加而減少，無法有效更新權重</strong>，有記憶性質的 RNN 在反向傳遞的誤差還會受到序列(例如從 <img src="https://latex.codecogs.com/png.latex?t+1"> 到 <img src="https://latex.codecogs.com/png.latex?t">)的影響，因此 <strong>一般 RNN 無法學習太長的 series data</strong>。</p>
<p>要改善 RNN 的問題，一般會朝兩個方向改進：使用更複雜的模型結構，或是與其他模型結合組成混合式深度學習。</p>
<p>LSTM 就是採前者的作法，使得他可以學習長期 series data，代價是，他的結構複雜，執行速度也拖慢很多。<del>一個 ep</del></p>
<blockquote class="blockquote">
<p>主要內容引用自 「李金洪. 2022. 全格局使用 PyTorch - 深度學習和圖神經網路 - 基礎篇. 深智數位」第 7.5 節。</p>
</blockquote>
<section id="lstm-的模型結構" class="level1">
<h1>LSTM 的模型結構</h1>
<p>懶得畫，直接找別人ㄉ</p>
<p><img src="https://raw.githubusercontent.com/aaubs/ds-master/main/data/Images/LSTM_cellstate.png" class="img-fluid"></p>
<p><a href="https://colab.research.google.com/github/aaubs/ds-master/blob/main/notebooks/M3_LSTM_Tutorial_v3.ipynb#scrollTo=8RuUMQ3WezoZ">來源</a></p>
<p>與一般 RNN 相比， LSTM 主要增加了 3 個東西</p>
<ol type="1">
<li>遺忘門</li>
<li>輸入門</li>
<li>輸出門</li>
</ol>
<section id="遺忘門" class="level2">
<h2 class="anchored" data-anchor-id="遺忘門">遺忘門</h2>
<p>遺忘門的作用是決定甚麼時候需要把以前的狀態忘記。LSTM 的遺忘門主要由三部分組成：</p>
<ul>
<li><p>輸入（<img src="https://latex.codecogs.com/png.latex?x_t">）：當前時刻的輸入。</p></li>
<li><p>隱藏狀態（<img src="https://latex.codecogs.com/png.latex?h_t"> − <img src="https://latex.codecogs.com/png.latex?h_%7Bt%E2%88%921%7D">）：前一時間步的隱藏狀態。</p></li>
<li><p>遺忘門的激勵函數：決定了多少先前的記憶被丟棄。</p></li>
</ul>
<p>所以寫成數學式為：</p>
<p><img src="https://latex.codecogs.com/png.latex?f_t%20=%20%5Csigma%20(%20W_f%20%5Ccdot%20%5Bh_%7Bt-1%7D,%20x_t%5D%20+%20b_f%20)"></p>
<p>其中：</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?f_t">：遺忘門的輸出結果</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Csigma">：激勵函數</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?b_f">：遺忘門的 bais</p></li>
</ul>
</section>
<section id="輸入門" class="level2">
<h2 class="anchored" data-anchor-id="輸入門">輸入門</h2>
<p>輸入門有兩個功能，一個是找到需要更新的細胞狀態，另一個是把需要更新的資訊更新到細胞狀態裡。具體而言數學式為：</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?i_t%20=%20%5Csigma%20(W_i%20%5Ccdot%20%5Bh_%7Bt-1%7D,x_t%5D%20+%20b_i)"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?%5Chat%20C_t%20=%20%5Ctanh%20(W_C%20%5Ccdot%20%5Bh_%7Bt-1%7D,x_t%5D%20+%20b_C)"></p></li>
</ul>
<p>其中：</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?i_t">︰要更新的細胞狀態</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?h_%7Bt-1%7D">︰前一個時間點的模型輸出</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?W_i">：計算 <img src="https://latex.codecogs.com/png.latex?i_t"> 的權重</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?W_C">：計算 <img src="https://latex.codecogs.com/png.latex?%5Chat%20C_t"> 的權重</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?b_i">：計算 <img src="https://latex.codecogs.com/png.latex?%5Chat%20C_t"></p></li>
</ul>
<p>當遺忘門找到需要忘記的資訊<img src="https://latex.codecogs.com/png.latex?f_t">時，會將其與舊的狀態相乘，然後再與輸入門產生的 <img src="https://latex.codecogs.com/png.latex?i_t%20%5Ctimes%20%5Chat%20C_%7Bt-1%7D">相加，使細胞獲得新的資訊，完成細胞狀態的更新，數學式為：</p>
<p><img src="https://latex.codecogs.com/png.latex?C_t%20=%20f_t%20%5Ctimes%20C_%7Bt-1%7D%20+%20i_t%20%5Ctimes%20%5Chat%20C_t"></p>
</section>
<section id="輸出門" class="level2">
<h2 class="anchored" data-anchor-id="輸出門">輸出門</h2>
<p>輸出門會透過一個啟動函數層來確定哪部份的資訊要被輸出，接著決定模型在特定時間點 <img src="https://latex.codecogs.com/png.latex?t"> 的數學結果，具體數學式如下：</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?o_t%20=%20%5Csigma%20(W_o%20%5Ccdot%20%5Bh_%7Bt-1%7D,x_t%5D%20+%20b_o)"></p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?h_t%20=%20o_t%20%5Ctimes%20(C_t)"></p></li>
</ul>
</section>
</section>
<section id="實作退位減法" class="level1">
<h1>實作：退位減法</h1>
<p>為了跟前面的手刻 RNN 比較，再拿退位減法當例子<del>雖然很大才小用</del>。再複述一次退位減法的語法結構：</p>
<p><img src="https://latex.codecogs.com/png.latex?a%20-%20b%20=%20c"></p>
<p>其中 a、b、c都是整數，且<img src="https://latex.codecogs.com/png.latex?a%3Eb">，<strong>我們的目標是a、b已知的前提下，印出來的c要是正確的</strong>，先來生成資料(a、b、c)。</p>
<p>資料生成大致與上次相同，不同的是多了將資料轉成可供 pytorch 的 <code>DataLoader</code> 格式，並且區分訓練集跟驗證集：</p>
<div id="cell-5" class="cell" data-execution_count="1">
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> torch.utils.data <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Dataset, DataLoader</span>
<span id="cb1-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> random</span>
<span id="cb1-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定隨機種子以保證可重現性</span></span>
<span id="cb1-6">SEED <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">822</span></span>
<span id="cb1-7">np.random.seed(SEED)</span>
<span id="cb1-8">torch.manual_seed(SEED)</span>
<span id="cb1-9">random.seed(SEED)</span>
<span id="cb1-10"></span>
<span id="cb1-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> requires_borrow(a, b):</span>
<span id="cb1-12">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""檢查 a - b 是否會需要退位（borrow）"""</span></span>
<span id="cb1-13">    a_str <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(a)[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 反轉為從低位開始</span></span>
<span id="cb1-14">    b_str <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(b)[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb1-15"></span>
<span id="cb1-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(b_str)):</span>
<span id="cb1-17">        a_digit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(a_str[i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(a_str) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb1-18">        b_digit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(b_str[i])</span>
<span id="cb1-19">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> a_digit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> b_digit:</span>
<span id="cb1-20">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb1-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span></span>
<span id="cb1-22"></span>
<span id="cb1-23"></span>
<span id="cb1-24">largest_number <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">255</span></span>
<span id="cb1-25">binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 每個數字最多 8 bits</span></span>
<span id="cb1-26"></span>
<span id="cb1-27">int2binary <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb1-28">binary <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.unpackbits(</span>
<span id="cb1-29">    np.array([<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>binary_dim)], dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>np.uint8).T, axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb1-30">)</span>
<span id="cb1-31"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>binary_dim):</span>
<span id="cb1-32">    int2binary[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> binary[i]</span>
<span id="cb1-33"></span>
<span id="cb1-34">data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb1-35"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(data) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>:</span>
<span id="cb1-36">    a_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.randint(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, largest_number)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 最大為 254</span></span>
<span id="cb1-37">    b_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.randint(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, a_int)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># b &lt; a，且最小是 1</span></span>
<span id="cb1-38"></span>
<span id="cb1-39">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> requires_borrow(a_int, b_int):</span>
<span id="cb1-40">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">continue</span></span>
<span id="cb1-41"></span>
<span id="cb1-42">    a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[a_int]</span>
<span id="cb1-43">    b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[b_int]</span>
<span id="cb1-44">    c_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> a_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> b_int</span>
<span id="cb1-45">    c <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[c_int]</span>
<span id="cb1-46"></span>
<span id="cb1-47">    data.append((a, b, c))</span>
<span id="cb1-48"></span>
<span id="cb1-49"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> BinarySubtractionDataset(Dataset):</span>
<span id="cb1-50">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, data):</span>
<span id="cb1-51">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.samples <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb1-52">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, b, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> data:</span>
<span id="cb1-53">            x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(a[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], b[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])), dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>np.float32)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># time_steps x input_dim</span></span>
<span id="cb1-54">            y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> c[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].astype(np.float32)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 目標也反轉（低位在前）</span></span>
<span id="cb1-55">            <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.samples.append((x, y))</span>
<span id="cb1-56"></span>
<span id="cb1-57">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__len__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb1-58">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.samples)</span>
<span id="cb1-59"></span>
<span id="cb1-60">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__getitem__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, idx):</span>
<span id="cb1-61">        x, y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.samples[idx]</span>
<span id="cb1-62">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> torch.tensor(x), torch.tensor(y)</span>
<span id="cb1-63"></span>
<span id="cb1-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 建立資料集</span></span>
<span id="cb1-65">dataset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> BinarySubtractionDataset(data)</span>
<span id="cb1-66">train_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(dataset))</span>
<span id="cb1-67">test_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(dataset) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> train_size</span>
<span id="cb1-68"></span>
<span id="cb1-69">train_set, test_set <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.utils.data.random_split(dataset, [train_size, test_size])</span>
<span id="cb1-70">train_loader <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> DataLoader(train_set, batch_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, shuffle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-71">test_loader <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> DataLoader(test_set, batch_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>)</span></code></pre></div>
</div>
<p>然後來製作模型</p>
<section id="模型定義" class="level2">
<h2 class="anchored" data-anchor-id="模型定義">模型定義</h2>
<p>LSTM 模型的結構雖然複雜，但在 pytorch 裡的語法結構與普通的 NN 差不多。只是本次的課題要處理減法算出的數字的二進位版本，輸出會是由 0 與 1 組成的序列，比較特殊，因此模型結構也以此設計。先來看看一般 LSTM 模型的架構在pythorch中是什麼樣子，以下是一個是個通用範例：</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> LSTMModel(nn.Module):</span>
<span id="cb2-5">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, input_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, hidden_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, output_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb2-6">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>(LSTMModel, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>).<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb2-7">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.hidden_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> hidden_size</span>
<span id="cb2-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.lstm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.LSTM(input_size, hidden_size, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb2-9">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.fc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(hidden_size, output_size)</span>
<span id="cb2-10">    </span>
<span id="cb2-11">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x):</span>
<span id="cb2-12">            out, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.lstm(x)    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [batch, seq_len, hidden_size]</span></span>
<span id="cb2-13">            out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.fc(out)       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [batch, seq_len, output_size]</span></span>
<span id="cb2-14">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span></code></pre></div>
<p>可以看到與之前看到的 NN 模型類似，都需要用一個 class 包覆，也需要設定 input、output、hidden數，以及正向傳遞。</p>
<p>由於本次課題的特殊性，本次使用的模型多了一個 <code>self.sigmoid = nn.Sigmoid()</code>，這是為了跟後續 loss function 的設定 <code>BCELoss()</code>配合。模型設定如下：</p>
<div id="cell-7" class="cell" data-execution_count="2">
<div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> LSTMSubtractor(nn.Module):</span>
<span id="cb3-4">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, input_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, hidden_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>, output_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb3-5">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>(LSTMSubtractor, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>).<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb3-6">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.lstm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.LSTM(input_size, hidden_size, batch_first<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb3-7">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.fc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(hidden_size, output_size)</span>
<span id="cb3-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.sigmoid <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Sigmoid()</span>
<span id="cb3-9"></span>
<span id="cb3-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x):</span>
<span id="cb3-11">        lstm_out, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.lstm(x)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># output: (batch, seq_len, hidden)</span></span>
<span id="cb3-12">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.fc(lstm_out)     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (batch, seq_len, 1)</span></span>
<span id="cb3-13">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.sigmoid(out)    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># predict bit 0 or 1</span></span></code></pre></div>
</div>
<section id="參數設定與模型訓練" class="level3">
<h3 class="anchored" data-anchor-id="參數設定與模型訓練">參數設定與模型訓練</h3>
<p>參數設定如下：</p>
<p>接著來設定參數：</p>
<div id="cell-10" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">device <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.device(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cuda"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> torch.cuda.is_available() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cpu"</span>)</span>
<span id="cb4-2">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> LSTMSubtractor().to(device)</span>
<span id="cb4-3"></span>
<span id="cb4-4">criterion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.BCELoss()</span>
<span id="cb4-5">optimizer <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb4-6"></span>
<span id="cb4-7">EPOCHS <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span></span></code></pre></div>
</div>
<p>這裡要解釋 <code>nn.BCELoss()</code> 是什麼東西，根據 <a href="https://docs.pytorch.org/docs/stable/generated/torch.nn.BCELoss.html">pytorch 官方文件</a> ，它的數學式為：</p>
<p>for all sample size <img src="https://latex.codecogs.com/png.latex?n"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cell_n%20=%20-%20w_n%20%5Cleft%5B%20y_n%20%5Ccdot%20%5Clog(x_n)%20+%20(1%20-%20y_n)%20%5Ccdot%20%5Clog(1%20-%20x_n)%20%5Cright%5D%0A"></p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?x_n">：模型輸出機率（預測值），需介於 0 與 1 之間</li>
<li><img src="https://latex.codecogs.com/png.latex?y_n">：真實值（0 或 1）</li>
<li><img src="https://latex.codecogs.com/png.latex?w_n">：逐樣本權重（若未指定權重則預設為 1）</li>
</ul>
<p>然後 <img src="https://latex.codecogs.com/png.latex?%5Cell_n"> 可以組成向量 <img src="https://latex.codecogs.com/png.latex?L%20=%20(l_1,%20%5Cdots%20,%20l_n)%5ET">，接著按照裡面的設定不同，輸出也會不同：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cell(x,%20y)%20=%0A%5Cbegin%7Bcases%7D%0A%5Ctext%7Bmean%7D(L),%20&amp;%20%5Ctext%7Bif%20reduction='mean'%7D%20%5C%5C%0A%5Ctext%7Bsum%7D(L),%20&amp;%20%5Ctext%7Bif%20reduction='sum'%7D%0A%5Cend%7Bcases%7D"></p>
<p>整批次資料的損失根據 <code>reduction</code> 的設定方式不同，被輸出為一個 scalar：</p>
<ul>
<li><p><strong><code>'mean'</code>（預設）</strong>：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctext%7Bloss%7D%20=%20%5Cfrac%7B1%7D%7BN%7D%20%5Csum_%7Bn=1%7D%5E%7BN%7D%20%5Cell_n%0A"></p></li>
<li><p><strong><code>'sum'</code></strong>：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctext%7Bloss%7D%20=%20%5Csum_%7Bn=1%7D%5E%7BN%7D%20%5Cell_n%0A"></p></li>
<li><p><strong><code>'none'</code></strong>： 保持逐元素損失，不做處理，輸出與輸入相同</p></li>
</ul>
<p>由於 <img src="https://latex.codecogs.com/png.latex?x_n%20=%200"> 或 <img src="https://latex.codecogs.com/png.latex?x_n%20=%201"> 時會導致 <img src="https://latex.codecogs.com/png.latex?%5Clog(0)"> 出現負無限大，PyTorch 在實作中將 log 的最小值限制為 <img src="https://latex.codecogs.com/png.latex?-100">，避免出現無限大或梯度爆炸的情況，確保訓練穩定。</p>
<p>接著，我們將　epoch　(模型掃過資料的次數)設為 100，對訓練集訓練：</p>
<div id="cell-12" class="cell" data-execution_count="4">
<div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"></span>
<span id="cb5-2"></span>
<span id="cb5-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(EPOCHS):</span>
<span id="cb5-4">    model.train()</span>
<span id="cb5-5">    total_loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb5-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> x_batch, y_batch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> train_loader:</span>
<span id="cb5-7">        x_batch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x_batch.to(device)</span>
<span id="cb5-8">        y_batch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> y_batch.to(device).unsqueeze(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (batch, 8, 1)</span></span>
<span id="cb5-9"></span>
<span id="cb5-10">        optimizer.zero_grad()</span>
<span id="cb5-11">        output <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(x_batch)</span>
<span id="cb5-12">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> criterion(output, y_batch)</span>
<span id="cb5-13">        loss.backward()</span>
<span id="cb5-14">        optimizer.step()</span>
<span id="cb5-15">        total_loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> loss.item()</span>
<span id="cb5-16"></span>
<span id="cb5-17">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Epoch </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>epoch<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">/</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>EPOCHS<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, Loss: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>total_loss<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>, end<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\r</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span>)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>Epoch 100/100, Loss: 0.0002</code></pre>
</div>
</div>
<p>定義預測函數、進行預測後，再來查看在測試集的效果：</p>
<div id="cell-14" class="cell" data-execution_count="5">
<div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> binary2int(binary_array):</span>
<span id="cb7-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""將二進位 ndarray 轉換為十進位整數"""</span></span>
<span id="cb7-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(b)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> binary_array), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb7-4"></span>
<span id="cb7-5"></span>
<span id="cb7-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> predict(model, a_int, b_int):</span>
<span id="cb7-7">    model.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">eval</span>()</span>
<span id="cb7-8">    a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[a_int]</span>
<span id="cb7-9">    b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[b_int]</span>
<span id="cb7-10">    x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(a[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], b[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])), dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>np.float32)</span>
<span id="cb7-11">    x_tensor <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor(x).unsqueeze(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>).to(device)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 8, 2)</span></span>
<span id="cb7-12"></span>
<span id="cb7-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> torch.no_grad():</span>
<span id="cb7-14">        pred <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(x_tensor).squeeze().cpu().numpy()</span>
<span id="cb7-15"></span>
<span id="cb7-16">    pred_bits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>(pred).astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>)[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 反轉回原本順序</span></span>
<span id="cb7-17">    pred_val <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>([bit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> i) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, bit <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(pred_bits[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])])</span>
<span id="cb7-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> pred_val, pred_bits</span></code></pre></div>
</div>
</section>
</section>
<section id="結果" class="level2">
<h2 class="anchored" data-anchor-id="結果">結果</h2>
<div id="cell-16" class="cell" data-execution_count="6">
<div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> bits_to_int(bit_tensor):</span>
<span id="cb8-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""將 bit tensor 轉為十進位整數（從低位開始）"""</span></span>
<span id="cb8-3">    bits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bit_tensor.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>().numpy().tolist()</span>
<span id="cb8-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(b) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> bits[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 注意反轉</span></span>
<span id="cb8-5"></span>
<span id="cb8-6"></span>
<span id="cb8-7"></span>
<span id="cb8-8">Error_list <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb8-9"></span>
<span id="cb8-10"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> j <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(test_set)):</span>
<span id="cb8-11">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 讀一筆</span></span>
<span id="cb8-12">    x, y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> test_set[j]</span>
<span id="cb8-13"></span>
<span id="cb8-14">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 拆開 a 和 b 的 bits（注意 shape 是 [seq_len, 2]）</span></span>
<span id="cb8-15">    a_bits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb8-16">    b_bits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb8-17">    c_bits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> y</span>
<span id="cb8-18">    </span>
<span id="cb8-19"></span>
<span id="cb8-20">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 還原整數</span></span>
<span id="cb8-21">    a_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bits_to_int(a_bits)</span>
<span id="cb8-22">    b_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bits_to_int(b_bits)</span>
<span id="cb8-23">    c_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bits_to_int(c_bits)  </span>
<span id="cb8-24">    d <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> predict(model, a_int, b_int)</span>
<span id="cb8-25">    </span>
<span id="cb8-26">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 紀錄誤差</span></span>
<span id="cb8-27">    error <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(c_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> d[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]) </span>
<span id="cb8-28">    Error_list.append(error)     </span>
<span id="cb8-29">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 取其中的五筆觀察</span></span>
<span id="cb8-30">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> j <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>: </span>
<span id="cb8-31">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 顯示</span></span>
<span id="cb8-32">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"True value:　</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>c_int<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> ; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>c<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb8-33">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Predicted value: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>d[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> ; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>d[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb8-34">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Predicted formula: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a_int<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> - </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b_int<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>d[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb8-35">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"---------------"</span>)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>True value:　62 ; [0 0 0 0 1 1 1 1]
Predicted value: 62 ; [0 0 1 1 1 1 1 0]
Predicted formula: 123 - 61 = 62
---------------
True value:　24 ; [0 0 0 0 1 1 1 1]
Predicted value: 24 ; [0 0 0 1 1 0 0 0]
Predicted formula: 118 - 94 = 24
---------------
True value:　116 ; [0 0 0 0 1 1 1 1]
Predicted value: 116 ; [0 1 1 1 0 1 0 0]
Predicted formula: 250 - 134 = 116
---------------
True value:　137 ; [0 0 0 0 1 1 1 1]
Predicted value: 137 ; [1 0 0 0 1 0 0 1]
Predicted formula: 204 - 67 = 137
---------------
True value:　185 ; [0 0 0 0 1 1 1 1]
Predicted value: 185 ; [1 0 1 1 1 0 0 1]
Predicted formula: 245 - 60 = 185
---------------</code></pre>
</div>
</div>
<p>把誤差化成圖：</p>
<div id="cell-18" class="cell" data-execution_count="7">
<div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt </span>
<span id="cb10-2">plt.figure(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>))</span>
<span id="cb10-3">plt.plot( Error_list, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Absolute Error"</span>)</span>
<span id="cb10-4">plt.xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Sample Index"</span>)</span>
<span id="cb10-5">plt.ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Error Value"</span>)</span>
<span id="cb10-6">plt.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prediction Error per Test Sample"</span>)</span>
<span id="cb10-7">plt.grid(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb10-8">plt.legend()</span>
<span id="cb10-9">plt.show()</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch3/pytorch3_files/figure-html/cell-8-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<p>在測試集的表現非常好，順便來介紹 &amp; 計算常用模型評估指標，RMSE。RMSE（Root Mean Squared Error）為 MSE 的平方根，是常用的模型誤差評估指標。</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctext%7BRMSE%7D%20=%20%5Csqrt%7B%20%5Cfrac%7B1%7D%7Bn%7D%20%5Csum_%7Bi=1%7D%5E%7Bn%7D%20(%5Chat%7By%7D_i%20-%20y_i)%5E2%20%7D%20=%20%5Csqrt%7B%5Ctext%7BMSE%7D%7D%0A"></p>
<div id="cell-20" class="cell" data-execution_count="8">
<div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb11-2"></span>
<span id="cb11-3">true_vals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb11-4">pred_vals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb11-5"></span>
<span id="cb11-6"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> x, y <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> test_set:</span>
<span id="cb11-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 還原 a、b 的 bit 並轉為十進位</span></span>
<span id="cb11-8">    a_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bits_to_int(x[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb11-9">    b_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bits_to_int(x[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb11-10">    true_val <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bits_to_int(y)</span>
<span id="cb11-11"></span>
<span id="cb11-12">    pred_val, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> predict(model, a_int, b_int)</span>
<span id="cb11-13"></span>
<span id="cb11-14">    true_vals.append(true_val)</span>
<span id="cb11-15">    pred_vals.append(pred_val)</span>
<span id="cb11-16"></span>
<span id="cb11-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 轉為 numpy 陣列</span></span>
<span id="cb11-18">true_vals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array(true_vals)</span>
<span id="cb11-19">pred_vals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array(pred_vals)</span>
<span id="cb11-20">errors <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(true_vals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> pred_vals)</span>
<span id="cb11-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 計算 RMSE</span></span>
<span id="cb11-22">rmse <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.sqrt(np.mean((true_vals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> pred_vals) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb11-23"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"RMSE: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>rmse<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>RMSE: 0.0000</code></pre>
</div>
</div>
<div id="cell-21" class="cell" data-execution_count="9">
<div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt </span>
<span id="cb13-2">plt.figure(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>))</span>
<span id="cb13-3">x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [ x <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> x <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">501</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1001</span>)]</span>
<span id="cb13-4">plt.plot( errors, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Absolute Error"</span>)</span>
<span id="cb13-5">plt.xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Sample Index"</span>)</span>
<span id="cb13-6">plt.ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Error Value"</span>)</span>
<span id="cb13-7">plt.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prediction Error per Test Sample"</span>)</span>
<span id="cb13-8">plt.grid(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb13-9">plt.legend()</span>
<span id="cb13-10">plt.show()</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch3/pytorch3_files/figure-html/cell-10-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<p>這裡因為誤差是 0 RMSE 想當然爾也是0囉。</p>
</section>
</section>
<section id="補充gru-模型" class="level1">
<h1>補充：GRU 模型</h1>
<p>GRU 模型與 LSTM 功能幾乎一樣，差異在<strong>GRU將遺忘門和輸入門簡化為單一一個門，同時將細胞狀態和隱藏狀態結合為一個狀態</strong>，大幅簡化原先 LSTM 模型的設計。</p>
<section id="gru-延伸閱讀" class="level2">
<h2 class="anchored" data-anchor-id="gru-延伸閱讀">GRU 延伸閱讀</h2>
<ul>
<li><a href="https://tengyuanchang.medium.com/%E6%AF%94%E8%BC%83%E9%95%B7%E7%9F%AD%E6%9C%9F%E8%A8%98%E6%86%B6%E6%A8%A1%E5%9E%8B-lstm-%E8%88%87%E6%94%B9%E8%89%AF%E5%BE%8C%E7%9A%84%E9%81%9E%E6%AD%B8%E7%A5%9E%E7%B6%93%E7%B6%B2%E8%B7%AF%E6%A8%A1%E5%9E%8B-gru-813dec22ec6d">LSTM與GRU比較論文摘要</a></li>
</ul>



</section>
</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>note</category>
  <category>python</category>
  <category>pytorch</category>
  <category>LSTM</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/pytorch3/pytorch3.html</guid>
  <pubDate>Wed, 27 Aug 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/pytorch3/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>重新認識pytorch(2)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/pytorch2/pytorch2.html</link>
  <description><![CDATA[ 






<p>RNN，全名為循環神經網路(Recurrent Neural Network)，是具有記憶功能的模型。它可以發現樣本彼此之間的相互關係，適合處理 series data 的特徵 ，可應用於處理文字資料分類及time series等。</p>
<p>RNN 的主要限制有：</p>
<ul>
<li><p><strong>梯度消失或爆炸</strong>：RNN 訓練深層時會不穩定</p></li>
<li><p><strong>長期依賴問題</strong>：無法記住太久以前的資訊</p></li>
<li><p>因此實務上通常用改良版：</p>
<ul>
<li>LSTM（Long Short-Term Memory）</li>
<li>GRU（Gated Recurrent Unit）</li>
</ul></li>
</ul>
<p>現今更常用的是 Transformer 模型，但 RNN 在資源有限或學習原理時仍很有價值。本次暫時不使用 pytorch 套件，除了簡單介紹 RNN 外，也會藉由實作手刻一個簡單的 RNN。</p>
<blockquote class="blockquote">
<p>主要內容引用自 「李金洪. 2022. 全格局使用 PyTorch - 深度學習和圖神經網路 - 基礎篇. 深智數位」第 7.5 節。</p>
</blockquote>
<section id="rnn-的模型結構" class="level1">
<h1>RNN 的模型結構</h1>
<div id="cell-2" class="cell" data-execution_count="1">
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch2/pytorch2_files/figure-html/cell-2-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<p><img src="https://latex.codecogs.com/png.latex?%5C%7Bx_t%5C%7D">表示 input；<img src="https://latex.codecogs.com/png.latex?%5C%7By_t%5C%7D"> 表示 output； <img src="https://latex.codecogs.com/png.latex?%5C%7Bh_t%5C%7D">表示 hidden layer；藍色箭頭表示依順序( 即 time series 中的 <img src="https://latex.codecogs.com/png.latex?t"> )產生的權重，在一開始還沒有東西，隨著 <img src="https://latex.codecogs.com/png.latex?t"> 增加而影響每次產生的 output，這就是 RNN 的記憶功能特性。</p>
<p>上圖為模型逐漸隨著<img src="https://latex.codecogs.com/png.latex?t%20=%205">增加而呈現的流程圖，如果是一般情形則如下圖：</p>
<div id="cell-4" class="cell" data-execution_count="1">
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch2/pytorch2_files/figure-html/cell-3-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
</section>
<section id="實作退位減法" class="level1">
<h1>實作：退位減法</h1>
<p>國小學的直式減法中，個位數不夠時會先向十位數字借位，再做減法，這種向前一位借數字再相減的動作叫做退位減法。在此我們利用退位減法，刻一個簡單 RNN ，藉此了解 RNN。退位減法的語法結構像這樣：</p>
<p><img src="https://latex.codecogs.com/png.latex?a%20-%20b%20=%20c"></p>
<p>其中 a、b、c都是整數，且<img src="https://latex.codecogs.com/png.latex?a%3Eb"> 與 <img src="https://latex.codecogs.com/png.latex?a-b"> 會發生需要做退位減法的情況，<strong>我們的目標是a、b已知的前提下，印出來的c要是正確的</strong>先來生成資料(a、b、c)</p>
<div id="cell-6" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-2"></span>
<span id="cb1-3">np.random.seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">822</span>)</span>
<span id="cb1-4"></span>
<span id="cb1-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 省動設定</span></span>
<span id="cb1-6">largest_number <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">255</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 例如，7位元</span></span>
<span id="cb1-7">binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 減法計算的位數</span></span>
<span id="cb1-8"></span>
<span id="cb1-9">int2binary <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb1-10">binary <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.unpackbits(</span>
<span id="cb1-11">    np.array([<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>binary_dim)], dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>np.uint8).T, axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb1-12">)</span>
<span id="cb1-13"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>binary_dim):</span>
<span id="cb1-14">    int2binary[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> binary[i]</span>
<span id="cb1-15"></span>
<span id="cb1-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> requires_borrow(a, b):</span>
<span id="cb1-17">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""檢查 a - b 是否會需要退位"""</span></span>
<span id="cb1-18">    a_str <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(a)[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 反轉為從低位開始</span></span>
<span id="cb1-19">    b_str <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(b)[::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb1-20"></span>
<span id="cb1-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(b_str)):</span>
<span id="cb1-22">        a_digit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(a_str[i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(a_str) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb1-23">        b_digit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(b_str[i])</span>
<span id="cb1-24">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> a_digit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> b_digit:</span>
<span id="cb1-25">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb1-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span></span>
<span id="cb1-27"></span>
<span id="cb1-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 資料生成</span></span>
<span id="cb1-29">data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb1-30"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(data) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>:</span>
<span id="cb1-31">    a_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.randint(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, largest_number)</span>
<span id="cb1-32">    b_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.randint(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, a_int)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 保證 b &lt; a</span></span>
<span id="cb1-33"></span>
<span id="cb1-34">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> requires_borrow(a_int, b_int):</span>
<span id="cb1-35">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">continue</span></span>
<span id="cb1-36"></span>
<span id="cb1-37">    a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[a_int]</span>
<span id="cb1-38">    b <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[b_int]</span>
<span id="cb1-39">    c_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> a_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> b_int</span>
<span id="cb1-40">    c <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> int2binary[c_int]</span>
<span id="cb1-41"></span>
<span id="cb1-42">    data.append((a, b, c))</span>
<span id="cb1-43"></span>
<span id="cb1-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># data 現在包含了會需要退位的減法資料</span></span>
<span id="cb1-45"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"共產生 </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(data)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> 筆退位減法資料"</span>)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>共產生 10000 筆退位減法資料</code></pre>
</div>
</div>
<p>然後來製作模型，我們的模型預測的原理是：把一個數字的二進位形式拆成一位一位的序列(series)，然後一個時間步(<img src="https://latex.codecogs.com/png.latex?t">)處理一位，很接近生成文字資料的處理方式了。</p>
<section id="模型定義" class="level2">
<h2 class="anchored" data-anchor-id="模型定義">模型定義</h2>
<p>因為是手刻，所以激勵函數(activation function)及其導函數需自行指定，這裡用的 <img src="https://latex.codecogs.com/png.latex?%5Csigma%20(.)"> 是</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B1%7D%7B1+e%5E%7B-x%7D%7D"></p>
<p>其導數為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cfrac%7Be%5E%7B-x%7D%7D%7B1+e%5E%7B-x%7D%7D=%5Cfrac%7B1%7D%7B1+e%5E%7B-x%7D%7D(1-%5Cfrac%7B1%7D%7B1+e%5E%7B-x%7D%7D)"></p>
<div id="cell-8" class="cell">
<div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> copy, numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 忽略所有數值運算警告（如 overflow, invalid value 等）</span></span>
<span id="cb3-4">np.seterr(over<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'ignore'</span>, invalid<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'ignore'</span>)</span>
<span id="cb3-5"></span>
<span id="cb3-6"></span>
<span id="cb3-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># activation function</span></span>
<span id="cb3-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> sigmoid(x):</span>
<span id="cb3-9">    output <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span>np.exp(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>x))</span>
<span id="cb3-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> output</span>
<span id="cb3-11"></span>
<span id="cb3-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># derivative of active function</span></span>
<span id="cb3-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> sigmoid_output_to_deriv(output):</span>
<span id="cb3-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> output<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>output)</span></code></pre></div>
</div>
<section id="定義模型參數" class="level3">
<h3 class="anchored" data-anchor-id="定義模型參數">定義模型參數</h3>
<p>再來在建立模型前，先定義模型參數，這裡我們將隱藏層的權重設為<code>synapse_0</code>(輸入及輸出符合 hidden dimension)；輸出層的權重設為<code>synapse_1</code>。</p>
<div id="cell-10" class="cell" data-execution_count="5">
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">lr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span></span>
<span id="cb4-2">input_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (a, b)</span></span>
<span id="cb4-3">hidden_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span></span>
<span id="cb4-4">out_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># c</span></span>
<span id="cb4-5"></span>
<span id="cb4-6"></span>
<span id="cb4-7">synapse_0 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>np.random.random((input_dim,hidden_dim))<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span></span>
<span id="cb4-8">synapse_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>np.random.random((hidden_dim,out_dim))<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span></span>
<span id="cb4-9">synapse_h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>np.random.random((hidden_dim,hidden_dim))<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span></span>
<span id="cb4-10"></span>
<span id="cb4-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 反向傳遞的權重更新(平時寫在最佳化器裡)</span></span>
<span id="cb4-12">synapse_0_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros_like(synapse_0)</span>
<span id="cb4-13">synapse_1_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros_like(synapse_1)</span>
<span id="cb4-14">synapse_h_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros_like(synapse_h)</span></code></pre></div>
</div>
<p>然後每次執行模型前應初始化模型，首次執行時 output 應為 0，總誤差應為 0。</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 預測值</span></span>
<span id="cb5-2">d <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros_like(c)</span>
<span id="cb5-3">overallError <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb5-4"></span>
<span id="cb5-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 反向傳遞的誤差紀錄</span></span>
<span id="cb5-6">layer_2_deltas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb5-7">layer_1_values <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb5-8"></span>
<span id="cb5-9">layer_1_values.append(np.ones(hidden_dim)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>) </span>
<span id="cb5-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 因為一開始沒有 hidden layer 初值設為0.1</span></span></code></pre></div>
</section>
</section>
<section id="執行模型正向" class="level2">
<h2 class="anchored" data-anchor-id="執行模型正向">執行模型：正向</h2>
<p>實際執行模型時，會讓資料先做正向 (方向是起點到終點)，然後再做反向更新權重(方向為終點回到起點)。</p>
<section id="任務說明概念" class="level4">
<h4 class="anchored" data-anchor-id="任務說明概念">任務說明（概念）</h4>
<p>我們的目標是： 對兩個二進位數 <img src="https://latex.codecogs.com/png.latex?a">、<img src="https://latex.codecogs.com/png.latex?b">，每一個位元進行減法 <img src="https://latex.codecogs.com/png.latex?c%20=%20a%20-%20b"> 的預測。 這裡的模型是一個簡單的 RNN，用來<strong>逐位</strong>（bit-by-bit）預測輸出 <img src="https://latex.codecogs.com/png.latex?c"> 的每一位元 <img src="https://latex.codecogs.com/png.latex?c_t">，其中 <img src="https://latex.codecogs.com/png.latex?t"> 表示時間步，從最低位（右邊）往左處理。</p>
</section>
<section id="符號定義" class="level4">
<h4 class="anchored" data-anchor-id="符號定義">符號定義</h4>
<table class="caption-top table">
<colgroup>
<col style="width: 18%">
<col style="width: 52%">
<col style="width: 29%">
</colgroup>
<thead>
<tr class="header">
<th>數學符號</th>
<th>意義</th>
<th>維度</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?x_t"></td>
<td>時間步 <img src="https://latex.codecogs.com/png.latex?t"> 的輸入向量</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?y_t"></td>
<td>正確輸出值（目標）</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D"></td>
</tr>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?%5Chat%7By%7D_t"></td>
<td>模型在時間步 <img src="https://latex.codecogs.com/png.latex?t"> 的預測</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D"></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?h_t"></td>
<td>隱藏層狀態</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5Eh"></td>
</tr>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?W_%7Bxh%7D"></td>
<td>輸入到隱藏層的權重矩陣</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E%7B2%20%5Ctimes%20h%7D"></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?W_%7Bhh%7D"></td>
<td>前一個隱藏狀態到現在的隱藏狀態</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E%7Bh%20%5Ctimes%20h%7D"></td>
</tr>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?W_%7Bhy%7D"></td>
<td>隱藏層到輸出層的權重</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E%7Bh%20%5Ctimes%201%7D"></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?%5Csigma(%5Ccdot)"></td>
<td>sigmoid 函數：<img src="https://latex.codecogs.com/png.latex?%5Csigma(x)%20=%20%5Cfrac%7B1%7D%7B1%20+%20e%5E%7B-x%7D%7D"></td>
<td></td>
</tr>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?e_t"></td>
<td>預測誤差：<img src="https://latex.codecogs.com/png.latex?y_t%20-%20%5Chat%7By%7D_t"></td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D"></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?%5Cdelta%5E%7B(2)%7D_t"></td>
<td>輸出層誤差對應的 delta</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D"></td>
</tr>
</tbody>
</table>
<p>整體流程為：對於每個位元時間步 <img src="https://latex.codecogs.com/png.latex?t">，模型進行以下計算：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0Ax_t%20&amp;=%20%5Cbegin%7Bbmatrix%7D%20a_t%20%5C%5C%20b_t%20%5Cend%7Bbmatrix%7D%20%5C%5C%0Ah_t%20&amp;=%20%5Csigma(x_t%20W_%7Bxh%7D)%20+%20h_%7Bt-1%7D%20W_%7Bhh%7D%20%5C%5C%0A%5Chat%7By%7D_t%20&amp;=%20%5Csigma(h_t%20W_%7Bhy%7D)%20%5C%5C%0Ae_t%20&amp;=%20y_t%20-%20%5Chat%7By%7D_t%20%5C%5C%0A%5Cdelta%5E%7B(2)%7D_t%20&amp;=%20e_t%20%5Ccdot%20%5Chat%7By%7D_t%20(1%20-%20%5Chat%7By%7D_t)%0A%5Cend%7Baligned%7D%0A"></p>
<p>其中 <img src="https://latex.codecogs.com/png.latex?h_0%20=%20%5Cvec%7B0%7D"> 初始為零向量。</p>
<p>步驟：</p>
<ol type="1">
<li>輸入（每一位 bit）</li>
</ol>
<p>給定時間步 <img src="https://latex.codecogs.com/png.latex?t"> 的輸入 bit：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ax_t%20=%20%5Cbegin%7Bbmatrix%7D%20a_t%20%5C%5C%20b_t%20%5Cend%7Bbmatrix%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E2%0A"></p>
<ol start="2" type="1">
<li>隱藏層計算</li>
</ol>
<p>（此模型使用的是「非標準」RNN 形式，即先對輸入做 sigmoid，再加上前一個 hidden state）</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ah_t%20=%20%5Csigma(x_t%20W_%7Bxh%7D)%20+%20h_%7Bt-1%7D%20W_%7Bhh%7D%0A"></p>
<p>其中：</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?h_%7Bt-1%7D"> 是上一時間步的隱藏層狀態</p></li>
<li><p>若要用「標準」RNN 形式應為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ah_t%20=%20%5Csigma(x_t%20W_%7Bxh%7D%20+%20h_%7Bt-1%7D%20W_%7Bhh%7D)%0A"></p></li>
</ul>
<ol start="3" type="1">
<li>輸出層計算</li>
</ol>
<p>將隱藏狀態傳到輸出層並經過 sigmoid：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_t%20=%20%5Csigma(h_t%20W_%7Bhy%7D)%0A"></p>
<ol start="4" type="1">
<li>預測誤差</li>
</ol>
<p>目標輸出為 <img src="https://latex.codecogs.com/png.latex?y_t">，模型預測為 <img src="https://latex.codecogs.com/png.latex?%5Chat%7By%7D_t">，則誤差為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ae_t%20=%20y_t%20-%20%5Chat%7By%7D_t%0A"></p>
</section>
<section id="輸出層的反向傳播誤差梯度" class="level3">
<h3 class="anchored" data-anchor-id="輸出層的反向傳播誤差梯度">輸出層的反向傳播（誤差梯度）</h3>
<p>使用平方誤差損失函數，其對預測輸出 <img src="https://latex.codecogs.com/png.latex?%5Chat%7By%7D_t"> 的導數為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cfrac%7B%5Cpartial%20L_t%7D%7B%5Cpartial%20%5Chat%7By%7D_t%7D%20=%20-(y_t%20-%20%5Chat%7By%7D_t)%20=%20-e_t%0A"></p>
<p>但由於輸出層有經過 sigmoid，需乘上 sigmoid 的導數：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Csigma'(%5Chat%7By%7D_t)%20=%20%5Chat%7By%7D_t%20(1%20-%20%5Chat%7By%7D_t)%0A"></p>
<p>因此輸出層 delta 為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cdelta%5E%7B(2)%7D_t%20=%20e_t%20%5Ccdot%20%5Chat%7By%7D_t%20(1%20-%20%5Chat%7By%7D_t)%0A"></p>
</section>
<section id="預測結果記錄量化輸出" class="level3">
<h3 class="anchored" data-anchor-id="預測結果記錄量化輸出">預測結果記錄（量化輸出）</h3>
<p>模型最終預測結果會被四捨五入為 0 或 1：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7Bc%7D_t%20=%20%5Ctext%7Bround%7D(%5Chat%7By%7D_t)%0A"></p>
</section>
<section id="整體誤差統計" class="level3">
<h3 class="anchored" data-anchor-id="整體誤差統計">整體誤差統計</h3>
<p>若將每一位的絕對誤差做加總，可以得到一整筆資料的總預測誤差：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctext%7BOverallError%7D%20=%20%5Csum_%7Bt=1%7D%5E%7BT%7D%20%7Ce_t%7C%0A"></p>
</section>
<section id="code" class="level3">
<h3 class="anchored" data-anchor-id="code">code</h3>
<p>開始執行，先做正向：</p>
<div id="cell-12" class="cell" data-execution_count="2">
<div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> RNN_positive(a, b, c):</span>
<span id="cb6-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 定義模型參數 (前面已做過)</span></span>
<span id="cb6-3">    d <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros_like(c)</span>
<span id="cb6-4">    overallError <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb6-5">    layer_1_values <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [np.zeros((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, hidden_dim))]</span>
<span id="cb6-6">    layer_2_deltas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb6-7"></span>
<span id="cb6-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> position <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(binary_dim):</span>
<span id="cb6-9">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># input genarate and output genarate</span></span>
<span id="cb6-10">        X <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([[a[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], b[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]])</span>
<span id="cb6-11">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># X = X.reshape(1, 2)  # 明確定義為 2D（1 row, 2 columns）</span></span>
<span id="cb6-12">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 正解</span></span>
<span id="cb6-13">        y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([[c[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]]).T </span>
<span id="cb6-14">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (input + hidden) -&gt; new hidden</span></span>
<span id="cb6-15">        layer_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sigmoid(np.dot(X,synapse_0)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> np.dot(layer_1_values[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],synapse_h) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 出來 1 x hidden_dim 需轉置</span></span>
<span id="cb6-16">        layer_2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sigmoid(np.dot(layer_1, synapse_1))</span>
<span id="cb6-17">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># hidden*( matrix of hidden to output) -&gt; output</span></span>
<span id="cb6-18"></span>
<span id="cb6-19">        layer_2_error <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> layer_2</span>
<span id="cb6-20">        layer_2_deltas.append((layer_2_error)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>sigmoid_output_to_deriv(layer_2))</span>
<span id="cb6-21">        overallError <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(layer_2_error[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb6-22"></span>
<span id="cb6-23">        d[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>(layer_2[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb6-24">        layer_1_values.append(copy.deepcopy(layer_1))</span>
<span id="cb6-25">        future_layer_1_delta <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros(hidden_dim)</span>
<span id="cb6-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> d, layer_1_values, layer_2_deltas, overallError</span></code></pre></div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
code 逐列解說
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>每一個迴圈步驟代表處理一個位元（從最低位開始）。</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> position <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(binary_dim):</span></code></pre></div>
<ul>
<li>這是循環處理每一個二進位的位元（從第 0 位到第 binary_dim - 1 位）。</li>
<li>通常是從右到左（最低位到最高位）。</li>
</ul>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1">X <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([a[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], b[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]])</span></code></pre></div>
<ul>
<li>取出第 <code>position</code> 位的兩個輸入 bit：<code>a</code> 和 <code>b</code>。</li>
<li>二進位是從右往左計算的，所以要用 <code>binary_dim - position - 1</code> 來取得正確位元。</li>
</ul>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1">y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([c[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]).T</span></code></pre></div>
<ul>
<li><code>y</code> 是正確答案 <code>c</code> 的當前位元。</li>
<li><code>.T</code> 是把它轉置成列向量，以配合後續矩陣運算。</li>
</ul>
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1">layer_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sigmoid(np.dot(X, synapse_0)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> np.dot(layer_2_values[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], synapse_h)</span></code></pre></div>
<ul>
<li>這是計算目前時間步的隱藏層輸出。</li>
<li>第一部分：<code>X @ synapse_0</code> 是輸入層乘上輸入到隱藏層的權重。</li>
<li>第二部分：<code>layer_2_values[-1] @ synapse_h</code> 是上一時間步的隱藏狀態與 recurrent 權重相乘。</li>
<li>加總後通過 sigmoid 激勵函數，得到目前時間步的隱藏層狀態。</li>
</ul>
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1">layer_2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sigmoid(np.dot(layer_1, synapse_1))</span></code></pre></div>
<ul>
<li>將隱藏層輸出通過輸出層權重 <code>synapse_1</code>，並套用 sigmoid，得到預測結果（0 或 1 的機率）。</li>
</ul>
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1">layer_2_error <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> layer_2</span>
<span id="cb12-2">layer_2_deltas.append(layer_2_error <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> sigmoid_output_to_deriv(layer_2))</span></code></pre></div>
<ul>
<li>計算這一位的預測誤差。</li>
<li>將誤差乘以 sigmoid 導數，得到反向傳播時的 delta（誤差梯度）。</li>
<li>把 delta 儲存起來，稍後會用來做權重更新。</li>
</ul>
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1">overallError <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(layer_2_error[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span></code></pre></div>
<ul>
<li>累加這一位的絕對誤差，用來觀察整體模型在這一筆資料上的表現。</li>
</ul>
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1">d[binary_dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>(layer_2[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span></code></pre></div>
<ul>
<li>根據預測值進行四捨五入（0 或 1），儲存為預測結果的其中一位。</li>
<li><code>d</code> 最終會是整筆輸出結果的二進位數。</li>
</ul>
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1">layer_1_values.append(copy.deepcopy(layer_1))</span></code></pre></div>
<p>是用來把當前時間步的 <strong>隱藏層輸出 (<code>layer_1</code>)</strong> 儲存起來，以便在未來的 <strong>反向傳播（Backpropagation Through Time, BPTT）</strong> 中使用。</p>
<ul>
<li><p>使用 <code>deepcopy()</code> 可以確保每個時間步的隱藏層輸出都正確保留下來，不被未來時間步覆蓋或污染</p></li>
<li><p>把每個時間步的 <code>layer_1</code> 加到 <code>layer_1_values</code> 這個 list 裡面</p></li>
<li><p>這個 list 最後會長成：</p>
<div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1">layer_1_values <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [h_{<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>}, h_0, h_1, ..., h_{T<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>}]</span></code></pre></div></li>
</ul>
<div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1">future_layer_1_deltas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros(hidden_dim)</span></code></pre></div>
<ul>
<li>為了做後續反向之用，清空 <code>future_layer_1_deltas</code>。</li>
</ul>
</div>
</div>
</div>
<p>再做反向的權重更新：</p>
</section>
</section>
<section id="執行模型反向" class="level2">
<h2 class="anchored" data-anchor-id="執行模型反向">執行模型：反向</h2>
<p>這裡因為 RNN 的特性，反向時會加入 BPTT 演算法處理。理論部分在此略過，直接說明流程，整體流程為：</p>
<p>給定時間步 <img src="https://latex.codecogs.com/png.latex?t%20=%20T-1,%20T-2,%20%5Cdots,%200">，反向傳播的數學表達為：</p>
<ol type="1">
<li>計算隱藏層 delta</li>
</ol>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cdelta_t%5E%7B(1)%7D%20=%20%5Cdelta_%7Bt+1%7D%5E%7B(1)%7D%20W_%7Bhh%7D%5ET%20+%20%5Cdelta_t%5E%7B(2)%7D%20W_%7Bhy%7D%5ET%0A"></p>
<ol start="2" type="1">
<li>累加權重梯度</li>
</ol>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CDelta%20W_%7Bhy%7D%20+=%20h_t%5ET%20%5Cdelta_t%5E%7B(2)%7D%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CDelta%20W_%7Bhh%7D%20+=%20h_%7Bt-1%7D%5ET%20%5Cdelta_t%5E%7B(1)%7D%0A"></p>
<ol start="3" type="1">
<li>權重更新（一次更新）</li>
</ol>
<p><img src="https://latex.codecogs.com/png.latex?%0AW_%7Bhy%7D%20%5Cleftarrow%20W_%7Bhy%7D%20+%20%5Ceta%20%5Ccdot%20%5CDelta%20W_%7Bhy%7D%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0AW_%7Bhh%7D%20%5Cleftarrow%20W_%7Bhh%7D%20+%20%5Ceta%20%5Ccdot%20%5CDelta%20W_%7Bhh%7D%0A"></p>
<section id="符號定義-1" class="level3">
<h3 class="anchored" data-anchor-id="符號定義-1">符號定義</h3>
<table class="caption-top table">
<colgroup>
<col style="width: 18%">
<col style="width: 53%">
<col style="width: 28%">
</colgroup>
<thead>
<tr class="header">
<th>程式變數</th>
<th>數學符號</th>
<th>意義</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>x_t</code></td>
<td><img src="https://latex.codecogs.com/png.latex?x_t%20%5Cin%20%5Cmathbb%7BR%7D%5E2"></td>
<td>第 <img src="https://latex.codecogs.com/png.latex?t"> 個時間步的輸入（兩個位元）</td>
</tr>
<tr class="even">
<td><code>h_t</code></td>
<td><img src="https://latex.codecogs.com/png.latex?h_t%20%5Cin%20%5Cmathbb%7BR%7D%5Eh"></td>
<td>第 <img src="https://latex.codecogs.com/png.latex?t"> 個時間步的隱藏層輸出</td>
</tr>
<tr class="odd">
<td><code>\delta_t^{(2)}</code></td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cdelta_t%5E%7B(2)%7D%20%5Cin%20%5Cmathbb%7BR%7D"></td>
<td>第 <img src="https://latex.codecogs.com/png.latex?t"> 步的輸出層 delta（來自前向誤差）</td>
</tr>
<tr class="even">
<td><code>\delta_t^{(1)}</code></td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cdelta_t%5E%7B(1)%7D%20%5Cin%20%5Cmathbb%7BR%7D%5Eh"></td>
<td>第 <img src="https://latex.codecogs.com/png.latex?t"> 步的隱藏層 delta</td>
</tr>
<tr class="odd">
<td><code>W_{hy}</code></td>
<td><img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bsynapse%5C_1%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bh%20%5Ctimes%201%7D"></td>
<td>隱藏層 → 輸出層的權重</td>
</tr>
<tr class="even">
<td><code>W_{hh}</code></td>
<td><img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bsynapse%5C_h%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bh%20%5Ctimes%20h%7D"></td>
<td>隱藏層（上一步）→ 隱藏層</td>
</tr>
<tr class="odd">
<td><code>lr</code></td>
<td><img src="https://latex.codecogs.com/png.latex?%5Ceta"></td>
<td>學習率</td>
</tr>
</tbody>
</table>
</section>
<section id="數學推導" class="level3">
<h3 class="anchored" data-anchor-id="數學推導">數學推導</h3>
<ol type="1">
<li>隱藏層 delta 的計算</li>
</ol>
<p>根據鏈式法則，隱藏層的 delta 可由兩個部分反向傳遞而來：</p>
<ul>
<li><p>來自輸出層的誤差反傳：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cdelta_t%5E%7B(2)%7D%20W_%7Bhy%7D%5ET%0A"></p></li>
<li><p>來自下一個時間步隱藏層的誤差反傳（RNN 的時間依賴）：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cdelta_%7Bt+1%7D%5E%7B(1)%7D%20W_%7Bhh%7D%5ET%0A"></p></li>
</ul>
<p>因此總合為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cdelta_t%5E%7B(1)%7D%20=%20%5Cdelta_%7Bt+1%7D%5E%7B(1)%7D%20W_%7Bhh%7D%5ET%20+%20%5Cdelta_t%5E%7B(2)%7D%20W_%7Bhy%7D%5ET%0A"></p>
<p>其中 <img src="https://latex.codecogs.com/png.latex?%5Cdelta_%7Bt+1%7D%5E%7B(1)%7D"> 是 <strong>未來時間的隱藏層 delta</strong>，一開始設為 0，然後逐步向後遞推。</p>
<ol start="2" type="1">
<li>權重的梯度累積</li>
</ol>
<p>這部分對應於：</p>
<ul>
<li>輸出層權重更新項</li>
</ul>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CDelta%20W_%7Bhy%7D%20+=%20h_t%5ET%20%5Cdelta_t%5E%7B(2)%7D%0A"></p>
<p>（隱藏層 → 輸出層）</p>
<ul>
<li>RNN recurrent 權重更新項</li>
</ul>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CDelta%20W_%7Bhh%7D%20+=%20h_%7Bt-1%7D%5ET%20%5Cdelta_t%5E%7B(1)%7D%0A"></p>
<p>（前一個隱藏層 → 當前隱藏層）</p>
<ul>
<li>權重更新</li>
</ul>
<p>在完成所有時間步的誤差累積之後，對所有權重進行梯度下降：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AW_%7Bhy%7D%20%5Cleftarrow%20W_%7Bhy%7D%20+%20%5Ceta%20%5Ccdot%20%5CDelta%20W_%7Bhy%7D%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0AW_%7Bhh%7D%20%5Cleftarrow%20W_%7Bhh%7D%20+%20%5Ceta%20%5Ccdot%20%5CDelta%20W_%7Bhh%7D%0A"></p>
<!-- 
###  備註

這段程式碼中 **沒有對 $W_{xh}$（輸入 → 隱藏層）做反向更新**，但在完整的 RNN 訓練中，也應有：

$$
\Delta W_{xh} += x_t^T \delta_t^{(1)} \odot \sigma'(a_t)
$$
 -->
</section>
<section id="code-1" class="level3">
<h3 class="anchored" data-anchor-id="code-1">code</h3>
<div id="cell-14" class="cell" data-execution_count="7">
<div class="sourceCode cell-code" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb18-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> RNN_negative(layer_1_values, layer_2_deltas, a, b):</span>
<span id="cb18-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">global</span> synapse_0, synapse_1, synapse_h</span>
<span id="cb18-3">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">global</span> synapse_0_up, synapse_1_up, synapse_h_up</span>
<span id="cb18-4"></span>
<span id="cb18-5">    future_layer_1_delta <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, hidden_dim))</span>
<span id="cb18-6"></span>
<span id="cb18-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> position <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(binary_dim):</span>
<span id="cb18-8">        X <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([[a[position], b[position]]])</span>
<span id="cb18-9">        layer_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_1_values[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb18-10">        prev_layer_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_1_values[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]</span>
<span id="cb18-11">        layer_2_delta <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_2_deltas[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>position <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb18-12"></span>
<span id="cb18-13">        layer_1_delta <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb18-14">            future_layer_1_delta.dot(synapse_h.T) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> layer_2_delta.dot(synapse_1.T)</span>
<span id="cb18-15">        ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> sigmoid_output_to_deriv(layer_1)</span>
<span id="cb18-16"></span>
<span id="cb18-17">        synapse_1_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.atleast_2d(layer_1).T.dot(layer_2_delta)</span>
<span id="cb18-18">        synapse_h_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)</span>
<span id="cb18-19"></span>
<span id="cb18-20">        synapse_0_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> X.T.dot(layer_1_delta)</span>
<span id="cb18-21"></span>
<span id="cb18-22">        future_layer_1_delta <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_1_delta</span>
<span id="cb18-23"></span>
<span id="cb18-24">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 更新權重</span></span>
<span id="cb18-25">    synapse_0 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> synapse_0_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> lr</span>
<span id="cb18-26">    synapse_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> synapse_1_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> lr</span>
<span id="cb18-27">    synapse_h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> synapse_h_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> lr</span>
<span id="cb18-28"></span>
<span id="cb18-29">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 清空更新梯度</span></span>
<span id="cb18-30">    synapse_0_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb18-31">    synapse_1_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb18-32">    synapse_h_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span></code></pre></div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
code 逐列解說
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb19-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> position <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(binary_dim):</span></code></pre></div>
<ul>
<li><strong>作用</strong>：從第一個 bit（position=0）開始，依序向後處理所有位元（bit）的位置，做反向傳播（從最後一個時間步往前）。</li>
</ul>
<div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb20-1">X <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.array([a[position], b[position]])</span></code></pre></div>
<ul>
<li><strong>作用</strong>：取得第 <code>position</code> 位元的輸入，這裡是數字 <code>a</code> 和 <code>b</code> 在該位元的二進位值，組成一個長度為2的輸入向量 <img src="https://latex.codecogs.com/png.latex?x_t">。</li>
</ul>
<div class="sourceCode" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1">layer_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_2_values[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 目前時間的hidden layer</span></span></code></pre></div>
<ul>
<li><strong>作用</strong>：取得對應時間點的隱藏層狀態 <img src="https://latex.codecogs.com/png.latex?h_t">。</li>
<li>注意：<code>layer_2_values</code> 是時間序列的隱藏層列表，<code>-position-1</code> 從最後一個時間步往前索引。</li>
</ul>
<div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb22-1">prev_layer_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_2_values[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 前時間的hidden layer</span></span></code></pre></div>
<ul>
<li><strong>作用</strong>：取得前一時間步（<img src="https://latex.codecogs.com/png.latex?t-1">）的隱藏層狀態 <img src="https://latex.codecogs.com/png.latex?h_%7Bt-1%7D">，用於更新循環權重。</li>
</ul>
<div class="sourceCode" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1">layer_2_deltas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_2_deltas[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span></code></pre></div>
<ul>
<li><strong>作用</strong>：取得當前時間步輸出層的 delta <img src="https://latex.codecogs.com/png.latex?%5Cdelta_t%5E%7B(2)%7D">，也就是輸出層的誤差梯度。</li>
</ul>
<div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb24-1">layer_1_deltas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (future_layer_1_deltas.dot(synapse_h.T) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> layer_2_deltas.dot(synapse_1.T))</span></code></pre></div>
<ul>
<li><p><strong>作用</strong>：計算隱藏層的 delta <img src="https://latex.codecogs.com/png.latex?%5Cdelta_t%5E%7B(1)%7D">，包含：</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?%5Cdelta_%7Bt+1%7D%5E%7B(1)%7D"> 經由循環權重 <img src="https://latex.codecogs.com/png.latex?W_%7Bhh%7D"> 反向傳播的誤差 (future_layer_1_deltas.dot(synapse_h.T))</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Cdelta_t%5E%7B(2)%7D"> 經由輸出層權重 <img src="https://latex.codecogs.com/png.latex?W_%7Bhy%7D"> 反向傳播的誤差 (layer_2_deltas.dot(synapse_1.T)) <strong>這是 RNN 時間與層間反向誤差的加總。</strong></li>
</ul></li>
</ul>
<div class="sourceCode" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1">synapse_1_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.atleast_2d(layer_1).T.dot(layer_2_deltas)</span></code></pre></div>
<ul>
<li><strong>作用</strong>：累加輸出層權重 <img src="https://latex.codecogs.com/png.latex?W_%7Bhy%7D"> 的梯度： <img src="https://latex.codecogs.com/png.latex?%5CDelta%20W_%7Bhy%7D%20+=%20h_t%5ET%20%5Cdelta_t%5E%7B(2)%7D"> 這是根據誤差對權重的偏微分。</li>
</ul>
<div class="sourceCode" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb26-1">synapse_h_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.atleast_2d(prev_layer_1).T.dot(layer_1_deltas)</span></code></pre></div>
<ul>
<li><strong>作用</strong>：累加循環權重 <img src="https://latex.codecogs.com/png.latex?W_%7Bhh%7D"> 的梯度： <img src="https://latex.codecogs.com/png.latex?%5CDelta%20W_%7Bhh%7D%20+=%20h_%7Bt-1%7D%5ET%20%5Cdelta_t%5E%7B(1)%7D"> 用前一時間的隱藏狀態乘上當前時間的隱藏層 delta。</li>
</ul>
<div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb27-1">future_layer_1_deltas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> layer_1_deltas</span></code></pre></div>
<ul>
<li><strong>作用</strong>：將當前時間步的隱藏層 delta 儲存起來，作為下一個時間步（往前一位）計算的「未來隱藏層 delta」使用。</li>
<li>這是反向傳播的關鍵：誤差從後面時間步向前傳遞。</li>
</ul>
<div class="sourceCode" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb28-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 更新權重</span></span>
<span id="cb28-2">synapse_0 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> synapse_0_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> lr</span>
<span id="cb28-3">synapse_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> synapse_1_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> lr</span></code></pre></div>
<ul>
<li><strong>作用</strong>：使用累積的梯度 <img src="https://latex.codecogs.com/png.latex?%5CDelta%20W"> 按照學習率 <img src="https://latex.codecogs.com/png.latex?%5Ceta"> 更新權重。 這幾行程式碼的作用是完成 <strong>反向傳播與權重更新後的梯度清零（reset）</strong>，以下是每一行的說明（包含對應的數學概念）：</li>
</ul>
<div class="sourceCode" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb29-1">synapse_h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> synapse_h_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> lr</span></code></pre></div>
<p>意義：</p>
<ul>
<li>將 <strong>recurrent 權重</strong> <img src="https://latex.codecogs.com/png.latex?W_%7Bhh%7D"> 依照學習率 <img src="https://latex.codecogs.com/png.latex?%5Ceta"> 更新：</li>
</ul>
<p><img src="https://latex.codecogs.com/png.latex?%0AW_%7Bhh%7D%20%5Cleftarrow%20W_%7Bhh%7D%20+%20%5Ceta%20%5Ccdot%20%5CDelta%20W_%7Bhh%7D%0A"></p>
<ul>
<li>其中 <code>synapse_h_up</code> 是之前累積的權重梯度 <img src="https://latex.codecogs.com/png.latex?%5CDelta%20W_%7Bhh%7D">，學習率為 <code>lr</code>。</li>
</ul>
<div class="sourceCode" id="cb30" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb30-1">synapse_0_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb30-2">synapse_1_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb30-3">synapse_h_up <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span></code></pre></div>
<p>意義：</p>
<ul>
<li>將所有已經<strong>使用過的梯度變數清零</strong>，為下一筆資料的訓練準備。</li>
<li>對應數學上是將累積的梯度矩陣歸零：</li>
</ul>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CDelta%20W_%7Bxh%7D%20=%200,%20%5Cquad%20%5CDelta%20W_%7Bhy%7D%20=%200,%20%5Cquad%20%5CDelta%20W_%7Bhh%7D%20=%200%0A"></p>
<p>這麼做是因為每次訓練新一筆資料（或 batch）時，需要重新計算對應的權重梯度，避免與前一筆混在一起。</p>
</div>
</div>
</div>
</section>
</section>
<section id="結果" class="level2">
<h2 class="anchored" data-anchor-id="結果">結果</h2>
<div id="cell-16" class="cell" data-execution_count="8">
<div class="sourceCode cell-code" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb31-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> binary2int(binary_array):</span>
<span id="cb31-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""將二進位 ndarray 轉換為十進位整數"""</span></span>
<span id="cb31-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>.join(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(b)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> binary_array), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb31-4"></span>
<span id="cb31-5">Error_list <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb31-6"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> j, (a, b, c) <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(data):</span>
<span id="cb31-7">    a_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> binary2int(a)</span>
<span id="cb31-8">    b_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> binary2int(b)</span>
<span id="cb31-9">    c_int <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> binary2int(c)</span>
<span id="cb31-10">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># print(f"{a_int} + {b_int} = {c_int}")</span></span>
<span id="cb31-11"></span>
<span id="cb31-12">    d, layer_1_values, layer_2_deltas, overallError <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> RNN_positive(a, b, c)</span>
<span id="cb31-13">  </span>
<span id="cb31-14">    RNN_negative(layer_1_values, layer_2_deltas, a, b)</span>
<span id="cb31-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> j <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb31-16">      Error_list.append(overallError)</span>
<span id="cb31-17"></span>
<span id="cb31-18"></span>
<span id="cb31-19">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> j <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb31-20">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Total error: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>overallError<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb31-21">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Prediction value: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>d<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb31-22">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"True value: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>c<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb31-23">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb31-24">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> index, x <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">reversed</span>(d)):</span>
<span id="cb31-25">            out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(x) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">pow</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, index)</span>
<span id="cb31-26">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a_int<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> - </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b_int<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>out<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb31-27">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"----------"</span>)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>Total error: [3.97936582]
Prediction value: [0 0 0 0 0 0 0 0]
True value: [0 0 0 0 1 0 0 0]
40 - 32 = 0
----------
Total error: [3.24030251]
Prediction value: [0 0 0 0 0 0 0 0]
True value: [0 0 0 1 0 0 0 0]
34 - 18 = 0
----------
Total error: [3.93281865]
Prediction value: [0 0 0 0 0 0 0 0]
True value: [0 0 1 0 1 0 1 1]
107 - 64 = 0
----------
Total error: [3.99430241]
Prediction value: [0 0 0 0 0 0 0 1]
True value: [0 0 1 1 1 0 1 0]
111 - 53 = 1
----------
Total error: [3.87219041]
Prediction value: [0 1 1 0 1 1 1 1]
True value: [0 1 0 0 1 1 0 1]
119 - 42 = 111
----------
Total error: [3.14270565]
Prediction value: [0 0 0 0 0 0 0 0]
True value: [0 0 0 0 1 1 1 0]
60 - 46 = 0
----------
Total error: [3.16076726]
Prediction value: [0 0 0 0 0 0 0 0]
True value: [0 0 1 0 1 0 1 1]
239 - 196 = 0
----------
Total error: [2.5766328]
Prediction value: [0 0 0 0 0 0 1 0]
True value: [0 0 1 0 0 1 1 1]
133 - 94 = 2
----------
Total error: [2.59768901]
Prediction value: [0 0 0 0 0 0 0 0]
True value: [1 0 0 1 0 0 0 1]
223 - 78 = 0
----------
Total error: [3.55110057]
Prediction value: [0 0 0 0 1 0 0 0]
True value: [1 0 0 1 1 1 1 1]
167 - 8 = 8
----------</code></pre>
</div>
</div>
<p>從以上結果摘錄中可以看到，雖然因為都是手刻，有些資料在經過 acivative funciton 時發生溢位，倒致 Prediction value 變成 0 向量，但整體還算能動。把誤差化成圖：</p>
<div id="cell-18" class="cell" data-execution_count="9">
<div class="sourceCode cell-code" id="cb33" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb33-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb33-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># x 軸：資料點編號（第幾次）</span></span>
<span id="cb33-3">x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(Error_list)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>))</span>
<span id="cb33-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># y 軸：每個值</span></span>
<span id="cb33-5">y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Error_list</span>
<span id="cb33-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 畫圖</span></span>
<span id="cb33-7">plt.plot(x, y)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 折線圖 + 每點畫圈</span></span>
<span id="cb33-8">plt.xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Step"</span>)</span>
<span id="cb33-9">plt.ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Value"</span>)</span>
<span id="cb33-10">plt.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Value over Time"</span>)</span>
<span id="cb33-11">plt.grid(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb33-12">plt.show()</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch2/pytorch2_files/figure-html/cell-10-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<p>可以發現有隨著資料量增加訓練越來越好的趨勢。</p>
</section>
</section>
<section id="結論" class="level1">
<h1>結論</h1>
<p>這次我們手刻了一個簡單的 RNN，並用它來解決退位減法的問題，藉此理解它的運作原理。為了幫助理解，我略過一些數學細節。詳細可以參考其他書籍，或是前面提到的 main reference。</p>
</section>
<section id="補充為什麼有時候訓練出來都是-0-向量" class="level1">
<h1>補充：為什麼有時候訓練出來都是 0 向量</h1>
<p>問 AI 的結果如下：</p>
<p>這是非常常見的現象，當你將訓練資料從增加時，模型的預測結果全變成 0，可能是以下幾個常見的機制失衡或錯誤導致的。</p>
<section id="問題現象" class="level3">
<h3 class="anchored" data-anchor-id="問題現象">問題現象</h3>
<p>例如：</p>
<ul>
<li><strong>訓練資料：</strong> 10,000 筆 → 模型還會預測出接近正確的值</li>
<li><strong>訓練資料：</strong> 100,000 筆 → 模型幾乎只輸出 <code>0</code>（或其他固定值）</li>
</ul>
<p>這代表：</p>
<ul>
<li>模型沒有效學習到規律（可能卡在某個區域）</li>
<li>或者學習率太小、權重更新不動</li>
<li>或者資料不平衡、loss 消失</li>
</ul>
</section>
<section id="可能原因分析" class="level3">
<h3 class="anchored" data-anchor-id="可能原因分析">可能原因分析</h3>
<ol type="1">
<li><strong>學習率太小 → 權重幾乎沒更新</strong></li>
</ol>
<ul>
<li>小資料量下小學習率還能跑</li>
<li>資料一多，梯度很小時根本沒辦法推動權重更新</li>
</ul>
<p>✅ <strong>對策：</strong></p>
<ul>
<li>嘗試調整 <code>learning_rate</code>，例如從 <code>0.1</code> 提高到 <code>0.5</code>、<code>1.0</code> 甚至更高</li>
</ul>
<div class="sourceCode" id="cb34" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb34-1">learning_rate <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 試著加大看看效果</span></span></code></pre></div>
<ol start="2" type="1">
<li><strong>梯度消失（vanishing gradient）</strong></li>
</ol>
<ul>
<li>你用了 <code>sigmoid</code>，如果輸入太大或太小，導數會趨近 0</li>
<li>時間步過長 + sigmoid，導致梯度傳不回來</li>
</ul>
<p>✅ <strong>對策：</strong></p>
<ul>
<li>替代激活函數（如 <code>tanh</code> 或 <code>ReLU</code>）</li>
<li>使用 clipped gradient（進階方法）</li>
<li>降低 binary_dim 的數量或減少時間步數測試</li>
</ul>
<ol start="3" type="1">
<li><strong>資料不平衡</strong></li>
</ol>
<ul>
<li>訓練資料中，可能大部分位元都為 <code>0</code></li>
<li>模型學會「都預測 0」反而 loss 最小</li>
</ul>
<p><strong>對策：</strong></p>
<ul>
<li>分析資料中各 bit 的 0/1 分布</li>
<li>平衡輸出資料、加權 loss</li>
</ul>
<ol start="4" type="1">
<li><strong>模型訓練不夠輪（太早看結果）</strong></li>
</ol>
<ul>
<li>資料多但 epoch 數太少</li>
<li>每筆資料更新一次，epoch 效果變小</li>
</ul>
<p><strong>對策：</strong></p>
<ul>
<li>多跑幾輪 epoch（例：多次迴圈訓練資料）</li>
</ul>
<div class="sourceCode" id="cb35" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb35-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>):</span>
<span id="cb35-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, b, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> data:</span>
<span id="cb35-3">        ...</span></code></pre></div>
<ol start="5" type="1">
<li><strong>整批資料記憶體吃不消（爆量時變慢、失效）</strong></li>
</ol>
<ul>
<li>如果你用 numpy，每筆 forward/backward 都是單筆處理，100,000 筆會非常慢</li>
<li>可能導致某些變數錯誤、梯度計算壞掉而沒注意到</li>
</ul>
<p><strong>對策：</strong></p>
<ul>
<li>測試小批次先確保正確性</li>
<li>或先試 20,000 → 50,000 → 再到 100,000 筆觀察效應</li>
</ul>
<hr>
</section>
<section id="建議步驟debug-順序" class="level3">
<h3 class="anchored" data-anchor-id="建議步驟debug-順序">建議步驟（Debug 順序）</h3>
<ol type="1">
<li><strong>先確認資料分布</strong>：</li>
</ol>
<div class="sourceCode" id="cb36" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb36-1">ones <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>([<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(c) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _, _, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> data])</span>
<span id="cb36-2">zeros <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(data) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(data[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> ones</span>
<span id="cb36-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"1 的比例: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>ones <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (ones <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> zeros)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div>
<ol start="2" type="1">
<li><p><strong>加入預測值與 loss 的紀錄與觀察（是否都變成 0）</strong></p></li>
<li><p><strong>試著多跑幾次 epoch</strong>：</p></li>
</ol>
<div class="sourceCode" id="cb37" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb37-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>):</span>
<span id="cb37-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a, b, c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> data:</span>
<span id="cb37-3">        ...</span></code></pre></div>
<ol start="4" type="1">
<li><p><strong>調整學習率</strong></p>
<div class="sourceCode" id="cb38" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb38-1">learning_rate <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 可嘗試 1.0</span></span></code></pre></div></li>
<li><p><strong>嘗試替代 sigmoid 為 tanh（適合雙向輸出）</strong></p></li>
</ol>
<hr>
</section>
<section id="最簡單測法加速-debug" class="level3">
<h3 class="anchored" data-anchor-id="最簡單測法加速-debug">最簡單測法（加速 debug）</h3>
<p>試用以下條件：</p>
<ul>
<li><code>learning_rate = 0.5</code></li>
<li><code>binary_dim = 8</code></li>
<li><code>hidden_dim = 16</code></li>
<li>資料量減少，觀察漸變情況</li>
</ul>
</section>
<section id="總結對策表" class="level3">
<h3 class="anchored" data-anchor-id="總結對策表">總結對策表：</h3>
<table class="caption-top table">
<thead>
<tr class="header">
<th>問題</th>
<th>解法建議</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>資料太多但沒多輪訓練</td>
<td>多跑幾個 epoch</td>
</tr>
<tr class="even">
<td>學習率太小</td>
<td>提高 learning rate 到 0.5 或 1.0</td>
</tr>
<tr class="odd">
<td>梯度消失（使用 sigmoid）</td>
<td>試 <code>tanh</code>、<code>ReLU</code> 或梯度剪裁</td>
</tr>
<tr class="even">
<td>資料偏向 0</td>
<td>加入 loss 權重或數據平衡</td>
</tr>
<tr class="odd">
<td>權重沒初始化或錯誤</td>
<td>確認隨機初始化沒被覆蓋</td>
</tr>
</tbody>
</table>
</section>
</section>
<section id="補充rnn-應用" class="level1">
<h1>補充：RNN 應用</h1>
<section id="文字資料應用原理" class="level2">
<h2 class="anchored" data-anchor-id="文字資料應用原理">文字資料應用原理</h2>
<p>文字是一種有順序的資料（句子 = 單字序列），例如：</p>
<blockquote class="blockquote">
<p>“I love time series.”</p>
</blockquote>
<p>每個字對整體句子的意思都有影響。RNN 透過「記憶」前面的資訊，來理解目前的輸入。</p>
<p>以模型結構來說，<img src="https://latex.codecogs.com/png.latex?%5C%7Bx_t%5C%7D">便是正確的文字序列資料(ex. “I love time series.”)</p>
<p>RNN 處理文字資料的基本流程：</p>
<ol type="1">
<li><p><strong>文字預處理</strong></p>
<ul>
<li>將句子分詞（tokenization）</li>
<li>轉成數字（word to index）</li>
<li>建立詞嵌入（embedding）</li>
</ul></li>
<li><p><strong>建構 RNN 模型</strong></p>
<ul>
<li>輸入層：嵌入向量</li>
<li>隱藏層：RNN / LSTM / GRU</li>
<li>輸出層：看任務類型（分類、預測下一字、標註）</li>
</ul></li>
<li><p><strong>模型訓練與推論</strong></p>
<ul>
<li>使用標準的損失函數（如交叉熵）</li>
<li>訓練完模型後就能進行文字生成、分類或翻譯等任務</li>
</ul></li>
</ol>
<p>應用場景舉例：</p>
<table class="caption-top table">
<colgroup>
<col style="width: 25%">
<col style="width: 74%">
</colgroup>
<thead>
<tr class="header">
<th>任務</th>
<th>RNN 的應用方式</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>文字分類</td>
<td>輸入整個句子，最後用 RNN 最後一個 hidden state 做分類（如情感分析）</td>
</tr>
<tr class="even">
<td>語言模型 / 文字生成</td>
<td>給定前面幾個字，RNN 預測下一個字（或詞）</td>
</tr>
<tr class="odd">
<td>機器翻譯</td>
<td>使用 Encoder-Decoder 架構（雙 RNN），前半編碼原始語句，後半生成翻譯</td>
</tr>
<tr class="even">
<td>命名實體識別（NER）</td>
<td>每個字都產出一個預測，使用 bidirectional RNN 處理前後文</td>
</tr>
</tbody>
</table>



</section>
</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>note</category>
  <category>python</category>
  <category>pytorch</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/pytorch2/pytorch2.html</guid>
  <pubDate>Sat, 23 Aug 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/pytorch2/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>重新認識pytorch(1)</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1.html</link>
  <description><![CDATA[ 






<blockquote class="blockquote">
<p>主要內容引用自 「李金洪. 2022. 全格局使用 PyTorch - 深度學習和圖神經網路 - 基礎篇. 深智數位」。</p>
</blockquote>
<section id="安裝注意事項" class="level1">
<h1>安裝注意事項</h1>
<p>不同電腦適用的安裝程序不同，一定要去官網觀看，<a href="https://pytorch.org/get-started/locally/">點此</a>。我的情況是 Windows + 沒有CUDA + virtualenv虛擬環境，只需要在虛擬環境中 pip install 即可。</p>
</section>
<section id="從建立一個簡易的模型分析分類問題開始" class="level1">
<h1>從建立一個簡易的模型分析分類問題開始</h1>
<p>本例使用模擬出的月亮資料，先生成資料，並繪圖看資料長相：</p>
<div id="cell-2" class="cell" data-execution_count="1">
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># import sklearn.datasets</span></span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sklearn.datasets <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> make_moons</span>
<span id="cb1-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb1-6"></span>
<span id="cb1-7"></span>
<span id="cb1-8">np.random.seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>)</span>
<span id="cb1-9">X, Y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> make_moons(n_samples<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>, noise<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2組資料</span></span>
<span id="cb1-10"></span>
<span id="cb1-11">arg <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.squeeze(np.argwhere(Y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>), axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-12">arg2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.squeeze(np.argwhere(Y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-13"></span>
<span id="cb1-14">plt.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"moon data"</span>)</span>
<span id="cb1-15">plt.scatter(X[arg,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], X[arg,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, c<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'b'</span>,marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'+'</span>,label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'data1'</span>)</span>
<span id="cb1-16">plt.scatter(X[arg2,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], X[arg2,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>, c<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'r'</span>,marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'+'</span>,label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'data2'</span>)</span>
<span id="cb1-17">plt.legend()</span>
<span id="cb1-18">plt.show()</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1_files/figure-html/cell-2-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
code 逐列解說
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sklearn.datasets <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> make_moons</span></code></pre></div>
<p>匯入 <code>scikit-learn</code> 的資料集模組中的<code>make_moons</code>，用來產生內建的資料集，例如月牙型的 <code>make_moons</code>。</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span></code></pre></div>
<p>匯入 PyTorch，用於後續的神經網路模型（尚未使用，但後面會用）。</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span></code></pre></div>
<p>匯入 NumPy，進行數值與陣列操作（例如索引與隨機種子）。</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span></code></pre></div>
<p>匯入 Matplotlib 的 <code>pyplot</code> 模組，用來畫圖顯示資料分佈或訓練過程。</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1">np.random.seed(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span></code></pre></div>
<p>設定 NumPy 的隨機種子，確保每次執行結果一致（資料點位置固定）。</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1">X, Y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sklearn.datasets.make_moons(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>, noise<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2組資料</span></span></code></pre></div>
<p>產生 200 筆「moon」型資料（非線性分類資料集），加入 0.2 的隨機雜訊（增加資料真實性）。</p>
<ul>
<li><code>X</code>: shape = (200, 2)，為 2D 特徵資料點。</li>
<li><code>Y</code>: shape = (200,)，標籤為 0 或 1，對應兩個半月形。</li>
</ul>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1">arg <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.squeeze(np.argwhere(Y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>), axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div>
<p>找出所有 <code>Y==0</code> 的索引值，用於分組顯示。</p>
<ul>
<li><code>argwhere(Y==0)</code> → 回傳符合條件的索引陣列（形如 (n, 1)）</li>
<li><code>squeeze(..., axis=1)</code> → 壓縮掉多餘的維度，讓 <code>arg</code> 成為 1D 陣列</li>
</ul>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1">arg2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.squeeze(np.argwhere(Y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div>
<ul>
<li>同上，找出標籤為 1 的資料索引。</li>
</ul>
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1">plt.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"moon data"</span>)</span></code></pre></div>
<ul>
<li>設定圖形的標題為 “moon data”</li>
</ul>
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1">plt.scatter(X[arg,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], X[arg,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, c<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'r'</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'+'</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'data1'</span>)</span></code></pre></div>
<ul>
<li>畫出標籤為 0 的資料點（藍色 + 號，大小 100）。</li>
</ul>
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1">plt.scatter(X[arg2,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], X[arg2,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>, c<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'b'</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'+'</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'data2'</span>)</span></code></pre></div>
<p>畫出標籤為 1 的資料點（紅色 + 號，大小 40）。</p>
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1">plt.legend()</span></code></pre></div>
<p>顯示圖例，對應 <code>label='data1'</code> 與 <code>label='data2'</code>。</p>
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1">plt.show()</span></code></pre></div>
<p>我們的目標是建立一個模型可以好好區分這兩組資料。</p>
</div>
</div>
</div>
<section id="定義模型及函數" class="level2">
<h2 class="anchored" data-anchor-id="定義模型及函數">定義模型及函數</h2>
<p>因為只是模擬資料，我們略過區分訓練集與測試集的步驟，直接來建立模型。本次使用一個簡單的兩層nn模型<code>LogicNet</code>，這個模型可視作羅吉斯迴歸的延伸，只是多個隱藏層(hidden layer)的設定。一個 nn 模型中還會需要搭配損失函數，這裡配的是交叉熵損失函數(Cross-Entrop)用來做分類。</p>
<p><code>LogicNet</code>的模型結構與數學式分別如下：</p>
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode scss code-with-copy"><code class="sourceCode scss"><span id="cb15-1">Input (inputdim)</span>
<span id="cb15-2">    │</span>
<span id="cb15-3">    ▼</span>
<span id="cb15-4">Linear1 ────► <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">[Linear Layer: inputdim → hiddendim]</span></span>
<span id="cb15-5">    │</span>
<span id="cb15-6">    ▼</span>
<span id="cb15-7">Tanh ───────► <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">[非線性(NL): tanh]</span></span>
<span id="cb15-8">    │</span>
<span id="cb15-9">    ▼</span>
<span id="cb15-10">Linear2 ────► <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">[Linear Layer: hiddendim → outputdim]</span></span>
<span id="cb15-11">    │</span>
<span id="cb15-12">    ▼</span>
<span id="cb15-13">Output (logits)</span></code></pre></div>
<p>令：</p>
<ul>
<li>輸入：<img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7B%5Ctext%7Binputdim%7D%7D"></li>
<li>第一層(Linear1)權重、偏差：<img src="https://latex.codecogs.com/png.latex?W_1,%20b_1"></li>
<li>第二層(Linear2)權重、偏差：<img src="https://latex.codecogs.com/png.latex?W_2,%20b_2"></li>
</ul>
<p>那模型的輸出為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0A%5Cmathbf%7Bh%7D%20&amp;=%20%5Ctanh(W_1%20%5Cmathbf%7Bx%7D%20+%20b_1)%20%5Cquad%20%5Ctext%7B%EF%BC%88%E9%9A%B1%E8%97%8F%E5%B1%A4%E8%BC%B8%E5%87%BA%EF%BC%89%7D%20%5C%5C%0A%5Cmathbf%7Bz%7D%20&amp;=%20W_2%20%5Cmathbf%7Bh%7D%20+%20b_2%20%5Cquad%20%5Ctext%7B%EF%BC%88logits%EF%BC%89%7D%20%5C%5C%0A%5Cend%7Baligned%7D%0A"></p>
<p>最終輸出 <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D"> 是 <strong>未經 softmax 演算法 的 logits</strong>，這是為了符合 PyTorch 的 <code>CrossEntropyLoss()</code> 要求，因為它內部會自動做 softmax。</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
softmax 演算法是什麼?
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>softmax 本質上也屬於 activation function的一種，它能將一個含任意實數的K維向量 <img src="https://latex.codecogs.com/png.latex?z">「壓縮」到另一個K維實向量 <img src="https://latex.codecogs.com/png.latex?%5Csigma%20(z)"> ，使得每一個元素的範圍都在(0,1)之間，公式為：</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Csigma%20(z)_j=%5Cfrac%7Be%5E%7Bz_j%7D%7D%7B%5Csum%20_%7Bk=1%7D%5E%7BK%7De%5E%7Bz_%7Bjk%7D%7D%7D"></p>
<p><a href="https://en.wikipedia.org/wiki/Softmax_function">詳細可見wiki</a></p>
<p>簡單來說，softmax 算出來的東西是機率。</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
為什麼這裡 activation function 用 <code>tanh</code>？
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li><code>tanh</code> 是一種非線性函數，將輸出壓在 <img src="https://latex.codecogs.com/png.latex?%5B-1,%201%5D"></li>
<li>適用於資料已經經過標準化（例如 <code>make_moons</code> 資料範圍在 ±1 內）</li>
<li>也可以改用更為通用的 <code>ReLU</code>，它的收斂速度通常更快。不過 <code>tanh</code> 有時在小模型且資料差異大中表現穩定。</li>
</ul>
</div>
</div>
<p>整體建立模型的流程為：</p>
<ol type="1">
<li><p>定義模型的網路結構</p></li>
<li><p>將網路結構按前向傳播(forward propagation)建立</p></li>
<li><p>利用架設好的介面，得到模型預測結果</p></li>
<li><p>計算模型誤差，在反向傳播(back propagation)時使用</p></li>
</ol>
<p>程式部分我們建立可以控制input data 的 dimension、hidden dimension(=隱藏層數量)，output data 的 dimension等的模型函數。 code 如下，可另存 <code>.py</code>後再匯入到主程式裡：</p>
<div id="cell-4" class="cell" data-execution_count="2">
<div class="sourceCode cell-code" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nn</span>
<span id="cb16-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb16-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb16-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb16-5"></span>
<span id="cb16-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> LogicNet(nn.Module): </span>
<span id="cb16-7">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, inputdim, hiddendim, outputdim):</span>
<span id="cb16-8">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>(LogicNet,<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>).<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb16-9">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.Linear1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(inputdim,hiddendim) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 連階層</span></span>
<span id="cb16-10">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.Linear2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.Linear(hiddendim,outputdim)</span>
<span id="cb16-11">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.criterion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nn.CrossEntropyLoss() <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 定義交叉熵</span></span>
<span id="cb16-12"></span>
<span id="cb16-13">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 前向傳播</span></span>
<span id="cb16-14">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x):</span>
<span id="cb16-15">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.Linear1(x) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 將資料傳入第一層</span></span>
<span id="cb16-16">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tanh(x) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 做 NL</span></span>
<span id="cb16-17">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.Linear2(x)</span>
<span id="cb16-18">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> x</span>
<span id="cb16-19"></span>
<span id="cb16-20">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> predict(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x):</span>
<span id="cb16-21">        pred <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.softmax(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.forward(x), dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb16-22">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> torch.argmax(pred,dim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb16-23"></span>
<span id="cb16-24">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 反向傳播</span></span>
<span id="cb16-25">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> getloss(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>,x,y):</span>
<span id="cb16-26">        y_pred <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.forward(x)</span>
<span id="cb16-27">        loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.criterion(y_pred,y) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 計算loss CrossEntropy</span></span>
<span id="cb16-28">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> loss</span></code></pre></div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-4-contents" aria-controls="callout-4" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
為什麼這裡所有函數都要包在 <code>class</code> 裡？
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-4" class="callout-4-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p><strong>PyTorch 中的所有神經網路模型都應繼承自 <code>torch.nn.Module</code></strong>。這樣做的好處是：</p>
<ol type="1">
<li><p>可以自動註冊所有子層（如 <code>nn.Linear</code>）</p></li>
<li><p>支援 <code>.to(device)</code>、<code>.eval()</code>、<code>.train()</code> 等常用方法</p></li>
<li><p>可以很簡單地使用 PyTorch 提供的訓練工具，例如 <code>optimizer.step()</code>、<code>loss.backward()</code> 等</p></li>
</ol>
<p>此外，從OOP的角度而言，把模型寫成一個class可以讓模型變成一個完整的物件，更易維護。</p>
</div>
</div>
</div>
<p>這裡除了 activation function 外，還定義了 criterion，即 loss function。loss function是<strong>決定模型學習品質的關鍵</strong>，用來計算輸出值與目標值間的誤差。一般在連續的實數資料上會用 MSE ，不過在實務上會根據不同的結構、任務決定不同的 loss function ，這裡使用的是交叉熵損失函數(Cross Entropy Loss)，適用於分類問題。</p>
</section>
<section id="建立模型" class="level2">
<h2 class="anchored" data-anchor-id="建立模型">建立模型</h2>
<p>如前所述，我們現在來正式建立模型。因為模擬資料都是2維的，dimension = 2，隱藏層數量可以自行設定，這裡設定 3 個隱藏層：</p>
<div id="cell-6" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> LogicNet(inputdim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, hiddendim<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, outputdim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb17-2">optimizer <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(model.parameters(), lr<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span> )</span>
<span id="cb17-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 印出模型資訊</span></span>
<span id="cb17-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"model structure and loss function/criterion: </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb17-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(model)</span>
<span id="cb17-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"optimizer is : </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb17-7"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(optimizer)</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>model structure and loss function/criterion: 

LogicNet(
  (Linear1): Linear(in_features=2, out_features=3, bias=True)
  (Linear2): Linear(in_features=3, out_features=2, bias=True)
  (criterion): CrossEntropyLoss()
)
optimizer is : 

Adam (
Parameter Group 0
    amsgrad: False
    betas: (0.9, 0.999)
    capturable: False
    decoupled_weight_decay: False
    differentiable: False
    eps: 1e-08
    foreach: None
    fused: None
    lr: 0.01
    maximize: False
    weight_decay: 0
)</code></pre>
</div>
</div>
<section id="學習率learning-rate" class="level3">
<h3 class="anchored" data-anchor-id="學習率learning-rate">學習率(learning rate)</h3>
<p>lr = 學習率(learning rate)learning rate，代表控制模型中梯度下降的速度，它決定了每次迭代的步長，使得optimizer向損失函數的最小值前進。數值越小，模型越能訓練準確，但相對應訓練較耗時。</p>
<p>學習率與模型權重間的關係為：</p>
<p>新權重= 舊權重- 學習率* 梯度</p>
</section>
<section id="最佳化器optimizer" class="level3">
<h3 class="anchored" data-anchor-id="最佳化器optimizer">最佳化器(optimizer)</h3>
<p>在模型訓練的過程中，往往難以一次就將權重參數調整好，需要透過多次迭代，搭配好的最佳化策略才可以實現。這裡的最佳化策略就是一個演算法，也就是梯度下降法。梯度下降法有很多種，因此也是一個可以調整的參數。</p>
<p>在 pytorch 中，在訓練模型前需要先指定一個最佳化器物件，也就是最佳化器(optimizer)。實務上經常使用<code>Adam</code>作為最佳化器，<a href="https://en.wikipedia.org/wiki/Stochastic_gradient_descent#Adam">數學式請見此</a>。<code>Adam</code>本身包含很多參數，常用的有待最佳化權重參數(固定為<code>model.parameters()</code>)及學習率。</p>
</section>
</section>
<section id="訓練模型" class="level2">
<h2 class="anchored" data-anchor-id="訓練模型">訓練模型</h2>
<p>在 pytorch 中，訓練模型前需先將資料轉成張量(Tensor)後再處理。</p>
<section id="張量" class="level3">
<h3 class="anchored" data-anchor-id="張量">張量</h3>
<p>張量是一種矩陣形式，可以理解成在一個大矩陣中分割出來的小矩陣。因此：</p>
<ul>
<li>0 dimension 的張量 = 純量 (Scalar)</li>
<li>1 dimension 的張量 = 向量 (Vector)</li>
<li>2 dimension 的張量 = 2 <img src="https://latex.codecogs.com/png.latex?%5Ctimes"> 矩陣 (2 <img src="https://latex.codecogs.com/png.latex?%5Ctimes"> 2 Ｍatrix)</li>
</ul>
<p>圖示如下：</p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch1/tensor.png" class="img-fluid"></p>
</section>
<section id="訓練模型與計算-loss" class="level3">
<h3 class="anchored" data-anchor-id="訓練模型與計算-loss">訓練模型與計算 loss</h3>
<p>回到正題，接著再訓練模型與計算 loss：</p>
<div id="cell-9" class="cell" data-execution_count="5">
<div class="sourceCode cell-code" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb19-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## 訓練模型</span></span>
<span id="cb19-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 轉成張量</span></span>
<span id="cb19-3">xt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(X).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">type</span>(torch.FloatTensor) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># numpy to tensor</span></span>
<span id="cb19-4">yt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>  torch.from_numpy(Y).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">type</span>(torch.LongTensor)</span>
<span id="cb19-5">epochs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 迭代次數</span></span>
<span id="cb19-6">losses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[] <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#準備接收每一步的loss</span></span>
<span id="cb19-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs):</span>
<span id="cb19-8">    loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.getloss(xt,yt)</span>
<span id="cb19-9">    losses.append(loss.item())</span>
<span id="cb19-10">    optimizer.zero_grad() <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#清空之前的梯度</span></span>
<span id="cb19-11">    loss.backward()</span>
<span id="cb19-12">    optimizer.step() <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#更新參數</span></span></code></pre></div>
</div>
<p>接著看看訓練結果：　</p>
<div id="cell-11" class="cell" data-execution_count="6">
<div class="sourceCode cell-code" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb20-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb20-2"></span>
<span id="cb20-3">plt.plot(losses)</span>
<span id="cb20-4">plt.xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Epoch'</span>)</span>
<span id="cb20-5">plt.ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Loss'</span>)</span>
<span id="cb20-6">plt.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Training Loss Over Time'</span>)</span>
<span id="cb20-7">plt.grid(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb20-8">plt.show()</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1_files/figure-html/cell-6-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<p>或者先對 loss 值做平滑處理，再視覺化，可以使某些訓練過程中產生的loss線條看起來比較平滑。這裡以moving average為例：</p>
<div id="cell-13" class="cell" data-execution_count="7">
<div class="sourceCode cell-code" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> moving_average(a, w<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>):</span>
<span id="cb21-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(a)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>w:</span>
<span id="cb21-3">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> a[:]</span>
<span id="cb21-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> [val <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> idx <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> w <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(a[(idx<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>w):idx])<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>w <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx, val <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(a)]</span>
<span id="cb21-5"></span>
<span id="cb21-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> plot_losses(losses):</span>
<span id="cb21-7">    avgloss<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> moving_average(losses)</span>
<span id="cb21-8">    plt.figure(i)</span>
<span id="cb21-9">    plt.plot(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(avgloss)), avgloss, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'b--'</span>)</span>
<span id="cb21-10">    plt.xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Epoch'</span>)</span>
<span id="cb21-11">    plt.ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Loss'</span>)</span>
<span id="cb21-12">    plt.show()</span>
<span id="cb21-13"></span>
<span id="cb21-14">plot_losses(losses)</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1_files/figure-html/cell-7-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
</section>
</section>
<section id="評估模型" class="level2">
<h2 class="anchored" data-anchor-id="評估模型">評估模型</h2>
<p><code>accuracy_score()</code>是一個來自 <code>sklearn.metrics</code> 的函數，用來計算模型預測結果的 分類準確率（Accuracy），直觀理解為：模型預測正確的樣本數 / 全部樣本數。</p>
<p>按<a href="https://scikit-learn.org/stable/modules/model_evaluation.html#accuracy-score">klearn文件</a>紀錄，詳細數學定義為：</p>
<p>If $_i$ is the predicted value of the $i$-th sample and <img src="https://latex.codecogs.com/png.latex?y_i"> is the corresponding true value, then the fraction of correct predictions over <img src="https://latex.codecogs.com/png.latex?n_%7Bsamples%7D"> samples is defined as:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctext%7BAccuracy%7D(y,%20%5Chat%20y)%20=%20%5Cfrac%7B1%7D%7Bn_%7Bsamples%7D%7D%20%5Csum_%7Bi=1%7D%5E%7Bn_%7Bsamples%7D-1%7D%20%5Cmathbf%7B1%7D(%5Chat%7By%7D_i%20=%20y_i)%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7B1%7D(%5Chat%7By%7D_i%20=%20y_i)"> is the indicator function, which equals 1 if the prediction is correct, and 0 otherwise.</p>
<p>code實際執行起來很簡單：</p>
<div id="cell-15" class="cell">
<div class="sourceCode cell-code" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb22-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sklearn.metrics <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> accuracy_score</span>
<span id="cb22-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"accuracy_score:"</span>, accuracy_score(model.predict(xt),yt))</span></code></pre></div>
</div>
<p>除此之外，因為本次資料dimension只有2維，可以畫決策邊界圖看看分得如何：</p>
<div id="cell-17" class="cell" data-execution_count="8">
<div class="sourceCode cell-code" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> predict(x):</span>
<span id="cb23-2">    x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.from_numpy(x).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">type</span>(torch.FloatTensor)</span>
<span id="cb23-3">    ans<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>model.predict(x)</span>
<span id="cb23-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> ans.numpy()</span>
<span id="cb23-5"></span>
<span id="cb23-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> plot_decision_boundary(pred_func, X, Y):</span>
<span id="cb23-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 計算設定值範圍</span></span>
<span id="cb23-8">    x_min, x_max <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>X[:,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">.5</span>,X[:,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">.5</span></span>
<span id="cb23-9">    y_min, y_max <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>X[:,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">.5</span>,X[:,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">.5</span>  </span>
<span id="cb23-10">    h <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span></span>
<span id="cb23-11">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 生成網格矩陣</span></span>
<span id="cb23-12">    xx, yy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>np.meshgrid(np.arange(x_min,x_max,h), np.arange(y_min,y_max,h))</span>
<span id="cb23-13">    Z <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pred_func(np.c_[xx.ravel(),yy.ravel()])</span>
<span id="cb23-14">    Z <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Z.reshape(xx.shape)</span>
<span id="cb23-15">    plt.contourf(xx,yy,Z,cmap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>plt.cm.Spectral)</span>
<span id="cb23-16">    arg <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.squeeze(np.argwhere(Y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>),axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb23-17">    arg2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.squeeze(np.argwhere(Y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),axis<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb23-18">    plt.scatter(X[arg,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>],X[arg,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, c<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'r'</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'+'</span>)</span>
<span id="cb23-19">    plt.scatter(X[arg2,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>],X[arg2,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, c<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'b'</span>, marker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'+'</span>)</span>
<span id="cb23-20">    plt.show()</span>
<span id="cb23-21">plot_decision_boundary(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> x: predict(x), xt.numpy(),yt.numpy())</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1_files/figure-html/cell-9-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<p>實際執行時，nn的 node 的初始值是隨機的，每次訓練過程都是從初始值開始調節，所以每次執行完做的模型評估結果都會有些微的不同，但<code>accuracy_score()</code>分數整體會在一個範圍浮動，差異不大。</p>
</section>
</section>
<section id="補充區塊" class="level1">
<h1>補充區塊</h1>
<section id="學習率調整-learning-rates-scheduler" class="level3">
<h3 class="anchored" data-anchor-id="學習率調整-learning-rates-scheduler">學習率調整 (Learning Rates Scheduler)</h3>
<p>學習率基本上是固定的，不過也有方法讓學習率隨迭代次數上升而變小，叫做學習率調整 (Learning Rates Scheduler)，在pytorch裡可以用 <code>lr_scheduler()</code>實現。</p>
<p>這裡做個示範，設定為：每50步<img src="https://latex.codecogs.com/png.latex?%5Ctimes"> 0.99。</p>
<div id="cell-19" class="cell" data-execution_count="9">
<div class="sourceCode cell-code" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb24-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># record losses and lr</span></span>
<span id="cb24-2">losses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[]</span>
<span id="cb24-3">lr_list <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb24-4">epochs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span></span>
<span id="cb24-5">scheduler <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.lr_scheduler.StepLR(optimizer, step_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>,gamma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.99</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定:每50步x0.99</span></span>
<span id="cb24-6"></span>
<span id="cb24-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs):</span>
<span id="cb24-8">    loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.getloss(xt,yt)</span>
<span id="cb24-9">    losses.append(loss.item())</span>
<span id="cb24-10">    optimizer.zero_grad()</span>
<span id="cb24-11">    loss.backward()</span>
<span id="cb24-12">    optimizer.step()</span>
<span id="cb24-13">    scheduler.step()</span>
<span id="cb24-14">    lr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> optimizer.state_dict()[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'param_groups'</span>][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'lr'</span>]</span>
<span id="cb24-15">    lr_list.append(lr)</span>
<span id="cb24-16">plt.plot(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs),lr_list,color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'r'</span>)</span>
<span id="cb24-17">plt.xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Epoch'</span>)</span>
<span id="cb24-18">plt.ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Learning rate'</span>)</span>
<span id="cb24-19">plt.show()</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1_files/figure-html/cell-10-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
<p>還有一種常用的學習率調整設定－MultiStepLR，可以設定迭代到第幾次再做調整，使得訓練起來比前一種有效率。</p>
<div id="cell-21" class="cell" data-execution_count="10">
<div class="sourceCode cell-code" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># record losses and lr</span></span>
<span id="cb25-2">losses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[]</span>
<span id="cb25-3">lr_list <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb25-4">epochs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span></span>
<span id="cb25-5">scheduler <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">700</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">800</span>],gamma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定:每50步x0.99</span></span>
<span id="cb25-6"></span>
<span id="cb25-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs):</span>
<span id="cb25-8">    loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model.getloss(xt,yt)</span>
<span id="cb25-9">    losses.append(loss.item())</span>
<span id="cb25-10">    optimizer.zero_grad()</span>
<span id="cb25-11">    loss.backward()</span>
<span id="cb25-12">    optimizer.step()</span>
<span id="cb25-13">    scheduler.step()</span>
<span id="cb25-14">    lr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> optimizer.state_dict()[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'param_groups'</span>][<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'lr'</span>]</span>
<span id="cb25-15">    lr_list.append(lr)</span>
<span id="cb25-16">plt.plot(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(epochs),lr_list,color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'r'</span>)</span>
<span id="cb25-17">plt.xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Epoch'</span>)</span>
<span id="cb25-18">plt.ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Learning rate'</span>)</span>
<span id="cb25-19">plt.show()</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1_files/figure-html/cell-11-output-1.png" class="img-fluid figure-img"></p>
</figure>
</div>
</div>
</div>
</section>
<section id="保存模型" class="level2">
<h2 class="anchored" data-anchor-id="保存模型">保存模型</h2>
<p>通常一個模型需要長時間的訓練，所以 pytorch 可以保存模型，供下次進行預訓練或直接使用：</p>
<div class="sourceCode" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb26-1">torch.save(model.state_dict(),<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'./model.pth'</span>)</span></code></pre></div>
<p>這個<code>model.pth</code>便是模型檔案。如要載入模型，則可以：</p>
<div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb27-1">model.load_state_dict(torch.load(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'./model.pth'</span>))</span></code></pre></div>
<p>執行後 model 中的值將跟<code>model.pth</code>同步。</p>



</section>
</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>note</category>
  <category>python</category>
  <category>pytorch</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/pytorch1/pytorch1.html</guid>
  <pubDate>Wed, 20 Aug 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/pytorch1/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>使用python做離線倒數計時器</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/make-app/</link>
  <description><![CDATA[ 






<!-- 使用 AI -->
<div data-ai-tag="2">

</div>
<script src="../../../../asserts/box.js" defer=""></script>
<p>這篇文章有點流水帳，就是一篇製作玩具的過程隨筆。用AI寫程式可以做到自己以前做不到的，但遇到問題還是要想辦法解決，解決問題還是需要思考跟經驗，所以我想還是紀錄一下，供日後參考囉。</p>
<hr>
<section id="緣起" class="level1">
<h1>緣起</h1>
<p>因為最近覺得用眼過度，想要一個小玩具來提醒自己要定時休息，由於倒數計時的功能不會很複雜，自己用還可以客製化自己想要的功能，所以就來動手做做看！</p>
<p>我先叫AI生了靜態倒數計時器的網頁，UI看起來很不錯：</p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/make-app/image1.png" class="img-fluid"></p>
<p>不過很就發現，它沒有辦法準時計時，因為瀏覽器會暫停閒置的網站，哭哭。</p>
<p>然後我就把歪腦筋動到了用 python 製作 exe 上，之前只是耳聞過 python 可以做到，但我其實沒有嘗試過製作一個 exe 檔，正好是個機會 :) 就來試試看吧！</p>
<section id="使用環境與套件" class="level2">
<h2 class="anchored" data-anchor-id="使用環境與套件">使用環境與套件</h2>
<p><strong>使用環境</strong></p>
<ul>
<li><p>Windows 11</p></li>
<li><p>python version: 3.12.5</p></li>
</ul>
<p><strong>使用套件:</strong></p>
<ul>
<li>tkinter</li>
</ul>
<blockquote class="blockquote">
<p>製作 GUI 的基本套件，優點是簡單，缺點是UI有些陽春</p>
</blockquote>
<ul>
<li>customtkinte</li>
</ul>
<blockquote class="blockquote">
<p>UI比較美觀的套件，用來優化舊有UI。</p>
</blockquote>
<ul>
<li>playsound</li>
</ul>
<blockquote class="blockquote">
<p>播放聲音用的套件</p>
</blockquote>
<ul>
<li>pyinstaller</li>
</ul>
<blockquote class="blockquote">
<p>製作exe的套件</p>
</blockquote>
</section>
</section>
<section id="製作過程" class="level1">
<h1>製作過程</h1>
<section id="第一步開需求" class="level2">
<h2 class="anchored" data-anchor-id="第一步開需求">第一步：開需求</h2>
<p>我的需求如下：</p>
<ul>
<li>每30分鐘跳視窗提醒休息5分鐘，使用者按按鈕再繼續計時。</li>
<li>螢幕上要顯示剩下幾分鐘。</li>
<li>以秒為單位更新畫面。</li>
</ul>
</section>
<section id="第二步丟ai生架構" class="level2">
<h2 class="anchored" data-anchor-id="第二步丟ai生架構">第二步：丟AI生架構</h2>
<p>把需求丟給AI後，生了一個架構出來</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> tkinter <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> tk</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> tkinter <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> messagebox</span>
<span id="cb1-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> time</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> threading</span>
<span id="cb1-5"></span>
<span id="cb1-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 時間設定（秒）</span></span>
<span id="cb1-7">WORK_DURATION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">30</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 30分鐘</span></span>
<span id="cb1-8">BREAK_DURATION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 5分鐘</span></span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># GUI 主視窗</span></span>
<span id="cb1-11">root <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tk.Tk()</span>
<span id="cb1-12">root.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"番茄鐘計時器"</span>)</span>
<span id="cb1-13">root.geometry(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"320x180"</span>)</span>
<span id="cb1-14">root.resizable(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb1-15"></span>
<span id="cb1-16">status_label <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tk.Label(root, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"準備中..."</span>, font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Arial"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>))</span>
<span id="cb1-17">status_label.pack(pady<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb1-18"></span>
<span id="cb1-19">time_label <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tk.Label(root, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"剩餘時間：00:00"</span>, font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Arial"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>))</span>
<span id="cb1-20">time_label.pack(pady<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb1-21"></span>
<span id="cb1-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 控制計時的旗標</span></span>
<span id="cb1-23">stop_flag <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> threading.Event()</span>
<span id="cb1-24"></span>
<span id="cb1-25"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> format_time(seconds):</span>
<span id="cb1-26">    minutes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> seconds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span></span>
<span id="cb1-27">    secs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> seconds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span></span>
<span id="cb1-28">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>minutes<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:02}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>secs<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:02}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-29"></span>
<span id="cb1-30"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> run_timer(duration, status_text, done_message):</span>
<span id="cb1-31">    remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> duration</span>
<span id="cb1-32"></span>
<span id="cb1-33">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 更新狀態文字</span></span>
<span id="cb1-34">    root.after(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span>: status_label.config(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>status_text))</span>
<span id="cb1-35"></span>
<span id="cb1-36">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb1-37">        display <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"剩餘時間：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>format_time(remaining)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-38">        root.after(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> d<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>display: time_label.config(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>d))</span>
<span id="cb1-39">        time.sleep(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-40">        remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb1-41"></span>
<span id="cb1-42">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb1-43">        root.after(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span>: messagebox.showinfo(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"提醒"</span>, done_message))</span>
<span id="cb1-44"></span>
<span id="cb1-45"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> timer_loop():</span>
<span id="cb1-46">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb1-47">        run_timer(WORK_DURATION, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"⏱ 工作中"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"⏰ 工作時間結束！請休息 5 分鐘"</span>)</span>
<span id="cb1-48">        run_timer(BREAK_DURATION, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"☕ 休息中"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"✅ 休息結束，準備進入下一輪"</span>)</span>
<span id="cb1-49"></span>
<span id="cb1-50"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> start_timer():</span>
<span id="cb1-51">    stop_flag.clear()</span>
<span id="cb1-52">    threading.Thread(target<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>timer_loop, daemon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>).start()</span>
<span id="cb1-53"></span>
<span id="cb1-54"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> stop_timer():</span>
<span id="cb1-55">    stop_flag.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">set</span>()</span>
<span id="cb1-56">    status_label.config(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"計時已停止"</span>)</span>
<span id="cb1-57">    time_label.config(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"剩餘時間：00:00"</span>)</span>
<span id="cb1-58"></span>
<span id="cb1-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 按鈕 UI</span></span>
<span id="cb1-60">btn_frame <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tk.Frame(root)</span>
<span id="cb1-61">btn_frame.pack(pady<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>)</span>
<span id="cb1-62"></span>
<span id="cb1-63">start_btn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tk.Button(btn_frame, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"開始計時"</span>, command<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>start_timer, font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Arial"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>))</span>
<span id="cb1-64">start_btn.pack(side<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>tk.LEFT, padx<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb1-65"></span>
<span id="cb1-66">stop_btn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tk.Button(btn_frame, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"結束計時"</span>, command<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>stop_timer, font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Arial"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>))</span>
<span id="cb1-67">stop_btn.pack(side<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>tk.RIGHT, padx<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb1-68"></span>
<span id="cb1-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 啟動 GUI 主迴圈</span></span>
<span id="cb1-70">root.mainloop()</span></code></pre></div>
<p>做出來的畫面像這樣：</p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/make-app/image2.png" class="img-fluid"></p>
<p>但用 <code>tkinter</code> 做出來的介面有點醜，於是改用<code>customtkinte</code>替代，再追加希望有聲音提醒的需求，於是有了第二版：</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> customtkinter <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> ctk</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> time</span>
<span id="cb2-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> threading</span>
<span id="cb2-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> tkinter <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> messagebox</span>
<span id="cb2-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> playsound <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> playsound</span>
<span id="cb2-6"></span>
<span id="cb2-7">WORK_DURATION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">30</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span></span>
<span id="cb2-8">BREAK_DURATION <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span></span>
<span id="cb2-9">SOUND_FILE <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ding.mp3"</span></span>
<span id="cb2-10"></span>
<span id="cb2-11">ctk.set_appearance_mode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dark"</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 或 "light"</span></span>
<span id="cb2-12">ctk.set_default_color_theme(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue"</span>)</span>
<span id="cb2-13"></span>
<span id="cb2-14">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ctk.CTk()</span>
<span id="cb2-15">app.title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"番茄鐘計時器"</span>)</span>
<span id="cb2-16">app.geometry(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"360x220"</span>)</span>
<span id="cb2-17"></span>
<span id="cb2-18">status_label <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ctk.CTkLabel(app, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"準備中..."</span>, font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Arial"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">18</span>))</span>
<span id="cb2-19">status_label.pack(pady<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb2-20"></span>
<span id="cb2-21">time_label <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ctk.CTkLabel(app, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"剩餘時間：00:00"</span>, font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Arial"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>))</span>
<span id="cb2-22">time_label.pack(pady<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb2-23"></span>
<span id="cb2-24">stop_flag <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> threading.Event()</span>
<span id="cb2-25"></span>
<span id="cb2-26"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> format_time(seconds):</span>
<span id="cb2-27">    minutes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> seconds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span></span>
<span id="cb2-28">    secs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> seconds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span></span>
<span id="cb2-29">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>minutes<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:02}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>secs<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:02}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb2-30"></span>
<span id="cb2-31"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> run_timer(duration, status_text, done_message):</span>
<span id="cb2-32">    remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> duration</span>
<span id="cb2-33">    status_label.configure(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>status_text)</span>
<span id="cb2-34">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb2-35">        time_label.configure(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"剩餘時間：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>format_time(remaining)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb2-36">        time.sleep(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb2-37">        remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb2-38">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb2-39">        threading.Thread(target<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span>: playsound(SOUND_FILE), daemon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>).start()</span>
<span id="cb2-40">        messagebox.showinfo(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"提醒"</span>, done_message)</span>
<span id="cb2-41"></span>
<span id="cb2-42"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> timer_loop():</span>
<span id="cb2-43">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb2-44">        run_timer(WORK_DURATION, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"⏱ 工作中"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"⏰ 工作時間結束！請休息 5 分鐘"</span>)</span>
<span id="cb2-45">        run_timer(BREAK_DURATION, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"☕ 休息中"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"✅ 休息結束，準備進入下一輪"</span>)</span>
<span id="cb2-46"></span>
<span id="cb2-47"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> start_timer():</span>
<span id="cb2-48">    stop_flag.clear()</span>
<span id="cb2-49">    threading.Thread(target<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>timer_loop, daemon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>).start()</span>
<span id="cb2-50"></span>
<span id="cb2-51"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> stop_timer():</span>
<span id="cb2-52">    stop_flag.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">set</span>()</span>
<span id="cb2-53">    status_label.configure(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"計時已停止"</span>)</span>
<span id="cb2-54">    time_label.configure(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"剩餘時間：00:00"</span>)</span>
<span id="cb2-55"></span>
<span id="cb2-56">btn_frame <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ctk.CTkFrame(app)</span>
<span id="cb2-57">btn_frame.pack(pady<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb2-58"></span>
<span id="cb2-59">start_btn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ctk.CTkButton(btn_frame, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"開始計時"</span>, command<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>start_timer)</span>
<span id="cb2-60">start_btn.pack(side<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"left"</span>, padx<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb2-61"></span>
<span id="cb2-62">stop_btn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ctk.CTkButton(btn_frame, text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"結束計時"</span>, command<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>stop_timer)</span>
<span id="cb2-63">stop_btn.pack(side<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>, padx<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb2-64"></span>
<span id="cb2-65">app.mainloop()</span></code></pre></div>
<p>介面長得像這樣：　</p>
<p><img src="https://paperfishblog.netlify.app/posts/tech/make-app/image3.png" class="img-fluid"></p>
<p>好看多了！</p>
<p>其實不用聲音檔，光有這樣的架構，再寫個 bat 呼叫腳本其實就能用了，但到這裡我開始越來越貪心，想要弄得更精細，於是又加了2個需求</p>
<ol type="1">
<li>聲音檔分成2個，工作時和休息時的音效不同</li>
<li>聲音只播一次，且要跟訊息一起出現</li>
</ol>
</section>
<section id="第三步微調程式與參數" class="level2">
<h2 class="anchored" data-anchor-id="第三步微調程式與參數">第三步：微調程式與參數</h2>
<section id="聲音檔分成2個" class="level3">
<h3 class="anchored" data-anchor-id="聲音檔分成2個">聲音檔分成2個</h3>
<p>實際上需要改動2個地方，一個是在函數<code>run_timer</code>增加區分工作跟休息的音效，另一個是在函數<code>timer_loop</code>中塞入2個聲音檔。音檔可以自行去網路上找喜歡的，並且建議轉成<code>.wav</code>檔格式，執行程式時比較不會出問題(像<code>.mp3</code>就常會有有時可以順利播放有時無法)</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 主循環：工作 → 休息 → 循環</span></span>
<span id="cb3-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> timer_loop():</span>
<span id="cb3-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb3-4">        run_timer(WORK_DURATION, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"⏱ 工作中"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"⏰ 工作結束！請休息 5 分鐘"</span>, SOUND_FILE)</span>
<span id="cb3-5">        run_timer(BREAK_DURATION, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"☕ 休息中"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"✅ 休息結束，準備進入下一輪"</span>, SOUND_FILE2)</span></code></pre></div>
</section>
<section id="聲音只播一次且跟訊息一起出現" class="level3">
<h3 class="anchored" data-anchor-id="聲音只播一次且跟訊息一起出現">聲音只播一次，且跟訊息一起出現</h3>
<p>這需要調整兩個函數：</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 播放音效（非阻塞）</span></span>
<span id="cb4-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> play_sound(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>):</span>
<span id="cb4-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb4-4">        pygame.mixer.music.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>)</span>
<span id="cb4-5">        pygame.mixer.music.play()</span>
<span id="cb4-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb4-7">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"⚠ 播放音效失敗：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-8"></span>
<span id="cb4-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 執行計時器</span></span>
<span id="cb4-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 這裡使用非阻塞方式播放音效，避免阻塞主線</span></span>
<span id="cb4-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> run_timer(duration, status_text, done_message, sound_file):</span>
<span id="cb4-12">    remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> duration</span>
<span id="cb4-13">    status_label.configure(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>status_text)</span>
<span id="cb4-14"></span>
<span id="cb4-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb4-16">        time_label.configure(text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"剩餘時間：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>format_time(remaining)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-17">        time.sleep(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb4-18">        remaining <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb4-19"></span>
<span id="cb4-20">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stop_flag.is_set():</span>
<span id="cb4-21">        play_sound(sound_file)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ✅ 非阻塞播放音效</span></span>
<span id="cb4-22">        messagebox.showinfo(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"提醒"</span>, done_message)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ✅ 等待使用者按下「確定」</span></span></code></pre></div>
</section>
<section id="自訂音效開啟關閉" class="level3">
<h3 class="anchored" data-anchor-id="自訂音效開啟關閉">自訂音效開啟/關閉</h3>
<p>這裡指的是自訂音效，window預設的視窗跳出音效不在此範圍。需要增加/修改的指令有：</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1">sound_enabled <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ctk.BooleanVar(value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 預設開啟音效</span></span></code></pre></div>
<p>這條程式需要放在主視窗 UI 剛建立的時候，也就是<code>app = ctk.CTk()</code>之後，不然會報錯。</p>
<p>其次是修改 <code>play_sound()</code>：</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> play_sound(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>):</span>
<span id="cb6-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> sound_enabled.get():</span>
<span id="cb6-3">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 如果沒勾選，就不播放音效</span></span>
<span id="cb6-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb6-5">        pygame.mixer.music.load(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>)</span>
<span id="cb6-6">        pygame.mixer.music.play()</span>
<span id="cb6-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb6-8">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"⚠ 播放音效失敗：</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div>
<p>就可以得到一個勾選是否要啟用音效的欄位了。</p>
</section>
<section id="配合打包成exe的調整" class="level3">
<h3 class="anchored" data-anchor-id="配合打包成exe的調整">配合打包成exe的調整</h3>
<p>因為py檔在執行程式時會認附加檔案的路徑，即使打包成exe後也是如此，因此需要就這一段做微調。</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> os</span>
<span id="cb7-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb7-3"></span>
<span id="cb7-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 取得正確路徑（支援 pyinstaller 打包後的執行檔）</span></span>
<span id="cb7-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> resource_path(relative_path):</span>
<span id="cb7-6">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""取得資源真實路徑（支援開發階段與打包後）"""</span></span>
<span id="cb7-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb7-8">        base_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sys._MEIPASS  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># PyInstaller 的暫存資料夾</span></span>
<span id="cb7-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span>:</span>
<span id="cb7-10">        base_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.path.abspath(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"."</span>)</span>
<span id="cb7-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> os.path.join(base_path, relative_path)</span>
<span id="cb7-12"></span>
<span id="cb7-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 使用方式：</span></span>
<span id="cb7-14">sound_file_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> resource_path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sound.wav"</span>)</span>
<span id="cb7-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 程式使用 resource_path()，就可以正確找到聲音檔了。</span></span>
<span id="cb7-16"></span>
<span id="cb7-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 例如播放聲音</span></span>
<span id="cb7-18"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> playsound</span>
<span id="cb7-19">playsound.playsound(sound_file_path)</span></code></pre></div>
<p>執行時，PyInstaller 會自動把聲音檔解壓到臨時目錄中，這樣就可以找到聲音檔了。</p>
<blockquote class="blockquote">
<p>如果不使用<code>resource_path()</code>會怎麼樣？</p>
<p>由於 PyInstaller 會把打包成一個 <code>.exe</code>。當執行這個 <code>.exe</code> 時，它會先解壓所有檔案到一個臨時目錄（通常是 %TEMP%_MEIxxxxx），然後才開始執行程式。如果沒有<code>resource_path()</code>就會導致找不到檔案，因為它找到臨時目錄去了。</p>
</blockquote>
</section>
<section id="ico" class="level3">
<h3 class="anchored" data-anchor-id="ico">加入自定義圖示</h3>
<p>正常不做調整的情況下，視窗圖示為<img src="https://paperfishblog.netlify.app/posts/tech/make-app/image4.png" class="img-fluid">，但我想換掉變成自己的。先找一個 <code>.png</code> 轉成 <code>.ico</code> 圖示，放進同一個專案資料夾。接下來要做的事跟前面的音檔類似 :</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 設定視窗圖示</span></span>
<span id="cb8-2">icon_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> resource_path(ICON_FILE)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 使用之前定義的資源路徑函數</span></span>
<span id="cb8-3">app.iconbitmap(icon_path)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 必須是 .ico 格式</span></span></code></pre></div>
</section>
</section>
<section id="第四步打包成exe" class="level2">
<h2 class="anchored" data-anchor-id="第四步打包成exe">第四步：打包成exe</h2>
<p>用<code>pyinstaller</code>進行打包，首先要先安裝套件:</p>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb9-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install pyinstaller</span></code></pre></div>
<section id="基本指令" class="level3">
<h3 class="anchored" data-anchor-id="基本指令">基本指令</h3>
<p>打包的基本指令為</p>
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb10-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pyinstaller</span> your_script.py</span></code></pre></div>
<p>執行完成後系統會提示放在 <code>.\dist</code>的資料夾下，具體結構為：</p>
<pre><code>your_script/
├── dist/
│   └── your_script.exe  ← 可執行檔在這裡
├── build/
├── your_script.spec </code></pre>
<p><code>build/</code> 和 <code>.spec</code> 是中間產物，可以保留也可以刪除。</p>
<p>除此之外還有常用附加指令：</p>
<table class="caption-top table">
<colgroup>
<col style="width: 40%">
<col style="width: 59%">
</colgroup>
<thead>
<tr class="header">
<th>參數</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>--onefile</code></td>
<td>打包成單一 <code>.exe</code> 檔案（預設會拆成很多檔）</td>
</tr>
<tr class="even">
<td><code>--noconsole</code></td>
<td>不顯示命令列（適合正式 GUI 程式），不輸入時點擊<code>exe</code>會跳出cmd畫面。</td>
</tr>
<tr class="odd">
<td><code>--icon=icon.ico</code></td>
<td>指定執行檔圖示</td>
</tr>
</tbody>
</table>
<blockquote class="blockquote">
<p>實作注意事項：</p>
<ol type="1">
<li><p><code>--onefile</code>建議必加，不然會跑出很多資料夾or檔案 <del>有點像遊戲拆包</del></p></li>
<li><p><code>--noconsole</code> 在測試階段可以不用加入，方便檢查程式被打包成<code>exe</code>後會不會有前面沒有出現過的錯誤訊息。</p></li>
</ol>
</blockquote>
</section>
<section id="加入附加檔" class="level3">
<h3 class="anchored" data-anchor-id="加入附加檔">加入附加檔</h3>
<p>假設加入的聲音為<code>sounds.wav</code>，那麼指令為：</p>
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb12-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">--add-data</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sounds.wav;."</span></span></code></pre></div>
<p>這裡的<code>;</code>後面是資料夾的意思，<code>.</code>表示跟主程式一樣放在同一個資料夾。如果有兩個就在後面繼續使用此指令。</p>
<p>如果聲音檔跟主程式放在不同資料夾，像這樣：</p>
<pre><code>project/
├── main.py
└── sounds/
    ├── click.wav
    └── error.wav</code></pre>
<p>那指令就是：</p>
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb14-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pyinstaller</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--onefile</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--add-data</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sounds;sounds"</span> main.py</span></code></pre></div>
<p>代表「把 <code>sounds/</code> 整個資料夾加到 <code>.exe</code> 所在目錄中」</p>
<blockquote class="blockquote">
<p>注意：</p>
<p>如果有設定前面的圖示，一樣需要用 <code>--add-data "XXX.ico;."</code>，不然它會找不到檔案。</p>
</blockquote>
</section>
<section id="加入icon" class="level3">
<h3 class="anchored" data-anchor-id="加入icon">加入icon</h3>
<p>這裡指的是打包成 exe 時的圖示，指令為：</p>
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb15-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">--icon=icon.ico</span></span></code></pre></div>
<p>是顯示exe程式的圖示，可以自己找或加工一張png，再去轉成ico來用。生成exe後圖示不一定馬上會出現，通常過一段時間才會出現。</p>
</section>
</section>
<section id="打包過程遇到的困難" class="level2">
<h2 class="anchored" data-anchor-id="打包過程遇到的困難">打包過程遇到的困難</h2>
<p>在測試py腳本時一切順利，不過在打包exe遇到套件沒有安裝的神奇狀況，而且問AI，是它給的做法都完全沒用，後來仔細觀察<code>pyinstaller</code>產出的log才發現原來是我平常都把套件都安裝在 <code>venv</code> 的虛擬環境下，但是 <code>pyinstaller</code> 在執行時沒有調整過就會自動找全域環境，於是就用指令啟動虛擬環境，確認虛擬環境下有安裝<code>pyinstaller</code>後再執行打包就沒問題了。</p>
<p>中間還遇到一個有點蠢的問題XD，我常用的虛擬環境安裝在 “桌面” 下的資料夾，好死不死路徑名稱是中文的，導致專案在Vscode自動指定虛擬環境後還遇到因為無法辨識中文路徑而舞法啟用python的狀況XD。其實只要再建一個虛擬環境在C槽就好，但我就是不死心<del>對原本的虛擬環境有感情ㄌ</del>，一直找其他方法，後來用指令啟動虛擬環境後就好了，白繞了一點路XD。</p>
<p><del>不過最後我還是乖乖建新的虛擬環境了，不然其他專案早晚會出問題</del></p>
</section>
</section>
<section id="此-exe-檔背後執行原理" class="level1">
<h1>此 exe 檔背後執行原理</h1>
<p>涉及作業系統相關的知識，基礎用語請見<a href="https://medium.com/erens-tech-book/%E7%90%86%E8%A7%A3-process-thread-94a40721b492">此來源</a>。 每當執行一個 <code>.exe</code>，作業系統就會開一個新的 process，由這個 process 執行你的程式，並從主執行緒 ( main tread )開始跑起。在程式中用 <code>threading.Thread()</code> 則會再額外開新的tread，也就是說每按下一個按鈕事件，就會啟動一個新的 tread。</p>
<p>這也讓目前的小程式有個問題，當過於頻繁地切換按鈕事件時程式容易當機，且容易有兩個按鈕 tread 同時在跑的狀況，是未來可以再精進的部分。</p>
</section>
<section id="心得" class="level1">
<h1>心得</h1>
<p>雖然是個簡單的小玩具，但中間為了客製化、解決遇到的問題還是花了一天多的時間還做出稍微滿意的樣子。不過也學到很多~ <del>作為逃避寫論文的娛樂也很好</del>，下次有機會再來玩玩別的😄。</p>



</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>python</category>
  <category>toys</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/make-app/</guid>
  <pubDate>Fri, 08 Aug 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/make-app/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>如何使用 Python jupyter-book 建立一本書</title>
  <dc:creator>紙魚 </dc:creator>
  <link>https://paperfishblog.netlify.app/posts/tech/jpyterbook/</link>
  <description><![CDATA[ 






<!-- 使用 AI -->
<div data-ai-tag="1,2">

</div>
<script src="../../../../asserts/box.js" defer=""></script>
<section id="前言" class="level1">
<h1>前言</h1>
<p>說到 jupyter-book，可能很多人會想到 Jupyter Notebook，但這裡指的是一個 python 套件，用來建立線上書籍的工具，特別適合用於副檔名為 <code>.ipynb</code> 的 Jupyter Notebook 。最後產出的網站類似 gitbook 或是 bookdown 的靜態網站，適合用來寫教學、筆記或是書籍。也是在我使用 Quarto 之前考慮過的選項之一，雖然最後因為產出有點陽春、能改動的東西較少所以放棄了，但它的製作過程簡單，所以留下這個筆記，方便日後需要時可以快速上手。</p>
<section id="使用環境" class="level2">
<h2 class="anchored" data-anchor-id="使用環境">使用環境</h2>
<p>這是我的使用環境，不一定適用每個人，但截至目前為止都運作順利：</p>
<ul>
<li><p>IDE ： VsCode</p></li>
<li><p>Python version : 3.12.5</p></li>
</ul>
</section>
</section>
<section id="首次興建" class="level1">
<h1>首次興建</h1>
<p>依照以下步驟操作：</p>
<hr>
<section id="安裝-jupyter-book" class="level2">
<h2 class="anchored" data-anchor-id="安裝-jupyter-book">安裝 jupyter-book</h2>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-U</span> jupyter-book</span></code></pre></div>
<hr>
</section>
<section id="建立一本書的骨架" class="level2">
<h2 class="anchored" data-anchor-id="建立一本書的骨架">建立一本書的骨架</h2>
<p>使用命令行建立一個新的書籍專案：</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">jupyter-book</span> create mybook/</span></code></pre></div>
<p>這會建立一個名為 <code>mybook/</code> 的資料夾，裡面包含書籍的基本結構，包括 Markdown 與 Jupyter Notebook 範例，<code>mybook/</code> 亦可以自行換成其他資料夾名稱。</p>
<hr>
</section>
<section id="編輯內容" class="level2">
<h2 class="anchored" data-anchor-id="編輯內容">編輯內容</h2>
<blockquote class="blockquote">
<p>從這裡開始就是在編輯時會重複執行的步驟！</p>
</blockquote>
<p>可以在 <code>mybook/</code> 目錄中看到這些重要檔案和資料夾：</p>
<ul>
<li><code>mybook/_config.yml</code>：書籍的設定（標題、主題、logo 等）</li>
<li><code>mybook/_toc.yml</code>：書籍目錄（控制章節順序）</li>
<li><code>mybook/intro.md</code>、<code>mybook/chapters/*.ipynb</code>：實際內容，可新增 Markdown 或 Notebook 檔案</li>
</ul>
<section id="新增章節例子" class="level3">
<h3 class="anchored" data-anchor-id="新增章節例子">新增章節例子：</h3>
<ol type="1">
<li>在 <code>mybook/</code> 下新增一個檔案，例如 <code>chapter1.md</code></li>
<li>在 <code>_toc.yml</code> 中加上該檔案的設定：</li>
</ol>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> jb-book</span></span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">root</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> intro</span></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chapters</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapter1</span></span></code></pre></div>
<hr>
</section>
</section>
<section id="編譯網站" class="level2">
<h2 class="anchored" data-anchor-id="編譯網站">編譯網站</h2>
<p>在書籍資料夾中執行：</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">jupyter-book</span> build mybook/</span></code></pre></div>
<p>這會自動產生靜態網站，輸出目錄為：</p>
<pre><code>mybook/_build/html/</code></pre>
<hr>
</section>
<section id="預覽網站" class="level2">
<h2 class="anchored" data-anchor-id="預覽網站">預覽網站</h2>
<p>使用瀏覽器打開以下檔案即可：</p>
<pre><code>mybook/_build/html/index.html</code></pre>
<p>或用 Python 的 HTTP server 預覽：</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> mybook/_build/html</span>
<span id="cb7-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">python</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-m</span> http.server</span></code></pre></div>
<p>然後打開瀏覽器到 <a href="http://localhost:8000">http://localhost:8000</a></p>
<blockquote class="blockquote">
<ol type="1">
<li>注意：如果你有修改 <code>_toc.yml</code> 或其他設定，必須重新編譯網站才能看到變更，不然會顯示舊的內容。</li>
<li>如果編譯網站後有做<strong>刪除頁面</strong>的動作，請到 <code>_build/html/</code> 資料夾刪除對應的 html，不然他不會蓋到<del>很笨我知道</del>。</li>
<li>執行後 terminal 的 powershell 非必要請不要關閉，因為它在運行 HTTP server。需要關閉可以在 terminal 視窗 輸入 <code>Ctrl + C</code> 停止服務。或是另開新的 terminal 視窗來執行其他命令。</li>
</ol>
</blockquote>
<hr>
</section>
<section id="發布網站選擇性" class="level2">
<h2 class="anchored" data-anchor-id="發布網站選擇性">發布網站（選擇性）</h2>
<p>可以把 <code>_build/html</code> 上傳到 GitHub Pages、Netlify、Vercel 等平台，或用 GitHub Actions 自動部署。</p>



</section>
</section>

<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
<div class="list quarto-listing-default">

</div>
<div class="listing-no-matching d-none">無符合的項目</div>
</div> ]]></description>
  <category>other</category>
  <category>python</category>
  <guid>https://paperfishblog.netlify.app/posts/tech/jpyterbook/</guid>
  <pubDate>Thu, 24 Jul 2025 16:00:00 GMT</pubDate>
  <media:content url="https://paperfishblog.netlify.app/posts/tech/jpyterbook/image.png" medium="image" type="image/png" height="81" width="144"/>
</item>
</channel>
</rss>
