{ "cells": [ { "cell_type": "markdown", "id": "745510b1", "metadata": {}, "source": [ "# 第 3 章:最优状态价值与贝尔曼最优方程\n", "\n", "## 1. 什么是最优策略?\n", "如果一个策略 $\\pi^*$ 在所有状态下的价值,都大于或等于其他任何策略的价值,即对于所有 $s \\in \\mathcal{S}$,都有 $v_{\\pi^*}(s) \\ge v_\\pi(s)$,那么 $\\pi^*$ 就是最优策略。\n", "最优策略的状态价值,被称为**最优状态价值 (Optimal State Value)** $v^*$。\n", "\n", "## 2. 贝尔曼最优方程 (BOE)\n", "之前我们算的是某个固定策略的价值。现在我们要算的是“最优”价值。\n", "BOE 的核心思想是:在某个状态下的最优价值,等于**所有可能动作中,动作价值 $q(s,a)$ 的最大值**。\n", "\n", "数学表达为:\n", "$$v(s) = \\max_{a \\in \\mathcal{A}} q(s,a)$$" ] }, { "cell_type": "code", "execution_count": 2, "id": "f31bd6cc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--- 策略改进演示 ---\n", "动作 a1 的价值: 6.2\n", "动作 a2 的价值: 8.0\n", "动作 a3 的价值: 9.0\n", "动作 a4 的价值: 6.2\n", "动作 a5 的价值: 7.2\n", "\n", "=> 最佳动作是 'a3',其价值为 9.0。\n", "结论:只要我们在每个状态都选择 Q 值最大的动作,就能得到一个更好的、甚至是最优的策略!\n" ] } ], "source": [ "import numpy as np\n", "\n", "# 假设我们在状态 s1 算出了 5 个动作的 Q 值 (参考书中 3.1 节的计算)\n", "# a1: 向上, a2: 向右, a3: 向下, a4: 向左, a5: 原地不动\n", "q_values_s1 = {\n", " \"a1\": 6.2, # 撞墙,-1 + 0.9*v(s1)\n", " \"a2\": 8.0, # 原策略向右,进禁区,-1 + 0.9*v(s2)\n", " \"a3\": 9.0, # 向下避开禁区,0 + 0.9*v(s3)\n", " \"a4\": 6.2, # 撞墙,-1 + 0.9*v(s1)\n", " \"a5\": 7.2 # 原地不动,0 + 0.9*v(s1)\n", "}\n", "\n", "# 寻找使 Q 值最大的动作 (贪心策略 Greedy Policy)\n", "best_action = max(q_values_s1, key=q_values_s1.get)\n", "max_q_value = q_values_s1[best_action]\n", "\n", "print(\"--- 策略改进演示 ---\")\n", "for action, q_val in q_values_s1.items():\n", " print(f\"动作 {action} 的价值: {q_val}\")\n", "\n", "print(f\"\\n=> 最佳动作是 '{best_action}',其价值为 {max_q_value}。\")\n", "print(\"结论:只要我们在每个状态都选择 Q 值最大的动作,就能得到一个更好的、甚至是最优的策略!\")" ] }, { "cell_type": "markdown", "id": "618e235d", "metadata": {}, "source": [ "## 3. 求解 BOE:压缩映射与迭代法\n", "BOE 看起来很难解,因为它方程两边都有未知数 $v$。但数学上的**压缩映射定理 (Contraction Mapping Theorem)** 告诉我们三个好消息:\n", "1. **存在性**:BOE 一定有解。\n", "2. **唯一性**:最优状态价值 $v^*$ 的解是唯一的。\n", "3. **算法**:可以通过不断迭代 $v_{k+1} = \\max_\\pi(r_\\pi + \\gamma P_\\pi v_k)$ 来逼近最优解,这个过程叫**价值迭代 (Value Iteration)**=。\n", "\n", "## 4. 影响最优策略的因素\n", "* **折扣因子 $\\gamma$ 的魔力**:\n", " * $\\gamma$ 较大 (如 0.9):智能体目光长远,为了以最快速度拿到目标大奖,甚至愿意冒险穿过有惩罚的禁区。\n", " * $\\gamma$ 较小 (如 0.5 或更小):智能体变得短视且保守,宁愿绕远路也不敢冒险。\n", " * 此外,$\\gamma$ 天生就不鼓励“无意义的绕路”,因为绕路会让未来的奖励因折扣变得更小。\n", "* **奖励的仿射变换不变性**:如果把所有的奖励都乘以一个正数,或者都加上一个固定的常数,算出来的最优策略**完全不会改变**。" ] }, { "cell_type": "code", "execution_count": 3, "id": "41d7c569", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--- 情景 1:每走一步没有任何惩罚 (即时奖励为 0) ---\n", "直达路径(A)的回报: 9.0\n", "绕路路径(B)的回报: 7.290000000000001\n", "结论:哪怕每步惩罚是 0,因为有 gamma 的折现,智能体依然知道直达路径更好!\n", "\n", "--- 情景 2:给每一步都增加 -1 的惩罚 (仿射变换) ---\n", "增加惩罚后,直达路径(A)的回报: 8.00\n", "增加惩罚后,绕路路径(B)的回报: 4.58\n", "结论:增加统一惩罚后,直达路径(A)依然大于绕路路径(B)。相对好坏关系不变,最优策略不变。\n" ] } ], "source": [ "# 验证:假设两条路径,路径A直达终点(2步),路径B绕路(4步)\n", "gamma = 0.9\n", "target_reward = 10.0\n", "\n", "print(\"--- 情景 1:每走一步没有任何惩罚 (即时奖励为 0) ---\")\n", "# 路径A: 0 + 0.9 * 10\n", "return_path_A = 0 + (gamma ** 1) * target_reward \n", "# 路径B: 0 + 0.9*0 + 0.9^2*0 + 0.9^3 * 10\n", "return_path_B = 0 + 0 + 0 + (gamma ** 3) * target_reward\n", "\n", "print(f\"直达路径(A)的回报: {return_path_A}\")\n", "print(f\"绕路路径(B)的回报: {return_path_B}\")\n", "print(\"结论:哪怕每步惩罚是 0,因为有 gamma 的折现,智能体依然知道直达路径更好!\\n\")\n", "\n", "print(\"--- 情景 2:给每一步都增加 -1 的惩罚 (仿射变换) ---\")\n", "# 路径A: -1 + 0.9 * (10 - 1) -> 简化理解为每步 -1,最后一步拿到目标\n", "return_path_A_penalty = -1 + (gamma ** 1) * target_reward\n", "# 路径B: 绕远路\n", "return_path_B_penalty = -1 + gamma*(-1) + (gamma**2)*(-1) + (gamma**3)*target_reward\n", "\n", "print(f\"增加惩罚后,直达路径(A)的回报: {return_path_A_penalty:.2f}\")\n", "print(f\"增加惩罚后,绕路路径(B)的回报: {return_path_B_penalty:.2f}\")\n", "print(\"结论:增加统一惩罚后,直达路径(A)依然大于绕路路径(B)。相对好坏关系不变,最优策略不变。\")" ] }, { "cell_type": "code", "execution_count": null, "id": "413fb515", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "GymRL", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.9" } }, "nbformat": 4, "nbformat_minor": 5 }