强化学习极简实战:从手写环境到 Gymnasium 策略迭代 (Policy Iteration) 📅 发布时间:2026/9/6 4:38:14 👁 浏览次数: 强化学习极简实战从手写环境到 Gymnasium 策略迭代 (Policy Iteration)导读强化学习初学者往往容易被两件事卡住一是面对 Gym 框架时不知道它底层是怎么和数学公式关联的二是不知道策略评估和策略提升这两套循环到底在算什么。本教程以经典的冰湖环境 (FrozenLake-4x4)为例带你彻底穿透 Gymnasium 封装的本质手把手掌握基于模型的动态规划核心算法——策略迭代Policy Iteration。 最终实机运行效果经过策略迭代求解后智能体大脑中形成最优策略矩阵在冰湖地图上自动避开所有陷阱并以最短路径6步直达终点目录一、环境建模的两种范式手写 move() vs Gymnasium 的 env.P二、策略迭代核心算法数学推导与代码实现1. 广义策略迭代 (GPI) 双循环闭环2. 策略评估 (Policy Evaluation) 与贝尔曼期望方程3. 策略提升 (Policy Improvement) 与均分概率三、从“离线算脑”到“实机表演”1. 训练结果收敛可视化2. 深度理解加权随机抽样 np.random.choice四、框架进阶从 Jupyter 历史包袱到 Gymnasium 原生极简渲染五、完整工程代码结构与运行指南一、环境建模的两种范式手写 move() vs Gymnasium 的 env.P强化学习解决的是马尔可夫决策过程 (MDP)其数学五元组为( S , A , P , R , γ ) (S, A, P, R, \gamma)(S,A,P,R,γ)。对于 4×4 的格子世界状态S ∈ { 0 , … , 15 } S \in \{0, \dots, 15\}S∈{0,…,15}动作A ∈ { 0 : 左 , 1 : 下 , 2 : 右 , 3 : 上 } A \in \{0:\text{左}, 1:\text{下}, 2:\text{右}, 3:\text{上}\}A∈{0:左,1:下,2:右,3:上}。1. 传统手写环境的思路单步动态推算如果我们不借助任何框架纯手写一个物理规则函数通常会写一个move(row, col, action)defmove(row,col,action):# 如果已经掉入陷阱或到达终点无法再行动ifget_state(row,col)in[trap,terminal]:returnrow,col,0# 根据动作改变坐标ifaction0:row-1# 上ifaction1:row1# 下ifaction2:col-1# 左ifaction3:col1# 右# 不允许走出地图外面 (边界撞墙截断)rowmax(0,min(3,row))colmax(0,min(3,col))# 奖励机制每走一步扣1分掉入陷阱扣100分reward-1ifget_state(row,col)trap:reward-100returnrow,col,reward2. Gymnasium 的封装思想全局离线转移查找表 (env.P)Gymnasium 通过env.unwrapped.P直接向算法暴露了环境的动力学模型Transition Dynamics。它不是在运行时一步一步去move而是在初始化时就已经把 16 个格子 × 4 种动作可能产生的全部情况都预先计算好保存为一个嵌套字典env.P[state][action]-[(prop,next_state,reward,over),...]每个元组包含 4 个元素prop(Probability)状态转移概率P ( s ′ ∣ s , a ) P(s \mid s, a)P(s′∣s,a)。当设置is_slipperyFalse时不打滑概率为1.0 1.01.0若打滑则目标方向及垂直两侧各为1 3 \frac{1}{3}31。next_state执行动作后的下一个状态编号 (0 ∼ 15 0 \sim 150∼15)。reward到达下一个状态拿到的即时奖励。over(terminated)是否进入终止状态掉进冰洞H或到达终点G。 核心认知差异为什么 Gymnasium 不需要每步扣分手写move的逻辑每走一步reward -1通过负反馈逼迫算法快点到达终点Gymnasium 的逻辑普通格子和陷阱reward 0.0终点reward 1.0。为什么智能体不会在原地兜圈关键在于折扣因子γ 0.9 \gamma 0.9γ0.9第 1 步到终点拿1.0 1.01.0分第 2 步才到就变成1.0 × 0.9 0.9 1.0 \times 0.9 0.91.0×0.90.9分第 3 步就是0.81 0.810.81分……折扣机制天然惩罚了多余步数驱动算法自动寻找最短路径。二、策略迭代核心算法数学推导与代码实现1. 广义策略迭代 (GPI) 双循环闭环策略迭代由策略评估与策略提升交替执行直至收敛计算得到当前真实价值 V(s)贪婪更新策略 π否是初始策略 π₀ (各动作概率0.25)策略评估 Policy Evaluation策略提升 Policy Improvement策略是否稳定最优策略 π* 与最优价值 V*2. 策略评估 (Policy Evaluation) 与贝尔曼期望方程数学原理在当前策略π \piπ下计算每个状态s ss的长期折现价值V ( s ) V(s)V(s)V k 1 ( s ) ∑ a ∈ A π ( a ∣ s ) ∑ s ′ P ( s ′ ∣ s , a ) [ R ( s , a , s ′ ) γ V k ( s ′ ) ] V_{k1}(s) \sum_{a \in A} \pi(a \mid s) \sum_{s} P(s \mid s, a) \left[ R(s, a, s) \gamma V_k(s) \right]Vk1(s)a∈A∑π(a∣s)s′∑P(s′∣s,a)[R(s,a,s′)γVk(s′)]其中动作价值函数Q函数为Q ( s , a ) ∑ s ′ P ( s ′ ∣ s , a ) [ R ( s , a , s ′ ) γ V ( s ′ ) ] Q(s, a) \sum_{s} P(s \mid s, a) \left[ R(s, a, s) \gamma V(s) \right]Q(s,a)s′∑P(s′∣s,a)[R(s,a,s′)γV(s′)]代码实现 (common.py):# 计算在状态 state 执行 action 时的动作价值 Q(s, a)defget_qsa(env,values,state,action):value0.0# 遍历该动作可能产生的所有结果forprop,next_state,reward,overinenv.P[state][action]:# 贝尔曼折现项gamma 0.9next_valuevalues[next_state]*0.9# 如果掉进陷阱或到达终点终止状态未来没有后续收益ifover:next_value0.0# Q(s, a) 即时奖励 衰减的未来价值next_valuereward next_value*prop valuenext_valuereturnvalue# 全图状态价值更新defget_values(env,values,pi,algorithm):new_valuesnp.zeros([16])forstateinrange(16):action_valuenp.zeros(4)foractioninrange(4):action_value[action]get_qsa(env,values,state,action)ifalgorithm策略迭代:# 按策略概率加权求期望V(s) sum(pi(a|s) * Q(s, a))new_values[state](action_value*pi[state]).sum()elifalgorithm价值迭代:# 直接贪婪取最大值V(s) max_a Q(s, a)new_values[state]action_value.max()returnnew_values3. 策略提升 (Policy Improvement) 与均分概率数学原理基于评估出来的价值函数贪婪更新策略π ′ ( s ) arg max a Q ( s , a ) \pi(s) \arg\max_{a} Q(s, a)π′(s)argamaxQ(s,a)如果存在多个动作并列第一则让这些动作均分概率defget_pi(env,values):new_pinp.zeros([16,4])forstateinrange(16):action_valuenp.zeros(4)foractioninrange(4):action_value[action]get_qsa(env,values,state,action)# 统计达到最大分数的动作数量count(action_valueaction_value.max()).sum()# 让并列第一的最优动作均分概率foractioninrange(4):ifaction_value[action]action_value.max():new_pi[state,action]1/countelse:new_pi[state,action]0.0returnnew_pi三、从“离线算脑”到“实机表演”1. 训练结果收敛可视化训练代码只需一个清晰的交替循环for_inrange(10):for_inrange(100):valuesget_values(env,values,pi,algorithm)piget_pi(env,values)训练后得到的最优状态价值V ( s ) V(s)V(s)[[0.5905 0.6561 0.729 0.6561] [0.6561 0.0000 0.8100 0.0000] [0.7290 0.8100 0.9000 0.0000] [0.0000 0.9000 1.0000 0.0000]]陷阱H全为0.0终点G右下角为1.0距离终点越近数值越高以0.9 0.90.9逐步衰减。最优动作策略地图↓ → ↓ ← ↓ ○ ↓ ○ → ↓ ↓ ○ ○ → → ❤️2. 深度理解加权随机抽样np.random.choice在实机表演中执行动作的代码为actionnp.random.choice(np.arange(4),size1,ppi[index])[0]当只有一个第一名时该动作概率为1.0 1.01.0加权抽样等同于100 % 100\%100%必然触发决定性执行当存在多个并列第一时如起点[0.0, 0.5, 0.5, 0.0]向下和向右同样优秀若使用np.argmax()会死板地永远只选排在前面的“下”使用np.random.choice智能体会各以50 % 50\%50%的概率灵活选择两条最优路径四、框架进阶从 Jupyter 历史包袱到 Gymnasium 原生极简渲染很多学习者容易困惑为什么视频里用了SDL_VIDEODRIVERdummy还用matplotlib和IPython.display搞得极为繁琐原因Jupyter Notebook 在浏览器中运行无法弹出操作系统的原生 GUI 窗口所以被迫把画面转成图片数组并不断清屏重绘现代化本地写法Gymnasium 内置 Pygame 桌面渲染在本地开发只需设置render_modehuman在循环中显式调用env.render()即可实现极高帧率、零闪烁的原生游戏窗口渲染五、完整工程代码结构与运行指南1. 文件结构强化学习/ ├── common.py # Qsa计算、策略评估、策略提升公共算法库 ├── main.py # 主入口环境创建、模型训练、原生GUI实机表演 ├── frozenlake.gif # 实机运行录屏动画 └── README.md # 完整技术教程文档2. 快速运行# 激活虚拟环境.\.venv\Scripts\activate.bat# 启动训练与 100 遍动画表演python main.py终端将输出最优价值矩阵与箭头策略地图随后桌面将弹出 Pygame 窗口丝滑自动表演智能体 100 次通关全过程