<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://thaitanhaha.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://thaitanhaha.github.io/" rel="alternate" type="text/html" /><updated>2025-08-23T19:58:05+00:00</updated><id>https://thaitanhaha.github.io/feed.xml</id><title type="html">Nguyễn Thái Tân</title><subtitle>Student at Ho Chi Minh University of Technology</subtitle><author><name>Nguyễn Thái Tân</name><email>nguyenthaitan02@gmail.com</email></author><entry><title type="html">Reinforcement learning 5 - Trust Region Policy Optimization</title><link href="https://thaitanhaha.github.io/posts/reinforcement-learning-5/" rel="alternate" type="text/html" title="Reinforcement learning 5 - Trust Region Policy Optimization" /><published>2025-07-31T00:00:00+00:00</published><updated>2025-07-31T00:00:00+00:00</updated><id>https://thaitanhaha.github.io/posts/reinforcement-learning-5</id><content type="html" xml:base="https://thaitanhaha.github.io/posts/reinforcement-learning-5/"><![CDATA[<p>Các phương pháp Policy Gradient có thể dẫn đến các cập nhật chính sách quá lớn, khiến cho mô hình bị “lạc” khỏi những gì đã học được trong quá khứ và trở nên không ổn định. Điều này có thể làm cho quá trình huấn luyện trở nên kém hiệu quả.</p>

<p>Giống như trong gradient descent hay gradient ascent, nếu bước đi quá nhỏ, mô hình sẽ học rất chậm; còn nếu bước đi quá lớn, nó có thể đi sai hướng và mất ổn định.</p>

<!-- Mỗi một lần cập nhật chính sách, ta phải lấy mẫu toàn bộ một quỹ đạo, nói cách khác là cập nhật các trạng thái trong quỹ đạo đó. Các trạng thái trong quỹ đạo thì ít nhiều có sự tương đồng nhau, nên những thay đổi này sẽ chồng lắp lẫn nhau, làm cho quá trình huấn luyện trở nên rất nhạy cảm và không ổn định. -->

<h2 id="natural-policy-gradients">Natural policy gradients</h2>

<p>Nói chung, ta muốn chính sách được cập nhật sao cho \(\underbrace{\text{hàm mục tiêu thì vừa tăng}}_{1}\), vừa đảm bảo \(\underbrace{\text{chính sách mới và cũ không quá khác biệt}}_{2}\).</p>

<h3 id="1---mathcall_thetatheta">1 - \(\mathcal{L}_{\theta}(\theta’)\)</h3>

<p>Gọi \(\mathcal{L}_{\theta}(\theta')\) là hàm mục tiêu ước tính sự cải thiện trong phần thưởng kỳ vọng khi chuyển từ \(\pi_\theta\) sang \(\pi_{\theta’}\). Với \(A(s_t,a_t)\) là hàm lợi thế thì</p>

\[\mathcal{L}_{\theta}(\theta') = \mathbb{E}_{\tau \sim \pi_\theta} \left[ \sum_{t=0}^{T} \gamma^t \dfrac{\pi_{\theta'}(a_t \mid s_t)}{\pi_{\theta}(a_t \mid s_t)} A^{\pi_{\theta}}(s_t, a_t)\right]\]

<h3 id="2---mathcalbard_kl">2 - \(\mathcal{\bar{D}}_{KL}\)</h3>

<p>Vấn đề ở bước đi quá nhỏ hay quá lớn là vì ta lấy đạo hàm bậc nhất, nó chỉ cho chúng ta biết nên bước theo hướng nào, nhưng không cho biết bước đi đó phải lớn bao nhiêu \(\Rightarrow\) Nếu đang ở một đường cong, đạo hàm bậc nhất đưa ta đi rất xa \(\Rightarrow\) Để khắc phục vấn đề đó, Natural policy gradients tính luôn đạo hàm bậc hai.</p>

<p>Để làm điều này, chúng ta tính toán sự khác biệt giữa chính sách trước và sau khi cập nhật, mà chính sách là phân phối xác suất, nên sự khác biệt này chính là KL-divergence. Như đã nói, ta không muốn bước cập nhật khiến cho chính sách trở nên quá khác biệt, nên ta giới hạn KL-divergence lại bằng \(\delta\).</p>

\[\theta_{k+1} = \underset{\theta_k + \Delta \theta}{\operatorname{argmax}} \mathcal{L}_{\theta_k}(\theta_k + \Delta \theta)\]

\[\text{s.t. } \mathcal{\bar{D}}_{KL}(\theta_k + \Delta \theta \mid \mid \theta_k) \leq \delta\]

\[\text{với } \mathcal{\bar{D}}_{KL}(\theta \mid \mid \theta_k) = \mathbb{E}_{s \sim \pi_{\theta_k}} \mathcal{D}_{KL}(\pi_{\theta}(. | s) \mid \mid \pi_{\theta_k}(. | s))\]

<p>Để giải quyết phương trình này, ta có thể sử dụng chuỗi Taylor để mở rộng cả hai hạng tử trên đến bậc hai.</p>

<blockquote>
  <p>Dưới đây ta quy ước \(f(x) \mid _{x_0}\) là \(f(x_0)\)</p>
</blockquote>

\[\mathcal{L}_{\theta_k}(\theta) = \mathcal{L}_{\theta_k}(\theta_k) + (\nabla_\theta \mathcal{L}_{\theta_k}(\theta) \mid_{\theta_k})^\top \cdot (\theta - \theta_k)\]

\[= (\nabla_\theta \mathcal{L}_{\theta_k}(\theta) \mid_{\theta_k})^\top \cdot (\theta - \theta_k)\]

\[\mathcal{\bar{D}}_{KL}(\theta \mid \mid \theta_k) = \mathcal{\bar{D}}_{KL}(\theta_k \mid \mid \theta_k) + \nabla_\theta \mathcal{\bar{D}}_{KL}(\theta \mid \mid \theta_k)\mid_{\theta_k} \cdot (\theta - \theta_k) + \dfrac{1}{2}(\theta - \theta_k)^\top \cdot \nabla^2_\theta \mathcal{\bar{D}}_{KL}(\theta \mid \mid \theta_k)\mid_{\theta_k} \cdot (\theta - \theta_k)\]

\[= \dfrac{1}{2}(\theta - \theta_k)^\top \cdot \nabla^2_\theta \mathcal{\bar{D}}_{KL}(\theta \mid \mid \theta_k)\mid_{\theta_k} \cdot (\theta - \theta_k)\]

<p>Bài toán trên trở thành</p>

\[\theta_{k+1} = \underset{\theta}{\operatorname{argmax} \,} (\underbrace{\nabla_\theta \mathcal{L}_{\theta_k}(\theta)  \mid_{\theta_k}}_{\textcolor{blue}{g}})^\top \cdot (\theta - \theta_k)\]

\[\text{s.t. } \quad \dfrac{1}{2}(\theta - \theta_k)^\top \cdot \underbrace{\nabla^2_\theta \mathcal{\bar{D}}_{KL}(\theta \mid \mid \theta_k) \mid_{\theta_k}}_{\textcolor{purple}{H}} \cdot (\theta - \theta_k) \leq \delta\]

<p>Giải phương trình trên, ta thu được</p>

\[\theta_{k+1} = \theta_k + \textcolor{orange}{\sqrt{\dfrac{2 \delta}{g^\top H^{-1} g}} H^{-1}g}\]

<p>Về mặt tính toán, tính \(H\) đã khó, tính \(H^{-1}\) còn khó hơn, \(\mathcal{O}(n^3)\), nên người ta dùng Truncated Natural Policy Gradient để ước tính \(x = H^{-1}g\). Cụ thể hơn, ta giải phương trình \(Hx = g\) với thuật toán Conjugate Gradient.</p>

<div style="border: 2px solid gray; padding: 10px; border-radius: 16px">
  <details>
    <summary><a href="https://www.math.hkust.edu.hk/~mamu/courses/531/cg.pdf">Thuật toán Conjugate Gradient</a> để giải \(Ax=b\) </summary>

    <p>Gọi \(x_0 = 0\), \(r_0 = Ax_0 - b\) và \(p_0 = -r_0\).</p>

    <p>Với \(k\) chạy từ 0,</p>

    <ol>
      <li>\(\alpha_k = \dfrac{r_k^\top r_k}{p_k^\top A p_k}\)</li>
      <li>\(x_{k+1} = x_k + \alpha_k  p_k\)</li>
      <li>\(r_{k+1} = r_k - \alpha_k  A  p_k\)</li>
      <li>\(\beta_k = \dfrac{r_{k+1}^\top r_{k+1}}{r_k^\top r_k}\)</li>
      <li>\(p_{k+1} = -r_{k+1} + \beta_k  p_k\)</li>
    </ol>

    <p>Kết quả: \(x_{k+1}\) là nghiệm gần đúng của \(Ax = b\)</p>
  </details>
</div>

<p>Khi đó,</p>

\[\textcolor{orange}{\sqrt{\dfrac{2 \delta}{g^\top H^{-1} g}} H^{-1}g} = \sqrt{\dfrac{2\delta}{x^T H x}} x\]

<h3 id="tổng-kết-lại">Tổng kết lại</h3>

<p>Natural policy gradients chạy vòng lặp với \(k=0,1,\dots\):</p>

<ol>
  <li>Thu thập tập các quỹ đạo \(\mathcal{D}_k\) theo chính sách \(\pi_{\theta_k}\)</li>
  <li>Ước lượng hàm lợi thế \(A_t^{\pi_{\theta_k}}\)</li>
  <li>Tính các ước lượng mẫu cho \(g_k\)</li>
  <li>Sử dụng Conjugate Gradient để tìm \(x_k \approx H_k^{-1} g_k\)</li>
  <li>Cập nhật tham số chính sách \(\theta_{k+1} = \theta_k + \sqrt{\dfrac{2\delta}{x_k^T H_k x_k}} x_k\)</li>
</ol>

<h2 id="trust-region-policy-optimization-trpo">Trust Region Policy Optimization (TRPO)</h2>

<p>Vấn đề là, do các sai số xấp xỉ được tạo ra bởi khai triển Taylor, điều này có thể không thỏa mãn được ràng buộc KL. TRPO thêm một sửa đổi vào quy tắc cập nhật này: <strong>line search</strong>.</p>

<p>Tức là, giảm dần kích thước của cập nhật theo từng bước cho đến khi tìm được cập nhật đầu tiên không vi phạm ràng buộc. Quy trình này có thể được xem như là thu nhỏ vùng tin cậy, tức là vùng mà trong đó chúng ta tin rằng cập nhật thực sự sẽ cải thiện mục tiêu.</p>

