强化学习基础
本文目录 展开章节导航
1. 基本概念
- Agent(智能体):在环境中执行动作并学习如何最大化累积奖励的实体。
- Environment(环境):智能体与之交互的外部系统,定义了状态空间、动作空间和奖励机制。
- Observation(观察):智能体从环境中获取的当前状态信息。
- Action(动作):智能体在某个状态下可以执行的操作,影响环境的状态。
- Reward(奖励):智能体执行某个动作后环境反馈的即时信号,用于指导智能体的学习。

强化学习就是智能体和环境之间持续交互,通过与环境交互并观察环境的状态,学习如何采取进一步的行动,以最大化累积奖励,在不断试错的过程中学习如何在不同状态下做出最佳决策的过程。
2. 马尔可夫过程
2.1 马尔可夫性质
2.1.1 本质
- 一个随机过程在给定现在状态及所有过去状态情况下,其未来状态的条件概率分布仅依赖于当前状态,与历史状态无关。
2.1.2 数学定义
- 假设随机变量 构成一个随机过程。这些随机变量的所有可能取值的集合被称为状态空间。如果
则称其满足马尔可夫性质。
其中:
- 表示变量集合
- 表示变量集合
马尔可夫性质也可以描述为:给定当前状态时,将来的状态与过去状态条件独立。如果某过程满足马尔可夫性质,未来的转移与过去无关,只取决于现在。
2.1.3 直观理解
- 一个失忆的人:
- 只记得:我现在在哪里
- 不记得:我是怎么到这里来的
- 决策下一步行动时,只基于当前位置
假设机器人有三种状态:静止(S)、移动(M)、充电(C)
传统模型(有记忆性):
# 下一状态可能依赖于整个历史: # P(下一状态 | 历史 = [C, M, M, S, M]) = ?马尔科夫模型(无记忆性):
# 下一状态只依赖于当前状态: # P(下一状态 | 当前状态 = M) = ?
2.2 马尔科夫链
2.2.1 一阶马尔科夫链 (简单 强大)
基本思路
from enum import Enum
from dataclasses import dataclass
import numpy as np
class RobotState(Enum):
"""机器人状态空间"""
IDLE = "空闲" # 静止等待
MOVING = "移动" # 正在移动
CHARGING = "充电" # 正在充电
@dataclass
class MarkovChain:
"""一阶马尔科夫链实现"""
# 状态转移矩阵
# P[next_state | current_state]
transition_matrix = {
RobotState.IDLE: {
RobotState.IDLE: 0.5, # 保持空闲
RobotState.MOVING: 0.4, # 开始移动
RobotState.CHARGING: 0.1 # 开始充电
},
RobotState.MOVING: {
RobotState.IDLE: 0.3, # 停止移动
RobotState.MOVING: 0.5, # 继续移动
RobotState.CHARGING: 0.2 # 开始充电
},
RobotState.CHARGING: {
RobotState.IDLE: 0.8, # 充满电,空闲
RobotState.MOVING: 0.1, # 充满电,开始移动
RobotState.CHARGING: 0.1 # 继续充电
}
}
def next_state(self, current: RobotState) -> RobotState:
"""基于当前状态生成下一个状态"""
import random
# 获取当前状态的所有可能转移
transitions = self.transition_matrix[current]
# 随机选择(按概率权重)
states = list(transitions.keys())
weights = list(transitions.values())
return random.choices(states, weights=weights)[0]优点
- 计算简单
- 只需维护当前状态的转移概率,不存储历史状态
- 数据需求少
- 估计的参数数量少
- 完整理论体系支持
状态转移矩阵
主要是将转移考虑表示为矩阵的形式,便于运算
import numpy as np
# 状态顺序:[空闲, 移动, 充电]
# 行:当前状态,列:下一状态
P = np.array([
[0.5, 0.4, 0.1], # 空闲 → [空闲, 移动, 充电]
[0.3, 0.5, 0.2], # 移动 → [空闲, 移动, 充电]
[0.8, 0.1, 0.1] # 充电 → [空闲, 移动, 充电]
])
# 关键性质:每行和为1(概率归一化)
print("行和验证:", np.sum(P, axis=1)) # [1., 1., 1.]2. 高阶马尔科夫链 (捕捉时间依赖)
1. 一阶假设有时候过于简化,预测可能不准
# 一阶模型:P(下雨|今天=晴) = 0.2
# 问题:连续10天晴天后,下雨概率还是0.2吗?
# 三阶模型更准确:
weather_probs = {
# (前前天, 前天, 今天) → 明天天气概率
('晴', '晴', '晴'): {'雨': 0.6, '晴': 0.4}, # 长期晴天后更可能下雨
('雨', '晴', '晴'): {'雨': 0.3, '晴': 0.7},
('晴', '雨', '晴'): {'雨': 0.4, '晴': 0.6},
}通用高阶实现
class HigherOrderMarkovChain:
"""τ阶马尔科夫链通用实现"""
def __init__(self, order: int):
self.order = order # 记忆长度
self.memory = [] # 存储最近order个状态
# 转移概率表:P(X_t | X_{t-τ}, ..., X_{t-1})
self.transitions = {}
def add_transition(self, history: tuple, next_state: str, prob: float):
"""添加转移概率"""
if history not in self.transitions:
self.transitions[history] = {}
self.transitions[history][next_state] = prob
def predict(self) -> str:
"""基于历史预测下一个状态"""
if len(self.memory) < self.order:
return None
# 获取最近的order个状态作为历史
recent_history = tuple(self.memory[-self.order:])
if recent_history in self.transitions:
# 根据概率随机选择
probs = self.transitions[recent_history]
import random
return random.choices(list(probs.keys()),
weights=probs.values())[0]
return None高阶到一阶的转换技巧
- 复合状态方法 高阶马尔科夫链可以通过状态扩展转换为一阶链:
def convert_to_first_order(states: list, high_order_probs: dict, order: int):
"""
将高阶马尔科夫链转换为等价的一阶链
思想:将长度为order的历史序列视为一个"复合状态"
例如:二阶链的(S,M)视为一个新状态
"""
# 1. 创建所有可能的复合状态
composite_states = []
from itertools import product
# 生成所有长度为order的状态序列
for combo in product(states, repeat=order):
composite_states.append(combo)
# 2. 构建一阶转移矩阵
n_composite = len(composite_states)
P_first_order = np.zeros((n_composite, n_composite))
# 3. 填充转移概率
composite_index = {cs: i for i, cs in enumerate(composite_states)}
for history_tuple, next_probs in high_order_probs.items():
i = composite_index[history_tuple]
for next_state, prob in next_probs.items():
# 新历史:(移出最旧状态,加入新状态)
# 例如:(S,M) + M → (M,M)
new_history = history_tuple[1:] + (next_state,)
j = composite_index[new_history]
P_first_order[i, j] = prob
return composite_states, P_first_order
# 示例:二阶链转换
states = ['S', 'M', 'C']
second_order_probs = {
('S', 'S'): {'S': 0.6, 'M': 0.3, 'C': 0.1},
('S', 'M'): {'S': 0.2, 'M': 0.6, 'C': 0.2},
('M', 'S'): {'S': 0.4, 'M': 0.5, 'C': 0.1},
# ... 其他组合
}
composite_states, P_1st = convert_to_first_order(states, second_order_probs, order=2)
print(f"原始状态数: {len(states)}")
print(f"复合状态数: {len(composite_states)}") # 3² = 9例子:
import matplotlib.pyplot as plt
import networkx as nx
from matplotlib.patches import FancyBboxPatch
def visualize_markov_chains():
"""改进的可视化:二阶马尔科夫链转换为一阶链"""
# 创建图形
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
fig.suptitle('Transforming Second-Order Markov Chain to First-Order',
fontsize=16, fontweight='bold', y=0.95)
# ========== 左图:二阶马尔科夫链 ==========
ax1 = axes[0]
ax1.set_title('Second-Order Markov Chain\n(Needs memory of past 2 days)',
fontsize=14, fontweight='bold', pad=20)
ax1.set_xlim(-1, 11)
ax1.set_ylim(-1, 11)
ax1.axis('off')
# 时间线标签
time_labels = ['Day -2', 'Day -1', 'Today']
times = [2, 5, 8]
# 绘制时间线
ax1.plot([1.5, 8.5], [8, 8], 'k-', linewidth=2, alpha=0.7)
for i, (time, label) in enumerate(zip(times, time_labels)):
ax1.text(time, 8.3, label, ha='center', fontsize=11,
fontweight='bold', color='darkblue')
# 时间点标记
ax1.plot(time, 8, 'ko', markersize=10)
# 天气示例:Sunny, Sunny, Cloudy
weather_sequence = ['Sunny', 'Sunny', 'Cloudy']
weather_icons = {'Sunny': '☀️', 'Cloudy': '☁️', 'Rainy': '🌧️'}
for i, (time, weather) in enumerate(zip(times, weather_sequence)):
ax1.text(time, 7.5, weather_icons[weather], fontsize=40, ha='center')
ax1.text(time, 7.0, weather, ha='center', fontsize=12,
fontweight='bold', color='darkblue')
# 依赖箭头
ax1.arrow(times[0], 6.8, times[1]-times[0]-0.3, -0.7,
head_width=0.15, head_length=0.2, fc='red', ec='red', alpha=0.7)
ax1.arrow(times[1], 6.8, times[2]-times[1]-0.3, -0.7,
head_width=0.15, head_length=0.2, fc='red', ec='red', alpha=0.7)
# 状态空间说明
state_space_box = FancyBboxPatch((1, 3), 8, 2,
boxstyle="round,pad=0.3",
facecolor="lightcoral", alpha=0.2,
edgecolor="red", linewidth=1.5)
ax1.add_patch(state_space_box)
ax1.text(5, 4.5, 'State Space Size Problem',
ha='center', fontsize=12, fontweight='bold', color='darkred')
ax1.text(5, 3.8, '3 weather types → 3² = 9 possible history pairs',
ha='center', fontsize=10, color='darkred')
ax1.text(5, 3.3, 'P(Today | Yesterday, Day-before-yesterday)',
ha='center', fontsize=10, color='darkred', style='italic')
# 内存需求
memory_box = FancyBboxPatch((1, 0.5), 8, 1.5,
boxstyle="round,pad=0.3",
facecolor="lightblue", alpha=0.2,
edgecolor="blue", linewidth=1.5)
ax1.add_patch(memory_box)
ax1.text(5, 1.7, 'Memory Requirement',
ha='center', fontsize=12, fontweight='bold', color='darkblue')
ax1.text(5, 1.0, 'Need to remember last 2 states',
ha='center', fontsize=10, color='darkblue')
# ========== 右图:转换为一阶链 ==========
ax2 = axes[1]
ax2.set_title('Equivalent First-Order Markov Chain\n(Composite states)',
fontsize=14, fontweight='bold', pad=20)
ax2.set_xlim(-1, 11)
ax2.set_ylim(-1, 11)
ax2.axis('off')
# 复合状态的概念
composite_box = FancyBboxPatch((1, 8), 8, 2,
boxstyle="round,pad=0.3",
facecolor="lightgreen", alpha=0.2,
edgecolor="green", linewidth=2)
ax2.add_patch(composite_box)
ax2.text(5, 9.5, 'Composite State = Memory Encoded in State Name',
ha='center', fontsize=12, fontweight='bold', color='darkgreen')
ax2.text(5, 8.8, 'Instead of: P(Weather_today | Weather_yesterday, Weather_day-before)',
ha='center', fontsize=9, color='darkgreen')
ax2.text(5, 8.2, 'We use: P(Composite_today | Composite_yesterday)',
ha='center', fontsize=9, color='darkgreen')
# 复合状态示例
ax2.text(3, 7.0, 'Composite State Example:', ha='left', fontsize=11, fontweight='bold')
# 状态分解图示
# 复合状态
comp_state = FancyBboxPatch((3, 5.5), 4, 1,
boxstyle="round,pad=0.3",
facecolor="lightblue", alpha=0.3)
ax2.add_patch(comp_state)
ax2.text(5, 6.0, '"Sunny-Sunny"', ha='center', fontsize=12,
fontweight='bold', color='darkblue')
ax2.text(5, 5.5, 'represents: Sunny yesterday + Sunny day-before',
ha='center', fontsize=9, color='blue')
# 箭头到天气
ax2.arrow(5, 5.2, 0, -1, head_width=0.2, head_length=0.15,
fc='purple', ec='purple', alpha=0.7, linestyle='--')
# 对应天气
weather_box = FancyBboxPatch((3, 3.5), 4, 1,
boxstyle="round,pad=0.3",
facecolor="yellow", alpha=0.2)
ax2.add_patch(weather_box)
ax2.text(5, 4.0, 'Actual Weather Today', ha='center', fontsize=10, fontweight='bold')
ax2.text(5, 3.5, 'Sunny', ha='center', fontsize=14, fontweight='bold', color='darkorange')
ax2.text(5, 3.5, '☀️', fontsize=30, ha='center')
# 状态转移示例
ax2.text(3, 2.5, 'State Transition Example:', ha='left', fontsize=11, fontweight='bold')
# 转移箭头
ax2.plot([2, 8], [2, 2], 'k-', linewidth=1, alpha=0.5)
# 从状态 (Sunny, Sunny)
state1_box = FancyBboxPatch((1.5, 1.2), 2, 1,
boxstyle="round,pad=0.3",
facecolor="lightblue", alpha=0.4)
ax2.add_patch(state1_box)
ax2.text(2.5, 1.7, 'State: (S,S)', ha='center', fontsize=10, fontweight='bold')
# 转移箭头
ax2.arrow(3.5, 1.7, 2, 0, head_width=0.15, head_length=0.2,
fc='red', ec='red', alpha=0.7)
# 到状态 (Sunny, Cloudy)
state2_box = FancyBboxPatch((5.5, 1.2), 2, 1,
boxstyle="round,pad=0.3",
facecolor="lightblue", alpha=0.4)
ax2.add_patch(state2_box)
ax2.text(6.5, 1.7, 'State: (S,C)', ha='center', fontsize=10, fontweight='bold')
# 转移概率说明
prob_text = 'Transition Probability:\nP((S,C) | (S,S)) = 0.4'
ax2.text(6.5, 0.5, prob_text, ha='center', fontsize=9,
bbox=dict(boxstyle="round,pad=0.2", facecolor="yellow", alpha=0.3))
# 关键优势
advantage_box = FancyBboxPatch((1, -0.5), 8, 1.5,
boxstyle="round,pad=0.3",
facecolor="gold", alpha=0.2,
edgecolor="orange", linewidth=1.5)
ax2.add_patch(advantage_box)
ax2.text(5, 0.0, 'Key Advantage: Standard Markov techniques apply',
ha='center', fontsize=11, fontweight='bold', color='darkorange')
ax2.text(5, -0.5, 'State space: 9 composite states instead of complex memory',
ha='center', fontsize=9, color='darkorange')
plt.tight_layout()
plt.show()
visualize_markov_chains()
2.3 马尔科夫决策过程 (Markov Decision Process, MDP)
马尔可夫链在强化学习领域的具体应用,包括一组状态、一组动作、状态转移概率、奖励函数和折扣因子。
- 在MDP中,智能体可以选择动作,然后在环境下根据状态转移考虑确定下一个状态,并返回一个即时奖励。
- MDP的目标是找到一个最优策略,以最大化期望累计回报(或价值函数)

