无需环境模型的强化学习:蒙特卡洛与时序差分算法详解及21点游戏实践

📅 2026/7/4 6:14:32 👁️ 阅读次数 📝 编程学习
无需环境模型的强化学习:蒙特卡洛与时序差分算法详解及21点游戏实践

🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度

在实际学习强化学习的过程中,很多同学在理解了马尔可夫决策过程(MDP)和动态规划(DP)后,会遇到一个关键瓶颈:现实世界中的环境模型(即状态转移概率和奖励函数)往往是未知的。面对这种“模型未知”的挑战,如何评估策略和寻找最优策略?这正是蒙特卡洛方法和时序差分算法大显身手的地方。对于生物、化学、材料等交叉学科的研究者而言,理解这两种无需模型的经典方法,是将强化学习应用于实验设计、药物筛选、路径规划等实际问题的关键一步。

本文旨在为具备一定概率论和编程基础(如Python)的读者,特别是非计算机专业的理工科学生,提供一个清晰、可操作的入门指南。我们将从零开始,解释蒙特卡洛方法和时序差分算法的核心思想,并通过一个经典的“21点”纸牌游戏案例,手把手带你完成代码实现、策略评估和优化。你将不仅理解算法公式,更能掌握如何用代码实现它们,并学会分析结果、排查常见错误,最终将这套方法迁移到你自己的研究场景中。

1. 理解“模型未知”与两种核心思想

在强化学习中,“模型”特指对环境的完整描述,即已知状态转移概率P(s'|s, a)和奖励函数R(s, a, s')。动态规划(DP)方法正是在此基础上进行策略迭代和价值迭代。然而,在生物信息学分析、机器人控制、游戏对战等绝大多数现实场景中,我们无法预先知道这些概率分布。

1.1 从动态规划到无需模型的跨越

动态规划的核心是“自举”(bootstrapping),它利用已知的模型,通过贝尔曼方程迭代更新状态价值。但当模型未知时,我们失去了这个计算基础。此时,智能体必须通过与环境的真实交互来学习。这引出了两种根本不同的学习范式:

  1. 蒙特卡洛方法:核心思想是“用经验平均代替期望”。智能体通过运行完整的回合(从开始到终止),收集到一条真实的轨迹(状态、动作、奖励序列),然后用这个轨迹上获得的实际回报(Return)的平均值,来估计状态或状态-动作对的价值。它必须等到回合结束才能进行更新,是一种“离线”学习。
  2. 时序差分方法:核心思想是“用估计来更新估计”。它结合了蒙特卡洛的采样思想和动态规划的自举思想。智能体在每一步交互后,利用当前对下一个状态的价值的估计,来更新当前状态的价值。它不需要等到回合结束,可以“在线”学习。

为了更直观地理解它们的区别与联系,我们可以从更新目标、偏差/方差权衡和学习方式三个维度进行对比:

特性维度蒙特卡洛方法时序差分方法
更新目标实际回报 ( G_t )即时奖励 + 折扣后的下一状态估计值 ( R_{t+1} + \gamma V(S_{t+1}) )
偏差/方差无偏估计,但方差高(依赖单次完整轨迹)有偏估计,但方差低(利用了现有的价值估计)
学习方式必须等到回合结束(离线)每一步都可以更新(在线)
对环境的依赖需要回合有明确的终止状态可以处理连续无终止的任务
收敛速度通常较慢,但最终收敛通常更快,但可能收敛到次优点

1.2 关键概念:回报、价值函数与探索

在深入算法前,需要明确几个贯穿始终的概念:

  • 回报 (G_t):从时刻t开始,未来所有奖励的折扣总和,(G_t = R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + ...)。蒙特卡洛方法直接使用它。
  • 状态价值函数 (V_\pi(s)):在策略 (\pi) 下,从状态s出发能获得的期望回报。
  • 动作价值函数 (Q_\pi(s, a)):在策略 (\pi) 下,在状态s执行动作a后能获得的期望回报。在模型未知时,我们更常学习 (Q) 函数,因为它能直接指导动作选择。
  • 探索与利用:智能体需要在尝试新动作(探索)和选择当前认为最好的动作(利用)之间取得平衡。这是所有无需模型方法的核心挑战。

2. 环境准备与问题定义:21点游戏

为了将理论付诸实践,我们选择“21点”作为实验环境。它是一个经典的、有明确规则的序列决策问题,非常适合演示蒙特卡洛和时序差分方法。

