24/8/17算法笔记 SAC算法

SAC最大化Q函数的同时,最大化Q函数的熵

Soft Actor-Critic(SAC)算法是一种先进的强化学习算法,它结合了策略梯度和价值函数估计,特别适用于连续动作空间的问题。SAC算法通过引入熵正则化和软化策略更新,增强了智能体的探索能力,并提高了学习效率。

SAC算法的核心原理包括策略优化、价值函数学习和熵优化。它使用策略梯度方法进行优化,并通过最小化策略的熵来增强探索性,同时引入自适应温度参数 αα 来平衡探索与利用。

算法的更新规则涉及多个组件,包括策略网络、两个Q网络以及价值网络。SAC算法利用重参数技巧(reparameterization trick)来稳定策略的输出,并采用两个Q网络的最小值作为目标,减少过高估计Q值的风险。

SAC算法的两种变体包括固定熵正则化系数的SAC和变化熵正则化系数的SAC。固定熵正则化系数的SAC在整个训练过程中保持熵的正则化程度不变,而变化熵正则化系数的SAC则根据训练步数动态调整熵正则化系数的值,以在探索和利用之间取得平衡。

在实际应用中,SAC算法已经显示出了卓越的性能,例如在OpenAI Gym环境中的推车游戏中,SAC算法能够有效地训练智能体保持杆子竖直。此外,SAC算法在多个基准任务中的表现与顶尖算法相当,证明了其强大的性能和适用性。

import gym
from matplotlib import pyplot as plt
%matplotlib inline
#创建环境
env = gym.make('Pendulum-v1')
env.reset()

#打印游戏
def show():
    plt.imshow(env.render(mode='rgb_array'))
    plt.show()

action网络模型

import torch