3. 策略函数 (Policy Function)
- 在某个state下可以选择一个具体动作action,这依赖策略函数
- 确定性策略(Deterministic Policy)
- 给定一个状态,策略函数输出一个动作
- 随机性策略(Stochastic Policy) (实际主要是这种情况)
- 对于给定的状态,策略输出的是一个动作的概率分布
表示在状态 下选择动作 的概率。
- 注意:在深度学习中,这个策略函数由神经网络表示
- 所有的可能为树形结构
- 对于给定的状态,策略输出的是一个动作的概率分布
表示在状态 下选择动作 的概率。
- 确定性策略(Deterministic Policy)

3.1 策略序列/轨迹 (trajectory)
状态、动作、奖励的序列
奖励穿插在状态和动作之间。
3.2 轨迹对应的概率 :
- 描述了在策略 下,智能体agent在环境中采取一系列动作,从初始状态开始并最终达到某个终止状态的可能性有多大。这个概率分布通常用于强化学习算法中的策略优化,目标是找到使得期望回报最大化的最佳策略参数 .

(核心是 状态-动作交替)
- 环境动态: ,这是环境决定的(与策略无关)
- 策略
- 于是,一条给定轨迹的概率(假设初始状态分布为 )为:
- 具体展开
- 于是,一条给定轨迹的概率(假设初始状态分布为 )为:
3.3 确定性策略 vs 随机策略 的轨迹分布区别
- 确定性策略只固定“给定状态时的动作”,环境转移或初始状态随机时仍存在轨迹分布;随机策略还引入动作采样的随机性
- 确定性策略 (仅依赖环境随机)
- 在每个状态 s 下固定输出一个特定动作,即
(函数映射)
- 在完全确定性环境 + 确定性策略时:只要初始状态固定,整条轨迹完全固定。(只有一条轨迹概率为1,其他为0)
- 如果环境动态随机而策略确定:初始状态固定时,第一次动作固定;后续状态随机,动作随到达的状态变化,因此动作序列也未必固定。(不同轨迹的概率来自于环境随机,而不是策略随机)
- 在每个状态 s 下固定输出一个特定动作,即
(函数映射)
- 随机性策略(可依赖环境随机和轨迹随机)
- 在每个状态 s 下输出动作的概率分布,例如高斯或分类分布等
- 即使环境动态确定:是确定性的,因为策略选择动作是随机的,所以从同一个初始状态出发也可以获得多条不同轨迹。
- 如果环境也是随机的,那么此时随机性来自两者的叠加。
- 在每个状态 s 下输出动作的概率分布,例如高斯或分类分布等
- 确定性策略 (仅依赖环境随机)
确定性策略本身不会因为策略的选择而随机生成多条轨迹(动作固定),随机策略会在选择动作时引入随机性,从而即使环境确定也可能有多条轨迹。因此 是一个具备更宽的概率分布,表示由于策略的随机选择导致可能有很多条轨迹,每条有不同概率。
4. 强化学习衡量 Reward 的重要指标
智能体通过与环境交互后的Reward来学习最优策略, 而累计回报、状态价值、动作价值是理解这一过程的核心逻辑链条。他们从单条路径的收益到状态的平均价值,再到动作的具体价值,层层递进刻画智能体的决策依据。
4.1 累计回报
- 累计回报是从时刻 t 开始, 未来所有奖励的折扣累计和,公式为:
- 其中
- 是折扣因子,用于体现未来奖励的当前价值衰减;无限时域通常取 并要求回报可积, 越接近0,越重视即时奖励;越接近1,越重视长期奖励
- 是时刻 的即时奖励。
- 其中
4.2 状态价值
- 状态价值函数是策略 下,从状态 s 出发的累积回报的期望,公式为
4.3 动作价值
- 动作价值函数是在策略 下, 从状态 s 执行动作 a 后, 累积回报的期望, 公式为 它比状态价值更具体, 直接评估在状态 s 选动作 a , 再按策略行为的长期价值
4.4 、、 的关系
- 状态 s 的价值, 等于在这个状态下所有可能动作的 值,按照你选动作的策略 的概率加权平均
- 累积回报是 值和 值的计算基础: 值和 值都是对未来累积回报的期望,因为强化学习中存在随机性(比如环境随机反馈、动作随机选择),所以要用期望来描述长期规律。
4.5 将 、、 化成递推式:Bellman 方式
- 累计回报 、状态价值 、动作价值 都是长期价值,很难直接计算,需要遍历从当前时刻到任务结束的所有未来步骤,这在现实场景中几乎不可行。
- 任务无终止时(如持续运行的机器人控制),未来步骤是无限的,无法直接求和。
- 任务有终止但步骤极多(如复杂游戏通关),遍历所有未来路径的计算量会呈指数级增长,远超算力承载能力。而递推Bellman方程将无限/极多步骤的长期价值转化为当前步的奖励+下一步价值的折扣期望,只需关注当前与下一步的关联,大幅降低了计算复杂度。
- 同时,递推式让价值学习具备迭代优化的可能。例如 Q-Learning 使用下面的更新式,让 值在每次交互后逐步向最优值收敛:
4.6 价值递推核心:Bellman 方程
- 强化学习中,某状态(某状态-动作对)的价值,可分解为即时奖励和后续状态的价值的折扣期望。
- Bellman方程就是用递推公式来刻画这种现在与未来的价值关联:用选择策略的回报和可达的下一状态的值描述当前状态的值。
4.6.1 Bellman期望方程:针对 值
- 状态价值函数 的Bellman方程为
- 含义:在策略 下, 状态 s 的价值 = 『即时奖励 的期望』+ 『折扣后, 下一步状态 的价值 的期望』
- 与 的联系:(累积回报的递推式),而 ,因此 Bellman 方程是对累积回报期望的递推分解。
- 为什么 是 ?
- 执行当前动作后, 在进入下一个状态 的同时,才能获得对应的奖励
- 如何理解 ?
- 当模型有关 model-base(环境转移可推算)时:通过 可知
- 当模型无关 model-base(环境转移不可推算)时:通过下一步的真实情况采样获取
4.6.2 Bellman期望方程:针对 值
动作价值函数 的Bellman方程为:
含义:在策略 下, 状态 s 的价值 = 『即时奖励 的期望』+ 『折扣后, 转移概率 给出下一步状态通过采样选动作的 值的期望』
与 的联系: , 可从 的Bellman方程推到得到 的Bellman方程,体现了两者的递推一致性。
4.6.3 Bellman最优方程
- 这是表格价值迭代与 Q-Learning 分析的基础。收敛还依赖任务条件、充分访问状态动作对以及学习率条件;神经网络函数逼近不自动继承表格算法的收敛结论。
5. 无模型的学习方法:MC 与 TD
在无模型(Model-Free)场景下,我们无法依赖环境转移概率计算价值,只能通过与环境交互的经验学习。蒙特卡洛(MC)和时序差分(TD)是两种核心的无模型价值学习方法,前者依赖 “完整轨迹”,后者侧重 “单步 / 多步交互”,适用于不同场景需求。
5.1 MC 蒙特卡洛(Monte Carlo)
在强化学习中MC方法的本质是通过完整轨迹的累积回报,平均估计状态/动作的价值。它要求智能体完成一整个交互序列(从初始状态到终止状态),获得完整的累积回报 后,再用这个真实回报更新价值:不依赖任何估计值,只基于实际交互结果。
关键公式
对于每个经历过状态 s 的轨迹, 记录该轨迹中状态 s 对应的累积回报 , 多次交互后,状态价值的估计值为所有包含s的轨迹中 的平均值。
其中
是包含状态 的轨迹总数, 是第 条轨迹中状态 对应的累积回报。
对于原公式
我们发现这正是数学上蒙特卡洛算法,用暴力采样的方式,作为原复杂解的无偏估计。
案例
求圆周率
import random
def estimate_pi(num_samples, seed=42):
if num_samples <= 0:
raise ValueError("num_samples must be positive")
rng = random.Random(seed)
inside = sum(rng.uniform(-1, 1)**2 + rng.uniform(-1, 1)**2 <= 1
for _ in range(num_samples))
return 4 * inside / num_samples
for n in [100, 1000, 10000, 100000]:
estimate = estimate_pi(n)
print(f"N={n}: pi={estimate:.6f}")- 结果特点:
- 随机性:固定种子方便复现;大样本使估计在概率意义上更集中,但单次实验的误差不保证随 N 单调下降。
- 收敛速度:误差大致按 下降,这就是蒙特卡洛的典型特征。
- 求积分
import math
import random
def mc_integral(func, a, b, num_samples, seed=42):
if num_samples <= 0 or not a < b:
raise ValueError("need positive samples and a < b")
rng = random.Random(seed)
total = sum(func(rng.uniform(a, b)) for _ in range(num_samples))
return (b - a) * total / num_samples
for n in [1000, 10000, 100000]:
result = mc_integral(lambda x: math.exp(-x*x), 0, 1, n)
print(f"N={n}: integral={result:.6f}")- 核心思想
- 把问题转换成某种随机过程的概率或期望值。
- 求 → 随机点落在圆内的概率
- 求积分 → 随机变量 的期望值,其中 均匀分布
- 用大量随机样本来估计这个期望值。
- 估计的误差收敛速度是 ,与问题的维度无关(这是最大优点)。
- 特点与适用场景
- 优势:无偏差(仅用真实累积回报,不依赖估计值),逻辑直观,适合 “必须完成完整任务才能评估价值” 的场景(如棋类游戏、一次性决策任务)。
- 劣势:需等待轨迹终止才能更新,学习效率低;对轨迹数量要求高(需大量完整轨迹才能让平均值收敛),不适合 “无终止状态” 的持续任务(如机器人持续导航)。
5.2 TD 时序差分 (Temporal Difference)
- TD 方法结合了 MC 的 “经验采样” 和动态规划(DP)的自举(Bootstrapping)思想: 无需等待完整轨迹,每执行一步交互(获得 )后,立即用即时奖励 + 下一个状态的估计价值更新当前状态价值,是无模型场景下应用最广泛的方法。
| 方法 | 思想 | 局限性 |
|---|---|---|
| MC(蒙特卡洛) | 等完整一条轨迹跑完,再用累计回报更新前面所有状态 | 只能用于 episodic 场景,收敛慢 |
| DP(动态规划) | 用「当前奖励 + 下一状态的估计值」进行自举(用估计的未来状态价值,来辅助计算当前状态价值) | 必须知道环境模型(转移概率) |
比喻:学车时的“实时教练” 假设你在学开车,目标是掌握在不同路况(状态)下如何平稳驾驶(获得高回报)。
动态规划(DP)方法:像一个“理论派教练”。
他不开车,只坐在书房里研究地图和交通规则。
他会告诉你:“在十字路口(状态 ),如果你直行,根据规则,你可能会到达下一个街区(状态 ),而那个街区的驾驶难度评分是 X 分。所以,这个路口直行的价值是…”。
特点:需要世界模型(地图和规则表),完全依赖推理(自举),没有真实经验。
蒙特卡洛(MC)方法:像一个“事后复盘教练”。
他会让你开完全程(完成一个Episode),比如从家开到公司。
停好车后,他根据你这一趟的整体表现(是顺利到达还是磕磕碰碰)来给你一路上经过的每个路口打分。
特点:必须等待结局,学习是基于完整经验的,但更新延迟严重。
时序差分(TD)方法:像一个 “坐在副驾的实时教练”。
你每开过一个路口,他马上就会点评。
比如,刚才你平稳通过了这个拥堵路口(状态 ),得到了即时的良好感觉(即时奖励 ),并进入了下一个路口(状态 )。教练马上说:“刚才这个路口你处理得不错!而且看,下一个路口车流也很顺畅( 的价值估计很高),所以我判断你刚才的选择总体价值很高。”
他没有等到终点,就结合了:
你的即时感受(奖励)
他对下一个路口的预判(价值估计)
立刻更新了你对刚才那个路口的认知。
特点:边走边学,实时更新,结合了真实体验片段和原有认知预测。
因此在每一步交互后: 我们就立即用「即时奖励 + 下一状态的估计值」作为新的目标来更新当前状态的估计值。 这个思想其实是在逼近「期望回报」的定义式:
但我们没法一次算出所有未来奖励,于是参考上面的公式用一步近似:
这就是所谓的「自举(bootstrapping)」: 用当前估计值的一部分去更新自己。
5.2.1 核心(默认)公式
TD (0)(单步 TD,更新状态价值):仅用 “下一步状态的估计价值” 计算更新目标,是最基础的 TD 形式:
其中
是学习率, 决定了我们更新的幅度
成为TD目标
是TD误差, 衡量当前估计与目标的差距
同时, 它还可以用于 的递推.(sarsa算法)
5.2.2 SARSA (更新动作价值,On-Policy TD控制算法)
名称来源于
针对动作价值 , 更新时依赖实际执行的下一个动作 :
其中
被称为 TD目标
被称为 TD 误差(TD error)。
我们定义其更新目标(监督信号或标签)为:
TD目标,它代表了当前状态-动作的理想预测值
SARSA是一种 On-policy 学习算法:每次更新都基于智能体在当前策略下实际执行的下一步动作
6. 价值函数算法
6.1 Q-Learning
- Q-Learning 是一种典型的异策略(Off-Policy)时序差分(TD)强化学习算法。其核心目标是学习一个最优的动作价值函数 ,该函数表示在状态s下采取动作a后,遵循最优策略所能获得的期望累计折扣回报
6.1.1 算法流程
初始化
创建一个表格,存储所有 组合的 值,为所有状态-动作对赋予初始值
交互与更新
在每个时间步 ,智能体在状态 下根据某种策略(贪心算法等)选择动作
执行动作
执行动作 ,环境返回奖励 和下一个状态
更新值,也可以用收敛快的启发式算法得出
重复
令 ,重复2-4步骤,直至 表收敛 这样就可以反复迭代更新每个情况s下每一种动作a的动作价值
6.1.2 (查表)决策
最终优化好了 值表后,选择当前状态下 值最大的动作,通过查训练好的值表快速到达终点。
6.2 SARSA 和 Q-Learning 的对比测试
- Code :
import numpy as np
# 4x12:起点 36,终点 47,悬崖 37..46。
# 掉崖奖励 -100 并回到起点,但 episode 不终止;到达终点才终止。
def step(state, action):
row, col = divmod(state, 12)
dr, dc = [(-1, 0), (0, 1), (1, 0), (0, -1)][action]
row, col = np.clip(row + dr, 0, 3), np.clip(col + dc, 0, 11)
next_state = int(row * 12 + col)
if 37 <= next_state <= 46:
return 36, -100.0, False
return next_state, -1.0, next_state == 47
def epsilon_greedy(q, state, rng, epsilon):
if rng.random() < epsilon:
return int(rng.integers(4))
choices = np.flatnonzero(q[state] == q[state].max())
return int(rng.choice(choices))
def train(method, episodes=500, seed=42, alpha=0.5, gamma=1.0, epsilon=0.1):
if method not in ("sarsa", "q_learning"):
raise ValueError("unknown method")
rng = np.random.default_rng(seed)
q = np.zeros((48, 4))
returns = []
for _ in range(episodes):
state, total = 36, 0.0
action = epsilon_greedy(q, state, rng, epsilon)
for _ in range(10000):
next_state, reward, terminated = step(state, action)
next_action = epsilon_greedy(q, next_state, rng, epsilon)
bootstrap = (q[next_state, next_action] if method == "sarsa"
else q[next_state].max())
target = reward + gamma * (0.0 if terminated else bootstrap)
q[state, action] += alpha * (target - q[state, action])
total += reward
if terminated:
break
state, action = next_state, next_action
else:
raise RuntimeError("episode exceeded the demonstration step budget")
returns.append(total)
return q, np.array(returns)
if __name__ == "__main__":
for method in ("sarsa", "q_learning"):
_, returns = train(method)
print(method, "last-100 mean return:", returns[-100:].mean())