2.1 环境搭建与规则理解

我们将使用 OpenAI Gym 库中的Blackjack-v1环境。首先,通过 pip 安装必要的库。

pip install gym numpy matplotlib

21点游戏规则简述:

  • 目标:使手中牌的点数之和尽可能接近21点,但不能超过(爆牌),同时要大于庄家点数。
  • 牌值:2-10为面值,J、Q、K为10点,A可计为1点或11点。
  • 状态:在Gym环境中,一个状态是一个三元组(玩家当前点数, 庄家明牌点数, 是否有可用A)
  • 动作:0 表示“停牌”,1 表示“要牌”。
  • 奖励:回合结束时,赢+1,平局0,输-1。

2.2 初始化环境与策略定义

我们先创建一个简单的固定策略作为基线:当玩家点数达到18或以上时停牌,否则要牌。

import gym import numpy as np from collections import defaultdict import matplotlib.pyplot as plt # 创建环境 env = gym.make('Blackjack-v1', sab=True) # sab=True 使用简化规则 def simple_policy(observation): """ 一个简单的固定策略。 观察值 observation: (玩家点数, 庄家明牌点数, 是否有可用Ace) """ player_score, dealer_score, usable_ace = observation # 如果玩家点数 >= 18,选择停牌(0),否则要牌(1) return 0 if player_score >= 18 else 1 # 测试策略与环境交互 print("测试一个回合:") observation, info = env.reset() done = False while not done: action = simple_policy(observation) # 根据策略选择动作 observation, reward, terminated, truncated, info = env.step(action) done = terminated or truncated print(f"状态: {observation}, 动作: {action}, 奖励: {reward}, 结束: {done}") env.close()

运行上述代码,你会看到智能体根据简单策略与环境进行一个完整回合的交互过程。这是所有后续算法的基础。

3. 蒙特卡洛方法:首次访问与每次访问

蒙特卡洛策略评估的目标是,在给定策略 (\pi) 下,通过大量回合的经验来估计状态价值 (V_\pi(s))。

3.1 算法核心:首次访问MC预测

首次访问蒙特卡洛预测算法流程如下:

  1. 输入:待评估的策略 (\pi)。
  2. 初始化:价值字典V,用于记录每个状态的平均回报;返回字典returns,用于记录每个状态遇到的所有回报列表。
  3. 循环大量回合: a. 根据策略 (\pi) 生成一个回合的轨迹:(S_0, A_0, R_1, S_1, A_1, R_2, ..., S_T)。 b. 初始化回报G = 0。 c. 从回合最后一步向前遍历 (t = T-1, T-2, ..., 0): -G = γ * G + R_{t+1}(折扣累积回报) - 如果状态S_t在本回合中是第一次出现(首次访问): - 将G加入到returns[S_t]的列表中。 - 更新V[S_t] = average(returns[S_t])

以下是该算法的Python实现:

def mc_prediction_first_visit(policy, env, num_episodes, gamma=1.0): """ 首次访问蒙特卡洛策略评估。 返回状态价值函数 V(字典形式)。 """ # 初始化:记录每个状态的所有回报 returns = defaultdict(list) # 初始化状态价值表 V = defaultdict(float) for i_episode in range(1, num_episodes + 1): # 生成一个回合 episode = [] state, _ = env.reset() done = False while not done: action = policy(state) next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated # 记录 (状态, 动作, 奖励) episode.append((state, action, reward)) state = next_state # 计算回报并更新价值函数 G = 0.0 # 从后向前遍历,方便计算累积回报 visited_states_in_episode = set() for t in range(len(episode) - 1, -1, -1): state, action, reward = episode[t] # 回报是向未来累积的 G = gamma * G + reward # 首次访问判断 if state not in visited_states_in_episode: visited_states_in_episode.add(state) returns[state].append(G) V[state] = np.mean(returns[state]) # 更新为平均值 # 可选:每10000回合打印进度 if i_episode % 10000 == 0: print(f"已完成 {i_episode} 个回合") return V # 使用简单策略进行评估 print("\n开始蒙特卡洛策略评估(首次访问)...") V_1m = mc_prediction_first_visit(simple_policy, env, num_episodes=100000) print(f"评估完成,共评估了 {len(V_1m)} 个不同的状态。") # 查看几个典型状态的价值 sample_states = [(21, 10, True), (15, 6, False), (10, 5, False)] for s in sample_states: print(f"状态 {s} 的价值 V(s) ≈ {V_1m.get(s, 0.0):.4f}")

