强化学习2.2 MDP实践——Frozen lake

问题介绍

FrozenLake 指 OpenAI Gym 库中的一个经典强化学习环境。

  • 游戏场景:玩家控制角色在一个由冰冻湖面组成的网格世界中移动,目标是从起点到达终点,同时避免掉入冰面上的洞穴。网格通常为正方形,如 4x4 或 8x8 的地图,每个方格可以是起点、终点、冰冻区域或洞穴。
  • 动作空间:动作的形状是(1,)的 int64 变量,在范围 {0,3} 内,表示玩家应该向哪个方向移动。其中 0 为向左移动,1 为向下移动,2 为向右移动,3 为向上移动。
  • 观察空间:观察值是一个 int 值,代表玩家的当前位置,计算方式为 current_row * nrows + current_col(其中行和列都从 0 开始)。例如,在 4x4 地图中的目标位置可以这样计算:3 * 4 + 3 = 15。
  • 初始状态:游戏开始时,玩家处于状态 [0],即地图的左上角位置 [0,0]。
  • 奖励机制:达到目标获得奖励 + 1,掉入洞中或到达冰冻区域奖励为 0。
  • 游戏结束条件:玩家掉入洞中或到达目标位置时,游戏终止;若使用时间限制包装器,对于 4x4 环境,情节长度为 100 步,对于 8x8 环境,情节长度为 200 步,达到步数限制时游戏结束。

使用MDP解决问题

初始化环境

python 复制代码
from mdp import FrozenLakeEnv
mdp = FrozenLakeEnv(slip_chance=0)

mdp.render()

如下图所示,F表示正常的道路,H表示洞,G表示终点。

定义价值迭代函数

python 复制代码
def value_iteration(mdp, state_values=None, gamma=0.9, num_iter=1000, min_difference=1e-5):
    """ performs num_iter value iteration steps starting from state_values. Same as before but in a function """
    state_values = state_values or {s: 0 for s in mdp.get_all_states()}
    for i in range(num_iter):

        # 计算新的状态价值:对每个状态,取所有动作的最大期望回报
        new_state_values = {}
        for s in mdp.get_all_states():
            # 终端状态(无可用动作)的价值保持为0
            if mdp.is_terminal(s):
                new_state_values[s] = 0.0
                continue
            # 遍历所有可能的动作,计算每个动作的期望回报
            action_values = []
            for a in mdp.get_possible_actions(s):
                q_val = 0.0
                # 遍历该动作下的所有转移概率和后续状态
                for prob, next_s, reward in mdp.get_transitions(s, a):
                    q_val += prob * (reward + gamma * state_values[next_s])
                action_values.append(q_val)
            # 取最大的动作价值作为当前状态的新价值
            new_state_values[s] = max(action_values)

        assert isinstance(new_state_values, dict)

        # 计算与上一轮价值的最大差异
        diff = max(abs(new_state_values[s] - state_values[s])
                   for s in mdp.get_all_states())

        print("iter %4i   |   diff: %6.5f   |   V(start): %.3f " %
              (i, diff, new_state_values[mdp._initial_state]))

        state_values = new_state_values
        if diff < min_difference:
            break

    return state_values

进行价值迭代

python 复制代码
state_values = value_iteration(mdp)

下面展示运行过程

根据当前状态和动作计算累计奖励

python 复制代码
def get_action_value(mdp, state_values, state, action, gamma):
    """ Computes Q(s,a) as in formula above """
    q_value = 0.0
    # 获取从state执行action后所有可能的下一状态
    next_states = mdp.get_next_states(state, action)
    
    for next_state in next_states:
        # 获取转移概率 P(s'|s,a)
        prob = mdp.get_transition_prob(state, action, next_state)
        # 获取即时奖励 r(s,a,s')
        reward = mdp.get_reward(state, action, next_state)
        # 累加计算 Q(s,a) = sum[ P(s'|s,a) * (r + gamma*V(s')) ]
        q_value += prob * (reward + gamma * state_values[next_state])
    
    return q_value

根据当前状态选择最优动作

python 复制代码
def get_optimal_action(mdp, state_values, state, gamma=0.9):
    """ Finds optimal action using formula above. """
    if mdp.is_terminal(state):
        return None

    # 获取当前状态下所有可能的动作
    possible_actions = mdp.get_possible_actions(state)
    
    # 计算每个动作的Q值,存储为 {动作: Q值}
    action_q = {}
    for action in possible_actions:
        q_value = get_action_value(mdp, state_values, state, action, gamma)
        action_q[action] = q_value
    
    # 选择Q值最大的动作(argmax_a Q(s,a))
    optimal_action = max(action_q, key=action_q.get)
    
    return optimal_action

下面展示通过价值迭代函数所得到的状态价值,进行游戏的过程

python 复制代码
s = mdp.reset()
mdp.render()
for t in range(100):
    a = get_optimal_action(mdp, state_values, s, gamma=0.9)
    print(a, end='\n\n')
    s, r, done, _ = mdp.step(a)
    mdp.render()
    if done:
        break

角色依据状态价值进行运动的过程如下所示

下面进行可视化,定义函数

python 复制代码
import matplotlib.pyplot as plt
%matplotlib inline


