百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

从头开始绘制和编码18种RL算法(3/4)

moboyou 2025-05-28 21:31 17 浏览

接上文继续.......

DDPG(Actor-Critic for Continuous Actions)

深度确定性策略梯度 (DDPG) 将actor-critic的思想扩展到具有连续动作空间的环境(例如,施加特定的扭矩、设置速度)。

它巧妙地将 DQN 的思想(如重放缓冲区和目标网络)与确定性参与者策略结合起来。

核心思想是:

  • 确定性行动者:与输出动作概率的 REINFORCE/A2C/PPO 不同,DDPG 参与者网络为给定状态输出特定的确定性动作。
  • Q-Critic:批评家网络学习一个 Q 函数 Q(s, a),类似于 Q 学习,评估在状态下采取参与者选择的连续动作(a)的值。
  • Off-Policy:使用重放缓冲区来存储经验并采样小批量,从而可以从过去的数据中进行稳定的学习,类似于DQN
  • 目标网络:演员评论家采用单独的目标网络,以稳定评论家的目标值的学习。
  • 探索:由于策略是确定性的,因此需要手动添加探索,通常是在训练期间向参与者的输出动作添加噪声(例如高斯噪声Ornstein-Uhlenbeck 噪声)。

交互过程中,Actor网络根据当前状态(s )产生确定性的动作

