深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
简介: 软演员-评论家算法(Soft Actor-Critic, SAC)是深度强化学习领域的重要进展,基于最大熵框架优化策略,在探索与利用之间实现动态平衡。SAC通过双Q网络设计和自适应温度参数,提升了训练稳定性和样本效率。本文详细解析了SAC的数学原理、网络架构及PyTorch实现,涵盖演员网络的动作采样与对数概率计算、评论家网络的Q值估计及其损失函数,并介绍了完整的SAC智能体实现流程。SAC在连续动作空间中表现出色,具有高样本效率和稳定的训练过程,适合实际应用场景。

深度强化学习是人工智能领域最具挑战性的研究方向之一,其设计理念源于生物学习系统从经验中优化决策的机制。在众多深度强化学习算法中,软演员-评论家算法(Soft Actor-Critic, SAC)因其在样本效率、探索效果和训练稳定性等方面的优异表现而备受关注。

传统的深度强化学习算法往往在探索-利用权衡、训练稳定性等方面面临挑战。SAC算法通过引入最大熵强化学习框架,在策略优化过程中自动调节探索程度,有效解决了这些问题。其核心创新在于将熵最大化作为策略优化的额外目标,在保证收敛性的同时维持策略的多样性。

本文将系统阐述SAC算法的技术细节,主要包括:

  1. 基于最大熵框架的SAC算法数学原理
  2. 演员网络与评论家网络的具体架构设计
  3. 基于PyTorch的详细实现方案
  4. 网络训练的关键技术要点

SAC算法采用演员-评论家架构,演员网络负责生成动作策略,评论家网络评估动作价值。通过两个网络的协同优化,实现策略的逐步改进。整个训练过程中,演员网络致力于最大化评论家网络预测的Q值,同时保持适度的策略探索;评论家网络则不断优化其Q值估计的准确性。

接下来,我们将从演员网络的数学原理开始,详细分析SAC算法的各个技术组件:

演员(策略)网络

演员是由参数φ确定的策略网络,表示为:

这是一个基于状态输出动作的随机策略。它使用神经网络估计均值和对数标准差,从而得到给定状态下动作的分布及其对数概率。对数概率用于熵正则化,即目标函数中包含一个用于最大化概率分布广度(熵)的项,以促进智能体的探索行为。关于熵正则化的具体内容将在后文详述。演员网络的架构如图所示:

均值μ(s)和对数σ(s)用于动作采样:

其中N表示正态分布。但这个操作存在梯度不可微的问题,需要通过重参数化技巧来解决。

这里d表示动作空间维度,每个分量ε_i从标准正态分布(均值0,标准差1)中采样。应用重参数化技巧:

这样就解决了梯度截断问题。接下来通过激活函数将x_t转换为标准化动作:

该转换确保动作被限制在[-1,1]区间内。

动作对数概率计算

完成动作计算后,就可以计算奖励和预期回报。演员的损失函数中还包含熵正则化项,用于最大化分布的广度。计算采样动作𝑎t的对数概率Log(πϕ)时,从预tanh变换x_t开始分析更为便利。

由于x_t来自均值μ(s)和标准差σ(s)的高斯分布,其概率密度函数(PDF)为:

其中各独立分量x_t,i的分布为:

对两边取对数可简化PDF:

要将其转换为log(π_ϕ),需要考虑x_t到a_t的tanh变换,这可通过微分链式法则实现:

这个关系的推导基于概率守恒原理:两个变量在给定区间内的概率必须相等:

其中a_i = tanh(x_i)。将区间缩小到无穷小的dx和da:

tanh的导数形式为:

代入得到:

最终可得完整表达式:

至此完成了演员部分的推导,这里有动作又有对数概率,就可以进行损失函数的计算。下面是这些数学表达式的PyTorch实现:

 importgymnasiumasgym  
 fromsrc.utils.loggerimportlogger  
 fromsrc.models.callbackimportPolicyGradientLossCallback  
 frompydanticimportField, BaseModel, ConfigDict  
 fromtypingimportDict, List  
 importnumpyasnp  
 importos  
 frompathlibimportPath  
 importtorch  
 importtorch.nnasnn  
 importtorch.optimasoptim  
 importtorch.nn.functionalasF  
 fromtorch.distributionsimportNormal  

 '''演员网络:估计均值和对数标准差用于熵正则化计算'''  

 classActor(nn.Module):  
     def__init__(self,state_dim,action_dim):  
         super(Actor,self).__init__()  

         self.net=nn.Sequential(  
             nn.Linear(state_dim, 100),  
             nn.ReLU(),  
             nn.Linear(100,100),  
             nn.ReLU()  
         )  
         self.mean_linear=nn.Linear(100, action_dim)  
         self.log_std_linear=nn.Linear(100, action_dim)  

     defforward(self, state):  
         x=self.net(state)  
         mean=self.mean_linear(x)  
         log_std=self.log_std_linear(x)  
         log_std=torch.clamp(log_std, min=-20, max=2)  
         returnmean, log_std  

     defsample(self, state):  
         mean, log_std=self.forward(state)  
         std=log_std.exp()  
         normal=Normal(mean, std)  
         x_t=normal.rsample() # 重参数化技巧  
         y_t=torch.tanh(x_t)  
         action=y_t  
         log_prob=normal.log_prob(x_t)  
         log_prob-=torch.log(1-y_t.pow(2)+1e-6)  
         log_prob=log_prob.sum(dim=1, keepdim=True)  

         returnaction, log_prob

在讨论损失函数定义和演员网络的训练过程之前,需要先介绍评论家网络的数学原理。

评论家网络

评论家网络的核心功能是估计状态-动作对的预期回报(Q值)。这些估计值在训练过程中为演员网络提供指导。评论家网络采用双网络结构,分别提供预期回报的两个独立估计,并选取较小值作为最终估计。这种设计可以有效避免过度估计偏差,同时提升训练稳定性。其结构如图所示:

需要说明的是,此时的示意图是简化版本,主要用于理解演员和评论家网络的基本角色,暂不考虑训练稳定性的细节。另外,"智能体"实际上是演员和评论家网络的统称而非独立实体,图中分开表示只是为了清晰展示结构。假设评论家网络暂不需要训练,因为这样可以专注于如何利用评论家网络估计的Q值来训练演员网络。演员网络的损失函数表达式为:

更常见的形式是:

其中ρD表示状态分布。损失函数通过对所有动作空间和状态空间的熵项与Q值进行积分得到。但在实际应用中,无法直接获取完整的状态分布,因此ρD实际上是基于重放缓冲区样本的经验状态分布,期望其能较好地表征整体状态分布特征。

基于该损失函数可以通过反向传播对演员网络进行训练。以下是评论家网络的PyTorch实现:

 '''评论家网络:定义q1和q2'''  
 classCritic(nn.Module):  
     def__init__(self, state_dim, action_dim):  
         super(Critic, self).__init__()  

         # Q1网络架构  
         self.q1_net=nn.Sequential(  
             nn.Linear(state_dim+action_dim, 256),  
             nn.ReLU(),  
             nn.Linear(256, 256),  
             nn.ReLU(),  
             nn.Linear(256, 1),  
         )  

         # Q2网络架构  
         self.q2_net=nn.Sequential(  
             nn.Linear(state_dim+action_dim, 256),  
             nn.ReLU(),  
             nn.Linear(256, 256),  
             nn.ReLU(),  
             nn.Linear(256, 1),  
         )  

     defforward(self, state, action):  
         sa=torch.cat([state, action], dim=1)  
         q1=self.q1_net(sa)  
         q2=self.q2_net(sa)  
         returnq1, q2

前述内容尚未涉及评论家网络自身的训练机制。从重放缓冲区采样的每个数据点包含[st, s{t+1}, a_t, R]。对于状态-动作对的Q值,我们可以获得两种不同的估计。

第一种方法是直接将a_t和s_t输入评论家网络:

第二种方法是基于贝尔曼方程:

这种方法使用s_t+1、a_t+1以及执行动作a_t获得的奖励来重新估计。这里使用目标网络而非第一种方法中的评论家网络进行估计。采用目标评论家网络的主要目的是解决训练不稳定性问题。如果同一个评论家网络同时用于生成当前状态和下一状态的Q值(用于目标Q值),这种耦合会导致网络更新在目标计算的两端产生不一致的传播,从而引起训练不稳定。因此引入独立的目标网络为下一状态的Q值提供稳定估计。目标网络作为评论家网络的缓慢更新版本,确保目标Q值能够平稳演化。具体结构如图所示:

评论家网络的损失函数定义为:

通过该损失函数可以利用反向传播更新评论家网络,而目标网络则采用软更新机制:

其中ε是一个较小的常数,用于限制目标评论家的更新幅度,从而维持训练稳定性。

完整流程

以上内容完整阐述了SAC智能体的各个组件。下图展示了完整SAC智能体的结构及其计算流程:

下面是一个综合了前述演员网络、评论家网络及其更新机制的完整SAC智能体实现

 '''SAC智能体的实现:整合演员网络和评论家网络'''  

 classSACAgent:  
     def__init__(self, state_dim, action_dim, learning_rate, device):  
         self.device=device  

         self.actor=Actor(state_dim, action_dim).to(device)  
         self.actor_optimizer=optim.Adam(self.actor.parameters(), lr=learning_rate)  

         self.critic=Critic(state_dim, action_dim).to(device)  
         self.critic_optimizer=optim.Adam(self.critic.parameters(), lr=learning_rate)  

         # 目标网络初始化  
         self.critic_target=Critic(state_dim, action_dim).to(device)  
         self.critic_target.load_state_dict(self.critic.state_dict())  

         # 熵温度参数  
         self.target_entropy=-action_dim   
         self.log_alpha=torch.zeros(1, requires_grad=True, device=device)  
         self.alpha_optimizer=optim.Adam([self.log_alpha], lr=learning_rate)  

     defselect_action(self, state, evaluate=False):  
         state=torch.FloatTensor(state).to(self.device).unsqueeze(0)  
         ifevaluate:  
             withtorch.no_grad():  
                 mean, _=self.actor(state)  
                 action=torch.tanh(mean)  
                 returnaction.cpu().numpy().flatten()  
         else:  
             withtorch.no_grad():  
                 action, _=self.actor.sample(state)  
                 returnaction.cpu().numpy().flatten()  

     defupdate(self, replay_buffer, batch_size=256, gamma=0.99, tau=0.005):  
         # 从经验回放中采样训练数据  
         batch=replay_buffer.sample_batch(batch_size)  
         state=torch.FloatTensor(batch['state']).to(self.device)  
         action=torch.FloatTensor(batch['action']).to(self.device)  
         reward=torch.FloatTensor(batch['reward']).to(self.device)  
         next_state=torch.FloatTensor(batch['next_state']).to(self.device)  
         done=torch.FloatTensor(batch['done']).to(self.device)  

         # 评论家网络更新  
         withtorch.no_grad():  
             next_action, next_log_prob=self.actor.sample(next_state)  
             q1_next, q2_next=self.critic_target(next_state, next_action)  
             q_next=torch.min(q1_next, q2_next) -torch.exp(self.log_alpha) *next_log_prob  
             target_q=reward+ (1-done) *gamma*q_next  

         q1_current, q2_current=self.critic(state, action)  
         critic_loss=F.mse_loss(q1_current, target_q) +F.mse_loss(q2_current, target_q)  

         self.critic_optimizer.zero_grad()  
         critic_loss.backward()  
         self.critic_optimizer.step()  

         # 演员网络更新  
         action_new, log_prob=self.actor.sample(state)  
         q1_new, q2_new=self.critic(state, action_new)  
         q_new=torch.min(q1_new, q2_new)  
         actor_loss= (torch.exp(self.log_alpha) *log_prob-q_new).mean()  

         self.actor_optimizer.zero_grad()  
         actor_loss.backward()  
         self.actor_optimizer.step()  

         # 温度参数更新  
         alpha_loss=-(self.log_alpha* (log_prob+self.target_entropy).detach()).mean()  

         self.alpha_optimizer.zero_grad()  
         alpha_loss.backward()  
         self.alpha_optimizer.step()  

         # 目标网络软更新  
         forparam, target_paraminzip(self.critic.parameters(), self.critic_target.parameters()):  
             target_param.data.copy_(tau*param.data+ (1-tau) *target_param.data)

