1. 从零开始的强化学习:为什么它值得你投入时间
如果你对人工智能感兴趣,尤其是那些能下围棋、打游戏、甚至控制复杂物理系统的智能体,那么“强化学习”这个词你一定不陌生。它听起来很酷,但入门时常常让人望而生畏:马尔可夫决策过程、贝尔曼方程、策略梯度……一堆术语扑面而来。很多人可能翻了几页教材或看了几篇博客就放弃了,觉得这玩意儿离实际应用太远,或者理论过于艰深。我最初接触RL时也有同感,感觉像是在学一门全新的数学,而不是一个能动手实践的技术。
但我想告诉你的是,强化学习可能是目前最接近“通用人工智能”学习范式的一种方法。它的核心思想极其直观:一个智能体通过与环境互动,根据行动带来的奖励或惩罚来学习如何做出更好的决策。这和我们人类学习骑自行车、掌握一门新技能的过程何其相似——没人给你一本厚厚的操作手册,你通过尝试、摔倒、再尝试,最终找到了保持平衡的窍门。RL的魅力就在于,它将这种试错学习的过程数学化和自动化了。
所以,这篇“Lecture 1”的目的,不是带你快速浏览一遍教科书目录,而是帮你搭建一个坚实、直观的认知框架。我们会暂时抛开那些复杂的公式推导,先搞清楚几个最根本的问题:RL到底在解决什么问题?它和传统的监督学习、无监督学习有什么本质不同?一个典型的RL系统由哪些部分组成?理解了这些,你再去啃那些数学细节,就会有一种“原来如此”的通透感。无论你是学生、工程师,还是对AI有好奇心的爱好者,这篇文章都将为你打开一扇门,让你看到RL不仅是一门理论,更是一套强大且正在被广泛应用的工具。
2. 强化学习的核心范式:与监督学习的根本分野
要理解强化学习,最有效的方法就是把它和我们更熟悉的监督学习放在一起对比。很多人刚开始会混淆,觉得RL只是监督学习的一个变种,但事实上,它们的底层逻辑截然不同。
在监督学习中,我们面对的是一个静态的数据集。比如图像分类,我们有一大堆已经标注好“猫”、“狗”的图片。学习过程就是让模型去拟合这些已经存在的“标准答案”。数据是给定的,答案也是给定的,模型的任务是找到从输入到输出之间的映射关系。整个学习过程是“离线”的,模型不会因为它的预测而改变它接下来要学习的数据。
而强化学习处理的是一个动态的、序列决策的问题。想象一下训练一个机器人走路。我们无法给它一个包含所有可能状态和正确动作的“数据集”,因为环境是连续变化的,并且机器人的每一个动作都会改变它接下来所处的状态。这里没有现成的“标准答案”(即“在某个精确的时刻,腿关节应该转动多少度”)。我们只能给智能体提供一个模糊的“奖励”信号,比如“向前移动了就给正分,摔倒了就给负分”。智能体的目标,就是在与环境的持续交互中,通过尝试不同的动作序列,最大化它长期获得的总奖励。
这个根本性的差异,引出了RL的几个独特挑战:
- 试错与探索:智能体必须自己去探索哪些动作是好的。如果它一开始就找到一个能获得微小奖励的动作,它可能会陷入“局部最优”,而不敢尝试其他可能带来更大回报但暂时未知的动作。如何平衡“利用”已知的好动作和“探索”新的可能性,是RL的核心议题之一。
- 延迟奖励:奖励往往是延迟的。在下围棋时,只有最终赢了棋才能获得正奖励,而中间下的每一步棋,其好坏在当时是无法立即知晓的。智能体需要具备“远见”,能够为了长远的最终胜利而牺牲眼前的短期利益。
- 时间序列与信用分配:当智能体获得一个奖励(无论是好是坏)时,它需要弄清楚:这个结果主要是由我刚刚做的这个动作导致的,还是由之前一系列动作共同导致的?这个问题被称为“信用分配”。比如在玩《星际争霸》时,一场胜利是源于十分钟前的一次关键科技升级,还是最后一波犀利的操作?智能体需要学会将功劳或责任正确地归因到历史动作上。
为了应对这些挑战,RL建立了一套完整的数学框架来描述这个问题,其中最核心的就是马尔可夫决策过程。你可以把它理解为RL问题的“标准建模模板”。一个MDP通常包含五个关键元素:状态集合、动作集合、状态转移概率、奖励函数和折扣因子。它假设当前状态包含了做出最优决策所需的全部历史信息(即马尔可夫性),这大大简化了问题。虽然现实问题未必完全满足马尔可夫性,但MDP为我们提供了一个强大且通用的分析工具。理解MDP,是理解几乎所有现代RL算法的基础。
3. 智能体与环境的互动循环:解剖一个RL系统
现在,让我们把视角拉近,像一个系统架构师一样,拆解一个正在运行的RL智能体。这个互动过程是一个持续的循环,理解这个循环的每一个环节,就等于理解了RL系统是如何“呼吸”和“生长”的。
这个循环始于一个时间步t。此时,智能体从环境中接收到一个对当前世界的观测,我们称之为状态。状态需要尽可能准确地反映环境信息。例如,在自动驾驶场景中,状态可能包括车辆的速度、位置、周围其他车辆和行人的信息、交通信号灯状态等。
基于当前的状态,智能体内部的“大脑”——也就是它的策略——会决定要采取哪个动作。策略是一个函数,它把状态映射到动作上。它可以是确定性的(看到状态S,就一定输出动作A),也可以是随机性的(看到状态S,以某种概率分布输出不同的动作)。初期,这个策略可能是完全随机的,智能体只是在胡乱尝试。
智能体执行这个动作后,会作用到环境上。环境因此发生变化,进入一个新的状态t+1。同时,环境会给出一个奖励信号,告诉智能体刚才那个动作是好是坏。这个奖励是一个标量数值,是智能体唯一明确的优化目标。设计奖励函数是一门艺术,也是RL项目成功的关键。一个设计不当的奖励函数会导致智能体学到意想不到的、甚至有害的行为。比如,你让一个游戏AI“获得高分”,它可能会发现某种刷分的漏洞,而不是真正学会如何玩好游戏。
注意:奖励函数的设计原则是“你得到你所奖励的”。如果你奖励一个清理机器人移动的距离,它可能会在原地不停转圈;如果你奖励它收集的垃圾数量,它可能会把垃圾扔了又捡起来。一个好的奖励函数应该与最终目标高度一致,并且尽可能平滑、无歧义。
智能体接收到新的状态和奖励后,就完成了一次交互。它会将这次经历记录在案,通常存储为一个四元组(状态, 动作, 奖励, 新状态),也称为一个“转移”。这些转移被存储在经验回放缓冲区中。这个缓冲区是许多RL算法的关键组件,它有两大作用:一是打破数据之间的时间相关性,让学习更稳定;二是可以重复利用旧的经验,提高数据利用率。
接下来,就是学习的核心环节:策略更新。智能体会从经验回放缓冲区中采样一批过去的经历,用它来评估当前策略的好坏,并据此更新策略,使其在未来能获得更高的累积奖励。如何利用经验来更新策略,就衍生出了各种各样的RL算法。有的算法直接建模并优化策略本身(策略梯度方法),有的算法先评估状态或动作的价值,再根据价值来改进策略(价值迭代方法),还有的将两者结合(Actor-Critic方法)。
这个“观察-决策-行动-学习”的循环会一直持续,直到智能体的策略收敛(性能不再显著提升),或者达到了预设的训练步数。通过数百万甚至数十亿次这样的循环,智能体从一张白纸,逐渐进化成一个能在复杂环境中达成目标的专家。
4. 核心概念深度解析:价值、策略与模型
在RL的术语体系中,有三个概念如同三根支柱,支撑起了整个理论大厦:价值函数、策略和模型。深入理解它们,你就能看懂大多数RL算法论文在讨论什么。
4.1 价值函数:评估“位置”的好坏
价值函数回答的问题是:“从当前状态(或采取当前动作后)出发,我未来预期能获得多少总奖励?” 它是一个长远眼光下的评估指标。
- 状态价值函数 V(s):表示在状态
s下,遵循某个特定策略,所能获得的期望累积回报。它衡量的是某个状态的“潜在价值”。比如在象棋里,一个“车马炮俱全且阵型工整”的状态,其V值通常比一个“丢车保帅”的状态要高。 - 动作价值函数 Q(s, a):表示在状态
s下执行动作a,然后之后遵循某个特定策略,所能获得的期望累积回报。它衡量的是在某个状态下,执行某个特定动作的“好坏”。Q函数是许多经典算法(如Q-Learning、DQN)的核心。
它们之间的关系由著名的贝尔曼方程描述。贝尔曼方程本质上是一个递归等式:当前状态的价值,等于立即获得的奖励,加上下一个状态的折扣后价值。这个方程是RL中许多算法进行迭代更新的理论基础。因为它揭示了价值函数可以通过“自举”的方式,利用自身的估计来更新自己。
4.2 策略:智能体的行为准则
策略是智能体的决策函数,它定义了在何种状态下应该采取何种动作。我们可以把策略分为两大类:
- 确定性策略:
a = π(s)。给定一个状态,直接输出一个确定的动作。这种方式简单直接,但在需要探索的环境里可能不够灵活。 - 随机性策略:
π(a|s)。给定一个状态,输出的是一个动作的概率分布。比如,在状态s下,有70%的概率向左走,30%的概率向右走。随机性策略天然地包含了探索行为,在需要尝试不同动作的任务中非常有用。
策略可以是简单的查找表,也可以是复杂的深度神经网络。如今,深度强化学习的成功,很大程度上得益于使用深度神经网络来拟合非常复杂的策略函数和价值函数。
4.3 模型:智能体对世界的理解
模型是智能体对环境动力学的内部表示。它试图预测两件事:
- 状态转移:给定当前状态
s和动作a,下一个状态s'会是什么?即P(s'|s, a)。 - 奖励预测:给定当前状态
s和动作a,能获得的即时奖励r是多少?即R(s, a)。
根据智能体是否拥有或学习环境模型,RL算法可以分为两大类:
- 无模型方法:智能体不尝试理解环境如何运作,它直接通过试错来学习策略或价值函数。就像一个人学骑车,他不需要知道空气动力学和肌肉控制原理,只需要不断练习直到学会。DQN、Policy Gradient、A3C等都是无模型方法。它们更通用,但通常样本效率较低(需要大量交互数据)。
- 基于模型的方法:智能体会先学习一个环境模型,然后要么利用这个模型进行规划(像下棋时在脑海里推演未来几步),要么利用它来辅助策略学习。这就像学骑车前先研究了一下力学原理。基于模型的方法样本效率可能更高(因为可以在“脑海”中模拟,减少真实交互),但面临模型误差累积的风险——如果模型学得不准确,基于它的规划就会出错。
在实际应用中,无模型方法目前更为流行和成熟,但基于模型的方法是一个非常有前景的研究方向,特别是在真实物理世界交互成本高昂的场景下(如机器人控制)。
5. 强化学习算法的家族图谱
面对琳琅满目的RL算法,初学者很容易感到困惑。其实,我们可以根据两个关键维度对它们进行清晰的分类,这能帮助你快速定位不同算法的特点和适用场景。
维度一:策略优化 vs. 价值迭代
这个维度关注算法的更新目标是什么。
- 基于价值的:这类算法的核心是学习一个最优的价值函数(通常是Q函数)。一旦学到了准确的Q函数,最优策略就显而易见了:在每个状态选择那个能使Q值最大的动作。Q-Learning及其深度学习版本DQN是典型代表。它们的特点通常是更稳定,但处理连续动作空间或随机策略比较困难。
- 基于策略的:这类算法直接参数化策略,并通过梯度上升等方法,直接优化策略参数以最大化期望回报。REINFORCE算法是最经典的策略梯度方法。它们天然适用于连续动作空间和随机策略,但训练过程可能方差较大,不够稳定。
- 演员-评论家:这是前两者的混合体。它同时学习一个策略(演员)和一个价值函数(评论家)。演员负责产生动作,评论家负责评估演员在当前状态下的表现好坏,并指导演员的更新。A3C/A2C, TRPO, PPO等都属于这个家族。AC框架结合了价值和策略方法的优点,是目前解决复杂问题最主流的范式。
维度二:无模型 vs. 基于模型
这个维度我们上一节已经讨论过,关注算法是否学习环境模型。
- 无模型:绝大多数经典和流行的算法都属于此类,如DQN, Policy Gradient, PPO等。它们与环境的交互是“黑盒”的。
- 基于模型:如Dyna,MBPO等。它们先学习模型,然后利用模型。
将这两个维度组合起来,我们就可以把常见算法放到一个象限图里。例如,DQN是无模型+基于价值的,PPO是无模型+演员-评论家的。而像AlphaGo,其核心的蒙特卡洛树搜索过程,就是一个典型的基于模型+规划的方法(它使用了一个快速评估的策略和价值网络作为模型来进行推演)。
理解这个分类法,当你在面对一个新问题时,就可以先问自己:我的动作空间是离散的还是连续的?我是否需要随机策略?我与环境交互的成本高吗?回答这些问题,就能帮你初步筛选出合适的算法类型。
6. 实战第一步:如何设计你的第一个RL实验
理论说了这么多,不动手永远学不会。设计第一个RL实验,不需要一开始就挑战《星际争霸》或机器人行走。从简单的标准环境开始是关键。OpenAI Gym(及其后继者Gymnasium)是RL社区公认的“健身房”,它提供了大量从简单到复杂的环境,是入门实践的不二之选。
6.1 环境选择与问题定义
对于绝对新手,我强烈推荐从CartPole(车杆平衡)环境开始。它的目标非常直观:控制一个小车左右移动,使得它顶部的杆子保持直立不倒。状态包括小车位置、速度、杆子角度和角速度;动作是离散的(向左推或向右推);奖励规则很简单:每坚持一个时间步,就获得+1的奖励,杆子倒下或小车超出界限则回合结束。
选择CartPole的理由有三:第一,状态和动作空间都很小,你可以专注于理解算法逻辑,而不是处理复杂的输入输出。第二,问题足够简单,一个正确的算法能在几分钟到几十分钟内训练出成功的策略,你能快速获得正反馈。第三,它是检验你的代码和理解是否正确的“试金石”。如果你实现的算法连CartPole都解决不了,那肯定哪里出了问题。
6.2 算法实现:以DQN为例
Deep Q-Network是深度强化学习的里程碑,也是理解价值学习的最佳切入点。下面我们勾勒一下实现一个基础DQN来解决CartPole的关键步骤和代码逻辑。
首先,你需要一个神经网络来近似Q函数。输入是状态(4维向量),输出是对应两个动作的Q值(2维向量)。
import torch import torch.nn as nn class QNetwork(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.fc1 = nn.Linear(state_dim, 128) self.fc2 = nn.Linear(128, 128) self.fc3 = nn.Linear(128, action_dim) def forward(self, state): x = torch.relu(self.fc1(state)) x = torch.relu(self.fc2(x)) return self.fc3(x) # 输出每个动作的Q值接下来是DQN智能体的核心逻辑,主要包括经验回放和固定目标网络这两个关键技巧。
class DQNAgent: def __init__(self, state_dim, action_dim): self.action_dim = action_dim self.q_net = QNetwork(state_dim, action_dim) # 在线网络 self.target_net = QNetwork(state_dim, action_dim) # 目标网络 self.target_net.load_state_dict(self.q_net.state_dict()) # 初始同步 self.optimizer = torch.optim.Adam(self.q_net.parameters(), lr=1e-3) self.replay_buffer = [] # 简易经验回放池 self.batch_size = 64 self.gamma = 0.99 # 折扣因子 self.epsilon = 0.1 # 探索率 def select_action(self, state): # epsilon-greedy 策略 if random.random() < self.epsilon: return random.randint(0, self.action_dim-1) # 探索 else: with torch.no_grad(): state_tensor = torch.FloatTensor(state).unsqueeze(0) q_values = self.q_net(state_tensor) return q_values.argmax().item() # 利用 def store_transition(self, state, action, reward, next_state, done): self.replay_buffer.append((state, action, reward, next_state, done)) # 简单限制回放池大小 if len(self.replay_buffer) > 10000: self.replay_buffer.pop(0) def update(self): if len(self.replay_buffer) < self.batch_size: return # 随机采样一批经验 batch = random.sample(self.replay_buffer, self.batch_size) states, actions, rewards, next_states, dones = zip(*batch) # 转换为张量 states = torch.FloatTensor(states) actions = torch.LongTensor(actions).unsqueeze(1) # 形状 [batch, 1] rewards = torch.FloatTensor(rewards).unsqueeze(1) next_states = torch.FloatTensor(next_states) dones = torch.FloatTensor(dones).unsqueeze(1) # 计算当前Q值 (Q_net预测的,对应所执行动作的Q值) current_q_values = self.q_net(states).gather(1, actions) # shape: [batch, 1] # 计算目标Q值 (来自target_net) with torch.no_grad(): next_q_values = self.target_net(next_states).max(1)[0].unsqueeze(1) # 最大Q值 target_q_values = rewards + self.gamma * next_q_values * (1 - dones) # 计算损失 (MSE) loss = nn.MSELoss()(current_q_values, target_q_values) # 反向传播,更新在线网络 self.optimizer.zero_grad() loss.backward() self.optimizer.step() def update_target_network(self): # 定期将在线网络的参数复制给目标网络 self.target_net.load_state_dict(self.q_net.state_dict())在主训练循环中,你需要做的就是与环境交互,收集经验,并定期调用agent.update()。每隔一定步数(比如每100步),调用一次agent.update_target_network()。
6.3 核心技巧与避坑指南
即使在一个简单的环境里,直接实现“教科书版”的Q-Learning也常常失败。DQN的成功依赖于几个关键技巧,不理解它们,你的训练很可能无法收敛:
- 经验回放:这是打破数据相关性的利器。智能体连续经历的状态是高度相关的,直接用它们做训练会导致网络震荡甚至发散。经验回放将历史经验存储起来,每次随机抽样一批进行训练,相当于让数据变得“独立同分布”,大大提高了训练的稳定性。
- 固定目标网络:在计算目标Q值时,我们使用了另一个独立的网络(目标网络)。这个网络的参数每隔一段时间才从在线网络同步一次。如果使用同一个不断变化的在线网络来计算目标值,目标值就像是一个移动的靶子,会导致训练极其不稳定。固定目标网络相当于在一段时间内提供了一个稳定的学习目标。
- ε-贪婪策略:这是一个简单有效的探索策略。以概率ε随机选择动作(探索),以概率1-ε选择当前认为最好的动作(利用)。通常,ε会随着训练从较高的值(如1.0)衰减到一个较小的值(如0.01或0.1),让智能体从广泛探索逐渐过渡到精细利用。
实操心得:在
CartPole中,一个常见的失败现象是智能体似乎很快“学会”了保持平衡,但几十个回合后性能突然崩溃。这往往是过拟合或探索不足的迹象。智能体可能只是记住了一些特定的状态序列,而没有学到通用的策略。解决方法是确保经验回放池足够大,并且在整个训练过程中保持一个小的、固定的ε(如0.01)来进行持续探索。
7. 超越CartPole:经典挑战与进阶方向
当你成功用DQN解决了CartPole后,恭喜你,你已经跨过了RL实践的第一道门槛。但这只是开始。OpenAI Gym里有一系列难度递增的环境,可以帮你循序渐进地提升。
MountainCar:小车需要在一个山谷中左右摆动,积累动量才能冲上右侧山顶。这个环境的挑战在于,智能体在到达目标前几乎得不到任何正奖励(稀疏奖励),它必须学会“延迟满足”,为了最终的巨大奖励而进行一系列看似无用的前期动作。这非常考验算法的探索能力和长期规划能力。LunarLander:控制登月器平稳降落在两个旗帜之间的平地上。这个环境状态更复杂(位置、速度、角度等),动作是离散的(点火器开关),奖励函数结合了稀疏奖励(成功着陆+100)和稠密奖励(减少速度、靠近目标点等)。这是一个从离散控制过渡到更复杂任务的好台阶。Atari游戏:如Breakout,Pong,SpaceInvaders等。这是DQN当年一战成名的地方。这些环境的状态是图像像素(210x160x3),动作是游戏手柄按键。处理这类问题需要引入卷积神经网络来从图像中提取特征,并且会面临部分可观测、奖励延迟等更真实的挑战。
在尝试这些更复杂的环境时,你会遇到新的挑战,也需要掌握新的工具:
- 输入处理:对于图像输入,你需要使用CNN。一个常见的做法是将连续4帧图像堆叠在一起作为状态输入,以提供时间维度上的运动信息。
- 算法升级:基础的DQN可能不够用了。你需要了解它的改进版,如Double DQN(解决Q值过估计问题)、Dueling DQN(将Q值分解为状态价值和动作优势,学习更高效)、Prioritized Experience Replay(更重要的经验被采样概率更高)等。通常,我们会直接使用集成了这些改进的Rainbow DQN。
- 连续控制:当动作空间是连续的(如机器人的关节扭矩),基于价值的方法就不太方便了。这时你需要转向策略梯度方法,如PPO或SAC。这些算法能直接输出连续动作空间中的概率分布(如高斯分布),然后从中采样动作。
- 仿真到现实:这是机器人领域的核心挑战。在仿真器中训练的策略,直接部署到真实机器人上往往会失败,因为仿真模型和真实物理世界存在“现实差距”。这催生了领域随机化、系统辨识、仿真到现实的迁移学习等一系列研究方向。
从CartPole到Atari,再到真实的机器人抓取,每一步的跨越都意味着对算法、工程和问题理解深度的更高要求。但万变不离其宗,你在这个“第一课”中建立起来的关于智能体、环境、奖励、价值、策略的认知框架,将始终是你理解和运用新知识、解决新问题的基石。RL的学习之路是一场马拉松,找准节奏,从解决一个个具体的小问题开始,积累代码和直觉,你会发现自己能驾驭的领域越来越广阔。