3.2 结果可视化与分析

得到状态价值函数V后,我们可以将其可视化,以理解策略在不同情况下的表现。

def plot_value_function(V, title="状态价值函数"): """ 绘制21点游戏的状态价值函数(区分有无可用Ace)。 """ # 提取数据:我们需要两个矩阵,分别对应有无可用Ace player_range = range(12, 22) # 关注玩家点数12-21 dealer_range = range(1, 11) # 庄家明牌点数1-10 (Ace计为11) Z_no_ace = np.zeros((len(player_range), len(dealer_range))) Z_usable_ace = np.zeros((len(player_range), len(dealer_range))) for i, player in enumerate(player_range): for j, dealer in enumerate(dealer_range): Z_no_ace[i, j] = V.get((player, dealer, False), 0) Z_usable_ace[i, j] = V.get((player, dealer, True), 0) fig, axes = plt.subplots(1, 2, figsize=(14, 5), sharey=True) fig.suptitle(title, fontsize=16) # 无可用Ace im1 = axes[0].imshow(Z_no_ace, cmap='coolwarm', origin='lower', aspect='auto') axes[0].set_title('无可用 Ace') axes[0].set_xlabel('庄家明牌点数') axes[0].set_ylabel('玩家点数') axes[0].set_xticks(range(len(dealer_range)), dealer_range) axes[0].set_yticks(range(len(player_range)), player_range) plt.colorbar(im1, ax=axes[0]) # 有可用Ace im2 = axes[1].imshow(Z_usable_ace, cmap='coolwarm', origin='lower', aspect='auto') axes[2].set_title('有可用 Ace') axes[1].set_xlabel('庄家明牌点数') axes[1].set_xticks(range(len(dealer_range)), dealer_range) axes[1].set_yticks(range(len(player_range)), player_range) plt.colorbar(im2, ax=axes[1]) plt.tight_layout() plt.show() print("\n绘制简单策略下的状态价值函数...") plot_value_function(V_1m, title="首次访问MC评估的简单策略价值函数")

通过热图,你可以清晰地看到:当玩家点数较高(如20、21)时,价值普遍为正(接近1),因为胜率高;当玩家点数较低(如12)且庄家牌面大时,价值为负。有可用Ace时,价值普遍更高,因为Ace的灵活性降低了爆牌风险。

3.3 常见问题与排查

在实现蒙特卡洛方法时,初学者常遇到以下问题:

  1. 价值估计不收敛或波动大

    • 现象V(s)的值在不同运行间差异很大,或随着回合数增加变化剧烈。
    • 原因:回合数num_episodes不足。蒙特卡洛方法依赖大数定律,需要大量样本才能稳定。此外,探索不充分导致某些状态很少被访问。
    • 排查:增加回合数至50万或100万。检查returns[s]列表的长度,确认状态是否被足够访问。
    • 解决:确保有足够的探索。对于固定策略,如果某些状态永远无法到达,其价值将无法被评估。
  2. 折扣因子gamma设置错误

    • 现象:价值估计的绝对值异常大或小。
    • 原因gamma通常应小于等于1。如果gamma=1,且回合可能很长,回报G可能累积得很大。如果gamma>1,在无限回合任务中会导致发散。
    • 排查:检查G的计算公式G = gamma * G + reward。对于21点这种回合制游戏,gamma=1是合理的。
    • 解决:对于有终止状态的任务(如游戏),gamma可以设为1。对于连续任务,gamma应小于1(如0.99)。
  3. 首次访问 vs 每次访问

    • 现象:使用“每次访问”算法(即每次遇到状态都更新)时,价值估计可能略有不同。
    • 原因:在同一回合中,一个状态可能被多次访问。“首次访问”是无偏估计,“每次访问”也是无偏但通常方差更小、收敛更快。
    • 选择:大多数情况下两者都能工作。首次访问更易于理解和实现。如果你想尝试每次访问,只需移除visited_states_in_episode的判断即可。

4. 时序差分学习:TD(0) 与 SARSA

时序差分学习是蒙特卡洛思想和动态规划思想的结合。最基础的算法是 TD(0),也称为一步时序差分。

4.1 TD(0) 预测:用估计更新估计