<p>Với thuật toán trên, ở bước tính \(\Delta_k = \sqrt{\dfrac{2\delta}{x_k^T H_k x_k}} x_k\). Để giảm dần vùng tin cậy, ta dùng một \(0 &lt; \alpha &lt; 1\). Với \(j = 0,1,\dots,L\)</p>

<ol>
  <li>Tính \(\theta = \theta_k + \alpha^j \Delta_k\)</li>
  <li>Nếu \(\mathcal{L}_{\theta_k}(\theta) \ge 0\) và \(\mathcal{\bar{D}}_{KL}(\theta \mid \mid \theta_k) \le \delta\) thì dừng vòng lặp, chọn \(\theta_{k+1} = \theta_k + \alpha^j \Delta_k\).</li>
</ol>

<h2 id="nhận-xét">Nhận xét</h2>

<ul>
  <li>
    <p>TRPO rất nặng về toán và lý thuyết. Bài viết này cũng đã lượt bỏ khá nhiều phần chứng minh đằng sau các công thức.</p>
  </li>
  <li>
    <p>Tuy nhiên, chính điều đó đóng góp nền tảng lớn cho các phương pháp sau, tiêu biểu là Proximal Policy Optimization - PPO.</p>
  </li>
  <li>
    <p>Việc triển khai TRPO yêu cầu tính toán phức tạp và tốn kém về thời gian và tài nguyên. Các thuật toán yêu cầu việc tính toán Hessian và giải hệ phương trình tuyến tính với độ phức tạp tính toán cao, điều này khiến TRPO ít được áp dụng trong các bài toán quy mô lớn hoặc trong môi trường không có tài nguyên tính toán mạnh.</p>
  </li>
</ul>]]></content><author><name>Nguyễn Thái Tân</name><email>nguyenthaitan02@gmail.com</email></author><category term="reinforcement learning" /><summary type="html"><![CDATA[Trust Region Policy Optimization (TRPO) tối ưu chính sách một cách ổn định bằng cách giới hạn bước cập nhật trong một vùng tin cậy. Điều này giúp tránh việc thay đổi chính sách quá đột ngột, từ đó cải thiện hiệu suất và độ ổn định.]]></summary></entry><entry><title type="html">Reinforcement learning 4 - Actor Critic</title><link href="https://thaitanhaha.github.io/posts/reinforcement-learning-4/" rel="alternate" type="text/html" title="Reinforcement learning 4 - Actor Critic" /><published>2025-07-10T00:00:00+00:00</published><updated>2025-07-10T00:00:00+00:00</updated><id>https://thaitanhaha.github.io/posts/reinforcement-learning-4</id><content type="html" xml:base="https://thaitanhaha.github.io/posts/reinforcement-learning-4/"><![CDATA[<p>Ta đã biết hai cách học tăng cường:</p>

<ul>
  <li>Dựa trên chính sách (Policy-Based): như REINFORCE</li>
  <li>Dựa trên giá trị (Value-Based): như Q-learning, Deep Q-learning</li>
</ul>

<p>Value-based có vài vấn đề, Policy-based giải quyết được vấn đề đó, nhưng lại gặp vấn đề khác về phương sai, vậy ta giải quyết như thế nào? Kết hợp chúng lại!</p>

<p align="center">
  <img src="https://pylessons.com/media/Tutorials/Reinforcement-learning-tutorial/A2C-reinforcement-learning/A2C-reinforcement-learning.jpg" alt="Actor-Critic" />
</p>

<h2 id="hàm-cơ-sở---baseline-function">Hàm cơ sở - Baseline function</h2>

<p>Trước hết, ta tìm hiểu một cách cơ bản để giảm phương sai: chèn một hàm cơ sở \(b\) vào trong kỳ vọng</p>

\[\nabla_\theta J(\theta) = \nabla_\theta \mathbb{E}_{\tau \sim \pi_\theta}[R(\tau)] = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) R(\tau) \right]\]

<p>Với \(R(\tau) = \sum_{t=0}^{T} \gamma^t r_t \) và chèn hàm cơ sở \(b\) vào, ta thu được</p>

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) \left( R(\tau) - b\right) \right]\]

\[= \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) \left( \sum_{t'=0}^{T} \gamma^{t'} r_{t'} - b\right) \right]\]

<h3 id="hàm-cơ-sở-không-gây-sai-lệch-unbiased">Hàm cơ sở không gây sai lệch (unbiased)</h3>

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) \sum_{t'=0}^{T} \gamma^{t'} r_{t'} - \textcolor{blue}{\mathbb{E}_{\tau \sim \pi_\theta} \sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) b}\]

<p>Ta có</p>

\[\textcolor{blue}{\mathbb{E}_{\tau \sim \pi_\theta} \sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) b} = \sum_{a_t}^{} \pi_\theta(a_t \mid s_t) \sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) b\]

\[= \sum_{a_t}^{} \sum_{t=0}^{T} \textcolor{purple}{\pi_\theta(a_t \mid s_t) \nabla_\theta \log \pi_\theta(a_t \mid s_t)} b = \sum_{a_t}^{} \sum_{t=0}^{T} \textcolor{purple}{\nabla_\theta \pi_\theta(a_t \mid s_t)} b\]

\[= b \nabla_\theta \sum_{t=0}^{T} \sum_{a_t}^{} \pi_\theta(a_t \mid s_t) = b \nabla_\theta \sum_{t=0}^{T} 1 = 0\]

<p>Biểu thức này chứng minh rằng việc thêm một hàm cơ sở không gây sai lệch cho ước lượng đạo hàm, tức là không thay đổi giá trị kỳ vọng của biểu thức.</p>

<h3 id="hàm-cơ-sở-làm-giảm-phương-sai">Hàm cơ sở làm giảm phương sai</h3>

<p>Nhắc lại</p>

\[\text{Var} \left[ X \right] = \mathbb{E} \left[ X^2 \right] - \left( \mathbb{E} \left[ X \right]\right)^2\]

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\underbrace{\sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t)}_{\textcolor{red}{p(\tau)}} \left( R(\tau) - b\right) \right]\]

<p>Ta suy ra</p>

\[\text{Var} \left[ \nabla_\theta J(\theta) \right] = \mathbb{E}_{\tau} \left[\left(\textcolor{red}{p(\tau)} \left( R(\tau) - b\right)\right)^2 \right] - \left( \mathbb{E}_{\tau} \left[ \textcolor{red}{p(\tau)} \left( R(\tau) - b\right)  \right]\right)^2\]

\[= \mathbb{E}_{\tau} \left[\left(\textcolor{red}{p(\tau)} \left( R(\tau) - b\right)\right)^2 \right] - \left( \mathbb{E}_{\tau} \left[ \textcolor{red}{p(\tau)} R(\tau)  \right]\right)^2 \quad \text{(} b \text{ không gây sai lệch)}\]

<p>Ta muốn tìm giá trị nhỏ nhất của phương sai này, nên ta tính</p>

\[\dfrac{d}{db} \text{Var} \left[ \nabla_\theta J(\theta) \right] = \dfrac{d}{db} \mathbb{E}_{\tau} \left[\left(\textcolor{red}{p(\tau)} \left( R(\tau) - b\right)\right)^2 \right]\]

\[= \dfrac{d}{db} \left[ \mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2 R(\tau)^2 \right] -2 \mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2 R(\tau) b\right] +  \mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2 b^2 \right] \right]\]

\[= -2 \mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2 R(\tau) \right] +  2 b \mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2  \right]\]

<p>Giải phương trình</p>

\[\dfrac{d}{db} \text{Var} \left[ \nabla_\theta J(\theta) \right] = 0 \Leftrightarrow \mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2 R(\tau) \right] = b \mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2  \right]\]

\[\Leftrightarrow b = \dfrac{\mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2 R(\tau) \right]}{\mathbb{E}_{\tau} \left[\textcolor{red}{p(\tau)}^2  \right]}\]

<p>Vậy hàm cơ sở làm tối thiểu hóa phương sai chính là kỳ vọng của lợi nhuận trên các quỹ đạo được trọng số bởi độ lớn gradient. Tuy nhiên, chi phí tính toán độ lớn gradient lớn nên việc giảm phương sai không hiệu quả.</p>

<p>Do đó, người ta thường dùng một hàm cơ sở khác, là giá trị trạng thái.</p>

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t \mid s_t) \left( R(\tau) - b(s_t)\right) \right]\]

<h2 id="actor-critic">Actor-Critic</h2>

<p>Một cách khác để giảm phương sai là dùng Actor-Critic.</p>

<p>Ý tưởng chính là kết hợp hai mạng: Actor - học chính sách trực tiếp để quyết định hành động và Critic - đánh giá chất lượng hành động của Actor. Qua phản hồi từ Critic, Actor cải thiện chính sách. Cả hai mạng đều học song song, cải thiện lẫn nhau qua thời gian.</p>

<blockquote>
  <p>Rất giống với Generative Adversarial Network (GAN): Generator ra tạo ra các hình ảnh giả, còn discriminator đánh giá mức độ tốt của hình ảnh giả so với hình ảnh thực. Theo thời gian, generator có thể tạo ra những hình ảnh giả mà discriminator không thể phân biệt được.</p>
</blockquote>

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \nabla_\theta \log \underbrace{\pi_\theta(a_t \mid s_t)}_{\text{Actor}} \cdot \underbrace{\hat{q}_{\omega} (s_t, a_t)}_{\text{Critic}} \right]\]

<p>Điều này giúp giảm phương sai và đồng thời tạo ra <em>một chút</em> độ lệch trong ước tính đạo hàm.</p>

<p>Các bước thực hiện của Actor-Critic là</p>

<ol>
  <li>
    <p>Tại mỗi \(t\), đưa trạng thái hiện tại \(s_t\) làm đầu vào cho cả Actor và Critic.</p>
  </li>
  <li>
    <p>Actor nhận trạng thái \(s_t\) và xuất ra một hành động \(a_t\)</p>
  </li>
  <li>
    <p>Critic lấy  trạng thái \(s_t\) và hành động \(a_t\) đấy làm đầu vào, tính giá trị của việc thực hiện hành động đó tại trạng thái đó \(\hat{q}_{\omega} (s_t, a_t)\).</p>

    <p>\(\Rightarrow\) Trạng thái mới \(s_{t+1}\) và một phần thưởng \(r_{t+1}\)</p>
  </li>
  <li>
    <p>Cập nhật Actor theo công thức</p>