总结

本文系统地阐述了SAC算法的数学基础和实现细节。通过对演员网络和评论家网络的深入分析,我们可以看到SAC算法在以下几个方面具有显著优势:

理论框架

  • 基于最大熵强化学习的理论基础保证了算法的收敛性
  • 双Q网络设计有效降低了值函数估计的过度偏差
  • 自适应温度参数实现了探索-利用的动态平衡

实现特点

  • 采用重参数化技巧确保了策略梯度的连续性
  • 软更新机制提升了训练稳定性
  • 基于PyTorch的向量化实现提高了计算效率

实践价值

  • 算法在连续动作空间中表现优异
  • 样本效率高,适合实际应用场景
  • 训练过程稳定,调参难度相对较小

未来研究可以在以下方向继续深化:

  • 探索更高效的策略表达方式
  • 研究多智能体场景下的SAC算法扩展
  • 结合迁移学习提升算法的泛化能力
  • 针对大规模状态空间优化网络架构

强化学习作为人工智能的核心研究方向之一,其理论体系和应用场景都在持续发展。深入理解算法的数学原理和实现细节,将有助于我们在这个快速演进的领域中把握技术本质,开发更有效的解决方案。

https://avoid.overfit.cn/post/295d79c7db084a839a5410e278279b4f

作者:Najib Sharifi, Ph.D.

目录
相关文章
|
10天前
|
前端开发 网络协议 安全
【网络原理】——HTTP协议、fiddler抓包
HTTP超文本传输,HTML,fiddler抓包,URL,urlencode,HTTP首行方法,GET方法,POST方法
|
10天前
|
域名解析 网络协议 关系型数据库
【网络原理】——带你认识IP~(长文~实在不知道取啥标题了)
IP协议详解,IP协议管理地址(NAT机制),IP地址分类、组成、特殊IP地址,MAC地址,数据帧格式,DNS域名解析系统
|
10天前
|
存储 JSON 缓存
【网络原理】——HTTP请求头中的属性
HTTP请求头,HOST、Content-Agent、Content-Type、User-Agent、Referer、Cookie。
|
10天前
|
安全 算法 网络协议
【网络原理】——图解HTTPS如何加密(通俗简单易懂)
HTTPS加密过程,明文,密文,密钥,对称加密,非对称加密,公钥和私钥,证书加密
|
10天前
|
XML JSON 网络协议
【网络原理】——拥塞控制,延时/捎带应答,面向字节流,异常情况
拥塞控制,延时应答,捎带应答,面向字节流(粘包问题),异常情况(心跳包)
|
30天前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
2月前
|
Cloud Native Devops 云计算
云计算的未来:云原生架构与微服务的革命####
【10月更文挑战第21天】 随着企业数字化转型的加速,云原生技术正迅速成为IT行业的新宠。本文深入探讨了云原生架构的核心理念、关键技术如容器化和微服务的优势,以及如何通过这些技术实现高效、灵活且可扩展的现代应用开发。我们将揭示云原生如何重塑软件开发流程,提升业务敏捷性,并探索其对企业IT架构的深远影响。 ####
48 3
|
2月前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
30天前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
160 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
1月前
|
设计模式 负载均衡 监控
探索微服务架构下的API网关设计
在微服务的大潮中,API网关如同一座桥梁,连接着服务的提供者与消费者。本文将深入探讨API网关的核心功能、设计原则及实现策略,旨在为读者揭示如何构建一个高效、可靠的API网关。通过分析API网关在微服务架构中的作用和挑战,我们将了解到,一个优秀的API网关不仅要处理服务路由、负载均衡、认证授权等基础问题,还需考虑如何提升系统的可扩展性、安全性和可维护性。文章最后将提供实用的代码示例,帮助读者更好地理解和应用API网关的设计概念。
65 8