TD(0) 用于预测给定策略下的状态价值 (V_\pi)。其更新公式为: [ V(S_t) \leftarrow V(S_t) + \alpha [R_{t+1} + \gamma V(S_{t+1}) - V(S_t)] ] 其中 (\alpha) 是学习率。括号内的部分 (R_{t+1} + \gamma V(S_{t+1}) - V(S_t)) 称为TD误差,它代表了当前估计与更优估计(目标值)之间的差异。

def td_prediction(policy, env, num_episodes, alpha=0.01, gamma=1.0): """ TD(0) 策略评估。 """ # 初始化状态价值表 V = defaultdict(float) for i_episode in range(1, num_episodes + 1): state, _ = env.reset() done = False while not done: action = policy(state) next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated # TD(0) 更新 td_target = reward + gamma * V[next_state] td_error = td_target - V[state] V[state] += alpha * td_error state = next_state if i_episode % 10000 == 0: print(f"TD(0) 预测已完成 {i_episode} 回合") return V print("\n开始TD(0)策略评估...") V_td = td_prediction(simple_policy, env, num_episodes=100000, alpha=0.05) print(f"TD(0)评估完成。") plot_value_function(V_td, title="TD(0)评估的简单策略价值函数")

你会发现,TD(0) 评估的结果与蒙特卡洛方法相似,但它是在线更新的,无需等待回合结束。

4.2 SARSA:在线策略TD控制

预测是为了评估策略的好坏,而控制是为了找到最优策略。SARSA 是一种在线策略的TD控制算法,它直接学习动作价值函数 (Q(s, a)),并基于此改进策略(通常是 (\epsilon)-贪婪策略)。

SARSA 得名于其更新所涉及的序列 ((S_t, A_t, R_{t+1}, S_{t+1}, A_{t+1}))。其更新公式为: [ Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha [R_{t+1} + \gamma Q(S_{t+1}, A_{t+1}) - Q(S_t, A_t)] ]

def epsilon_greedy_policy(Q, state, epsilon=0.1): """ 基于Q表的 epsilon-贪婪策略。 """ if np.random.random() < epsilon: # 探索:随机选择动作 return env.action_space.sample() else: # 利用:选择当前状态下的最优动作 # 如果Q值都未初始化(为0),则随机选一个 q_values = [Q.get((state, a), 0.0) for a in range(env.action_space.n)] max_q = max(q_values) # 如果有多个动作具有相同的最大Q值,从中随机选一个 best_actions = [a for a, q in enumerate(q_values) if q == max_q] return np.random.choice(best_actions) def sarsa(env, num_episodes, alpha=0.1, gamma=1.0, epsilon=0.1): """ SARSA 算法:在线策略TD控制,学习最优动作价值函数 Q*。 """ # 初始化 Q表。使用defaultdict,默认值为0.0 Q = defaultdict(float) for i_episode in range(1, num_episodes + 1): state, _ = env.reset() # 根据当前Q表选择初始动作 action = epsilon_greedy_policy(Q, state, epsilon) done = False while not done: # 执行动作,得到下一个状态和奖励 next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated # 在下一个状态,根据当前策略选择下一个动作 next_action = epsilon_greedy_policy(Q, next_state, epsilon) # SARSA 更新 current_q = Q[(state, action)] # 如果下一个状态是终止状态,则下一个Q值为0 next_q = Q[(next_state, next_action)] if not done else 0.0 td_target = reward + gamma * next_q td_error = td_target - current_q Q[(state, action)] = current_q + alpha * td_error state, action = next_state, next_action if i_episode % 50000 == 0: print(f"SARSA 已完成 {i_episode} 回合") # 从Q表推导出最优策略 policy = {} for state_action, q_value in Q.items(): state, action = state_action if state not in policy: policy[state] = {} policy[state][action] = q_value # 对于每个状态,选择Q值最大的动作作为最优策略 optimal_policy = {} for state, action_dict in policy.items(): optimal_action = max(action_dict, key=action_dict.get) optimal_policy[state] = optimal_action return Q, optimal_policy print("\n开始SARSA算法寻找最优策略...") Q_sarsa, optimal_policy_sarsa = sarsa(env, num_episodes=500000, alpha=0.01, epsilon=0.1) print("SARSA 学习完成。")

4.3 从Q表到可视化策略

学习到Q表后,我们可以从中提取最优策略,并将其可视化,与最初的简单策略进行对比。