\[\theta \leftarrow \theta + \alpha \cdot \nabla_\theta \log \pi_\theta (a_t \mid s_t) \cdot \hat{q}_{\omega} (s_t, a_t)\]
  </li>
  <li>
    <p>Dựa trên tham số đã được cập nhật, Actor tạo ra hành động tiếp theo cần thực hiện \(a_{t+1}\) dựa trên trạng thái mới \(s_{t+1}\).</p>
  </li>
  <li>
    <p>Cập nhật Critic dựa trên MSE và semi-gradient</p>

\[\omega \leftarrow \textcolor{lightgray}{\omega - \dfrac{\beta}{2} \cdot \nabla_\omega \left(r_{t+1} + \gamma \hat{q}_{\omega} (s_{t+1}, a_{t+1}) - \hat{q}_{\omega} (s_t, a_t) \right)^2}\]

\[= \omega + \beta \cdot \left(r_{t+1} + \gamma \hat{q}_{\omega} (s_{t+1}, a_{t+1}) - \hat{q}_{\omega} (s_t, a_t) \right) \cdot \nabla_\omega \hat{q}_{\omega} (s_t, a_t)\]
  </li>
</ol>

<h2 id="advantage-actor-critic-a2c">Advantage Actor-Critic (A2C)</h2>

<p>Ta có thể “nâng cấp” Actor-Critic để làm ổn định quá trình học, bằng cách dùng hàm lợi thế (Advantage function) làm Critic thay vì hàm giá trị hành động đơn giản. Hàm lợi thế này sẽ có dạng như baseline \(A(s_t,a_t) = R(\tau) - b(s_t)\)</p>

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \nabla_\theta \log \underbrace{\pi_\theta(a_t \mid s_t)}_{\text{Actor}} \underbrace{A(s_t, a_t)}_{\text{Critic - Advantage}} \right]\]

<p>Câu hỏi tiếp theo là, làm thế nào để ước tính lợi thế khi thực hiện một hành động cụ thể trong một trạng thái cụ thể.</p>

<ul>
  <li>
    <p>\(A(s_t,a_t) = Q(s_t,a_t) - V(s_t)\): Hàm lợi thế sẽ tính toán lợi thế tương đối của một hành động so với các hành động khác có thể thực hiện tại một trạng thái</p>
  </li>
  <li>
    <p>Tuy nhiên, cách trên yêu cầu tính hai hàm giá trị \(Q(s_t, a_t)\) và \(V(s_t)\). Do đó, ta dùng một cách khác, chỉ dùng một mạng \(\hat{V}_{\omega}(s_t)\) là \(A(s_t, a_t) = r_{t+1} + \gamma \hat{V}_{\omega}(s_{t+1}) - \hat{V}_{\omega}(s_t)\)</p>
  </li>
</ul>

<p>Các bước thực hiện cũng tương tự như Actor-Critic, chỉ có sự khác biệt về cách cập nhật tham số</p>

\[\theta \leftarrow \theta + \alpha \cdot \nabla_\theta \log \pi_\theta (a_t \mid s_t) \cdot A(s_t, a_t)\]

<p align="center">
  và
</p>

\[\omega \leftarrow \textcolor{lightgray}{\omega - \dfrac{\beta}{2} \cdot \nabla_\omega \left(r_{t+1} + \gamma \hat{V}_{\omega} (s_{t+1}) - \hat{V}_{\omega} (s_t) \right)^2}\]

\[= \omega + \beta \cdot \left(r_{t+1} + \gamma \hat{V}_{\omega} (s_{t+1}) - \hat{V}_{\omega} (s_t) \right) \cdot \nabla_\omega \hat{V}_{\omega} (s_t)\]

<h2 id="nhận-xét">Nhận xét</h2>

<ul>
  <li>
    <p>Actor-Critic kết hợp policy-based và value-based để giải quyết các vấn đề của riêng chúng. Điều này cũng có nghĩa là nó cải thiện được hiệu quả mẫu (sample efficiency).</p>
  </li>
  <li>
    <p>Advantage Actor-Critic (A2C) là bản nâng cấp của Actor-Critic, giúp nó ổn định hơn. Do giảm phương sai và hiệu quả mẫu tốt hơn, A2C thường hội tụ đến chính sách tối ưu nhanh hơn REINFORCE.</p>
  </li>
  <li>
    <p>Hiển nhiên, Actor-Critic và A2C có độ phức tạp cao hơn so với các thuật toán đơn giản như REINFORCE.</p>
  </li>
</ul>]]></content><author><name>Nguyễn Thái Tân</name><email>nguyenthaitan02@gmail.com</email></author><category term="reinforcement learning" /><summary type="html"><![CDATA[Actor-Critic là một phương pháp học tăng cường kết hợp giữa hai thành phần Actor và Critic. Mô hình này tận dụng ưu điểm của cả Policy-based và Value-based để học nhanh và ổn định hơn trong các môi trường phức tạp.]]></summary></entry><entry><title type="html">Reinforcement learning 3 - Policy Gradient</title><link href="https://thaitanhaha.github.io/posts/reinforcement-learning-3/" rel="alternate" type="text/html" title="Reinforcement learning 3 - Policy Gradient" /><published>2025-07-05T00:00:00+00:00</published><updated>2025-07-05T00:00:00+00:00</updated><id>https://thaitanhaha.github.io/posts/reinforcement-learning-3</id><content type="html" xml:base="https://thaitanhaha.github.io/posts/reinforcement-learning-3/"><![CDATA[<p>Nhắc lại về Q-learning và Deep Q-learning, chúng vẫn tuân theo chính sách nhất định</p>

\[\pi(s) = 
  \begin{cases} 
  \text{argmax}_a Q(s, a) &amp; \text{với xác suất } 1 - \epsilon \\
  \text{random action} &amp; \text{với xác suất } \epsilon 
  \end{cases}\]

<p>Có thể thấy, việc tối ưu hóa hàm giá trị Q không trực tiếp tối ưu chính sách hành động (gọi là <strong>off-policy</strong>). Hơn nữa, đôi khi phần thưởng không xuất hiện liên tục, nên việc điều chỉnh giá trị Q trở nên rất khó khăn.</p>

<p>Và quan trọng nhất, chúng chỉ hoạt động tốt trong không gian hành động rời rạc.</p>

<p align="center">
  <img src="https://pylessons.com/media/Tutorials/Reinforcement-learning-tutorial/Beyond-DQN/PG_vs_DQN.png" alt="Policy Gradient vs Deep Q-learning" />
</p>

<h2 id="policy-gradient">Policy Gradient</h2>

<p>Đầu tiên, ta tham số hóa một chính sách ngẫu nhiên (stochastic policy). Cũng có nghĩa là, một mạng nơ-ron đưa ra một phân phối xác suất các hành động. Vậy thì mục tiêu là: tìm các tham số \(\theta\) để điều chỉnh phân phối các hành động này sao cho hành động đem lại nhiều phần thưởng nhất thì có xác suất chọn cao nhất \(\rightarrow\) tối đa hóa phần thưởng mong đợi.</p>

<blockquote>
  <p>Ta tối ưu hóa chính sách trực tiếp nên gọi là <strong>on-policy</strong>.</p>
</blockquote>

<p>Ý tưởng là: Trong một tập (episode), nếu thắng thì <strong>mỗi</strong> hành động đã thực hiện là tốt, tăng xác suất xảy ra của chúng; nếu thua thì <strong>mỗi</strong> hành động đã thực hiện là không tốt, giảm xác suất xảy ra của chúng.</p>

<p>Do đó, hàm phần thưởng hay hàm mục tiêu được viết như là: phần thưởng tích lũy kỳ vọng trên một quỹ đạo (trajectory - dãy trạng thái và hành động).</p>

\[J(\theta) = \mathbb{E}_{\tau \sim \pi} \left[ R(\tau)\right] = \sum_{\tau} \textcolor{blue}{P(\tau;\theta)} \textcolor{purple}{R(\tau)}\]

<ul>
  <li>
    <p>\(P(\tau;\theta)\): Xác suất mỗi quỹ đạo có thể xảy ra (xác suất này phụ thuộc vào \(\theta\))</p>

    <blockquote>
      <p>Có thể hiểu rằng, nếu \(\theta\) thay đổi \(\rightarrow\) chính sách thay đổi \(\rightarrow\) cách tác nhân chọn hành động trong từng bước sẽ khác \(\rightarrow\) các trạng thái được ghé thăm sẽ khác \(\rightarrow\) các hành động được chọn tiếp theo cũng sẽ khác \(\rightarrow\) xác suất của quỹ đạo \(\tau\) cũng sẽ thay đổi.</p>
    </blockquote>

\[\textcolor{blue}{P(\tau;\theta)} = \mu(s_0) \prod_{t=0}^{T} P(s_{t+1} \mid s_t, a_t) \pi_\theta (a_t \mid s_t)\]

    <p>trong đó \(\mu(s_0)\) là phân phối trạng thái ban đầu, \(\pi_\theta(a_t \mid s_t)\) là xác suất chính sách chọn hành động \(a_t\) từ trạng thái \(s_t\).</p>
  </li>
  <li>
    <p>\(R(\tau)\): Phần thưởng tích lũy từ một quỹ đạo bất kỳ.</p>

\[\textcolor{purple}{R(\tau)} = \sum_{t=0}^{T} \gamma^t r_t\]
  </li>
</ul>

<p>Để tối đa hóa \(J(\theta)\) thì rất tự nhiên, ta dùng <strong>gradient ascent</strong>,</p>

\[\theta \leftarrow \theta + \alpha \cdot \nabla_\theta J(\theta)\]

<p>Tuy nhiên, có hai vấn đề khi tính đạo hàm của \(J(\theta)\):</p>

<ol>
  <li>
    <p>Để đạo hàm, ta cũng cần biết đạo hàm của hàm phân phối trạng thái \(P(s_{t+1} \mid s_t, a_t)\), gọi là MDP dynamics. Điều này gắn liền với môi trường. Ta không lấy đạo hàm được vì ta có thể không biết về nó.</p>

    <blockquote>
      <p>Ở Policy Gradient, ta tối ưu hóa chính sách trực tiếp, nên không thể <em>bỏ qua</em> hàm phân phối trạng thái như Q-learning được.</p>
    </blockquote>

    <p>\(\Rightarrow\) Người ta chứng minh được rằng đạo hàm của hàm mục tiêu <strong>không liên quan</strong> đến đạo hàm của hàm phân phối trạng thái, gọi là <strong>Policy Gradient Theorem</strong>.</p>

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^T \left[ \textcolor{darkorange}{\nabla_\theta \log  \pi_\theta (a_t \mid s_t) R(\tau)}\right]\right]\]
  </li>
  <li>
    <p>Không thể tính toán chính xác đạo hàm của hàm mục tiêu vì nó yêu cầu tính xác suất của mỗi quỹ đạo có thể xảy ra \(\rightarrow\) rất tốn kém về mặt tính toán.</p>

    <p>\(\Rightarrow\) Ta ước lượng đạo hàm bằng cách sử dụng <em>một tập</em> quỹ đạo \(\mathcal{D} = \{\tau\}_{i=1,2,\dots,N}\), gọi là <strong>phương pháp Monte-Carlo</strong>.</p>

