167 lines
6.8 KiB
Plaintext
167 lines
6.8 KiB
Plaintext
{
|
||
"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
|
||
}
|