在环境中执行之前,会将噪声添加到此操作中进行探索。生成的转换(s, a,r,s',完成)存储在重播缓冲区中。

在训练期间,从缓冲区中抽取一批数据。

  • Critic Update:使用奖励(r)和在下一个状态(s')评估的目标参与者和目标批评家网络计算目标 Q 值(TD Target y)。主 Critic 网络计算采样状态和动作的 Q(s,a)。目标(y)和 Q(s,a)之间的差异(MSE 损失)用于更新主要 Critic 参数 (φ).
  • Actor Update:Actor 网络计算采样状态的动作。这些动作被馈送到主 Critic 网络以获取它们的 Q 值。Actor 被更新以产生最大化该 Q 值的动作(通常通过最小化负 Q 值,L_θ=-Q)。
  • 目标更新:目标参与者和目标批评家网络 (θ', φ') 使用软更新向主要网络参数缓慢更新。

DDPG 需要单独的 Actor 和 Critic 网络。Actor 输出特定的连续动作,通常使用 tanh 进行缩放。

评论家将状态和动作都作为输入。

# 简化的 DDPG Actor Network(输出连续动作)
class  ActorNetworkDDPG (nn.Module): 
    def  __init__ ( self, state_dim: int , action_dim: int , max_action: float ): 
        super (ActorNetworkDDPG, self).__init__() 
        self.layer1 = nn.Linear(state_dim, 256 ) 
        self.layer2 = nn.Linear( 256 , 256 ) 
        self.layer3 = nn.Linear( 256 , action_dim) 
        self.max_action = max_action # 缩放输出

    def  forward ( self, state: torch.Tensor ) -> torch.Tensor: 
        x = F.relu(self.layer1(state)) 
        x = F.relu(self.layer2(x)) 
        # 使用 tanh 输出 -1 到 1 之间的值,然后缩放
        action = self.max_action * torch.tanh(self.layer3(x)) 
        return action 

# 简化的 DDPG 评论网络(采用状态和动作)
class  CriticNetworkDDPG (nn.Module): 
    def  __init__ ( self, state_dim: int , action_dim: int ): 
        super (CriticNetworkDDPG, self).__init__() 
        # 分别或一起处理状态和动作
        self.layer1 = nn.Linear(state_dim + action_dim, 256 ) 
        self.layer2 = nn.Linear( 256 , 256 ) 
        self.layer3 = nn.Linear( 256 , 1 ) # 输出单个 Q 值

    def  forward ( self, state: torch.Tensor, action: torch.Tensor ) -> torch.Tensor: 
        # 连接状态和动作
        x = torch.cat([state, action], dim= 1 ) 
        x = F.relu(self.layer1(x)) 
        x = F.relu(self.layer2(x)) 
        q_value = self.layer3(x)
        return q_value

这些网络定义了核心组件。Actor 学习要做什么,而 Critic 学习这些动作的效果如何

我们还需要一个重放缓冲区(类似于 DQN,存储(状态、动作、奖励、下一个状态、完成))和目标网络,它们是缓慢更新的主网络的副本。

# 重放缓冲区(概念 - 使用双端队列或列表)
# 目标网络(概念 - 创建演员/评论家的副本)
 target_actor = ActorNetworkDDPG(...)
target_critic = CriticNetworkDDPG(...)
target_actor.load_state_dict(actor.state_dict())# 初始化
target_critic.load_state_dict(critic.state_dict())

核心更新逻辑涉及计算两个网络的损失并对目标应用软更新。

# --- DDPG 更新逻辑大纲 --- 
#(假设优化器:定义了 actor_optimizer、critic_optimizer)
#(假设 tau:软更新率,gamma:折扣因子已定义)

# 1. 从 replay_buffer 中抽取一批:states、actions、rewards、next_states、dones 

# --- 评论家更新 --- 
# 从目标参与者获取下一个动作:next_actions = target_actor(next_states) 
# 从目标评论家获取目标 Q 值:target_q = target_critic(next_states, next_actions) 
# 计算 TD 目标:td_target = rewards + gamma * (1 - dones) * target_q 
# 获取当前 Q 值估计:current_q = critical(states, action) 
# 计算评论家损失(MSE):critic_loss = F.mse_loss(current_q, td_target.detach()) 
# 更新评论家:
 critical_optimizer.zero_grad() 
critical_loss.backward() 
critical_optimizer.step() 

# --- 演员更新 --- 
# 从主演员获取当前状态的动作: actor_actions = actor(states) 
# 计算演员损失(来自主评论家的负 Q 值): actor_loss = -critic(states, actor_actions).mean() 
# 更新演员:
 actor_optimizer.zero_grad() 
actor_loss.backward() 
actor_optimizer.step() 

# --- 软更新目标网络 ---
 soft_update(target_critic, critical, tau) 
soft_update(target_actor, actor, tau)

这个大纲展示了离线策略数据、目标网络以及针对参与者和评论家的单独更新如何在 DDPG 中结合在一起,以实现在连续动作空间中的学习。

请记住,实际互动需要在演员的输出中添加噪音以供探索。

  1. 奖励:代理表现出明显的学习效果。每集的总奖励(左图)一开始非常低(大约 -1500),然后稳步增加,到第 100 集时移动平均值达到大约 -250。这表明在控制钟摆以最小化成本方面取得了显著的进步。性能有些嘈杂,但趋势非常积极。
  2. 评论家表现:平均评论家损失(中心图)在整个训练过程中稳步增加。这似乎违反直觉,但在 DDPG 中,随着参与者变得越来越好并达到更高价值的状态,目标 Q 值也会增加。评论家不断尝试学习这些不断发展的更高价值,因此在成功学习的同时可能会出现损失增加,这反映了被近似的价值函数的规模/复杂性不断增加。
  3. 演员表现:演员平均“损失”(右图,标记为平均 Q 值,可能显示演员实现的平均Q 值)呈现出强劲的上升趋势,与奖励有很好的相关性。这意味着演员成功地学会了选择评论家估计会带来更高奖励(更低成本)的行动,从而推动了性能的提升。它似乎正在向终点靠拢。

DDPG 成功学会了控制钟摆,在 100 集的比赛中显著提高了总奖励。尽管在这种设置中经常观察到批评家损失增加,但演员(寻找更好的行动)和批评家(估算价值)都表现出有效学习的迹象。

SAC(Maximum Entropy Actor-Critic)

软Actor-Critic (SAC) 是另一种先进的演员-评论家算法,专为连续动作空间而设计,基于 DDPG 和 TD3。其定义特征是结合了最大熵强化学习框架。

这意味着代理不仅受到激励去最大化奖励,而且还会在这样做的同时尽可能随机地行动(最大化策略熵)。

这种熵最大化鼓励更广泛的探索,提高鲁棒性,并且与 DDPG 相比,通常可以实现更快、更稳定的学习。SAC 也是离线策略的,使用像 DDPG 这样的重放缓冲区。

交互类似于其他演员 - 评论家,但是演员 (π_θ) 是随机的 —— 它输出一个采样动作的分布。经验(s,a,r,s',完成)存储在重放缓冲区中。

在训练期间(抽样一批

  • 批评家更新:目标 Q 值计算是唯一的。它使用目标网络来获得从当前策略中采样的下一个动作(a')的最小 Q 值估计(Min Q')(在下一个状态 s' 评估)。
  • 然后它从这个最小 Q 值中减去熵项(α*logπ(a'|s'))以获得 “软目标”。TD 目标(y)是使用奖励和这个软目标形成的。更新主要批评家(Q_,通常是双胞胎批评家 Q1、Q2),以最大限度地减少对 y 的 MSE 损失。
  • Actor 更新:Actor 对当前批处理状态 s 的一个动作(a_π)进行采样。损失鼓励导致高 Q 值(由主要批评家估计)并具有高熵(高 logπ)的动作。损失大约为
(α * log π - min(Q1, Q2)).mean()
  • Alpha 更新(可选):可以通过定义目标熵级别和更新 α 来自动调整熵温度(α),以鼓励策略的实际熵与目标匹配。
  • 目标更新:软更新应用于目标批评家网络。SAC 通常不使用目标参与者,使用当前参与者进行目标动作采样。

核心概念是这样的:

  • 最大熵:将策略的熵H(π(·∣s))H(π(·∣s))添加到标准奖励目标中。
  • 随机行为者:输出连续动作概率分布(如高斯分布)的参数(例如平均值、标准差)。动作是采样的。
  • 双重评论家:使用两个 Q 网络并取其目标值中的最小值来对抗 Q 值高估(类似于 TD3)。
  • 熵温度:平衡奖励和熵目标。可以固定或自动调整。
LOG_STD_MAX = 2
 LOG_STD_MIN = - 20
 EPSILON = 1e-6  # 用于log_prob计算中的数值稳定性

class  ActorNetworkSAC (nn.Module): 
    """ Stochastic Gaussian Actor for SAC. """ 
    def  __init__ ( self, state_dim: int , action_dim: int , max_action: float ): 
        super (ActorNetworkSAC, self).__init__() 
        self.layer1 = nn.Linear(state_dim, 256 ) 
        self.layer2 = nn.Linear( 256 , 256 ) 
        self.mean_layer = nn.Linear( 256 , action_dim)     # 输出平均值
        self.log_std_layer = nn.Linear( 256 , action_dim) # 输出对数标准差
        self.max_action = max_action 

    def  forward ( self, state: torch.Tensor ) -> Tuple [torch.Tensor, torch.Tensor]: 
        """ 输出压缩动作及其对数概率。 """
         x = F.relu(self.layer1(state)) 
        x = F.relu(self.layer2(x)) 
        mean = self.mean_layer(x) 
        log_std = self.log_std_layer(x) 
        log_std = torch.clamp(log_std, LOG_STD_MIN, LOG_STD_MAX) # 限制稳定性
        std = torch.exp(log_std) 

        # 使用重新参数化技巧创建分布和样本
        normal_dist = Normal(mean, std) 
        z = normal_dist.rsample() # 可微分样本(预压缩)
         action_squashed = torch.tanh(z) # 应用 tanh 压缩

        # 计算带有 tanh 校正的 log_prob 
        # log_prob = log_normal(z) - log(1 - tanh(z)^2 + eps)
         log_prob = normal_dist.log_prob(z) - torch.log( 1 - action_squashed. pow ( 2 ) + EPSILON) 
        # 如果 action_dim > 1,则在动作维度上对 log_prob
        求和 log_prob = log_prob. sum (dim=- 1 , keepdim= True ) 

        # 将动作缩放到环境边界
        action_scaled = action_squashed * self.max_action 

        return action_scaled, log_prob

Critic 网络通常采用 Twin Q 结构,类似于 TD3,以状态和动作作为输入。

# Critic Network (Twin Q) for SAC
class CriticNetworkSAC(nn.Module):
    def __init__(self, state_dim: int, action_dim: int):
        super(CriticNetworkSAC, self).__init__()
        # Q1 architecture
        self.l1_q1 = nn.Linear(state_dim + action_dim, 256)
        self.l2_q1 = nn.Linear(256, 256)
        self.l3_q1 = nn.Linear(256, 1)
        # Q2 architecture
        self.l1_q2 = nn.Linear(state_dim + action_dim, 256)
        self.l2_q2 = nn.Linear(256, 256)
        self.l3_q2 = nn.Linear(256, 1)

    def forward(self, state: torch.Tensor, action: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
        sa = torch.cat([state, action], 1)
        q1 = F.relu(self.l1_q1(sa))
        q1 = F.relu(self.l2_q1(q1))
        q1 = self.l3_q1(q1)

        q2 = F.relu(self.l1_q2(sa))
        q2 = F.relu(self.l2_q2(q2))
        q2 = self.l3_q2(q2)
        return q1, q2     

更新逻辑结合了演员、评论家和可选的 alpha 的损失。此大纲捕获了 SAC 的关键计算,强调了目标 Q 值中的熵项和演员损失,以及双胞胎评论家和可选 alpha 调整的使用。

  1. 奖励: SAC 表现出强大的学习能力,经过大约 40-50 轮训练后,总奖励从 -1500 左右迅速增加到 -200 左右。收敛后,性能相对稳定。
  2. 评论家表现:平均评论家损失最初显着增加,然后以一定的差异趋于稳定,类似于 DDPG,这反映了随着演员的进步,评论家学习到越来越高的状态动作值。
  3. 演员表现:演员损失最初迅速增加(表明成功向更高的 Q 值进行策略更新),在第 40 集左右达到峰值,然后随着策略可能稳定下来并且熵正则化发挥更大的作用而下降。
  4. 熵/Alpha:熵温度 (Alpha) 会自动调整。它最初会降低,然后在主要学习阶段 (ep 20-55) 大幅增加,然后随着策略在最后变得更加确定和自信而再次降低。Alpha 损失图在零附近波动,证实了调整机制正在发挥作用以维持目标熵。

SAC 有效地解决了 Pendulum 任务,表现出快速收敛到高回报的能力。熵系数 (alpha) 的自动调整在整个训练过程中动态平衡探索和利用,有助于实现稳定高效的学习。

TRPO(Constrained Policy Updates)

信赖域策略优化(TRPO)是一种较早的策略梯度算法,专注于实现单调的策略改进,确保(以高概率)每个策略更新步骤不会降低性能。

它通过限制单次更新中策略可以改变的程度来实现这种稳定性,以新旧策略之间的 KL 散度来衡量。

TRPO 是一种基于策略的演员评论家算法(尽管评论家主要用于优势估计,而不是在核心 TRPO 约束内更新)。

其主要挑战在于有效地解决约束优化问题。

与其他在线策略方法一样,TRPO 使用当前策略 (πold) 收集一批经验并计算优势 (A)。然后,它计算标准策略梯度 ('g')。

TRPO 的核心在于找到更新步骤。它不是简单地沿着“g”前进,而是旨在解决一个受约束的问题:在保持新旧策略之间的 KL 散度低于阈值(δ)的前提下,最大化策略改进。

这可以通过找到满足 Fs≈g 的步进方向“s”来近似解决,其中 F 是 Fisher 信息矩阵(近似 KL 约束曲率)。

TRPO 使用共轭梯度(CG)算法来有效地找到“s”,而无需明确形成或反转 F。CG 只需要计算Fisher-Vector Products (FVP)

一旦找到“s”,回溯线搜索就会确定最大步长 (α),以便更新满足实际的 KL 约束和替代优势改进条件

  • 信任区域:将策略更新限制在性能改进近似成立的区域,确保稳定性。
  • KL 散度约束:使用平均 Kullback-Leibler 散度DKL(πold∣∣πnew)≤δDKL(πold∣∣πnew)≤δ作为策略变化的衡量标准。
  • 费舍尔信息矩阵 (FIM):表示策略分布空间的曲率。TRPO 使用涉及 FIM 的二次形式来近似 KL 约束
  • 费舍尔向量积 (FVP):一种使用自动微分有效计算 FIM 与任意向量乘积的技术,避免计算和存储完整的 FIM。
  • 共轭梯度(CG):一种仅使用 FVP 解决线性系统Fs≈g 以找到更新方向的迭代算法。
  • 线搜索:确保采取的最后一步实际上满足 KL 约束并根据替代目标提高性能。

TRPO 需要 Actor(策略网络)和 Critic(价值网络)定义(与 A2C/PPO 相同)。

复杂的部分是 FVP、CG 和线搜索。以下是概念大纲(有关更详细、可运行但复杂的代码,请参阅12_trpo.ipynba3c_training.py )。

# Computes the Fisher vector product using Hessian-vector product approximation
# This is used in the conjugate gradient method for computing the search direction
# in the TRPO update step.
def fisher_vector_product(actor, states, vector, cg_damping):
    log_probs = actor.get_log_probs(states).detach()
    kl = (log_probs.exp() * (log_probs - log_probs.detach())).sum()
    grads = torch.autograd.grad(kl, actor.parameters(), create_graph=True)
    flat_grads = torch.cat([g.view(-1) for g in grads])
    
    gv = torch.dot(flat_grads, vector)
    hv = torch.autograd.grad(gv, actor.parameters())
    flat_hv = torch.cat([h.view(-1) for h in hv])
    
    # Adds a damping term to improve numerical stability
    return flat_hv + cg_damping * vector

# Implements the conjugate gradient method to solve Ax = b
# This is used to approximate the natural gradient direction
# in the TRPO update step.
def conjugate_gradient(fvp_func, b, cg_iters=10, tol=1e-10):
    x = torch.zeros_like(b)  # Initialize solution vector
    r = b.clone()  # Residual
    p = b.clone()  # Search direction
    rs_old = torch.dot(r, r)
    
    for _ in range(cg_iters):
        Ap = fvp_func(p)
        alpha = rs_old / torch.dot(p, Ap)
        x += alpha * p
        r -= alpha * Ap
        rs_new = torch.dot(r, r)
        
        if rs_new < tol:
            break
        
        p = r + (rs_new / rs_old) * p
        rs_old = rs_new
    
    return x

# Performs backtracking line search to find an acceptable step size
# Ensures that the KL divergence constraint is satisfied
# and that the new policy improves the surrogate loss.
def backtracking_line_search(actor, states, actions, advantages, old_log_probs,
                             step_direction, initial_step_size, max_kl, decay=0.8, max_iters=10):
    theta_old = {name: param.clone() for name, param in actor.named_parameters()}  # Store old parameters
    
    for i in range(max_iters):
        step_size = initial_step_size * (decay ** i)  # Reduce step size progressively
        
        # Apply the step to actor parameters
        for param, step in zip(actor.parameters(), step_size * step_direction):
            param.data.add_(step)
        
        # Compute KL divergence and surrogate loss
        kl = actor.kl_divergence(states, old_log_probs)
        surrogate = actor.surrogate_loss(states, actions, advantages, old_log_probs)
        
        # Check if KL is within constraint and surrogate loss has improved
        if kl <= max_kl and surrogate >= 0:
            return step_size * step_direction, True
        
        # Restore old parameters if step is not successful
        for name, param in actor.named_parameters():
            param.data.copy_(theta_old[name])
    
    return None, False  # Return failure if no valid step found

# Updates the actor (policy) and critic (value function) using TRPO algorithm.
def update_trpo(actor, critic, actor_optimizer, critic_optimizer,
                states, actions, advantages, returns_to_go, log_probs_old,
                max_kl=0.01, cg_iters=10, cg_damping=0.1, line_search_decay=0.8,
                value_loss_coeff=0.5, entropy_coeff=0.01):
    
    # Compute policy gradient
    policy_loss = actor.surrogate_loss(states, actions, advantages, log_probs_old)
    grads = torch.autograd.grad(policy_loss, actor.parameters())
    g = torch.cat([grad.view(-1) for grad in grads])
    
    # Compute the natural gradient direction using conjugate gradient
    fvp_func = lambda v: fisher_vector_product(actor, states, v, cg_damping)
    step_direction = conjugate_gradient(fvp_func, g, cg_iters)
    
    # Compute step size based on KL constraint
    sAs = torch.dot(step_direction, fvp_func(step_direction))
    step_size = torch.sqrt(2 * max_kl / (sAs + 1e-8))
    
    # Perform backtracking line search to ensure KL constraint is satisfied
    step, success = backtracking_line_search(actor, states, actions, advantages, log_probs_old,
                                             step_direction, step_size, max_kl, line_search_decay)
    
    # Apply the step if successful
    if success:
        with torch.no_grad():
            for param, step_val in zip(actor.parameters(), step):
                param.data.add_(step_val)
    
    # Compute and update value function using MSE loss
    value_loss = nn.MSELoss()(critic(states), returns_to_go)
    critic_optimizer.zero_grad()
    value_loss.backward()
    critic_optimizer.step()
    
    return policy_loss.item(), value_loss.item()  # Return loss values for monitoring

TRPO 通过使用二阶信息(由 FIM 近似)明确约束策略更新大小来确保稳定性,通过 CG 和 FVP 有效计算,并通过线搜索进行验证。

  1. 学习进度和表现: TRPO 表现出极快的学习速度。平均奖励在约 20-30 次迭代内迅速攀升至接近最优值并保持稳定。同样,平均情节长度急剧下降并很快稳定在较低水平。
  2. 评论稳定性:评论损失在一些初始波动之后相对较快地稳定下来,表明价值函数得到有效且一致的学习。
  3. 策略更新约束 (KL): “实际 KL 散度”图显示每次迭代中策略的变化。TRPO 旨在将其保持在较小水平(低于红色最大 KL 线,通常约为 0.01),以确保稳定更新。绘制的值不寻常(通常为负值,这对于 KL 来说不应该发生),但目的是实现稳定、受约束的更新。
  4. 优化目标: “替代目标”图显示了在采取步骤之前 TRPO 计算出的预期改进。它在零附近波动表明逐步改进的保证并不总是完美实现,但总体结果还是不错的。

TRPO 表现出了出色的样本效率,在此网格任务上快速收敛到高性能且稳定的策略。尽管内部优化指标图(KL、替代目标)中存在一些噪声/异常,但核心信任区域方法有效地引导学习走向强大的解决方案。

DQN(Deep Q-Learning)

Q-Learning 非常适合具有少量且可管理的离散状态的环境。

然而,当状态空间变得非常大或连续时(例如处理游戏像素或机器人传感器数据),它就会遇到困难。

深度 Q 网络(DQN)通过用近似Q 值深度神经网络替换 Q 表来解决此问题,Q(s,a;θ) 表示网络的参数。

DQN 引入了两项关键创新来在使用 Q 学习的神经网络时稳定学习:经验重放目标网络

代理使用基于其主 Q 网络 ( Q_θ) 的 epsilon-greedy 策略与环境进行交互。

每个经验元组(s, a, r, s', done)都存储在重放缓冲区 (Replay Buffer)中。

对于训练,代理从缓冲区中采样这些经验的小批量。对于批次中的每个经验,使用观察到的奖励 r 和下一个状态 s 可实现的最大 Q 值计算目标 Q 值(“TD 目标 y”),由单独的、缓慢更新的目标网络(Q_θ)估计。

主 Q 网络预测状态 s 中实际采取的动作 a 的 Q 值。计算目标 y 与预测 Q 值之间的差异(MSE 或 Huber 损失),并使用梯度下降来更新主 Q 网络的参数(θ)。

定期地,主网络的权重被复制到目标网络:θ←θ。

DQN的核心概念包含:

  • 近似的神经网络(例如 MLP、CNN)Q(s, a)。它通常将状态s作为输入,并输出所有离散动作的Q 值。
  • 存储过去转换的缓冲区。随机小批量采样打破了数据相关性并提高了稳定性和效率(s, a, r, s', done)
  • Q 网络单独副本,其权重 ( ) 更新频率较低(例如,每一步或通过缓慢的“软”更新)。它提供稳定的目标θC

它为训练主 Q 网络提供了稳定的目标,防止出现振荡。

首先,定义 Q 网络。对于简单的矢量状态(例如我们的网格世界的归一化坐标),MLP 就足够了。

# DQN 网络(MLP)
class  DQN(nn.Module):
    def  __init__ ( self, n_observations: int , n_actions: int ): 
        super (DQN, self).__init__() 
        self.layer1 = nn.Linear(n_observations, 128 ) 
        self.layer2 = nn.Linear( 128 , 128 ) 
        self.layer3 = nn.Linear( 128 , n_actions) # 输出每个动作的 Q 值

    def  forward ( self, x: torch.Tensor ) -> torch.Tensor: 
        """ 前向传递以获取 Q 值。 """ 
        # 确保输入是正确设备上的浮点张量
        if  not  isinstance (x, torch.Tensor): 
            x = torch.tensor(x, dtype=torch.float32, device=x.device) 
        elif x.dtype != torch.float32:
            x = x.to(dtype=torch.float32)

        x = F.relu(self.layer1(x))
        x = F.relu(self.layer2(x))
        return self.layer3(x)# 原始 Q 值

该网络学习从状态到动作值的映射。

接下来,重播记忆存储经验。

# 用于存储转换的结构
Transition = namedtuple( 'Transition' , 
                        ( 'state' , 'action' , 'next_state' , 'reward' , 'done' )) 

# 重放内存缓冲区
class  ReplayMemory : 
    def  __init__ ( self , capacity: int ): 
        self .memory = deque([], maxlen=capacity) 

    def  push ( self , * args: Any ) -> None: 
        "" " 保存转换元组 (s, a, s', r, done)。" "" 
        # 确保张量存储在 CPU 上,以避免缓冲区出现 GPU 内存问题
        processing_args = [] 
        for arg in  args: 
            if isinstance(arg, torch.Tensor): 
                processing_args.append(arg.cpu()) 
            elif isinstance(arg, bool): # 将完成标志存储为张量以保持一致性
                 processing_args.append(torch.tensor([arg], dtype=torch.bool)) 
            else:
                 processing_args.append(arg) 

        self .memory.append(Transition(*processed_args)) 


    def  sample ( self , batch_size: int ) -> Optional[List[Transition]]: 
        "" " 随机抽样一批转换。 " "" 
        if len( self .memory) < batch_size: 
            return None 
        return random.sample( self .memory, batch_size) 

    def  __len__ ( self ) -> int: 
        return len( self .memory)

此缓冲区允许对不相关的批次进行采样以进行训练更新。动作选择使用基于主 Q 网络输出的 epsilon-greedy。

# 动作选择(使用 DQN 的 Epsilon-Greedy)
def  select_action_dqn ( state: torch.Tensor, 
                        policy_net: nn.Module, 
                        epsilon: float , 
                        n_actions: int , 
                        device: torch.device ) -> torch.Tensor: 
    """ 使用策略 Q 网络以 epsilon-greedily 方式选择动作。 """ 
    if random.random() < epsilon: 
        # 探索:选择一个随机动作
        action = torch.tensor([[random.randrange(n_actions)]], device=device, dtype=torch.long) 
    else : 
        # 利用:根据 Q 网络选择最佳动作
        with torch.no_grad(): 
            # 如果需要,添加批量暗淡,确保张量在正确的设备上
            state = state.unsqueeze( 0 ) if state.dim() == 1  else state 
            state = state.to(device) 
            # 获取 Q 值并使用最大值选择动作Q
             action = policy_net(state). max ( 1 )[ 1 ].view( 1 , 1 )
    return action

优化步骤利用目标网络计算TD目标并更新主策略网络

# DQN 优化步骤概述
def  optimize_model_dqn ( memory: ReplayMemory, 
                         policy_net: DQN, 
                         target_net: DQN, 
                         optimizer: optim.Optimizer, 
                         batch_size: int , 
                         gamma: float , 
                         device: torch.device ): 
    """ 在 DQN 策略网络上执行一步优化。 """ 
    # 1. 从内存中采样批次
    # 2. 在“设备”上准备批次张量(状态、动作、奖励、下一个状态、完成)
    # 3. 使用 policy_net 计算所采取动作的 Q(s_t, a_t)
            state_action_values = policy_net(state_batch).gather( 1 , action_batch) 
    # 4. 计算 V(s_{t+1}) = max_{a'} Q(s_{t+1}, a'; θ) 使用 target_net
           与torch.no_grad():下一个状态值 = 目标网络 (非最终下一个状态)。max ( 1 )[ 0 ] 
    # 5. 计算 TD 目标 y = 奖励 + gamma * V(s_{t+1})(处理终端状态)
               expected_state_action_values = (next_state_values * gamma) + reward_batch 
    # 6. 计算 Q(s_t, a_t) 和 TD 目标 y 之间的损失(例如,Huber 损失)
               loss = F.smooth_l1_loss(state_action_values, expected_state_action_values.unsqueeze( 1 )) 
    # 7. 优化 policy_net
               optimizer.zero_grad() 
              loss.backward() 
              torch.nn.utils.clip_grad_value_(policy_net.parameters(), 100 ) # 可选的梯度裁剪
              optimizer.step() 
    
# 请参阅 13_dqn.ipynb 中的完整实现

这个核心循环与目标网络的定期更新相结合,使得 DQN 即便使用神经网络等复杂的函数逼近器也能够有效地学习。

  1. 学习进度: DQN 表现出清晰的学习效果。平均奖励从负值显著增加,在约 200-250 集后趋向稳定的正奖励。
  2. 效率:随着时间的推移,情节长度大幅减少,与奖励增加相关,表明代理学会更快地达到目标。
  3. 探索: epsilon 衰减图显示探索逐渐减少,使得代理能够在后续情节中有效地利用其学到的知识。
  4. 学习策略:最终的策略网格展示了一个连贯的策略,其中的动作通常会引导代理走向目标状态“G”(右下)。

DQN 成功学会了在自定义网格环境中导航,并找到了通往目标的有效路径。以奖励和情节长度衡量的性能显著提高,并且随着探索次数的减少而趋于稳定。

未完待续......

相关推荐

2025春新教材|七年级下册数学(实数混合运算)专项训练,可打印

2025春新教材|七年级下册数学(实数混合运算)专项训练,可打印!计算能力是数学学习的基础,也是衡量学生数学素养的重要指标。在七年级数学学习中,计算训练尤为重要。这一阶段的数学知识涉及代数运算、几何计...

《类题通法》7.2.2:复数的乘、除运算

一、复数的乘、除法运算的思路方法(1)复数的乘法运算可以把i看作字母,类比多项式的乘法进行,注意要把i方化为﹣1,进行最后结果的化简。(2)复数的除法一般先写成分式形式,再把分母实数化,即分子、分母同...

GESP C++五级考试的难点突破指南

GESPC++五级考试难点突破指南结合考试大纲与备考经验,从核心知识点、高频难点、备考策略三方面深入剖析,助力高效备考。一、核心知识点梳理GESPC++五级考试聚焦数论、数据结构、算法设计与优...

初一数学上册期末总结

涵盖重点知识、示例解析、练习题及综合试卷,帮助学生系统复习:一、重点难点总结第一单元:有理数重点:有理数的分类(整数/分数、正/负/0);数轴、相反数、绝对值的概念及应用;有理数的混合运算...

数列求和中的放缩法

数列求和的本质是将多项式的和式化简,其最基本的方法是利用等差﹑等比求和公式化简,此外常见的求和方法还有倒序相加法、错位相减法、裂项相消法、并项求和法等.而高考中,数列解答题更多地表现为数列求和的“不等...

高中数学:整体换元法例题之二(附答案)

整体换元法例题之二。上一讲讲了用整体换元法进行计算,今天再讲一道比上一讲更复杂的一道题。·第一个括号从二分之一一直加到二零二四分之一。·第二个括号是从一加二分之一一直加到二零二三分之一。·第三个括号是...

25版本七上数学勤学早大培优P54-化简求值-整体求值 #数学

看了第六个板块整体求值,整体求值第一个整体带入,整体带入来看一下它到底想干什么。当x等于二十,mx的立方加上二x的平方加上nx加四等于十八,问当x等于二十求该多强值的值?就求这个多强值的值。这题还是来...

10秒读懂因式分解,10分钟后你再也不会做错任何一道因式分解题目

因式分解其实初中数学里比较简单的数学运算了,学习因式分解的主要作用是在后面解一元二次不等式及高中函数题目作铺垫的内容,因此因式分解是基础性必须掌握的内容,没有掌握会对后面的学习造成阻碍。因式分解简单的...

用拆项、添项法分解因式

因式分解是多项式乘法的逆运算。在多项式乘法运算时,整理、化简常将几个同类项合并为一项,或将两个仅符号相反的同类项相互抵消为零。在对某些多项式分解因式时,需要恢复那些被合并或相互抵消的项,即把多项式...

“乘法公式——平方差公式”教学设计与分析

谢立光(江西省赣县教育局教研室)摘要:教材安排学生在学习了多项式的乘法以后,学习平方差公式,这是教材编排遵循从一般到特殊的认知规律的典型范例.平方差公式的结构特点及公式中字母的含义对学生来讲非常抽...

单项式与多项式

本篇介绍单项式和多项式的概念,阐述多项式乘法法则。5.2.1单项式在上一节,我们了解到没有加减运算的整式叫做单项式。我们把单项式中的常数因数叫做这个单项式的系数(coefficient),所有变量的指...

整式的加、减、乘、除混合运算完全指南 ——初中数学核心技能突破

一、整式运算基础概念回顾1.整式定义由数字、字母通过加、减、乘、乘方运算构成的代数式(分母不含字母)。示例:3,-2ab+5,4-y+12.同类项识别-条件:字母相同,相同字母指数相同...

初一数学:合并同类项和去括号知识点和题型总结,分基础和提升

初一数学:合并同类项和去括号知识点和题型总结,分基础和提升整式加减的核心是合并同类项,所以学好合并同类项非常得关键,接下来老师整理了合并同类项和去括号的知识点和题型,值得每一位初一的孩子学习。首先我们...

初中数学:有关绝对值的多项式计算

初一年级的绝对值问题是一个重点难点,多项式计算许也有一定的难度,二者结合同学对此还有些理解不到位的情况,下面通过两道计算题来讲述这类计算问题。解:∵平方项和绝对值项都不可能是负数,∴二者只能都为0(x...

初一数学:4.2 整式的加法与减法

一、重点难点总结(一)核心重点同类项的概念所含字母相同,且相同字母的指数也相同的项叫做同类项。常数项都是同类项。例:(3x^2y)与-5x^2y是同类项,(2ab)与(3bc)不是同类项(字母不同)。...