\[\nabla_\theta J(\theta) \approx \dfrac{1}{N} \sum_{i=1}^{N} \sum_{t=0}^{T} \left[ \textcolor{darkorange}{\nabla_\theta \log  \pi_\theta \left(a_t^{(i)} \mid s_t^{(i)}\right) R\left(\tau^{(i)}\right)} \right]\]
  </li>
</ol>

<p>Đến đây, ta nhận thấy có một vấn đề khác: Môi trường có tính ngẫu nhiên (các hàm phân phối trạng thái \(P(s_{t+1} \mid s_t, a_t)\)) và chính sách cũng mang tính ngẫu nhiên \(\Rightarrow\) cùng một trạng thái bắt đầu có thể dẫn đến các giá trị phần thưởng rất khác nhau \(\Rightarrow\) phần thưởng bắt đầu từ cùng một trạng thái có thể thay đổi đáng kể giữa các tập (episode) \(\Rightarrow\) phương sai giữa các tập cao.</p>

<p>Giải pháp để giảm phương sai là sử dụng một số lượng lớn các quỹ đạo, tuy nhiên việc tăng kích thước (batch size) một cách đáng kể sẽ làm giảm hiệu quả sử dụng mẫu (sample efficiency).</p>

<blockquote>
  <p>Hiệu quả sử dụng mẫu có thể hiểu là mức độ hiệu quả mà một mô hình học được từ một số lượng mẫu (dữ liệu) nhất định.</p>
</blockquote>

<h2 id="thuật-toán-reinforce">Thuật toán REINFORCE</h2>

<p>Kết hợp tất cả những thứ trên lại một cách đơn giản nhất, ta được thuật toán REINFORCE - là một dạng cụ thể của Policy Gradient.</p>

<ol>
  <li>
    <p>Khởi tạo bộ tham số \(\theta\) bất kỳ</p>
  </li>
  <li>
    <p>Sử dụng chính sách \(\pi_\theta\) để thu thập một tập (episode) \(\tau\).</p>
  </li>
  <li>
    <p>Sử dụng tập này để ước tính gradient \(\nabla_\theta J(\theta)\).</p>
  </li>
  <li>
    <p>Cập nhật trọng số của chính sách: \(\theta \leftarrow \theta + \alpha \cdot \nabla_\theta J(\theta)\)</p>
  </li>
</ol>

<h2 id="policy-gradient-theorem">Policy Gradient Theorem</h2>

<p>Giờ ta quay lại chứng minh Policy Gradient Theorem nào!</p>

\[\nabla_\theta J(\theta) = \nabla_\theta \sum_{\tau}P(\tau;\theta)R(\tau) = \sum_{\tau} \nabla_\theta \left(P(\tau;\theta)R(\tau)\right) = \sum_{\tau} \nabla_\theta P(\tau;\theta) R(\tau)\]

\[= \sum_{\tau} P(\tau; \theta) \frac{\nabla_\theta P(\tau; \theta)}{P(\tau; \theta)} R(\tau) = \sum_{\tau} P(\tau; \theta) \nabla_\theta \log P(\tau; \theta) R(\tau)\]

\[= \mathbb{E}_{\tau \sim \pi_\theta} \nabla_\theta \log \textcolor{blue}{P(\tau; \theta)} R(\tau)\]

<p>Ta tiếp tục rút gọn \(\nabla_\theta \log \textcolor{blue}{P(\tau; \theta)}\).</p>

<p>Với \(\mu(s_0)\) là phân phối trạng thái ban đầu và \(P\left(s_{t+1}^{(i)} \mid s_{t}^{(i)}, a_{t}^{(i)}\right)\) là MDP dynamics của một quỹ đạo \(\tau^{(i)}\) nào đó,</p>

\[\nabla_\theta \log \textcolor{blue}{P\left(\tau^{(i)}; \theta \right)} = \nabla_\theta \log \left[ \mu(s_0) \prod_{t=0}^{T} P\left(s_{t+1}^{(i)} \mid s_{t}^{(i)}, a_{t}^{(i)}\right) \pi_\theta \left(a_{t}^{(i)} \mid s_{t}^{(i)}\right) \right]\]

\[= \nabla_\theta \left[ \log \mu(s_0) + \sum_{t=0}^{T} \log P\left(s_{t+1}^{(i)} \mid s_{t}^{(i)}, a_{t}^{(i)}\right) + \sum_{t=0}^{T} \log \pi_\theta\left(a_{t}^{(i)} \mid s_{t}^{(i)}\right) \right]\]

\[= \nabla_\theta \log \mu(s_0) + \nabla_\theta \sum_{t=0}^{T} \log P\left(s_{t+1}^{(i)} \mid s_{t}^{(i)}, a_{t}^{(i)}\right) + \nabla_\theta \sum_{t=0}^{T} \log \pi_\theta\left(a_{t}^{(i)} \mid s_{t}^{(i)}\right)\]

\[= 0 + 0 + \nabla_\theta \sum_{t=0}^{T} \log \pi_\theta\left(a_{t}^{(i)} \mid s_{t}^{(i)}\right)
= \sum_{t=0}^{T} \nabla_\theta \log \pi_\theta\left(a_{t}^{(i)} \mid s_{t}^{(i)}\right)\]

<p>Vậy</p>

\[\nabla_\theta J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^T \left[ \textcolor{darkorange}{\nabla_\theta \log  \pi_\theta (a_t \mid s_t) R(\tau)}\right]\right]\]

<h2 id="nhận-xét">Nhận xét</h2>

<ul>
  <li>Policy gradient đã giải quyết được vấn đề về không gian hành động liên tục mà Q-learning hay Deep Q-learning không làm được.</li>
  <li>Policy gradient có thể học một chính sách ngẫu nhiên. Ta không cần phải thực hiện đánh đổi khám phá/khai thác. Vì ta có một phân phối xác suất trên các hành động, nên tác nhân khám phá không gian trạng thái mà không phải lúc nào cũng đi theo cùng một quỹ đạo.</li>
  <li>Tuy nhiên, Policy gradient rất hay hội tụ đến tối ưu cục bộ thay vì tối ưu toàn cục.</li>
  <li>Có thể mất nhiều thời gian hơn để huấn luyện Policy gradient.</li>
  <li>Có thể có phương sai cao.</li>
</ul>]]></content><author><name>Nguyễn Thái Tân</name><email>nguyenthaitan02@gmail.com</email></author><category term="reinforcement learning" /><summary type="html"><![CDATA[Policy Gradient cho phép mô hình học trực tiếp chính sách hành động thông qua tối ưu hóa gradient. Kỹ thuật này đặc biệt hiệu quả trong các bài toán có không gian hành động liên tục hoặc phức tạp.]]></summary></entry><entry><title type="html">Reinforcement learning 2 - Deep Q-Learning</title><link href="https://thaitanhaha.github.io/posts/reinforcement-learning-2/" rel="alternate" type="text/html" title="Reinforcement learning 2 - Deep Q-Learning" /><published>2025-07-01T00:00:00+00:00</published><updated>2025-07-01T00:00:00+00:00</updated><id>https://thaitanhaha.github.io/posts/reinforcement-learning-2</id><content type="html" xml:base="https://thaitanhaha.github.io/posts/reinforcement-learning-2/"><![CDATA[<p>Trong Q-Learning, bảng Q phải được lưu trữ và cập nhật cho mọi trạng thái và hành động, điều này trở nên rất khó khăn khi không gian trạng thái rất lớn.</p>

<blockquote>
  <p>Các giá trị trạng thái trong trò chơi CartPoleBalance là liên tục, thực ra là không phù hợp với Q-learning.</p>
</blockquote>

<p>Deep Learning + Q-learning = Deep Q-learning ra đời để giải quyết vấn đề này!</p>

<h2 id="deep-q-learning">Deep Q-learning</h2>

<p>Deep Q-Learning sử dụng một mạng nơ-ron (Neural network) để ước tính các giá trị Q, giúp mô hình có thể xử lý được các không gian trạng thái phức tạp và liên tục mà không cần phải lưu trữ tất cả các giá trị Q. Những gì cần làm chỉ là bỏ đi bảng Q(s,a) và thay thế bằng một mạng thần kinh đơn giản.</p>

<p><img src="https://www.baeldung.com/wp-content/uploads/sites/4/2023/04/dql-vs-ql-1.png" alt="Deep Q-learning" /></p>

<p>Trong mạng nơ-ron thì <strong>hàm mất mát</strong> là vô cùng quan trọng! Tất nhiên, hàm mất mát phải tính được sai số giữa giá trị Q dự đoán và giá trị Q mục tiêu (Q-target). Vậy thì đơn giản thôi, kết hợp TD và MSE lại. Với \(Q(s,a;\theta)\) là mạng cần học, hàm mất mát chính là</p>