def plot_policy(policy, title="最优策略"): """ 绘制21点游戏的最优策略图(0:停牌,1:要牌)。 """ player_range = range(12, 22) dealer_range = range(1, 11) Z_no_ace = np.zeros((len(player_range), len(dealer_range))) Z_usable_ace = np.zeros((len(player_range), len(dealer_range))) for i, player in enumerate(player_range): for j, dealer in enumerate(dealer_range): # 默认动作为要牌(1),如果策略中指定了停牌(0),则改为0 Z_no_ace[i, j] = policy.get((player, dealer, False), 1) # 1是要牌 Z_usable_ace[i, j] = policy.get((player, dealer, True), 1) fig, axes = plt.subplots(1, 2, figsize=(14, 5)) fig.suptitle(title, fontsize=16) # 无可用Ace的策略 im1 = axes[0].imshow(Z_no_ace, cmap='binary', vmin=0, vmax=1, origin='lower', aspect='auto') axes[0].set_title('无可用 Ace (0:停牌, 1:要牌)') axes[0].set_xlabel('庄家明牌点数') axes[0].set_ylabel('玩家点数') axes[0].set_xticks(range(len(dealer_range)), dealer_range) axes[0].set_yticks(range(len(player_range)), player_range) # 添加网格线以便看清每个单元格 axes[0].set_xticks(np.arange(-.5, len(dealer_range), 1), minor=True) axes[0].set_yticks(np.arange(-.5, len(player_range), 1), minor=True) axes[0].grid(which='minor', color='gray', linestyle='-', linewidth=0.5) # 有可用Ace的策略 im2 = axes[1].imshow(Z_usable_ace, cmap='binary', vmin=0, vmax=1, origin='lower', aspect='auto') axes[1].set_title('有可用 Ace (0:停牌, 1:要牌)') axes[1].set_xlabel('庄家明牌点数') axes[1].set_xticks(range(len(dealer_range)), dealer_range) axes[1].set_yticks(range(len(player_range)), player_range) axes[1].set_xticks(np.arange(-.5, len(dealer_range), 1), minor=True) axes[1].set_yticks(np.arange(-.5, len(player_range), 1), minor=True) axes[1].grid(which='minor', color='gray', linestyle='-', linewidth=0.5) plt.tight_layout() plt.show() print("\n绘制SARSA学习到的最优策略...") plot_policy(optimal_policy_sarsa, title="SARSA学习的最优策略")

在策略图中,黑色(0)代表“停牌”,白色(1)代表“要牌”。你会看到学习到的策略比我们最初设定的简单策略(点数>=18停牌)要精细得多。例如,当玩家点数较低时,无论庄家牌面如何,几乎总是要牌;而当点数较高(如20)时,几乎总是停牌。在中间区域(如玩家16点,庄家7点),策略会做出更复杂的决策。

5. 算法对比、调参与生产环境考量

5.1 蒙特卡洛 vs 时序差分:深入对比

通过实践,我们可以总结出两种方法更细致的差异:

对比项蒙特卡洛方法时序差分方法
更新时机回合结束后(离线)每一步后(在线)
偏差/方差无偏,高方差有偏(依赖当前估计),低方差
收敛性对函数近似可能不稳定通常更稳定,能保证收敛(在表格型情况下)
数据效率必须使用完整轨迹,数据利用率低可以复用经验,数据效率高
对初始值敏感度不敏感,最终收敛到真值敏感,不同的初始值可能导致不同收敛点
适用场景回合制任务,需要精确评估连续任务,需要快速在线学习

注意:TD方法因为有偏,在非表格型(如使用神经网络)情况下可能不收敛,而蒙特卡洛虽然方差大,但在函数近似中有时更稳定。

5.2 关键参数调优指南

