7.4 KiB
7.4 KiB
In [4]:
import numpy as np
# 1. 设定折扣因子
gamma = 0.9
# 2. 定义书本图 2.5 中的期望奖励向量 r_pi (Reward vector)
# s1 的期望奖励是 0.5*0 + 0.5*(-1) = -0.5
r_pi = np.array([-0.5, 1.0, 1.0, 1.0])
# 3. 定义状态转移矩阵 P_pi (Transition matrix)
# 每一行代表 s1, s2, s3, s4; 每一列代表转移到 s1, s2, s3, s4 的概率
P_pi = np.array([
[0.0, 0.5, 0.5, 0.0], # s1 有 0.5 概率到 s2, 0.5 概率到 s3
[0.0, 0.0, 0.0, 1.0], # s2 有 1.0 概率到 s4
[0.0, 0.0, 0.0, 1.0], # s3 有 1.0 概率到 s4
[0.0, 0.0, 0.0, 1.0] # s4 有 1.0 概率停留在 s4
])
# 4. 迭代法求解状态价值 (Iterative solution)
v_values = np.zeros(4) # 初始时,假设所有状态价值为 0
iterations = 100
print("--- 贝尔曼方程迭代求解过程 ---")
for k in range(iterations):
# 核心公式:v_{k+1} = r_pi + gamma * P_pi * v_k
v_next = r_pi + gamma * P_pi.dot(v_values)
if k < 5 or k == iterations - 1:
print(f"第 {k+1} 次迭代: v(s1)={v_next[0]:.4f}, v(s2)={v_next[1]:.4f}, v(s3)={v_next[2]:.4f}, v(s4)={v_next[3]:.4f}")
v_values = v_next
print("\n最终收敛的状态价值:", np.round(v_values, 2))
# 你可以将这个输出结果与书中 2.5 节手算的 8.5, 10, 10, 10 进行对比!--- 贝尔曼方程迭代求解过程 --- 第 1 次迭代: v(s1)=-0.5000, v(s2)=1.0000, v(s3)=1.0000, v(s4)=1.0000 第 2 次迭代: v(s1)=0.4000, v(s2)=1.9000, v(s3)=1.9000, v(s4)=1.9000 第 3 次迭代: v(s1)=1.2100, v(s2)=2.7100, v(s3)=2.7100, v(s4)=2.7100 第 4 次迭代: v(s1)=1.9390, v(s2)=3.4390, v(s3)=3.4390, v(s4)=3.4390 第 5 次迭代: v(s1)=2.5951, v(s2)=4.0951, v(s3)=4.0951, v(s4)=4.0951 第 100 次迭代: v(s1)=8.4997, v(s2)=9.9997, v(s3)=9.9997, v(s4)=9.9997 最终收敛的状态价值: [ 8.5 10. 10. 10. ]
In [5]:
import numpy as np
# 1. 继承之前的已知数据
gamma = 0.9
# 假设这是我们在 Cell 8 算出来的最终状态价值
v_values = {"s1": 8.5, "s2": 10.0, "s3": 10.0, "s4": 10.0}
# 2. 定义动作价值计算函数 q(s, a)
def calculate_q_value(state, action, reward, next_state, gamma, v_values):
# q(s,a) = 立即奖励 + gamma * 下一个状态的价值
return reward + gamma * v_values[next_state]
print("--- 计算在状态 s1 下,所有可能动作的价值 (Q-values) ---")
# --- 策略会选择的动作 (向右 a2, 向下 a3) ---
# a2: 向右进入 s2,得奖励 -1
q_s1_a2 = calculate_q_value("s1", "a2", -1, "s2", gamma, v_values)
print(f"策略内的动作: q(s1, a2向右) = -1 + 0.9 * v(s2) = {q_s1_a2}") # 预期为 8.0
# a3: 向下进入 s3,得奖励 0
q_s1_a3 = calculate_q_value("s1", "a3", 0, "s3", gamma, v_values)
print(f"策略内的动作: q(s1, a3向下) = 0 + 0.9 * v(s3) = {q_s1_a3}") # 预期为 9.0
# --- 策略不会选择的动作 (向上 a1, 向左 a4, 原地 a5) ---
# a1: 向上撞墙,弹回 s1,得奖励 -1
q_s1_a1 = calculate_q_value("s1", "a1", -1, "s1", gamma, v_values)
print(f"\n策略外的动作: q(s1, a1向上) = -1 + 0.9 * v(s1) = {q_s1_a1:.2f}") # 预期为 6.65
# a4: 向左撞墙,弹回 s1,得奖励 -1
q_s1_a4 = calculate_q_value("s1", "a4", -1, "s1", gamma, v_values)
print(f"策略外的动作: q(s1, a4向左) = -1 + 0.9 * v(s1) = {q_s1_a4:.2f}") # 预期为 6.65
# a5: 原地不动,停在 s1,得奖励 0
q_s1_a5 = calculate_q_value("s1", "a5", 0, "s1", gamma, v_values)
print(f"策略外的动作: q(s1, a5原地) = 0 + 0.9 * v(s1) = {q_s1_a5:.2f}") # 预期为 7.65
print("\n--- 验证公式:状态价值是动作价值的加权平均 ---")
# 我们的策略是: 0.5概率选a2, 0.5概率选a3
v_s1_calculated = 0.5 * q_s1_a2 + 0.5 * q_s1_a3
print(f"根据 Q 值反推的 v(s1) = 0.5 * {q_s1_a2} + 0.5 * {q_s1_a3} = {v_s1_calculated}")
print(f"这与我们之前迭代出来的 v(s1) = {v_values['s1']} 完全一致!")--- 计算在状态 s1 下,所有可能动作的价值 (Q-values) --- 策略内的动作: q(s1, a2向右) = -1 + 0.9 * v(s2) = 8.0 策略内的动作: q(s1, a3向下) = 0 + 0.9 * v(s3) = 9.0 策略外的动作: q(s1, a1向上) = -1 + 0.9 * v(s1) = 6.65 策略外的动作: q(s1, a4向左) = -1 + 0.9 * v(s1) = 6.65 策略外的动作: q(s1, a5原地) = 0 + 0.9 * v(s1) = 7.65 --- 验证公式:状态价值是动作价值的加权平均 --- 根据 Q 值反推的 v(s1) = 0.5 * 8.0 + 0.5 * 9.0 = 8.5 这与我们之前迭代出来的 v(s1) = 8.5 完全一致!