\[L(\theta) = \left( r + \gamma \cdot \max_{a'} Q(s', a'; \theta) - Q(s,a;\theta) \right)^2\]

<p>Trong đó, \(\max_{a’} Q(s’, a’; \theta)\) là dự đoán về giá trị tối ưu cho hành động tiếp theo sau khi chuyển sang trạng thái mới (chính là Q-target).</p>

<p>Mặc dù \(Q(s’, a’; \theta)\) phụ thuộc vào \(\theta\), người ta coi đây là một giá trị hằng số khi tính hàm mất mát, tức là <strong>không có đạo hàm</strong> ở đại lượng này.</p>

<blockquote>
  <p>Đây gọi là semi-gradient. Có thể hiểu rằng, tính luôn đạo hàm cho \(Q(s’, a’; \theta)\) sẽ làm thay đổi chính mục tiêu mà ta đang cố gắng học theo. Điều này dẫn đến học không ổn định, vì mục tiêu luôn “di chuyển”.</p>
</blockquote>

<p>Khi đó,</p>

\[\nabla_\theta L(\theta) = -2 \left( r + \gamma \cdot \max_{a'} Q(s', a'; \theta) - Q(s,a;\theta) \right) \nabla_\theta Q(s,a;\theta)\]

<h2 id="experience-replay">Experience Replay</h2>

<p>Trong Deep Q-Learning, Experience Replay là một kỹ thuật thường được dùng giúp cải thiện sự ổn định của quá trình huấn luyện.</p>

<h3 id="tác-dụng">Tác dụng</h3>

<ul>
  <li>
    <p>Tránh quên đi những trải nghiệm trước đây (hiện tượng catastrophic forgetting).</p>

    <blockquote>
      <p>Catastrophic forgetting: Vấn đề gặp phải nếu ta cung cấp các mẫu trải nghiệm tuần tự cho mạng nơ-ron thì mạng có xu hướng quên các kinh nghiệm trước đó khi có được các kinh nghiệm mới.</p>
    </blockquote>
  </li>
  <li>
    <p>Giảm mối tương quan giữa các trải nghiệm</p>
  </li>
</ul>

<h3 id="cách-hiện-thực">Cách hiện thực</h3>

<p>Ta dùng một bộ đệm (replay buffer) chứa các tuple (trạng thái, hành động, phần thưởng, trạng thái tiếp theo) để lưu các mẫu trải nghiệm mà ta có thể sử dụng lại trong quá trình huấn luyện. Bộ đệm này có giới hạn về kích thước. Khi bộ nhớ đầy, các trải nghiệm cũ nhất sẽ bị loại bỏ.</p>

<p>Trong <strong>mỗi bước</strong> huấn luyện, một nhóm trải nghiệm <strong>ngẫu nhiên</strong> (mini-batch) sẽ được lấy từ bộ nhớ để huấn luyện mô hình. Điều này giúp mô hình không học theo thứ tự các trải nghiệm, mà là từ một tập hợp các trải nghiệm đa dạng và không có sự phụ thuộc thời gian. Nếu tác nhân chỉ học từ các trải nghiệm liên tiếp, chúng có thể rất giống nhau và không cung cấp thông tin phong phú. Điều này cũng cho phép tác nhân học hỏi từ cùng một trải nghiệm nhiều lần.</p>

<blockquote>
  <p>Trong Q-learning, quá trình huấn luyện diễn ra theo các bước <strong>liên tiếp</strong>, nghĩa là mô hình học từ các trạng thái liên tiếp mà không có cơ chế lưu trữ hoặc trộn lẫn các trải nghiệm như Deep Q-learning.</p>
</blockquote>

<h2 id="code">Code</h2>
<p>Code để dễ hình dung nhé!</p>

<details>
  <summary>Môi trường, Tác nhân, Mạng nơ-ron</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CartPoleEnv</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c1"># Trạng thái: [x, x_velocity, theta, theta_velocity]
</span>        <span class="c1"># Hành động: 0 di chuyển sang trái, 1 di chuyển sang phải
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="n">low</span><span class="o">=-</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">4</span><span class="p">,))</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">g</span> <span class="o">=</span> <span class="mf">9.8</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">m</span> <span class="o">=</span> <span class="mf">0.1</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">M</span> <span class="o">=</span> <span class="mf">1.0</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">L</span> <span class="o">=</span> <span class="mf">0.5</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dt</span> <span class="o">=</span> <span class="mf">0.02</span>
        
    <span class="k">def</span> <span class="nf">step</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action</span><span class="p">):</span>
        <span class="c1"># Hàm dùng để mô phỏng
</span>        <span class="n">x</span><span class="p">,</span> <span class="n">x_velocity</span><span class="p">,</span> <span class="n">theta</span><span class="p">,</span> <span class="n">theta_velocity</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span>
        
        <span class="n">force</span> <span class="o">=</span> <span class="mf">20.0</span> <span class="k">if</span> <span class="n">action</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="o">-</span><span class="mf">20.0</span>
        
        <span class="n">cos_theta</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">cos</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
        <span class="n">sin_theta</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
        <span class="n">total_mass</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">M</span> <span class="o">+</span> <span class="bp">self</span><span class="p">.</span><span class="n">m</span>
        <span class="n">pole_mass_length</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">m</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">L</span>
        
        <span class="n">temp</span> <span class="o">=</span> <span class="p">(</span><span class="n">force</span> <span class="o">+</span> <span class="n">pole_mass_length</span> <span class="o">*</span> <span class="n">theta_velocity</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">sin_theta</span><span class="p">)</span> <span class="o">/</span> <span class="n">total_mass</span>
        <span class="n">theta_acc</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">g</span> <span class="o">*</span> <span class="n">sin_theta</span> <span class="o">-</span> <span class="n">cos_theta</span> <span class="o">*</span> <span class="n">temp</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">L</span> <span class="o">*</span> <span class="p">(</span><span class="mi">4</span> <span class="o">/</span> <span class="mi">3</span> <span class="o">-</span> <span class="bp">self</span><span class="p">.</span><span class="n">m</span> <span class="o">*</span> <span class="n">cos_theta</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">/</span> <span class="n">total_mass</span><span class="p">))</span>
        <span class="n">x_acc</span> <span class="o">=</span> <span class="n">temp</span> <span class="o">-</span> <span class="n">pole_mass_length</span> <span class="o">*</span> <span class="n">theta_acc</span> <span class="o">*</span> <span class="n">cos_theta</span> <span class="o">/</span> <span class="n">total_mass</span>
        
        <span class="n">x</span> <span class="o">+=</span> <span class="n">x_velocity</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        <span class="n">x_velocity</span> <span class="o">+=</span> <span class="n">x_acc</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        <span class="n">theta</span> <span class="o">+=</span> <span class="n">theta_velocity</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        <span class="n">theta_velocity</span> <span class="o">+=</span> <span class="n">theta_acc</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        
        <span class="n">lose</span> <span class="o">=</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="o">-</span><span class="mf">2.4</span> <span class="ow">or</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mf">2.4</span> <span class="ow">or</span> <span class="n">theta</span> <span class="o">&lt;</span> <span class="o">-</span><span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">15</span> <span class="ow">or</span> <span class="n">theta</span> <span class="o">&gt;</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">15</span>
        <span class="n">reward</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">lose</span> <span class="k">else</span> <span class="mf">0.0</span>
        
        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="n">x</span><span class="p">,</span> <span class="n">x_velocity</span><span class="p">,</span> <span class="n">theta</span><span class="p">,</span> <span class="n">theta_velocity</span><span class="p">])</span>
        
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">lose</span>

    <span class="k">def</span> <span class="nf">reset</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="n">low</span><span class="o">=-</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">4</span><span class="p">,))</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span>

<span class="k">class</span> <span class="nc">DQN</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state_dim</span><span class="p">,</span> <span class="n">action_dim</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">DQN</span><span class="p">,</span> <span class="bp">self</span><span class="p">).</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">fc1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">state_dim</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">fc2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">24</span><span class="p">,</span> <span class="mi">24</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">fc3</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">24</span><span class="p">,</span> <span class="n">action_dim</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">fc1</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">fc2</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">fc3</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">DQNAgent</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state_dim</span><span class="p">,</span> <span class="n">action_dim</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state_dim</span> <span class="o">=</span> <span class="n">state_dim</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">action_dim</span> <span class="o">=</span> <span class="n">action_dim</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">memory</span> <span class="o">=</span> <span class="n">deque</span><span class="p">(</span><span class="n">maxlen</span><span class="o">=</span><span class="mi">2000</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">gamma</span> <span class="o">=</span> <span class="mf">0.95</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">epsilon</span> <span class="o">=</span> <span class="mf">1.0</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">epsilon_min</span> <span class="o">=</span> <span class="mf">0.01</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">epsilon_decay</span> <span class="o">=</span> <span class="mf">0.995</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">batch_size</span> <span class="o">=</span> <span class="mi">32</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">DQN</span><span class="p">(</span><span class="n">state_dim</span><span class="p">,</span> <span class="n">action_dim</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">optimizer</span> <span class="o">=</span> <span class="n">optim</span><span class="p">.</span><span class="n">Adam</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">.</span><span class="n">parameters</span><span class="p">(),</span> <span class="n">lr</span><span class="o">=</span><span class="mf">0.001</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">criterion</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">MSELoss</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">remember</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">next_state</span><span class="p">,</span> <span class="n">done</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">memory</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">state</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">next_state</span><span class="p">,</span> <span class="n">done</span><span class="p">))</span>
    
    <span class="k">def</span> <span class="nf">act</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">rand</span><span class="p">()</span> <span class="o">&lt;=</span> <span class="bp">self</span><span class="p">.</span><span class="n">epsilon</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">random</span><span class="p">.</span><span class="n">randrange</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">action_dim</span><span class="p">)</span>
        <span class="n">state</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">FloatTensor</span><span class="p">(</span><span class="n">state</span><span class="p">).</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="k">with</span> <span class="n">torch</span><span class="p">.</span><span class="n">no_grad</span><span class="p">():</span>
            <span class="n">q_values</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">torch</span><span class="p">.</span><span class="n">argmax</span><span class="p">(</span><span class="n">q_values</span><span class="p">).</span><span class="n">item</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">replay</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">memory</span><span class="p">)</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="p">.</span><span class="n">batch_size</span><span class="p">:</span>
            <span class="k">return</span>
        <span class="n">minibatch</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">sample</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">memory</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">batch_size</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">state</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">next_state</span><span class="p">,</span> <span class="n">done</span> <span class="ow">in</span> <span class="n">minibatch</span><span class="p">:</span>
            <span class="n">target</span> <span class="o">=</span> <span class="n">reward</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">done</span><span class="p">:</span>
                <span class="n">next_state</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">FloatTensor</span><span class="p">(</span><span class="n">next_state</span><span class="p">).</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
                <span class="n">target</span> <span class="o">+=</span> <span class="bp">self</span><span class="p">.</span><span class="n">gamma</span> <span class="o">*</span> <span class="n">torch</span><span class="p">.</span><span class="nb">max</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">(</span><span class="n">next_state</span><span class="p">)).</span><span class="n">item</span><span class="p">()</span>
            <span class="n">state</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">FloatTensor</span><span class="p">(</span><span class="n">state</span><span class="p">).</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
            <span class="n">target_f</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">(</span><span class="n">state</span><span class="p">).</span><span class="n">clone</span><span class="p">().</span><span class="n">detach</span><span class="p">()</span>
            <span class="n">target_f</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="n">action</span><span class="p">]</span> <span class="o">=</span> <span class="n">target</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">optimizer</span><span class="p">.</span><span class="n">zero_grad</span><span class="p">()</span>
            <span class="n">output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
            <span class="n">loss</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">criterion</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="n">target_f</span><span class="p">)</span>
            <span class="n">loss</span><span class="p">.</span><span class="n">backward</span><span class="p">()</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">optimizer</span><span class="p">.</span><span class="n">step</span><span class="p">()</span>
        <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">epsilon</span> <span class="o">&gt;</span> <span class="bp">self</span><span class="p">.</span><span class="n">epsilon_min</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">epsilon</span> <span class="o">*=</span> <span class="bp">self</span><span class="p">.</span><span class="n">epsilon_decay</span>
</code></pre></div>  </div>

</details>

<details>
  <summary>Huấn luyện</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">episodes</span><span class="p">):</span>
    <span class="n">state</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">reset</span><span class="p">()</span>
    <span class="n">total_reward</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">time</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">200</span><span class="p">):</span>
        <span class="n">action</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="n">act</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
        <span class="n">next_state</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">done</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">step</span><span class="p">(</span><span class="n">action</span><span class="p">)</span>
        <span class="n">agent</span><span class="p">.</span><span class="n">remember</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">next_state</span><span class="p">,</span> <span class="n">done</span><span class="p">)</span>
        <span class="n">state</span> <span class="o">=</span> <span class="n">next_state</span>
        <span class="n">total_reward</span> <span class="o">+=</span> <span class="n">reward</span>
        <span class="k">if</span> <span class="n">done</span><span class="p">:</span> <span class="k">break</span>
    <span class="n">agent</span><span class="p">.</span><span class="n">replay</span><span class="p">()</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">%</span><span class="mi">50</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Episode </span><span class="si">{</span><span class="n">e</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">episodes</span><span class="si">}</span><span class="s">, Reward: </span><span class="si">{</span><span class="n">total_reward</span><span class="si">}</span><span class="s">, Epsilon: </span><span class="si">{</span><span class="n">agent</span><span class="p">.</span><span class="n">epsilon</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div>  </div>

</details>

<details>
  <summary>Kiểm thử</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">evaluate</span><span class="p">(</span><span class="n">agent</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">episodes</span><span class="o">=</span><span class="mi">10</span><span class="p">):</span>
    <span class="n">total_rewards</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">episodes</span><span class="p">):</span>
        <span class="n">state</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">reset</span><span class="p">()</span>
        <span class="n">total_reward</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">lose</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="k">while</span> <span class="ow">not</span> <span class="n">lose</span><span class="p">:</span>
            <span class="n">action</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="n">act</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
            <span class="n">state</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">lose</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">step</span><span class="p">(</span><span class="n">action</span><span class="p">)</span>
            <span class="n">total_reward</span> <span class="o">+=</span> <span class="n">reward</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'Episode </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">total_reward</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