在实际应用中,参数设置对学习效果至关重要。

  • 学习率 (\alpha)

    • 作用:控制每次更新的步长。
    • 设置:通常设置在0.01到0.1之间。太大会导致震荡不收敛,太小则学习过慢。
    • 技巧:可以使用衰减学习率,例如 (\alpha_t = \frac{1}{1 + t}) 或 (\alpha_t = \frac{0.1}{1 + 0.01*t})。
  • 探索率 (\epsilon)

    • 作用:在ε-贪婪策略中控制探索的概率。
    • 设置:初始可设为0.1或0.2。随着学习进行,可以逐渐衰减(如指数衰减),让智能体后期更多利用学到的知识。
    • 代码示例(衰减探索率)
      initial_epsilon = 0.2 decay_rate = 0.999995 min_epsilon = 0.01 for episode in range(num_episodes): epsilon = max(min_epsilon, initial_epsilon * (decay_rate ** episode)) # 在每一步使用当前的 epsilon
  • 折扣因子 (\gamma)

    • 作用:衡量未来奖励的重要性。γ=0表示只关心即时奖励,γ接近1表示非常重视长远回报。
    • 设置:对于有明确终止状态的任务(如游戏),可以设为1。对于持续任务,通常设为0.9、0.99或0.999。

5.3 从实验到生产:注意事项与扩展

在学术实验或小型项目中跑通算法只是第一步。若要应用于更严肃的生物信息学分析或机器人控制,还需考虑:

  1. 状态/动作空间爆炸:21点的状态空间很小(约200个)。实际问题中状态可能是高维连续空间(如图像、基因序列)。此时需要引入函数近似,如线性函数、神经网络(即深度强化学习)来逼近Q函数或策略。

  2. 采样效率与经验回放:与环境交互收集数据成本可能很高(如真实生物实验)。经验回放技术可以存储过去的经验((s, a, r, s')),并随机抽样用于训练,打破数据间的相关性,大幅提升数据利用效率。DQN算法就采用了这一技术。

  3. 探索策略的改进:ε-贪婪策略简单但低效。可以尝试上置信界汤普森采样或基于内在好奇心的探索方法,在复杂环境中更有效地探索。

  4. 算法选择:SARSA是在线策略算法,学习的是执行策略本身的价值。Q-Learning是离线策略算法,它学习的是最优动作价值函数 (Q^*),更新时使用max_a Q(s', a),通常比SARSA更激进,收敛更快,但也可能因为过度估计而带来风险。其更新公式为: [ Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha [R_{t+1} + \gamma \max_{a} Q(S_{t+1}, a) - Q(S_t, A_t)] ]

  5. 评估与监控:在生产中,不能只关注最终策略。需要持续监控:

    • 学习曲线:绘制每个回合的总奖励随时间的变化,观察是否收敛。
    • 策略稳定性:定期测试当前策略的性能,避免策略震荡。
    • Q值分布:检查Q值是否出现异常大的数值,这可能意味着学习率过高或奖励设置不合理。

6. 总结与下一步方向

通过本文,我们完成了从理论到代码的跨越,实现了蒙特卡洛策略评估、TD(0)预测以及SARSA控制算法,并在21点游戏中验证了其有效性。你应当已经理解:

  1. 蒙特卡洛方法通过平均完整回报来估计价值,无需模型,但需要回合终止且方差较高。
  2. 时序差分方法通过自举和采样相结合,实现了在线、增量式学习,是强化学习的核心思想。
  3. SARSA作为一种在线策略TD控制算法,通过ε-贪婪探索,能够学习到一个实用的最优策略。

作为生物等领域的研究者,你可以将这套框架迁移到你的问题中:将“状态”定义为你的实验条件或系统观测值,将“动作”定义为你的操作选择(如添加何种试剂、选择哪个参数),将“奖励”定义为实验结果的量化评价(如产物产量、细胞活性)。虽然真实问题会更复杂,但蒙特卡洛和时序差分提供了无需知道精确环境模型的强大学习起点。

下一步,你可以沿着以下方向深入:

  1. 实现Q-Learning:修改SARSA代码,将更新目标改为reward + gamma * max_a Q(s', a),对比两者在21点游戏中的表现差异。
  2. 引入函数近似:当状态空间变大时,用神经网络代替Q表。尝试用PyTorch或TensorFlow实现一个简单的DQN来解决CartPole-v1环境。
  3. 应用于你的领域问题:尝试用SARSA或Q-Learning解决一个简单的生物学模型问题,例如设计一个虚拟的“药物剂量优化”环境。
  4. 学习更高级算法:理解多步TD(如TD(λ))、策略梯度方法以及演员-评论家架构,这些是解决复杂连续控制问题的主流工具。

强化学习是一个在实践中不断试错和调参的领域。最好的学习方式就是选择一个明确的问题,亲手实现算法,观察其行为,分析失败原因,并持续迭代。从这个简单的21点游戏开始,你已经掌握了打开这个领域大门的钥匙。

🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度