def draw_policy(mdp, state_values):
    plt.figure(figsize=(3, 3))
    h, w = mdp.desc.shape
    states = sorted(mdp.get_all_states())
    V = np.array([state_values[s] for s in states])
    Pi = {s: get_optimal_action(mdp, state_values, s, gamma) for s in states}
    plt.imshow(V.reshape(w, h), cmap='gray', interpolation='none', clim=(0, 1))
    ax = plt.gca()
    ax.set_xticks(np.arange(h)-.5)
    ax.set_yticks(np.arange(w)-.5)
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    Y, X = np.mgrid[0:4, 0:4]
    a2uv = {'left': (-1, 0), 'down': (0, -1), 'right': (1, 0), 'up': (0, 1)}
    for y in range(h):
        for x in range(w):
            plt.text(x, y, str(mdp.desc[y, x].item()),
                     color='g', size=12,  verticalalignment='center',
                     horizontalalignment='center', fontweight='bold')
            a = Pi[y, x]
            if a is None:
                continue
            u, v = a2uv[a]
            plt.arrow(x, y, u*.3, -v*.3, color='m',
                      head_width=0.1, head_length=0.1)
    plt.grid(color='b', lw=2, ls='-')
    plt.show()

重新训练价值迭代函数并进行可视化

python 复制代码
state_values = {s: 0 for s in mdp.get_all_states()}

for i in range(10):
    print("after iteration %i" % i)
    state_values = value_iteration(mdp, state_values, num_iter=1)
    draw_policy(mdp, state_values)
# please ignore iter 0 at each step
定义8*8的问题,并进行可视化
python 复制代码
from IPython.display import clear_output
from time import sleep
mdp = FrozenLakeEnv(map_name='8x8', slip_chance=0.1)
state_values = {s: 0 for s in mdp.get_all_states()}

for i in range(30):
    clear_output(True)
    print("after iteration %i" % i)
    state_values = value_iteration(mdp, state_values, num_iter=1)
    draw_policy(mdp, state_values)
    sleep(0.5)
# please ignore iter 0 at each step

整体迭代过程如下所示

大规模测试

连续跑1000次进行测试

python 复制代码
mdp = FrozenLakeEnv(slip_chance=0)
state_values = value_iteration(mdp)

total_rewards = []
for game_i in range(1000):
    s = mdp.reset()
    rewards = []
    for t in range(100):
        s, r, done, _ = mdp.step(
            get_optimal_action(mdp, state_values, s, gamma=0.9))
        rewards.append(r)
        if done:
            break
    total_rewards.append(np.sum(rewards))

print("average reward: ", np.mean(total_rewards))
assert(1.0 <= np.mean(total_rewards) <= 1.0)
print("Well done!")

迭代过程如下

调整slip_chance重新进行测试,打滑概率为10%

python 复制代码
# Measure agent's average reward
mdp = FrozenLakeEnv(slip_chance=0.1)
state_values = value_iteration(mdp)

total_rewards = []
for game_i in range(1000):
    s = mdp.reset()
    rewards = []
    for t in range(100):
        s, r, done, _ = mdp.step(
            get_optimal_action(mdp, state_values, s, gamma=0.9))
        rewards.append(r)
        if done:
            break
    total_rewards.append(np.sum(rewards))

print("average reward: ", np.mean(total_rewards))
assert(0.8 <= np.mean(total_rewards) <= 0.95)
print("Well done!")

迭代过程如下

继续调整slip_chance重新进行测试,打滑概率为25%

python 复制代码
# Measure agent's average reward
mdp = FrozenLakeEnv(slip_chance=0.25)
state_values = value_iteration(mdp)

total_rewards = []
for game_i in range(1000):
    s = mdp.reset()
    rewards = []
    for t in range(100):
        s, r, done, _ = mdp.step(
            get_optimal_action(mdp, state_values, s, gamma=0.9))
        rewards.append(r)
        if done:
            break
    total_rewards.append(np.sum(rewards))

print("average reward: ", np.mean(total_rewards))
assert(0.6 <= np.mean(total_rewards) <= 0.7)
print("Well done!")

迭代过程如下

改为8*8的问题规模,定义打滑概率为20%

python 复制代码
# Measure agent's average reward
mdp = FrozenLakeEnv(slip_chance=0.2, map_name='8x8')
state_values = value_iteration(mdp)

total_rewards = []
for game_i in range(1000):
    s = mdp.reset()
    rewards = []
    for t in range(100):
        s, r, done, _ = mdp.step(
            get_optimal_action(mdp, state_values, s, gamma=0.9))
        rewards.append(r)
        if done:
            break
    total_rewards.append(np.sum(rewards))

print("average reward: ", np.mean(total_rewards))
assert(0.6 <= np.mean(total_rewards) <= 0.8)
print("Well done!")

迭代过程如下

相关推荐
我的世界伊若15 小时前
AI重塑IT职场:挑战与机遇并存
人工智能
lapiii35815 小时前
[智能体设计模式] 第4章:反思(Reflection)
人工智能·python·设计模式
IT_Beijing_BIT17 小时前
tensorflow 图像分类 之四
人工智能·分类·tensorflow
卡奥斯开源社区官方18 小时前
NVIDIA Blackwell架构深度解析:2080亿晶体管如何重构AI算力规则?
人工智能·重构·架构
百锦再19 小时前
第11章 泛型、trait与生命周期
android·网络·人工智能·python·golang·rust·go
数新网络21 小时前
The Life of a Read/Write Query for Apache Iceberg Tables
人工智能·apache·知识图谱
Yangy_Jiaojiao1 天前
开源视觉-语言-动作(VLA)机器人项目全景图(截至 2025 年)
人工智能·机器人
gorgeous(๑>؂<๑)1 天前
【ICLR26匿名投稿】OneTrackerV2:统一多模态目标跟踪的“通才”模型
人工智能·机器学习·计算机视觉·目标跟踪
坠星不坠1 天前
pycharm如何导入ai大语言模型的api-key
人工智能·语言模型·自然语言处理
周杰伦_Jay1 天前
【智能体(Agent)技术深度解析】从架构到实现细节,核心是实现“感知环境→处理信息→决策行动→影响环境”的闭环
人工智能·机器学习·微服务·架构·golang·数据挖掘