</code></pre></div>  </div>

</details>

<h2 id="fixed-q-target">Fixed Q-Target</h2>

<p>Trong hàm mất mát, mặc dù đã dùng semi-gradient, ta vẫn đang sử dụng cùng một tập tham số để ước lượng cả giá trị Q-Target lẫn giá trị Q. Do đó, tồn tại một mối tương quan nào đó giữa Q-Target và các tham số mà chúng ta đang thay đổi. Cho nên, ở mỗi bước huấn luyện, cả giá trị Q và Q-Target đều thay đổi.</p>

<p>Vì vậy, người ta thường</p>

<ol>
  <li>Sử dụng một mạng riêng biệt với các tham số cố định để ước lượng giá trị Q-target. Khi đó hàm mất mát thường được viết lại là
 \(L(\theta) = \left( r + \gamma \cdot \max_{a'} \hat{Q}(s', a'; \theta^{-}) - Q(s,a;\theta) \right)^2\)</li>
  <li>Sao chép các tham số từ mạng Deep Q-Network sau mỗi \(C\) bước để cập nhật mạng này.</li>
</ol>

<h2 id="nhận-xét">Nhận xét</h2>

<ul>
  <li>Với mạng nơ-ron, ta có thể xử lý đầu vào phức tạp hơn.
    <blockquote>
      <p>Với hình ảnh, ta có thể áp dụng CNN</p>
    </blockquote>
  </li>
  <li>Deep Q-Learning đã giải quyết phần nào được vấn đề của Q-learning là xử lý được trạng thái liên tục hoặc lượng trạng thái lớn. Tuy nhiên, hành động vẫn đang là rời rạc (như di chuyển trái phải), còn liên tục (như di chuyển sang trái bao nhiêu đơn vị) thì sao?</li>
</ul>]]></content><author><name>Nguyễn Thái Tân</name><email>nguyenthaitan02@gmail.com</email></author><category term="reinforcement learning" /><summary type="html"><![CDATA[Deep Q-Learning là sự kết hợp giữa Q-Learning và mạng nơ-ron, cho phép tác nhân học các hành động tối ưu trong môi trường phức tạp có không gian trạng thái lớn.]]></summary></entry><entry><title type="html">Reinforcement learning 1 - Q-Learning</title><link href="https://thaitanhaha.github.io/posts/reinforcement-learning-1/" rel="alternate" type="text/html" title="Reinforcement learning 1 - Q-Learning" /><published>2025-06-30T00:00:00+00:00</published><updated>2025-06-30T00:00:00+00:00</updated><id>https://thaitanhaha.github.io/posts/reinforcement-learning-1</id><content type="html" xml:base="https://thaitanhaha.github.io/posts/reinforcement-learning-1/"><![CDATA[<p>Học tăng cường là một lĩnh vực con trong học máy, trong đó một <em>tác nhân</em> học cách tối ưu hóa <em>hành động</em> của mình trong một <em>môi trường</em> để đạt được mục tiêu lâu dài.</p>

<p>Khác với các phương pháp học có giám sát và không giám sát, học tăng cường <strong>không có</strong> tập dữ liệu đã được gán nhãn.</p>

<ul>
  <li>
    <p>Tác nhân (Agent): Là người đưa ra quyết định.</p>
  </li>
  <li>
    <p>Môi trường (Environment): Môi trường có thể thay đổi theo các hành động của tác nhân.</p>
  </li>
  <li>
    <p>Hành động (Action): Những hành động mà tác nhân có thể thực hiện để tương tác với môi trường.</p>
  </li>
  <li>
    <p>Trạng thái (State): Đại diện cho tình trạng hiện tại của môi trường tại một thời điểm cụ thể, bao gồm tất cả thông tin mà tác nhân cần để quyết định hành động tiếp theo.</p>
  </li>
  <li>
    <p>Phần thưởng (Reward): Mỗi hành động của tác nhân trong môi trường sẽ nhận một phần thưởng hoặc hình phạt.</p>
  </li>
  <li>
    <p>Chính sách (Policy): Chiến lược cho phép tác nhân chọn hành động ở mỗi trạng thái.</p>
  </li>
</ul>

<blockquote>
  <p><strong>Ví dụ:</strong> Trong trò chơi CartPoleBalance, <em>tác nhân</em> là người điều khiển chiếc xe; <em>môi trường</em> là chiếc xe và cột; các <em>hành động</em> là di chuyển xe sang trái hoặc di chuyển xe sang phải; <em>trạng thái</em> bao gồm vị trí của xe, vận tốc của xe, góc của cột, vận tốc góc của cột; <em>phần thưởng</em> có thể được định nghĩa là +1 mỗi lần tác nhân giữ cột đứng trong một bước và +0 nếu cột ngã quá 15 độ.</p>
</blockquote>

<p>Mục tiêu của học tăng cường là giúp tác nhân học được một chính sách tối ưu, tức là một chiến lược cho phép tác nhân chọn hành động tốt nhất ở mỗi trạng thái để tối đa hóa tổng phần thưởng tích lũy theo thời gian. Tức là</p>

<ol>
  <li>Tác nhân bắt đầu ở một trạng thái cụ thể.</li>
  <li>Dựa trên trạng thái hiện tại, tác nhân chọn một hành động để thực hiện.
    <ul>
      <li>Khám phá (exploration): Tác nhân thử những hành động <em>mới</em>.</li>
      <li>Khai thác (exploitation): Tác nhân tận dụng những hành động <em>đã biết</em> để đạt được phần thưởng cao nhất.</li>
    </ul>
  </li>
  <li>Môi trường thay đổi trạng thái theo hành động của tác nhân và trả về phần thưởng.</li>
  <li>Tác nhân nhận phần thưởng và cập nhật chiến lược hành động của mình.</li>
</ol>

<h2 id="q---learning">Q - learning</h2>

<p>Một vấn đề quan trọng trong học tăng cường là <strong>cập nhật chiến lược như thế nào</strong>. Q-learning là một cách để giải quyết câu hỏi này.</p>

<ul>
  <li>Q-value (Q-value table): Giá trị dự đoán của tác nhân về phần thưởng mà nó sẽ nhận được khi thực hiện một hành động trong một trạng thái cụ thể. Mỗi <strong>cặp trạng thái và hành động</strong> có <strong>một giá trị Q</strong> được lưu trong bảng.</li>
</ul>

<p align="center">
  <img src="https://www.researchgate.net/profile/Manel-Abdellatif/publication/361359070/figure/fig1/AS:1167899863584769@1655460434256/Cart-Pole-balancing-problem.png" alt="CartPoleBalance" />
</p>

<p>Với bài toán CartPoleBalance trên, Q-table được viết như thế nào? Giả sử ta chia mỗi thành phần của trạng thái thành 6 khoảng, tổng số trạng thái rời rạc là 6 (vị trí xe) × 6 (vận tốc xe) × 6 (góc của cột) × 6 (vận tốc góc của cột) = 1296 trạng thái.</p>

<table>
  <thead>
    <tr>
      <th>Trạng thái (vị trí xe, vận tốc xe, góc của cột, vận tốc góc của cột)</th>
      <th>Hành động 0 (di chuyển trái)</th>
      <th>Hành động 1 (di chuyển phải)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>(0, 0, 0, 0)</td>
      <td>0.01</td>
      <td>0.04</td>
    </tr>
    <tr>
      <td>(0, 0, 0, 1)</td>
      <td>0.03</td>
      <td>0.07</td>
    </tr>
    <tr>
      <td>(…, …, …, …)</td>
      <td>…</td>
      <td>…</td>
    </tr>
    <tr>
      <td>(5, 5, 5, 5)</td>
      <td>0.02</td>
      <td>0.05</td>
    </tr>
  </tbody>
</table>

<p>Nghĩa là, ví dụ khi gặp trạng thái (0,0,0,1), hành động được chọn là di chuyển sang phải bởi vì nó cho phần thưởng trung bình cao nhất.</p>

<p>Vậy cập nhật như thế nào để có các số như 0.01, 0.04,… trong bảng? Các giá trị ban đầu của Q-table thường được gán là 0 hoặc một giá trị ngẫu nhiên. Sau đó, công thức cập nhật Q-value là:</p>

\[Q(s, a) \leftarrow Q(s, a) + \alpha \left( r + \gamma \cdot \max_{a'} Q(s', a') - Q(s, a) \right)\]

<p>Trong đó:</p>

<ul>
  <li>\(Q(s, a)\): Giá trị Q hiện tại của cặp trạng thái \(s\) và hành động \(a\).</li>
  <li>\(\alpha\): Tốc độ học (learning rate), giúp điều chỉnh mức độ cập nhật Q-value.</li>
  <li>\(r\): Phần thưởng nhận được sau khi thực hiện hành động \(a\) tại trạng thái \(s\).</li>
  <li>\(\gamma\): Hệ số chiết khấu (discount factor), dùng để điều chỉnh sự quan trọng của các phần thưởng trong tương lai.</li>
  <li>\(\max_{a’} Q(s’, a’)\): Giá trị Q tối đa của các hành động có thể thực hiện tại trạng thái tiếp theo \(s’\).</li>
</ul>

<p>Trong học tăng cường, có hai khái niệm là khám phá (exploration) và khai thác (exploitation), là cách mà tác nhân chọn một hành động để thực hiện. Để chọn hành động, thuật toán sử dụng chính sách epsilon-greedy. Nghĩa là, với Q-learning, với xác suất \(1−\epsilon\), tác nhân chọn hành động có giá trị Q cao nhất (khai thác); với xác suất \(\epsilon\), tác nhân chọn hành động ngẫu nhiên (khám phá) để khám phá môi trường.</p>

<p><strong>Tóm lại</strong>, để bắt đầu học tăng cường, khởi đầu từ một trạng thái \(\rightarrow\) ta mô phỏng môi trường và hành động của tác nhân \(\rightarrow\) cập nhật bảng Q \(\rightarrow\) tiếp tục mô phỏng và cập nhật cho đến khi kết thúc trò chơi, hoặc đến số bước nhất định.</p>

<h2 id="code">Code</h2>
<p>Code để dễ hình dung nhé!</p>

<details>
  <summary>Môi trường, Tác nhân</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CartPoleEnv</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c1"># Trạng thái: [x, x_velocity, theta, theta_velocity]
</span>        <span class="c1"># Hành động: 0 di chuyển sang trái, 1 di chuyển sang phải
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="n">low</span><span class="o">=-</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">4</span><span class="p">,))</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">g</span> <span class="o">=</span> <span class="mf">9.8</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">m</span> <span class="o">=</span> <span class="mf">0.1</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">M</span> <span class="o">=</span> <span class="mf">1.0</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">L</span> <span class="o">=</span> <span class="mf">0.5</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dt</span> <span class="o">=</span> <span class="mf">0.02</span>
        
    <span class="k">def</span> <span class="nf">step</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action</span><span class="p">):</span>
        <span class="c1"># Hàm dùng để mô phỏng
</span>        <span class="n">x</span><span class="p">,</span> <span class="n">x_velocity</span><span class="p">,</span> <span class="n">theta</span><span class="p">,</span> <span class="n">theta_velocity</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span>
        
        <span class="n">force</span> <span class="o">=</span> <span class="mf">20.0</span> <span class="k">if</span> <span class="n">action</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="o">-</span><span class="mf">20.0</span>
        
        <span class="n">cos_theta</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">cos</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
        <span class="n">sin_theta</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
        <span class="n">total_mass</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">M</span> <span class="o">+</span> <span class="bp">self</span><span class="p">.</span><span class="n">m</span>
        <span class="n">pole_mass_length</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">m</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">L</span>
        
        <span class="n">temp</span> <span class="o">=</span> <span class="p">(</span><span class="n">force</span> <span class="o">+</span> <span class="n">pole_mass_length</span> <span class="o">*</span> <span class="n">theta_velocity</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">sin_theta</span><span class="p">)</span> <span class="o">/</span> <span class="n">total_mass</span>
        <span class="n">theta_acc</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">g</span> <span class="o">*</span> <span class="n">sin_theta</span> <span class="o">-</span> <span class="n">cos_theta</span> <span class="o">*</span> <span class="n">temp</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">L</span> <span class="o">*</span> <span class="p">(</span><span class="mi">4</span> <span class="o">/</span> <span class="mi">3</span> <span class="o">-</span> <span class="bp">self</span><span class="p">.</span><span class="n">m</span> <span class="o">*</span> <span class="n">cos_theta</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">/</span> <span class="n">total_mass</span><span class="p">))</span>
        <span class="n">x_acc</span> <span class="o">=</span> <span class="n">temp</span> <span class="o">-</span> <span class="n">pole_mass_length</span> <span class="o">*</span> <span class="n">theta_acc</span> <span class="o">*</span> <span class="n">cos_theta</span> <span class="o">/</span> <span class="n">total_mass</span>
        
        <span class="n">x</span> <span class="o">+=</span> <span class="n">x_velocity</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        <span class="n">x_velocity</span> <span class="o">+=</span> <span class="n">x_acc</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        <span class="n">theta</span> <span class="o">+=</span> <span class="n">theta_velocity</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        <span class="n">theta_velocity</span> <span class="o">+=</span> <span class="n">theta_acc</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">dt</span>
        
        <span class="n">lose</span> <span class="o">=</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="o">-</span><span class="mf">2.4</span> <span class="ow">or</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mf">2.4</span> <span class="ow">or</span> <span class="n">theta</span> <span class="o">&lt;</span> <span class="o">-</span><span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">15</span> <span class="ow">or</span> <span class="n">theta</span> <span class="o">&gt;</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">15</span>
        <span class="n">reward</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">lose</span> <span class="k">else</span> <span class="mf">0.0</span>
        
        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="n">x</span><span class="p">,</span> <span class="n">x_velocity</span><span class="p">,</span> <span class="n">theta</span><span class="p">,</span> <span class="n">theta_velocity</span><span class="p">])</span>
        
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">lose</span>

    <span class="k">def</span> <span class="nf">reset</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="n">low</span><span class="o">=-</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">4</span><span class="p">,))</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span>
        
<span class="k">class</span> <span class="nc">QLearningAgent</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action_space</span><span class="p">,</span> <span class="n">state_space</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">gamma</span><span class="o">=</span><span class="mf">0.99</span><span class="p">,</span> <span class="n">epsilon</span><span class="o">=</span><span class="mf">0.1</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">action_space</span> <span class="o">=</span> <span class="n">action_space</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state_space</span> <span class="o">=</span> <span class="n">state_space</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">alpha</span> <span class="o">=</span> <span class="n">alpha</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">gamma</span> <span class="o">=</span> <span class="n">gamma</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">epsilon</span> <span class="o">=</span> <span class="n">epsilon</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">q_table</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">state_space</span> <span class="o">+</span> <span class="p">(</span><span class="n">action_space</span><span class="p">,))</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">state_bins</span> <span class="o">=</span> <span class="p">[</span>
            <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="o">-</span><span class="mf">2.4</span><span class="p">,</span> <span class="mf">2.4</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="c1"># vị trí của xe
</span>            <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="o">-</span><span class="mf">3.0</span><span class="p">,</span> <span class="mf">3.0</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="c1"># vận tốc của xe
</span>            <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="o">-</span><span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">15</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="c1"># góc của cột
</span>            <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="o">-</span><span class="mf">3.0</span><span class="p">,</span> <span class="mf">3.0</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="c1"># vận tốc góc của cột
</span>        <span class="p">]</span>

    <span class="k">def</span> <span class="nf">discretize_state</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">):</span>
        <span class="c1"># Chia trạng thái (hiện là giá trị liên tục) thành các khoảng rời rạc
</span>        <span class="n">discretized</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">state</span><span class="p">)):</span>
            <span class="n">discretized</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">digitize</span><span class="p">(</span><span class="n">state</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="bp">self</span><span class="p">.</span><span class="n">state_bins</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
        
        <span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">discretized</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">choose_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">rand</span><span class="p">()</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="p">.</span><span class="n">epsilon</span><span class="p">:</span> <span class="c1"># Khám phá
</span>            <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">choice</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">action_space</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span> <span class="c1"># Khai thác
</span>            <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">argmax</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">q_table</span><span class="p">[</span><span class="n">state</span><span class="p">])</span>

    <span class="k">def</span> <span class="nf">update_q_table</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">next_state</span><span class="p">):</span>
        <span class="c1"># Cập nhật theo công thức
</span>        <span class="n">best_next_action</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">argmax</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">q_table</span><span class="p">[</span><span class="n">next_state</span><span class="p">])</span>
        <span class="n">td_target</span> <span class="o">=</span> <span class="n">reward</span> <span class="o">+</span> <span class="bp">self</span><span class="p">.</span><span class="n">gamma</span> <span class="o">*</span> <span class="bp">self</span><span class="p">.</span><span class="n">q_table</span><span class="p">[</span><span class="n">next_state</span> <span class="o">+</span> <span class="p">(</span><span class="n">best_next_action</span><span class="p">,)]</span>
        <span class="n">td_error</span> <span class="o">=</span> <span class="n">td_target</span> <span class="o">-</span> <span class="bp">self</span><span class="p">.</span><span class="n">q_table</span><span class="p">[</span><span class="n">state</span> <span class="o">+</span> <span class="p">(</span><span class="n">action</span><span class="p">,)]</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">q_table</span><span class="p">[</span><span class="n">state</span> <span class="o">+</span> <span class="p">(</span><span class="n">action</span><span class="p">,)]</span> <span class="o">+=</span> <span class="bp">self</span><span class="p">.</span><span class="n">alpha</span> <span class="o">*</span> <span class="n">td_error</span>
</code></pre></div>  </div>

</details>

<details>
  <summary>Huấn luyện</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">train</span><span class="p">(</span><span class="n">agent</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">episodes</span><span class="p">,</span> <span class="n">max_timesteps</span><span class="o">=</span><span class="mi">200</span><span class="p">):</span>
    <span class="n">rewards</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">episode</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">episodes</span><span class="p">):</span>
        <span class="n">state</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">reset</span><span class="p">()</span>
        <span class="n">state</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="n">discretize_state</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
        <span class="n">total_reward</span> <span class="o">=</span> <span class="mi">0</span>
        
        <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_timesteps</span><span class="p">):</span>
            <span class="n">action</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="n">choose_action</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
            <span class="n">next_state</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">lose</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">step</span><span class="p">(</span><span class="n">action</span><span class="p">)</span>
            <span class="n">next_state</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="n">discretize_state</span><span class="p">(</span><span class="n">next_state</span><span class="p">)</span>
            
            <span class="n">agent</span><span class="p">.</span><span class="n">update_q_table</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">next_state</span><span class="p">)</span>
            
            <span class="n">state</span> <span class="o">=</span> <span class="n">next_state</span>
            <span class="n">total_reward</span> <span class="o">+=</span> <span class="n">reward</span>
            
            <span class="k">if</span> <span class="n">lose</span><span class="p">:</span> <span class="k">break</span>
        
        <span class="n">rewards</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">total_reward</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">episode</span> <span class="o">%</span> <span class="mi">500</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">episode</span> <span class="o">==</span> <span class="n">episodes</span><span class="o">-</span><span class="mi">1</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Episode </span><span class="si">{</span><span class="n">episode</span><span class="si">}</span><span class="s">, Total Reward: </span><span class="si">{</span><span class="n">total_reward</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">rewards</span>