class Model(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.fc_state = torch.nn.Sequential(
            torch.nn.Linear(3,128),
            torch.nn.ReLU(),
        )
        self.fc_mu = torch.nn.Linear(128,1)
        self.fc_std = torch.nn.Sequential(
            torch.nn.Linear(128,1),
            torch.nn.Softplus(),
        )
    def forward(self,state): 
        state = self.fc_state(state)
        mu = self.fc_mu(state)
        std = self.fc_std(state)
        dist = torch.distribution.Noemal(mu,std)
        
        #采样b个样本
        #这里用的是rsample,表示重采样,其实就是先从一个标准正太分布中采样,然后乘以标准差,加上均值
        sample = dist.rsample()
        
        #样本压缩到-1,1之间,求动作
        action = sample.tanh()
        
        #求概率对数
        prob = dist.log_prob(sample).exp()
        
        #这个值描述动作的熵
        entropy = prob/(1-action.tanh()**2 + 1e-7)
        entropy = -entropy.log()
        return action*2,entropy

forward 函数是神经网络模型的核心,它定义了如何从输入状态计算输出动作和相关值。在强化学习中,这个函数是智能体与环境交互并进行决策的基础。在训练过程中,模型的参数会根据算法的反馈进行更新,以优化策略性能。

定义两对value模型

class ModelValue(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.sequential = torch.nn.Sequential(  #构建一个前馈神经网络
            torch.nn.Linear(4,128),
            torch.nn.ReLU(),
            torch.nn.Linear(128,128),
            torch.nn.ReLU(),
            torch.nn.Linear(128,1),
        )
    def forward(self,state,action):
        state = torch.cat([state,action],dim=1)
        return self.sequential(state)

model_value1 = ModelValue()
model_value2 = ModelValue()

model_value_next1 = ModelValue()
model_value_next2 = ModelValue()

model_value_next1.load_state_dict(model_value1.state_dict())
model_value_next2.load_state_dict(model_value2.state_dict())

model_value1(torch.randn(2,3),torch.randn(2,1))

获取动作函数

import random
import numpy as np
def get_action(state):
    state = torch.FloatTensor(state).reshape(1,3)
    action, _  = model_action(state)
    return action
  1. 临时或未使用变量:当函数返回一个元组或多个值,而你只对其中某些值感兴趣时,可以用 _ 来接收那些不需要的值。例如,在 get_action 函数中,_ 用于接收 model_action 返回的第二个值,即熵值,这里没有使用它。

更新data函数

#样本池
datas = []

#向样本池中添加N条数据,删除M条最古老的数据
def update_data():
    #初始化游戏
    state = env.reset()
    
    #玩到游戏结束为止
    over = False
    while not over:
        #根据当前状态得到一个动作
        action = get_action(state)
        
        #执行当作,得到反馈
        next_state,reward,over, _ = env.step([action])
        
        #记录数据样本
        datas.append((states,action,reward,next_state,over))
        
        #更新游戏状态,开始下一个当作
        state = next_state
    #数据上限,超出时从最古老的开始删除
    while len(datas)>10000:
        datas.pop(0)
        

获取样本数据

#获取一批数据样本
def get_sample():
    samples = random.sample(datas,64)
    #[b,4]
    state = torch.FloatTensor([i[0]for i in samples]).reshape(-1,3)
    #[b,1]
    action = torch.LongTensor([i[1]for i in samples]).reshape(-1,1)
    #[b,1]
    reward = torch.FloatTensor([i[2]for i in samples]).reshape(-1,1)
    #[b,4]
    next_state = torch.FloatTensor([i[3]for i in samples]).reshape(-1,3)
    #[b,1]
    over = torch.LongTensor([i[4]for i in samples]).reshape(-1,1)

    return state,action,reward,next_state,over

state,action,reward,next_state,over=get_sample()

state[:5],action[:5],reward[:5],next_state[:5],over[:5]

测试函数

from IPython import display

def test(play):
    #初始化游戏
    state = env.reset()
    
    #记录反馈值的和,这个值越大越好
    reward_sum= 0
    
    #玩到游戏结束为止
    over =False
    while not over:
        #根据当前状态得到一个动作
        action = get_action(state)
        
        #执行动作,得到反馈
        staet,reward,over,_=env.step(action)
        reward_sum+=reward
        
        #打印动画
        if play and random.random()<0.2:
            display.clear_output(wait=True)
            show()
    return  reward_sum

软更新函数

#软更新函数
def soft_update(model,model_next):
    for param,param_next in zip(model.parameters(),model_next.parameters()):
        #以一个小的比例更新/
        value = param_next.data*0.995+param.data*0.005
        param_next.data.copy_(value)

定义alpha

import math

#这也是一个可学习的参数
alpha = torch.tensor(math.log(0.01))
alpha.requires_grad = True

alpha

计算target

#计算target,同时要考虑熵
def get_target(reward,next_state,over):
    #首先使用model_action计算动作和动作熵
    action,entropy = model_action(next_state)
    
    #评估next_state的价值
    target1 = model_value_next1(next_state,action)
    target2 = model_value_next2(next_state,action)

    #取价值小的,这是出于稳定性考虑
    target = torch.min(target1,target2)
    
    #exp和log互为反操作,这里是把alpha还原了
    #这里的操作是在target熵加上了动作的熵,alpha作为权重系数
    target+=alpha.exp()*entropy
    
    target*=0.99
    target*=(1-over)
    target+=reward
    
    return target

计算loss,要求最大化熵,增强动作的随机性

def get_loss_action(state):
    action,entropy = model_action(state)
    
    #使用两个value网络评估action的价值
    value1 = model_value1(state,action)
    value2 = model_value2(state,action)
    
    #取价值小的,处于稳定性考虑
    value =torch.min(value1,value2)
    
    #alpha还原后乘以熵,这个值期望的是越大越好,但是合理是计算loss,所以符号取反
    loss_action = -alpha.exp()*entropy
    
    #减去value,所以value越大越好,这样loss就会越小
    loss_action = -value
    
    
    return loss_action.mean(),entropy

训练函数

def train():
    optimizer_action = torch.optim.Adan(model.action.parameters(),lr=3e-4)
    optimizer_value1 = torch.optim.Adam(model_value1.parameters(),lr=3e-3)
    optimizer_value2 = torch.optim.Adam(model_value2.parameters(),lr=3e-3)
    
    
    #alpha也是要更新的参数,所以这里要定义优化器
    optimizer_alpha = torch.optim.Adam([alpha],lr=3e-4)
    
    loss_fn = torch.nn.MSELoss()
    
    #训练N次
    for epoch in range(100):
        #更新N条数据
        update_data()
        
        #每次更新过数据后,学习N次
        for i in range(200):
            #玩一局游戏,得到数据
            states,rewards,actions,next_states,overs = get_sample()

            #对reward偏移,为了便于训练
            reward= (reward+8)/8
            
            #计算target,这个target里一局考虑了动作的熵
            target = get_target(reward,next_state,over)
            target = target.detach()
            
            #计算两个value
            value1 = model_value(state,action)
            value2 = model_value(state,action)

            #计算两个loss,两个value的目标都是要贴近target
            loss_value1 = loss_fn(value1,target)
            loss_value2 = loss_fn(value2,target)

            #更新参数
            optimizer.zero_grad()   #作用是清除(重置)模型参数的梯度
            loss.backward()       #反向传播计算梯度的标准方法
            optimizer.step()     #更新模型的参数

            optimizer_td.zero_grad()  
            loss_td.backward()       
            optimizer_td.step()  

            #使用model_value计算model_action的loss
            loss_action,entropy = get_loss_action(state)
            optimizer_action.zero_grad()
            loss_action.backward()
            optimizer_action.step()
            
            #熵乘以alpha就是alpha的loss
            loss_alpha = (entropy+1).detach()*alpha.exp()
            loss_alpha = loss_alpha.mean()

            #更新alpha值
            optimizer_alpha.zero_grad()
            loss_alpha.bakcward()
            optimizer_alpha.step()
            
            #增量更新next模型
            soft_update(model_value1,model_value_next1)
            soft_update(model_value2, model_value_next2)

        if i % 10 ==0:
            test_result = sum([test(play=False)for _ in range(10)])/10
            print(epoch,len(datas),test_result)

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值