上方两张图保留历史实验的路径与回报对比,不是当前精简代码的固定输出。运行代码会打印所选种子的末 100 回合平均回报;多种子实验才适合比较稳定性。
6.3 值过估计(Overrstimation Bias)
6.3.1 问题定义
- 同一组带噪声估计同时用于选择最大动作与评估其价值时,会产生最大化偏差;但不能断言每个训练阶段、每个状态动作的估计都高于 Q*。
6.3.2 产生原因
纯价值函数方法容易产生价值过估计,原因出在迭代过程中的取最大动作价值,在于更新公式中 操作和估计误差的结合。
更新公式
其中 α 是学习率,d 表示真正终止;时间限制截断不能一概当作终止。
采样时
其中 是估计误差,可能正或可能负,由于操作倾向于选择误差最大的那个动作,很可能存在某个样本导致:
因此,纯价值函数方法(如Q-Learning、DQN)天然容易出现过估计
Actor-Critic 将动作生成与价值评估分开,但 Actor 仍依赖 Critic 的估计,因此结构本身不能消除过估计。TD3 的双 Critic 取小目标和延迟更新才是针对性机制,见 TD3 官方说明。

6.4 从最大化偏差到 Double Q-Learning
下面先隔离“同一组带噪声估计既选择又评估动作”这一机制。所有动作的真实价值设为零;这个统计实验不是完整的强化学习训练,也不能证明任意环境中 Double Q 的回报一定更高。
import numpy as np
rng = np.random.default_rng(42)
selector = rng.normal(size=(100000, 10))
evaluator = rng.normal(size=selector.shape)
chosen = selector.argmax(axis=1)
coupled = selector[np.arange(len(selector)), chosen]
decoupled = evaluator[np.arange(len(selector)), chosen]
print("same estimator:", coupled.mean())
print("independent evaluator:", decoupled.mean())
def double_q_update(q1, q2, state, action, reward, next_state,
terminated, rng, alpha=0.1, gamma=0.99):
"""两个同形状二维 Q 表;随机更新其中一个,另一个负责评估。"""
select, evaluate = (q1, q2) if rng.random() < 0.5 else (q2, q1)
target = reward
if not terminated:
next_action = int(np.argmax(select[next_state]))
target += gamma * evaluate[next_state, next_action]
select[state, action] += alpha * (target - select[state, action])训练时可用 q1 + q2 形成 ε-greedy 行为策略。两个表由同一条经验流学习,并不严格统计独立;Double Q 将动作选择与评估解耦,缓解最大化偏差,但仍可能低估,不能保证偏差永远为零。