</code></pre></div>  </div>

</details>

<details>
  <summary>Kiểm thử</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">evaluate</span><span class="p">(</span><span class="n">agent</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">episodes</span><span class="p">,</span> <span class="n">is_render</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">episode</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">episodes</span><span class="p">):</span>
        <span class="n">state</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">reset</span><span class="p">()</span>
        <span class="n">state</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="n">discretize_state</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
        <span class="n">lose</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="n">total_reward</span> <span class="o">=</span> <span class="mi">0</span>
        
        <span class="k">while</span> <span class="ow">not</span> <span class="n">lose</span><span class="p">:</span>
            <span class="c1"># Với kiểm thử, chỉ dùng Khai thác chứ không dùng Khám phá
</span>            <span class="n">action</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">argmax</span><span class="p">(</span><span class="n">agent</span><span class="p">.</span><span class="n">q_table</span><span class="p">[</span><span class="n">state</span><span class="p">])</span>
            <span class="n">state</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">lose</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">step</span><span class="p">(</span><span class="n">action</span><span class="p">)</span>
            <span class="n">state</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="n">discretize_state</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
            <span class="n">total_reward</span> <span class="o">+=</span> <span class="n">reward</span>
            
            <span class="k">if</span> <span class="n">is_render</span><span class="p">:</span> <span class="n">render</span><span class="p">(</span><span class="n">env</span><span class="p">)</span>
        
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Episode </span><span class="si">{</span><span class="n">episode</span><span class="si">}</span><span class="s">, Total Reward: </span><span class="si">{</span><span class="n">total_reward</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div>  </div>

</details>

<h2 id="tại-sao">Tại sao?</h2>

<p>Ta đã biết công thức cập nhật của Q: lấy giá trị Q cũ, cộng thêm một phần nhỏ của chênh lệch giữa phần thưởng tương lai dự kiến  và giá trị Q cũ. Nhưng tại sao người ta lại dùng công thức này?</p>

<h3 id="quy-trình-ra-quyết-định-markov-markov-decision-process---mdp">Quy trình ra quyết định Markov (Markov Decision Process - MDP)</h3>

<p>Trong trí tuệ nhân tạo, người ta dùng thuật ngữ <code class="language-plaintext highlighter-rouge">Quy trình ra quyết định Markov (Markov Decision Process - MDP)</code> để mô hình hóa các tình huống mà trong đó các quyết định được đưa ra liên tiếp và kết quả của các hành động là không chắc chắn.</p>

<blockquote>
  <p>Tính chất Markov là tính chất mà tức là tương lai chỉ phụ thuộc vào hiện tại, và không phụ thuộc vào quá khứ.</p>
</blockquote>

\[MDP = (S, A, P, R, \gamma)\]

<ul>
  <li>\(S\): Tập trạng thái.</li>
  <li>\(A\): Tập hành động.</li>
  <li>\(P(s’ \mid s, a)\): Xác suất chuyển từ trạng thái \(s\) sang trạng thái \(s’\) khi thực hiện hành động \(a\).</li>
  <li>\(R(s, a)\): Phần thưởng nhận được khi thực hiện hành động \(a\) tại trạng thái \(s\).</li>
  <li>\(\gamma\): Hệ số chiết khấu.</li>
</ul>

<p>Để giải được MDP, người ta thường dùng <code class="language-plaintext highlighter-rouge">Quy hoạch động (Dynamic Programming - DP)</code> dựa trên <code class="language-plaintext highlighter-rouge">Phương trình Bellman</code>.</p>

<p>Trong Q-learning, ta không cần phải biết mô hình môi trường (bao gồm các xác suất chuyển trạng thái \(P(s’ \mid s, a)\)) khi cập nhật Q-values. Thay vào đó, Q-learning chỉ cần quan sát trạng thái, hành động, phần thưởng và trạng thái tiếp theo trong quá trình tương tác với môi trường là được. Do đó, ta có thể “tạm quên đi” \(P(s’ \mid s, a)\).</p>

<h3 id="phương-trình-bellman-cho-giá-trị-trạng-thái-value-function">Phương trình Bellman cho Giá trị Trạng thái (Value Function)</h3>

<p>Phương trình Bellman cho <strong>giá trị trạng thái</strong> \(V(s)\) được viết như sau:</p>

\[V(s) = \max_a \left(R(s,a) + \gamma V(s') \right)\]

<h3 id="phương-trình-bellman-cho-giá-trị-hành-động-q-value-function">Phương trình Bellman cho Giá trị Hành động (Q-value Function)</h3>

<p>Trong phương trình Bellman cho giá trị trạng thái, chúng ta quan tâm đến tất cả các trạng thái và tất cả các hành động khả thi. Vậy khi bỏ hàm \(\max\), chúng ta sẽ được công thức như là giá trị của một trạng thái được tạo ra cho chỉ một hành động khả thi.</p>

<p>Dựa trên ý tưởng đó, phương trình Bellman áp dụng cho <strong>giá trị hành động</strong> được viết như sau:</p>

\[Q(s, a) = R(s, a) + \gamma V(s')\]

<p>Để tạo nên sự đồng nhất, ta viết lại \(V(s’)\) bằng \(\max_{a’} Q(s’, a’)\) vì chúng ta coi giá trị của một trạng thái được tính bằng giá trị lớn nhất có thể của hành động \(Q(s, a)\).</p>

\[Q(s, a) = R(s, a) + \gamma \max_{a'} Q(s', a')\]

<h3 id="chênh-lệch-thời-gian-temporal-difference---td">Chênh lệch thời gian (Temporal Difference - TD)</h3>

<p>Môi trường không phải bất biến, vậy phải làm như nào để nắm bắt được sự thay đổi của môi trường? Rất đơn giản, lấy giá trị Q mới trừ giá trị Q cũ!</p>

\[\alpha TD(s,a) = Q(s, a)_{new} - Q(s,a)_{old}\]

<p>Vậy</p>

\[Q(s, a)_{new} \leftarrow Q(s, a)_{old} + \alpha TD(s,a) = Q(s, a)_{old} + \alpha \left( r + \gamma \cdot \max_{a'} Q(s', a') - Q(s, a)_{old} \right)\]

<h2 id="sự-hội-tụ">Sự hội tụ</h2>

<p>Một điều đặc biệt, Q-learning là thuật toán đầu tiên được chứng minh hội tụ đến chính sách tối ưu (optimal policy) dưới một số điều kiện nhất định. Nó được đề xuất ban đầu bởi Watkins 1989 và được chứng minh hội tụ bởi Watkins &amp; Dayan 1992.</p>

<p>Nói chung, phải đáp ứng hai điều kiện để đảm bảo sự hội tụ (chính sách sẽ trở nên gần với chính sách tối ưu một cách tùy ý sau một khoảng thời gian dài tùy ý):</p>

<ul>
  <li>
    <p>Tốc độ học phải tiến tới 0, nhưng không quá nhanh.</p>

    <p>Về mặt hình thức, điều này đòi hỏi tổng tỷ lệ học phải phân kỳ, nhưng tổng bình phương của chúng phải hội tụ.</p>

\[\sum_{t}^{\infty} \alpha_t = \infty \quad \quad\sum_{t}^{\infty} \alpha^2_t &lt; \infty\]

    <blockquote>
      <p>Ví dụ: <code class="language-plaintext highlighter-rouge">1/1, 1/2, 1/3, 1/4, ...</code></p>
    </blockquote>
  </li>
  <li>
    <p>Mỗi cặp trạng thái - hành động phải được truy cập vô hạn lần.</p>

    <p>Nghĩa là, về mặt toán học, mỗi hành động trong mọi trạng thái phải có xác suất được chính sách chọn &gt; 0. Trong thực tế, sử dụng chính sách epsilon-greedy (\(\epsilon &gt; 0\)) đảm bảo rằng điều kiện này được đáp ứng.</p>
  </li>
</ul>

<p>Dưới các điều kiện này, Q-learning sẽ hội tụ tới giá trị tối ưu trong thời gian vô hạn, nhưng trong thực tế, thường có một số yếu tố như độ chính xác của mô phỏng, việc dừng quá sớm, hoặc tốc độ học không phù hợp có thể ảnh hưởng đến quá trình hội tụ.</p>

<h2 id="nhận-xét">Nhận xét</h2>

<ul>
  <li>
    <p>Về mặt lý thuyết, Q-Learning có thể hội tụ về giải pháp tối ưu nhưng thường không rõ ràng về cách điều chỉnh các siêu tham số như \(\epsilon\) và \(\alpha\).</p>
  </li>
  <li>
    <p>Khi số lượng trạng thái và hành động tăng lên, số lượng giá trị Q cần lưu trữ và cập nhật cũng tăng theo cấp số nhân, làm cho Q-learning gặp phải vấn đề về hiệu suất và bộ nhớ.</p>

    <blockquote>
      <p>Ví dụ trong bài toán CartPoleBalance, thật ra các yếu tố về góc hay vị trí là liên tục, nhưng để giảm bộ nhớ, ta đã chia giá trị liên tục thành rời rạc.</p>
    </blockquote>
  </li>
</ul>

<p>Hãy nhớ rằng Q-learning là một thuật toán cũ và hơi lỗi thời, đây là một cách cơ bản và sơ khai để tìm hiểu về học tăng cường, nhưng có những cách tốt hơn để giải quyết một vấn đề thực tế!</p>]]></content><author><name>Nguyễn Thái Tân</name><email>nguyenthaitan02@gmail.com</email></author><category term="reinforcement learning" /><summary type="html"><![CDATA[Q-Learning là một thuật toán học tăng cường nền tảng, giúp các tác nhân học được hành động tốt nhất cần thực hiện trong một môi trường nhất định bằng cách tối đa hóa phần thưởng theo thời gian.]]></summary></entry></feed>