以上保留的两张图来自历史实验,并非上方精简示例的本次运行结果。曲线差异受到环境、随机种子、训练预算和估计器相关性的影响,不据单次试验宣称固定提升比例。
6.5 DQN (Deep Q-Network 使用神经网络的Q-Learning)
- 核心思想
- 一旦S和A的组合增加,Q值表的计算和存储的开销都会很大。用深度学习网络近似Q函数,通过输入状态 s 直接预测所有动作 a 的 Q 值,解决传统Q-Learning在高维状态空间下的"维数灾难"问题。

输入维度:适应高维状态
输出维度:等于离散动作空间的尺寸
DQN 的逻辑:拟合 Q 值,间接生成策略
神经网络的角色:用深度神经网络拟合 Q(s,a),输入是状态 s(如游戏画面),输出是所有动作的 Q 值(如 “向左走的 Q 值、向右走的 Q 值”)。
一次反向传播用到的数据:
反向传播只回归当前采样动作对应的标量
Q(s,a),不是把所有动作输出当作同一个目标向量。TD 目标由目标网络计算并停止梯度;真终止时目标只有奖励。策略的生成:训练完成后,策略是 “选 Q 值最大的动作”(贪心策略),策略由 Q 值间接推导,而非网络直接输出动作概率。
训练机制
前向传播: 输入状态 -> 网络 -> 得到所有动作的预测Q值
选择动作: ε-greedy(训练时)或 greedy策略(测试时)
执行动作: 获得奖励 和新状态
计算目标:
反向传播:更新 θ,最小化
(y - Q(s,a;θ))²或对应 Huber 损失,而不是有符号误差本身。下方回放与目标网络代码块是流程伪代码。关键技术: DQN 通过经验回放(Experience Replay)和目标网络(Target Network)等技术解决了 Q-learning 中样本相关性和目标值不稳定的问题。
经验回放
# 传统Q-Learning问题:序列样本强相关
for (s, a, r, s') in sequential_experience:
update(Q) # 连续相关样本 → 训练不稳定
# DQN解决方案:经验回放
replay_buffer.append((s, a, r, s'))
batch = random.sample(replay_buffer, k) # 随机采样 → 打破相关性
update(Q) # 稳定训练- 目标网络
# 传统问题:目标值y与预测值Q来自同一网络
y = r + γ * max Q(s', a'; θ) # θ快速变化 → 目标值波动大
# DQN解决方案:固定目标网络
y = r + γ * max Q(s', a'; θ^-) # θ^-每N步同步一次θ → 目标稳定- 总结:
| 特性 | Q-Learning | DQN |
|---|---|---|
| 状态表示 | 表格(离散状态) | 神经网络(连续/高维状态) |
| Q值存储 | Q表(S×A矩阵) | 网络权重参数 |
| 泛化能力 | 无(查表) | 强(函数逼近) |
| 适用场景 | 小型离散环境 | 复杂高维环境(Atari游戏等) |
| 训练样本 | 在线更新 | 经验回放池 |
| 目标稳定性 | 不稳定 | 目标网络稳定训练 |
- Q-Learning:仅适用于低维离散状态 / 动作(如 10×10 网格世界)
- DQN:可处理高维状态(如图像、传感器数据),但动作仍需是离散的(如 Atari 游戏的上下左右按键)
- 因此,DQN标志着深度强化学习时代的开启,将深度学习的表示能力与强化学习的决策框架相结合,实现了从低维表格到高维函数逼近的跨越,为处理真实世界复杂问题奠定了基础。
7. 策略梯度算法(Policy Gradient, PG)
- 价值学习: 先学习价值函数(Q-learning、DQN等),再根据价值选择动作。
- 策略梯度: 直接学习一个参数化的策略函数 ,输出动作的概率分布,通过梯度上升直接优化策略参数。
7.1 策略梯度的数学形式
目标函数
目标是最大化期望回报
其中 是轨迹 , 是轨迹的总回报。
策略梯度定理
梯度表达式为
其中 是从时刻 开始的累积回报。
7.2 强化学习梯度的反向传播
- 策略梯度适用于可微的参数化策略,不要求策略一定是神经网络;反向传播是计算复杂组合函数梯度的工具。
- 策略梯度和深度学习里面的梯度基本上是一样的,都是用来找更优解:以蒙特卡洛的思路,利用真实的样本采样来更新模型参数,计算回报对策略参数的梯度,通过梯度上升更新参数,让高回报动作出现概率增加。
7.3 优化目标与学习信号的区别
| 维度 | 监督学习优化 | 策略梯度优化 |
|---|---|---|
| 目标函数 | 损失函数(可计算) | 期望回报(需估计) |
| 优化对象 | 对样本定义的预测损失 | 策略诱导的期望回报 |
| 学习信号 | 标签或监督目标,也可能带噪声 | 环境奖励,可能稀疏或延迟 |
| 数据关系 | 拟合现有数据分布 | 创造新的数据分布 |
| 本质任务 | 模式识别:发现数据中的模式 | 策略搜索:在动作空间中搜索最优路径 |
- 传统神经网络的反向传播

监督学习:数据 -> 预测 -> 误差 -> 梯度下降
监督学习梯度 = 误差 × 输入特征
监督学习只看当前样本 传统网络的梯度来源于误差,目标是减小误差,所以是梯度下降。
策略神经网络的反向传播

- 强化学习:状态 -> 动作 -> 奖励 -> 回报 -> 梯度上升
- 策略梯度 = 回报 × (增加当前动作概率的方向)
- 强化学习必须考虑整个轨迹的长期回报
- 策略网络的梯度来源于回报,目标是增大回报,所以是梯度上升。其梯度公式可以看作是用回报 对提高动作概率的梯度进行加权。
- 策略梯度不是简单地「把梯度下降变成梯度上升」,而是将优化范式从「误差最小化」转变为「期望最大化」,从而解决了传统方法无法处理的序列决策和环境交互问题。
7.4 REINFORCE算法和策略梯度定理
7.4.1 REINFORCE的核心突破
- 核心问题解决:如何直接优化策略
- 在REINFORCE之前,强化学习主要基于价值函数(如Q-learning)。REINFORCE开创了直接策略优化的新范式:
- 传统方法:价值函数 → 策略(间接)
- REINFORCE:直接优化策略参数
7.4.2 关键数学工具:对数导数技巧(Log-Derivative Trick)
# 问题:无法直接对采样期望求导
∇_θ E_{τ∼p_θ}[R(τ)] = ?
# 解决方案:对数导数技巧
∇_θ E_{τ∼p_θ}[R(τ)] = E_{τ∼p_θ}[R(τ) · ∇_θ log p_θ(τ)]
# 然后分解轨迹概率
∇_θ log p_θ(τ) = Σ_t ∇_θ log π_θ(a_t|s_t)7.4.3 策略梯度定理的完整推导
马尔可夫决策过程(MDP)定义
MDP五元组
各元素含义:
:状态空间(State Space)
:动作空间(Action Space)
:状态转移概率
:奖励函数
:折扣因子
参数化策略
表示策略 其中 是策略参数,通常为神经网络权重。
轨迹定义
一条完整轨迹:
轨迹的概率分布:
各部分的含义:
:初始状态分布
:策略选择的动作概率
:环境状态转移概率
目标函数
折扣回报:
期望回报(目标函数):
梯度计算的核心问题
梯度表达式
直接计算的困境
问题分析:
梯度算子 同时作用于:
分布 (与 相关)
回报 (通常与 无关)
无法直接对概率分布求导
对数导数技巧(Log-Derivative Trick)
为什么要取对数?
乘法变加法:更容易处理
避免数值下溢:概率相乘会变得极小
求导方便:对数和求导更简单
技巧定义
对于任意可微的概率密度函数 :
核心公式:
证明过程
策略梯度定理推导
应用对数导数技巧
步骤1:交换积分与梯度
步骤2:应用对数导数技巧
步骤3:整理为期望形式
分解轨迹概率的对数
展开 :(公式分解 按照log对数运算规则以及连乘转求和)
对 求梯度:
分析各项:
(初始状态分布与环境有关, 与 无关)
(环境转移概率是环境特性, 与 无关)
(这是策略部分, 由参数 控制)
简化结果:
得到最终定理 代入梯度表达式:
最终形式:
7.4.4 REINFORCE算法的理论价值矩阵
| 层面 | 传统价值方法 | REINFORCE(策略梯度) |
|---|---|---|
| 优化对象 | 价值函数 | 策略函数 $\pi(a |
| 梯度来源 | 时序差分误差(TD error) | 轨迹回报 |
| 可导性 | 需要对环境模型求导(model-based) | model-free:环境转移概率在梯度中消掉 |
| 探索方式 | ε-greedy等启发式方法 | 策略的随机性自然提供探索 |
| 适用动作空间 | 离散、低维 | 连续、高维动作空间 |
7.4.5 REINFORCE算法的理论价值矩阵
阶段1:基础REINFORCE(Williams, 1992)
梯度公式
问题:使用整个轨迹的回报更新每个动作,方差极大
阶段2:因果性改进(引入时间因果性)
关键洞察:动作 只影响 时刻之后的回报
改进公式:
其中
效果:减少了不必要的噪声,但仍方差大
阶段3:基线技巧(Baseline Trick)
核心思想:减去一个基准值,保留相对优势
公式:
常见基线是 。它并非一般情况下严格的最小方差基线;最优基线还与 score-function 梯度的大小有关。
Actor-Critic 方法
特点:用优势估计表示动作相对当前策略平均水平的好坏;学到的 Critic 存在估计误差。
其中:
PPO 方法 (Proximal Policy Optimization)
特点:通过裁剪代理目标抑制过大的有利更新;不保证策略比率或 KL 满足硬边界。
其中:
(概率比)
是裁剪参数(通常 0.1-0.3)
SAC(Soft Actor-Critic)
SAC的核心特点:最大熵框架
实际简化形式(更常用的表示):
7.4.6 从REINFORCE到现代方法的演变

| 算法 | 核心思想 | 公式特点 | 主要改进 |
|---|---|---|---|
| REINFORCE | 基础策略梯度 | 首次实现直接策略优化 | |
| 因果改进 | 时间因果性 | 减少不相关噪声 | |
| 基线技巧 | 降低方差 | 方差减少,训练更稳定 | |
| Actor-Critic | 价值评估 | 更精确的动作评估 | |
| PPO | 约束更新 | 稳定的大步幅更新 |
7.4.7 附录:符号说明表
| 符号 | 含义 | 备注 |
|---|---|---|
| 状态空间 | 所有可能状态的集合 | |
| 动作空间 | 所有可能动作的集合 | |
| $\pi_\theta(a | s)$ | 参数化策略 |
| $P(s' | s,a)$ | 状态转移概率 |
| 奖励函数 | 即时奖励信号 | |
| 折扣因子 | 权衡近期与远期奖励 | |
| 轨迹 | 状态-动作序列 | |
| 轨迹回报 | 折扣奖励之和 | |
| 目标函数 | 期望回报 | |
| 梯度算子 | 对参数 求导 |
8. 连续动作空间:动作优化与策略参数化
8.1 连续动作空间:为什么策略梯度算法更适合
8.1.1 问题的本质:连续 vs 离散
时间离散化的必然性 关键认知:
物理世界是连续的,但决策过程必须是离散的
受限于计算速度(神经网络推理时间)和硬件响应时间(电机延迟)
典型决策频率:10-100 Hz(每0.01-0.1秒决策一次)
动作参数离散化的弊端 问题分析:
- 精度损失:电机实际精度可达0.001°,离散化为0.1°档位造成浪费
- 维度爆炸:若要达到0.001°精度,180°范围需要180,000个离散动作
- 平滑性问题:离散动作导致机械臂抖动,影响控制稳定性
8.1.2 连续动作空间的技术实现
- 为什么计算机能处理"连续"动作?
- 本质:计算机使用浮点数近似连续空间
- float32 约有 7 位十进制有效数字,float64 约有 16 位。是否足够取决于数值尺度、条件数与误差预算,不能仅凭“机器人控制”判断。
- 价值函数方法的局限 价值函数方法(如DQN)的问题:
- 输出维度固定: 需要为每个动作 输出值
- 连续动作空间无限:无法为无限个动作都计算 值
- 解决方法受限:
- 离散化:精度损失
- 函数拟合:需要额外优化过程
8.1.3 策略梯度算法的优势
- 高斯分布是常见的连续动作参数化,不保证适合多峰策略。下面演示未压缩高斯与 REINFORCE 的 score-function 梯度,不是完整 SAC。
import torch
from torch import nn
from torch.distributions import Normal
class GaussianPolicy(nn.Module):
"""未压缩高斯策略,演示 REINFORCE 的 score-function 梯度。"""
def __init__(self, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(state_dim, 128), nn.ReLU(),
nn.Linear(128, 64), nn.ReLU(),
nn.Linear(64, action_dim * 2),
)
def forward(self, state):
mean, log_std = self.net(state).chunk(2, dim=-1)
return mean, log_std.clamp(-5, 2).exp()
def sample_action(self, state):
mean, std = self(state)
normal = Normal(mean, std)
# score-function:把抽到的动作视为固定样本。
action = normal.sample()
log_prob = normal.log_prob(action).sum(dim=-1)
return action, log_prob
policy = GaussianPolicy(state_dim=8, action_dim=2)
action, log_prob = policy.sample_action(torch.zeros(4, 8))
assert action.shape == (4, 2) and log_prob.shape == (4,)这个例子使用 sample(),使动作样本不带重参数化梯度。SAC 的路径导数则使用 rsample(),并对 tanh 压缩后的 log-probability 加 Jacobian 修正;二者不能直接互换,见 SAC 的策略更新说明。
未压缩高斯动作没有边界,不能直接发送给真实机械臂。受限动作需要明确压缩、缩放和密度变换;每个动作维度都有自己的均值和标准差,标准差也不保证随训练单调减小。
| 参数 | 物理意义 | 在强化学习中的作用 |
|---|---|---|
| 均值 μ | 分布中心,最可能采取的动作 | 利用:基于当前知识的最优动作 |
| 标准差 σ | 分布的宽度,探索程度 | 探索:尝试均值附近的其他动作 |
9. 强化学习三大方法:Actor-Critic架构的提出
- 强化学习的算法设计围绕 “如何选择最优动作” 展开,根据 “是否依赖价值函数”和 “是否直接建模策略” ,可分为三大主流方法:纯价值函数、纯策略梯度、Actor-Critic,前两个一一对应,最后一个是前两者的结合方法。
9.1 纯价值函数方法
代表算法:Q-Learning、SARSA、DQN
核心思想:学习价值函数 或 ,然后通过贪心(或ε-贪心)策略选动作
思想:TD (Temporal Difference)
优势:逻辑直观,价值函数的收敛性有理论保障;无需建模策略概率分布,计算成本较低。
劣势:标准表格法和 DQN 面向离散动作;连续动作也能建模 Q(s,a),但每次求 argmax 需要额外优化或结构假设。函数逼近和最大化目标还可能引入估计偏差。
适用场景:离散动作、低维 / 高维状态的任务(如 Atari 游戏、网格世界导航)。
9.2 纯策略梯度方法
代表算法:REINFORCE
核心思想:建模策略函数 直接优化策略参数 ,让期望回报 最大。
思想:MC (Monte Carlo)
优势:可直接处理连续动作空间;策略更新更直接,不易受价值过估计影响。
劣势:策略梯度方差高(观测值 累计后方差大);收敛速度较慢,易陷入局部最优。
适用场景:连续动作、高动态性的任务(如机器人控制、自动驾驶、机械臂操作)。
9.3 Actor-Critic方法
- 代表算法:A2C、A3C、SAC、DDPG、PPO
- Actor-Critic将“策略梯度”和“价值函数”都考虑,并分成相互影响的两个串行板块:
- Actor(策略模块):建模可维的策略 ,负责 “选动作”;
- Critic(价值模块):建模评价标准 或 或使用优势函数 ,负责 “评估 Actor 选的动作好不好”
- 使用参数 就是为了和Actor的参数 做区分
- 通过 Critic 的评估结果指导 Actor 的策略更新,新Actor又会给Critic提供新样本,实现 “边评估、边改进”。
| 问题 | Actor-Critic 的解决方式 |
|---|---|
| PG 方差大 | Critic 使用降低方差的方法(如优势函数) |
| 连续动作的 argmax 难求 | Actor 学习动作映射,减少在线动作搜索开销。 |
| 学习效率低 | Critic 提供更快的学习信号(TD 误差),比整段回报 R 快得多。 |
| 高方差策略更新 | Critic 提供可学习的估计,但也可能引入偏差,不能保证收敛。 |
9.4 Actor-Critic 思路
- 关键洞察:将决策者(Actor) 和评价者(Critic) 分开,各自专注于自己的任务:
- Actor:专注于如何选择动作(策略优化)
- Critic:专注于如何评价动作(价值评估)
- 核心思想:分而治之,专业分工
- 分离关注点:将"选择动作"和"评估动作"分开
- 专业化分工:每个网络专注于自己的任务
- 互相促进:Actor为Critic提供数据,Critic为Actor提供指导
9.4.1 优势函数的演进
- 为什么需要替代整个时间段的回报?
- 问题分析:蒙特卡洛回报的缺陷
# REINFORCE使用的完整回报
G_t = r_t + γr_{t+1} + γ²r_{t+2} + ... + γ^{T-t}r_T
# 问题:
1. 高方差:需要等到轨迹结束才能计算#
2. 延迟更新:无法实现单步学习#
3. 样本效率低:需要完整轨迹- 优势函数的本质任务
- 任务:在"立即反馈"和"长期效果"间找到平衡
- 目标:用部分信息准确估计动作的额外价值
- 优势函数的三种基础形式
- 完整对比矩阵
| 形式 | 公式 | 需要学习 | 更新时机 | 偏差 | 方差 | 适用场景 |
|---|---|---|---|---|---|---|
| Q-V形式 | Q网 + V网 | 随时 | 低 | 中 | 理论研究 | |
| TD残差形式 | 仅V网 | 单步后 | 中 | 中 | 实时控制 | |
| 蒙特卡洛形式 | 仅V网 | 轨迹结束 | 低 | 高 | 稀疏奖励 |
- 核心替代方法: 从基础到高级
- 路线图

- n步优势函数
def n_step_advantage(rewards, values, terminated, t, n, gamma):
"""单条、不跨 reset 的轨迹;values 长度 T+1,其他数组长度 T。"""
if len(values) != len(rewards) + 1 or len(terminated) != len(rewards):
raise ValueError("inconsistent trajectory lengths")
if not 0 <= t < len(rewards) or n < 1 or not 0 <= gamma <= 1:
raise ValueError("invalid t, n or gamma")
total = 0.0
end = min(t + n, len(rewards))
for index in range(t, end):
total += gamma**(index - t) * rewards[index]
if terminated[index]:
return total - values[t] # 真终止:不 bootstrap。
# 采样片段/时间限制截断:用最后一个真实 observation 的价值。
total += gamma**(end - t) * values[end]
return total - values[t]- n 的选择策略:
- n=1:TD残差,高偏差低方差(密集奖励)
- n=5:常用折中(大多数任务)
- n=10+:接近蒙特卡洛,低偏差高方差(稀疏奖励)
9.4.2 广义优势估计(GAE):黄金标准
核心思想: 从单步到多步的平滑过渡
GAE的核心公式
其中
关键转换: 用TD残差替代完整回报
核心突破:
将不确定的 替换为可预测的 + TD残差
利用Critic的可训练性来稳定估计
通过 实现平滑的偏差-方差权衡

的数学效应
GAE的递归形式
展开后权重分布:
gamma, lam = 0.99, 0.95
weights = {lag: (gamma * lam)**lag for lag in range(11)}
print(weights[10]) # 约 0.5415- 具体数值示例 ( )
| λ值 | 10步后权重 | 物理意义 |
|---|---|---|
| 0.0 | 0% | 只看当前步 |
| 0.5 | (0.99×0.5)^10 ≈ 0.088% | 快速衰减 |
| 0.95 | (0.99×0.95)^10 ≈ 54.1% | 缓慢衰减 |
| 1.0 | (0.99)^10 ≈ 90% | 几乎不衰减 |
- 常见问题与解决
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 训练波动大 | 可能来自价值误差、学习率或回报方差 | 同时检查 TD 残差、价值损失和多种子结果,不能仅凭现象调 λ |
| 收敛缓慢 | λ太大(如0.99)→ 方差高更新慢 | 减小λ到0.9-0.95 |
| 早期探索差 | 奖励稀疏、策略方差或 Critic 误差 | 分别检查探索和价值估计,不把先训练 Critic 当成通用规则 |
| 优势值过小 | 奖励尺度问题 | 标准化优势,缩放奖励 |
10. Actor-Critic算法
10.1 TRPO:Trust Region Policy Optimization
TRPO指出在 REINFORCE 或普通 Policy Gradient 中,我们直接按梯度方向更新参数:
但是
步长 很难调,太大容易让策略突然偏离原策略,性能骤降;
对真实光滑目标,在非零精确梯度方向上取足够小的正步长可获得局部改进;采样梯度不自动具备这一性质。
策略变化太快会导致采样分布变化过大,旧数据估计的梯度不再准确(off-policy 失效)。 训练中的采样误差、函数逼近和优化误差都可能破坏理想的改进条件,因此不能把“小步”直接当作实际回报不下降的保证。
这意味着:梯度上升没有安全步幅的保证。所以TRPO希望能找到一个有安全步幅的方法,通过信任域概念确保:
策略性能单调改进(或至少不降低)
更新步长自动适应
有效利用样本数据
推导过程
策略性能度量为
其中
为折扣因子
为状态 下的即时奖励
已知优势函数, 表示在状态 采取动作 相对于平均水平的优势
状态访问分布, 折扣加权状态访问频率
它表示在折扣权重下,一个策略访问每个状态的频率
不是我们传统的简单的概率,而是转移概率分布,所以这里的加法不是求和,而是在每一个 维度的相加
策略性能的恒等变换
任意两个策略 和 的性能满足:
物理意义:新策略的性能 = 旧策略性能 + 在旧策略优势函数下的期望提升
对旧策略加上优势函数,来代表新策略
(将时间步 消去, 化为 和 )
这个式子在大部分情况是递增的
因为新策略比旧策略更偏向那些 的动作,则权重落在"好动作"上多一些, 那么加权平均自然也会 > 0.
所以加号后应该是一个非负的分量,即
但是因为估计和近似的误差,难免避免存在 的情况。
出现一个方案:当更新步长很小时,选择忽略状态分布的变化,用旧策略分布代替:
引入代理目标函数(Surrogate Objective), 同时用旧策略分布代替, 可以构造一个代理(surrogate)函数 :
这个函数有一个好处,就是它消除了耦合的影响
强化学习的耦合(Coupling)
原式中的 和 都取决于 。
例如 , 是一个变量,而 和 是一个概率分布
当修改 值,的二次项和一次项同步变化,而和 ,任意修改一个分量,另外一个都会变化,在数学上变得很难处理
于是定义
这样一来
是固定的(不依赖 )
优化变量只剩下
可方便用样本估计、求梯度
仍能在小步长范围内保证与真实 一阶等价 (当 与 接近时, 是的一阶近似) 可微的在当前点的一阶展开与真实相等
混合策略更新
存在下界
其中
因而给出了单调改进的充分条件
推广到任意策略
将混合策略推广到任意策略对,用总变差距离(Total Variation Divergence)度量策略差异:
得到新的下界
再用 得到最终近似形式
其中 。
这就是 TRPO 的理论核心不等式, 优化 同时限制 KL 散度,可以保证策略单调改进。(拓展成任意两个随机策略(而非上面 的形式),最后找到了一个包含KL散度的下界)
换元后
其中
是在旧策略分布下定义的 surrogate 目标,
限制新旧策略间的最大 KL 散度,
是理论上推导出的常数。
在保证上面不等式成立的情况下,只要让右边的值尽量大就好了
TRPO不是纯策略梯度算法了!因为它根本没有以梯度的损失函数为目标。
惩罚项优化变成:
TRPO 优化问题
理论优化目标
其中:
为策略参数
为平均KL散度:
但要找到最大的(max)的KL散度就得一个个去算,计算开销非常大,于是TRPO用期望代替了最大值
这就是所谓的"average KL"或"expected KL".
于是约束变成
那么就是不要求每个状态都满足 KL ≤ δ, 而是只要求在旧策略访问的状态分布下,平均KL足够小,在要求一定探索性的强化学习背景下是可以接受的。
注意上面的期望不是对所有状态均匀平均,而是用旧策略的访问分布加权:
旧策略 是我们手里已经采样过的策略;我们有这些状态的样本,能准确估计期望。
对极少访问(或没访问过)的状态,即使 KL 大一点也无所谓,因为它们几乎不会影响到 的实际值。
重要性采样形式
使用分布变换技巧,计算新策略下的期望
但手里只有旧策略的数据
因此采用用重要性采样恒等式
其中 称为重要性采样权重
而 称为TRPO实际优化时使用的采样形式目标函数。
10.1.1 采样与优化:常见训练循环
- PPO、DDPG、SAC、TRPO 等在线训练通常包含采样和优化两个环节;可以交替、流水线或异步执行。离线 RL 则可能只使用固定数据集。
- 采样阶段(data collection) → 用当前策略与环境交互,得到一批数据 (s, a, r, s′)。
- 优化阶段(policy/value update) → 用当前 rollout 或经验回放中的样本更新参数,取决于算法。
- 一种常见在线组织形式是:
- 「采样 → 优化 → 再采样」的交替过程。
- On-policy —— “在” 策略上学习
- 用于执行的动作策略与用于学习的策略相同
- Off-policy —— “离” 策略而学
- 当用于执行动作的策略与用于更新的策略不同
| 类型 | 定义 | 举例 |
|---|---|---|
| On-policy(在) | 用当前策略 πθ 产生的数据来更新自己。更新后旧数据丢弃。 | PPO、A2C、TRPO、SARSA |
| Off-policy(离) | 可以用旧策略或别的策略产生的数据来更新当前策略。 | DQN、DDPG、TD3、SAC |
10.2 PPO(Proximal Policy Optimization)
https://arxiv.org/pdf/1707.06347
从 TRPO 到 PPO:动机与思想简化
在 TRPO 中,策略更新需要满足 KL 散度约束:
其核心思想是控制新旧策略之间的信任域,避免策略更新过大导致性能崩溃。然而,TRPO 需要二阶优化(如共轭梯度法)去求解约束问题,这使得算法复杂,不利于部署在 GPU 集群上工业训练.
为求解上面带约束的最值问题,需要使用数学技巧
对KL散度做二阶(Hessian)近似
然后用共轭梯度(Conjugate Gradient, CG)求解该近似下的约束优化问题
避免处理二阶问题
裁剪形式(Clipped Surrogate Objective)
设重要性采样的策略分布比值为
这个形式下的损失函数为:
其中 是一个超参数(一般取0.1-0.3)
策略分布比值为 能反应新旧分布的相似性程度
裁剪发生在代理目标内部,不是把新策略的实际比率强制限制在区间内。共享参数、多轮 minibatch 更新仍可能使其他样本的比率或 KL 显著变化。推导与实现应对照 PPO 原论文。

用四个标量样本检查裁剪方向,避免负优势分支写反:
import numpy as np
def ppo_surrogate(ratio, advantage, epsilon=0.2):
ratio, advantage = np.asarray(ratio), np.asarray(advantage)
if not 0 < epsilon < 1 or ratio.shape != advantage.shape:
raise ValueError("invalid epsilon or shapes")
if (not np.isfinite(ratio).all() or not np.isfinite(advantage).all()
or np.any(ratio <= 0)):
raise ValueError("ratio must be positive and inputs finite")
clipped = np.clip(ratio, 1 - epsilon, 1 + epsilon)
return np.minimum(ratio * advantage, clipped * advantage)
np.testing.assert_allclose(ppo_surrogate([1.4, 0.6], [1.0, -1.0]), [1.2, -0.8])
np.testing.assert_allclose(ppo_surrogate([0.6, 1.4], [1.0, -1.0]), [0.6, -1.4])优势为正时,增大该动作概率有利,但比率超过上界后该样本的有利收益被截平。
优势为负时,降低该动作概率有利,但比率低于下界后同样停止增加该样本的有利收益。反方向的坏更新并不会被对称地消除。
因此实现仍应监控近似 KL、clip fraction 和熵;KL 提前停止是额外保护机制,不是裁剪公式自动带来的硬约束。
于是优化就退化为一个普通的“一阶梯度上升问题”:
直接用 SGD 或 Adam 等深度学习方法即可优化参数
自适应KL散度惩罚项
设散度KL的期望值为
其中 是惩罚系数,并会根据目标 KL 值 动态调整:
PPO的惩罚项形式用一个启发式规则自适应调 以把平均 KL 推到目标附近( ),而不是通过 KKT/对偶最优把它精确等价为一个硬约束问题,这样就避免了求复杂方程.
TRPO关于带惩罚项的无约束问题和带约束问题(拉格朗日/KKT)等价转换的。
而PPO损失函数看起来像TRPO的减法形式。但KL散度前面的参数 和TRPO的参数 (一个用数学公式严谨计算出的式子)是不一样的。
Actor与Critic网络共享参数时的形式
这个形式是有时代背景的,在强化学习早期,硬件很难面对大参数量的形式,常让两个网络共享参数以降低参数量。
但是这样的操作有一个问题:其中一个网络被优化时,会干扰另一个。
假设我们只优化策略的损失(例如 PPO 的 ),那么反向传播时,梯度会更新共享的底层参数,使底层特征偏向于更适合策略输出。
反之亦然,如果只最小化价值函数的误差 ,底层特征又会偏向拟合价值任务,导致策略分支学到的特征不再对动作分布有区分性。
这就会让训练过程出现:
不稳定(两个头互相干扰)
收敛缓慢(梯度方向不一致) 共享参数时,一种常见做法是联合优化策略、价值和熵项。先统一优化方向:下面的 是要最大化的收益目标,而交给梯度下降优化器的是损失 。联合训练可以协调梯度来源,但并不保证两个任务的梯度没有冲突。
其中 分别控制价值误差和熵奖励。裁剪项必须把优势乘在两个候选项上:
这是 PPO 原论文公式 7 与 9 的最大化约定。若使用最小化损失,策略项和熵项前面取负号,价值误差前面取正号;不能把两种写法混在同一个优化器中。
采样长度(horizon)与优化 mini-batch 大小是两个参数。多个环境各采集 T 步,先计算回报目标和优势,再把数据分成 mini-batch 进行多轮更新。T 不必等于完整 episode 的长度,也不是最小训练单元。非终止片段末尾通常需要价值 bootstrap;真正终止与时间截断的处理应按环境语义区分。
PPO是On-Policy学习
PPO 收集数据 → 使用这些数据更新策略几次 → 丢弃旧数据 → 重新采样新轨迹 1️⃣ 采样阶段(第一阶段):
由当前策略 采样 T 步。
所有数据都与 直接对应。 2️⃣ 优化阶段 (第二阶段):
在这批数据上做 K 轮 mini-batch 更新。
这时使用的比率 。
因为数据来自 ,更新时是严格基于自己刚刚的表现进行学习。 3️⃣ 更新后丢弃旧数据:
当 更新完后( < ),旧数据对应的分布已不再一致,
所以下一轮必须重新采样新轨迹。 这正是 on-policy 的关键约束。
PPO 每一轮的优化都只依赖于当前策略 采集的数据,
旧数据不会被放进经验池反复使用(那是 off-policy 的做法,如 DDPG、SAC)。
10.3 DDPG(Deep Deterministic Policy Gradient)
DDPG 是 神经网络版的 DPG(Deterministic Policy Gradient),是 连续版的DQN
DDPG 是首个将深度神经网络与确定性策略结合的算法(适用于连续动作空间)
核心特征
确定性策略:输出确定性的动作值,而非动作概率分布
连续动作空间:专门设计用于连续控制问题(如机器人控制、自动驾驶)
Actor-Critic架构:结合策略网络(Actor)和价值网络(Critic)
离线学习:使用经验回放机制,支持从历史经验中学习
关键技术组件
双网络架构(Actor-Critic)
Actor网络(策略网络):输入状态,输出确定性动作
参数:
目标:最大化价值函数
Critic网络(价值网络):评估状态-动作对的价值
参数:
目标:准确估计 值
目标网络(Target Networks)
独立的Actor和Critic目标网络
参数更新采用软更新(缓慢跟踪):
(通常 )
减少价值估计的波动,提高训练稳定性
经验回放(Experience Replay)
存储转移元组
随机采样打破数据相关性
提高数据效率和训练稳定性
探索策略
在确定性动作上添加噪声:
常用噪声类型:OU过程噪声、高斯噪声
推导过程
Actor-Critic主网络:
Actor 输出动作
Critic 评估动作
是一个神经网络,直接预测 的最佳值。换字母以和 (预测动作的概率分布)区分.
但 不一定不是输出确定值的,也就是说也可以用 表示确定值输出。
DDPG用的 Ornstein-Uhlenbeck 噪声做探索,确保预测确定值具备探索性
有上标
数学上,尤其是强化学习领域,上标表示标记属于某个特定网络,下标通常用来标记索引、时间步或样本
Actor 和 Critic 的参数是分开的,两套参数来自完全独立的神经网络,不共享.
DDPG 用目标 Actor 产生下一步动作,再用目标 Critic 构造 TD 目标;它是带估计误差的监督信号,不是真实价值标签。
而之前非确定网络的输出还需要使用贪心策略挑选:
Critic 的损失函数:
Actor 策略梯度的损失函数
这里使用复合函数求导的链式法则,不是条件概率公式。
目标网络: 解决损失函数难收敛问题
用均方误差构造损失函数, 会通过梯度下降更新 , 以更新Q网络
实际上这里有更新循环依赖的问题: 目标值 也来自于待更新的 网络
当 每次更新时, 下一次的 计算基准也跟着改变
如果网络预测产生一点噪声或过估计噪声,它会在下一轮目标计算中被放大
这种连锁方法效应导致TD目标不稳定,表现为训练震荡甚至 值发散
直接用同一个网络计算目标值往往会使损失函数难以收敛。
一种让 变化不要那么剧烈的方法:
直接复制一份原有网络 网络,记为
原有 网络依然按照梯度下降更新
其中 ,论文中取 。
这是“软更新”的方法,与DQN第二篇论文里面“硬更新”的方法不同
同理, Actor网络也运用相同的思路:
于是DDPG 中不但有两套神经网络,而且每套又有对应的目标网络,- 共四个网络
| 网络类型 | 参数 | 功能 |
|---|---|---|
| Actor 主网络 | 输出确定动作 | |
| Critic 主网络 | 评估动作价值 | |
| Actor 目标网络 | 提供稳定的策略估计 | |
| Critic 目标网络 | 提供稳定的 值估计 |
Actor-Critic目标网络:
Actor 输出动作
Critic 评估动作
两套主网络与相应目标网络分担训练和目标估计,能缓解目标快速变化,但不保证训练稳定或收敛。
经验回放(Replay Buffers)
当智能体在环境里探索时,存储过去交互经验,把每一步经验都存入回放池
每条经验包含:
: 当前状态
: 执行动作
: 奖励
: 下一个状态
为何需要经验回收?
打破时间相关性
强化学习数据是时序相关的,但是如果直接用顺序数据训练神经网络:
网络容易记住最近状态的模式
梯度更新方差大,训练不稳定
经验回放通过随机抽样 minibatch,打破时间依赖:
同时这个“池子”是有容量的,当它满了,最老的样本就要被抛弃 因为V或Q用时序差分计算时,都需要知道下一状态
提高样本利用率
一条经验可以被使用多次(在不同 minibatch 中),加快训练收敛
10.4 TD3(Twin Delayed Deep Deterministic policy gradient)
- TD3 可视为 DDPG 的 “增强版” 或 “修正版”,其通过三个关键改进大幅提升了性能。
- DDPG 三个核心问题:
- Q 值过估计:单 Critic 网络容易高估动作的真实价值(尤其是在高维空间),导致 Actor 学习到次优策略。
- 策略更新频繁:Actor 与 Critic 同步更新,Critic 的估计误差会直接传递给 Actor,导致策略震荡
- 在训练初期,Critic 输出的 Q 值可信度极低,同时在后期经验回放池也会召回优化效果差的样本(离线学习的固有问题),因此不好的优化参数被Critic在早期被过多的传递给了Actor去学习,会导致收敛慢。
- 探索噪声设计粗糙:依赖手动添加的高斯噪声,在复杂环境中难以平衡探索与利用。
- 基于解析的方法得出的噪声的探索性有限,不能满足真实场景的需求。
- TD3对DDPG对应的三大改进

- 双Q值裁剪 Critic 网络(Twin Critics)
- TD3 借鉴了Double Q-Learning的思路,维护两个独立的 Critic 网络( )。训练时取两者的最小值作为目标 Q 值( ),通过 “保守估计” 抑制单网络的过估计偏差。这是对 DDPG 单 Critic 设计的直接修正。
- 我们知道噪声是随机的,有大有小。易知两动作价值函数的噪声有以下四种情况,就是向下取(“裁剪”),刚好和过高估计形成了定性视角下一定程度的抵消。
- (偏大、次偏大) -> 次偏大
- (偏大、偏小) -> 偏小
- (偏小、偏大) -> 偏小
- (偏小、更偏小) -> 更偏小
- 延迟策略更新(Delayed Policy Updates)
- TD3 中,Actor 网络的更新频率低于 Critic(例如每更新 2 次 Critic 才更新 1 次 Actor),给 Critic 留出更多时间收敛到更准确的估计,减少了 Actor 因 Critic 误差导致的震荡。
- : 更新固定倍率
- : 软更新系数
- 目标网络软更新为 ,左侧是目标参数;它与降低 Actor 更新频率是不同机制。
- 冻结 Actor 时仍可以使用固定行为策略或随机探索采集新数据,Critic 也能继续从回放池学习。是否预热、以及策略/价值更新比例,应按算法与数据分布设计;不能断言“Actor 不更新就没有新样本”。
- 目标策略平滑(Target Policy Smoothing)
- 在计算网络预测的动作 时,TD3 会给目标 Actor 的输出添加少量噪声,用来更新标签 , 避免目标 值因动作微小变化而剧烈波动,进一步稳定训练。
- 与TD3不同的是 DDPG的噪声只是让输出有一点变化,给 网络加上噪声,没用来更新标签 .
- 动作 是希望策略网络 预测的操作,标签 是希望 预测出的评估动作好坏的值。
- 他们正是Actor-Critic的输出好动作+评估好坏的两个方面.
- 作用上的区别
- 在使用确定型动作输出时,容易过拟合,输出卡在一个尖点走不出去了
- 目标策略平滑在目标动作附近求稳健价值,与 SARSA 使用实际下一行为动作的更新规则不同。 其中 代表输入量在输入N的时候按照下界,上界进行截断
10.5 SAC(Soft Actor-Critic)
- https://arxiv.org/pdf/1801.01290
- Soft Actor-Critic (SAC) 是一种在连续控制任务中表现出色的深度强化学习算法,它结合了演员-评论家框架和最大熵强化学习思想,在探索与利用间实现了卓越平衡。其核心在于,智能体追求的目标不仅是最大化长期累积奖励,还要最大化策略的熵(不确定性)。这使得策略在寻找高回报动作的同时,尽可能保持随机性,从而进行充分的探索。
- 提出的背景: 主流DRL算法的局限
| 传统 RL 问题 | 说明 |
|---|---|
| 样本效率低(sample inefficiency) | On-policy 算法(PPO、TRPO、A3C 等)每次更新都必须重新采样 → 非常昂贵 |
| 训练不稳定(brittle training) | Off-policy 算法如 DDPG、NAF,虽然复用数据,但经常崩掉,参数敏感 |
因此目标是找一个既稳定又高效的 deep RL 算法。
SAC 的核心诉求:能像 DDPG 那样复用旧数据(off-policy),但比DDPG 更稳定;并像 PPO 那样稳定,但比 PPO 更高效。
SAC 算法核心:最大熵强化学习(MERL)
熵的定义:衡量随机变量的混乱度(无序性),熵越高,策略随机性越强
MERL 目标函数:相比标准 RL,额外加入熵项( 为温度系数,调节熵的权重):
其中 是状态-动作对的分布, 表示策略在状态 下的熵。
Soft 函数
SAC 基于最大熵框架推导出 Soft Q-Learning 方程
定义 soft 状态价值函数:
soft 动作价值函数就是把 代入进来:
相比标准Bellman方程,它在下一个状态的值里加入了对动作熵 的考虑,因此称为软Bellman方程.
Soft Policy Iteration(软策略迭代)
策略评估(Soft Policy Evaluation)
在评估值 迭代的过程中,论文使用了比较复杂的数学描述语言,实际上和我们之前的思路是一样的。算子是函数到函数的映射。
Soft Bellman方程是TD的思想,用下一步的 计算 ,而这样就可以实现反复迭代了。
实际是一个计算值方程:
是变量 的一个值,类似地, 是变量 在 时的取值。
当不想用 “值” -> “值"的描述语言,而是用"函数” -> “函数"的描述语言时,可以借助一个算子 :
当反复作用 后,它会收敛到唯一解 ,也就是最优 函数:
策略改进(Soft Policy Improvement)
能量函数(Energy Function) 在物理中,系统趋向于能量最低的稳定状态(例如物体往低处掉、电荷趋向最低势能)
基于能量的概率分布
能量函数的 “低 = 好” 特性,需要通过指数函数 转化为 “可用于计算概率的权重”
能量的约定是 “低能量 = 高概率”,但直接对 取指数会导致 “高能量->大指数值”,与我们的需求相反。加上负号后,关系完全反转:
低能量 -> 大的 ;
高能量 -> 小的 。
指数函数 有两个关键性质,完美适配 “概率权重” 的需求:
非负数: 无论 (这里是 ) 是正还是负, 的结果永远大于0 —— 而概率的取值范围 ,非负的权重是后续归一化的前提;
单调性: 是严格单调递增函数,即 “ -> ” —— 能量的 “概率排序” 能完整传递到权重上。
但是此时值之和不等于一,需进行归一化。
配分函数(Partition Function)
配分函数 的定义非常简单:所有事件的 “概率权重” 之和(离散事件)或积分(连续事件),数学表达式为:
离散事件:
连续事件:
最终的基于能量的概率分布公式
结合前两步,每个事件 的概率为:
其中 是配分函数
玻尔兹曼分布(Boltzmann Distribution)与 Softmax:
玻尔兹曼分布来自统计物理,用于描述一个系统在温度 下出现在能量状态 的概率:
其中
: 该状态的能量
: 温度
: 玻尔兹曼常数
: 配分函数
当把常数 合并进温度,把 视为一个可调超参数,就能够得到机器学习中常用形式:
其中 表示温度参数。
超参数用来控制 的重要性程度
-> 0:系统几乎只选最低能量(最大概率)的状态 -> 接近 argmax
-> ∞:所有状态差不多等概率 -> 完全随机
能量函数 换成 价值函数 / 值的相反数: 就得到:
这就是最大熵强化学习中的策略更新公式的关键思想:
值高 -> 该动作概率更大(利用探索性)
策略与 成正比
这意味着策略倾向于在高 值的动作附近分配更高概率。
通过最小化 KL 散度来实现策略迭代,并证明该迭代收敛到最优最大熵策略:
要找一个新策略 ,让它尽量接近诱导出来形如玻尔兹曼分布 的"目标分布”。
策略更新不是直接最大化 ,而是变成 “让策略更像高 的分布”
其中 是新策略分布预先给定的族.
是一组可被神经网络参数化的、可微、可采样的概率分布.
在连续动作领域,通常指高斯策略网络族:
“族” 是强调元素间有明确关联(如共同结构、来源)的特定汇集,
“集合” 是仅需元素满足某属性、不刻意突出关联性的通用汇集。
Ps: 为什么说是"给定":
因为我们不可能优化无限复杂的策略,只能优化可参数化、可微、可更新的一类策略, 如高斯分布.
Soft Actor-Critic 实现结构:
损失函数
value网络(辅助)
的作用是用来计算 ,进而计算更新预测 值的函数 :
value网络的更新公式使用了均方差MSE如下:
其中
指的是经验回收池(Replay Buffer)
<- ——表示每一步环境交互得到的四元组都会被加入.
| 好处 | 解释 |
|---|---|
| ⭐ 提升样本效率 | 不需要像 PPO 一样每更新一次就丢掉数据 |
| ⭐ 支持多次梯度更新 | 一条轨迹可用于多次训练 |
| ⭐ 训练更稳定 | 数据分布不会随策略快速漂移 |
目标value网络软更新
是目标网络
| 网络 | 符号 | 作用 |
|---|---|---|
| 训练中的 Value 网络 | 参与优化,反向传播损失 | |
| 目标 Value 网络 | 只用于计算 Q 的目标值,不反传梯度 |
网络
策略网络
等价于
其中 是重参数化。
重参数化技巧 (它重写函数,把随机性从分布参数里剥离,重新参数化到了这个独立的 身上,实现可导)
原本策略采样动作 是产生于它的概率分布:
也就是说动作是直接从一个带参数的概率分布里抽样出来。
动作采样过程本身不可微,梯度无法穿过它,所以只能用 REINFORCE似然比法(也就是REINFORCE方法)或其变式算法。
设 是关于动作的损失函数,且是新环境的主要决定因素
那么就算用上了优势函数降低方差:
奖励信号/优势函数依然是作为乘子,方差难以保持一个满意水平,因为它们都是来自随机采样。
SAC 采用重参数化法:
将随机采样写为确定性(可导)函数加上噪声(可导)的形式:
(⊙哈达玛积)
从而可以直接对策略网络 反向传播梯度
原来的参数化方式: 随机变量 直接由分布参数 决定:
随机性隐含在 里面
新的参数化方式: 引入了一个辅助变量
将 表示为 和 的确定性变换
将随机性从分布参数中剥离,重新参数化到这个独立的 上
假设没有这个技巧,生成 为
(在计算图中,这里有一个节点叫 采样)
前向传播(Forward):可以从正态分布中拿到一个数值 ,前向过程可以正常计算
反向传播(Backward):当我们想通过 反向传播梯度到分布参数 和 时,如果只看计算图的采样节点,它会说采样是随机过程,这次得到 只是我根据分布概率随机抽取的一个结果,并不是从 和 通过一个确定性可导函数算出的。因此,输出 对参数 和 的梯度在普通微积分意义下是不存在的 —— 因为稍微改变 或 ,采样结果并不会连续可导地变化,而是从另一个分布重新抽样,结果可能跳变。
为了解决这个问题,既然采样不可导,那就把随机性抽取出来,将其变成一个输入常量
将动作 的定义,从"一个从分布里抽出来的随机变量",重写为"一个确定性的函数":
决定了动作的基准
提供了随机方向
决定了随机的幅度
计算图变化
不再是运算过程中的随机行为,而变成了计算图的一个外部输入节点。对于这一次运算来说, 就是一个固定的常数(比如 0.5)
和 :采样变成了普通的加法和乘法
再来求导
现在的 对于 和 来说,是一个完全连续、可导的函数。梯度可以顺畅地流过加法和乘法节点,一直流回神经网络的权重里.
实际操作: 由 Tanh 限制范围
tanh 是有界动作的一种参数化,不是所有仿真环境的硬性要求。[-1,1] 常是归一化动作范围,并非电机物理单位;必须按环境上下界缩放。饱和区的 tanh 导数趋近于零,也不能声称它总能改善梯度:
10.5.1 区分早期 SAC 与无独立 V 网络的版本
上文的软价值函数推导不意味着每个 SAC 实现都必须训练一个独立 V 网络。早期变体使用 V 及目标 V;另一常见实现保留两个 Q 网络、两个目标 Q 网络及一个随机策略,直接构造软 TD 目标。参数计数时还要区分网络与可学习的温度 α。
令 d 表示真正终止,下一步动作 a′ 从当前策略新采样:
两个 Q 网络分别回归该停止梯度的目标:
策略通过重参数化动作最小化:
不要把“固定 α=1 的早期推导”“自适应温度”和“移除独立 V 网络”的公式拼成一个含义不明的实现。参见 SAC 算法说明。
10.5.2 tanh 改变动作,也改变概率密度
设 u 为高斯样本,a=c+b⊙tanh(u),其中 b=(high−low)/2 为正尺度,c=(high+low)/2 为中心。其 log-probability 为:
下面仅测试采样、密度变换和梯度,不是完整 SAC 训练。softplus 写法避免直接计算接近零的 1−tanh²(u)。
import math
import torch
from torch.nn import functional as F
from torch.distributions import Normal
torch.manual_seed(42)
mean = torch.zeros((4, 2), dtype=torch.float64, requires_grad=True)
log_std = torch.full((4, 2), -0.5, dtype=torch.float64, requires_grad=True)
low = torch.tensor([-2.0, -1.0], dtype=mean.dtype)
high = torch.tensor([2.0, 3.0], dtype=mean.dtype)
scale, center = (high - low) / 2, (high + low) / 2
distribution = Normal(mean, log_std.exp())
raw = distribution.rsample()
action = center + scale * torch.tanh(raw)
log_tanh_jacobian = 2 * (math.log(2) - raw - F.softplus(-2 * raw))
log_prob = (distribution.log_prob(raw) - scale.log()
- log_tanh_jacobian).sum(dim=-1)
assert torch.all((action >= low) & (action <= high))
assert torch.isfinite(log_prob).all()
(-log_prob.mean()).backward() # 只验证计算图,不代表实际 SAC 的策略损失。
assert torch.isfinite(mean.grad).all()
assert torch.isfinite(log_std.grad).all()动作经额外硬裁剪后,以上连续密度公式不能直接照搬;环境的动作变换也应与训练时记录的 log-probability 一致。
11. 训练日志的最小集合
至少同时记录 episode 回报、长度、终止/截断比例、价值损失、策略熵与实际动作范围。PPO 增加 KL 与 clip fraction,SAC 增加温度及双 Q 的统计量。日志只能帮助定位问题,不能由单个损失下降推断策略一定变好。
固定评估策略、环境版本和评估预算,报告多个随机种子的分布。不要把探索期回报、关闭探索后的回报以及不同 episode 终止规则的结果直接比较。
阅读自测与验收
- 手算正、负优势下 PPO 的四种裁剪情形,再检查 SAC 中 tanh 与动作缩放后的 log-probability。
- 区分在线/离线数据、同策略/异策略以及终止/截断;单一训练损失下降不能替代多种子的策略评估。