(10-3)Actor-Critic算法:Soft Actor-Critic (SAC)

10.3  Soft Actor-Critic (SAC)

Soft Actor-Critic(SAC)是一种深度强化学习算法,用于解决连续动作空间和高维状态空间下的强化学习问题。SAC是Actor-Critic(演员-评论家)算法的一种变体,它引入了一些改进和创新,使其在多方面具有出色的性能。

10.3.1  Soft Actor-Critic算法的核心思想

Soft Actor-Critic(SAC)的核心思想是通过最大熵强化学习来实现策略优化,以平衡探索和利用。它通过引入双值函数、目标熵的自动调整以及经验回放等技术来处理连续动作空间的问题,并通过深度神经网络来学习复杂的策略。SAC已经在许多强化学习任务中表现出色,特别适用于需要处理高维状态和连续动作的问题。

  1. 最大熵强化学习:SAC借鉴了最大熵强化学习的思想。最大熵强化学习不仅关注最大化累积奖励,还最大化策略的熵(或不确定性)。这意味着SAC的策略不仅会试图获得高回报,还会试图保持多样性和探索性,从而更全面地探索状态空间。最大熵正则化的引入有助于在策略优化中平衡探索和利用。
  2. 连续动作空间:SAC专门设计用于处理连续动作空间的问题。在连续动作空间中,动作可以是实数,因此通常有无限多个可能的动作。SAC使用一个高斯分布来参数化策略,允许演员网络输出连续动作的均值和标准差,从而灵活地适应连续动作空间。
  3. 双值函数(Q值和V值):SAC引入了两个值函数,即Q值函数和V值函数。Q值函数用于估计状态-动作对的价值,而V值函数用于估计状态的价值。这两个值函数有助于更准确地估计策略的性能。SAC使用Q值函数来计算优势函数,以指导策略学习。
  4. 目标熵的自动调整:SAC引入了一个目标熵的概念,它表示策略的期望熵。SAC会自动调整目标熵,以平衡探索和利用。当策略的熵低于目标熵时,SAC会鼓励更多的探索,从而保持策略的多样性。
  5. 经验回放:SAC通常使用经验回放来存储和重用以前的经验样本,以增加数据效率和稳定性。这有助于防止样本相关性,并允许SAC从历史经验中学习。

10.3.2  熵(Entropy)的作用及其在SAC中的应用

熵(Entropy)在信息理论和强化学习中都有重要的作用。在强化学习中,熵被引入到最大熵强化学习算法中,其中策略的熵用于探索和平衡探索与利用的关系。下面将详细介绍熵在强化学习中的作用以及在Soft Actor-Critic(SAC)算法中的应用:

1. 熵的作用

  1. 不确定性度量:熵是一种度量不确定性或随机性的指标。在强化学习中,策略的熵可以用来衡量策略的不确定性,即在给定状态下选择动作的随机性。高熵策略表示策略在相同状态下会选择多个不同的动作,而低熵策略则表示策略在相同状态下更倾向于选择相同的动作。
  2. 探索与利用的平衡:熵在强化学习中用于平衡探索和利用。高熵策略更加探索性,因为它使得策略在不同动作之间的选择更加均匀,从而有助于发现新的状态和动作组合。低熵策略更加利用,因为它使得策略更倾向于选择具有高估计价值的动作。
  3. 策略优化:最大化策略的熵是一种策略优化的正则化方法。在策略优化中,不仅要追求最大化期望回报,还要追求最大化策略的熵。这使得策略不仅会关注回报,还会注重保持多样性和探索。

2. 熵在Soft Actor-Critic中的应用

在Soft Actor-Critic(SAC)算法中,熵的应用是其核心思想之一。以下是熵在SAC中的具体应用:

  1. 最大化策略的熵:SAC的目标函数不仅包括最大化期望累积奖励,还包括最大化策略的熵。这可以表示为一个带有熵正则化项的优化问题,其中目标是最大化期望奖励和策略熵的加权和。这使得SAC策略更具探索性,因为它会鼓励策略选择多样性的动作。
  2. 自动调整目标熵:SAC通过自动调整目标熵的方式来平衡探索和利用。它使用一个目标熵的参数,然后通过学习过程中的自动调整来控制策略的探索性。当策略的熵低于目标熵时,SAC会鼓励更多的探索,以保持策略的多样性。
  3. 探索性能的提升:通过引入熵正则化项,SAC能够在探索性能和利用性能之间找到平衡。这有助于在强化学习任务中更好地处理探索问题,特别是在高维状态和连续动作空间中。

总之,熵在SAC中用于平衡探索和利用,通过最大化策略的熵来提高探索性能。这使得SAC成为一种有效处理连续动作空间问题的算法,并在实际应用中取得了出色的性能。

10.3.3  SAC算法的训练过程

Soft Actor-Critic(SAC)算法的训练过程通常包括以下步骤:

(1)初始化

初始化演员(Actor)、目标演员、评论家(Critic)、目标评论家、策略(Policy)和目标策略网络的神经网络参数,然后设置其他算法参数,如学习率、目标熵、折扣因子等。

(2)数据采集

与环境交互以收集样本数据。这通常涉及使用当前策略(演员网络)与环境互动,并记录状态、采取的动作、即时奖励和下一个状态。为了提高数据利用效率,通常使用经验回放缓冲区存储和重用以前的经验样本。

(3)计算目标策略熵

计算目标策略的熵,以用于自动调整目标熵。目标策略的熵通常是一个固定值,或者它可以通过训练过程自动学习。

(4)更新评论家网络

使用经验回放缓冲区中的样本数据,计算Q值(状态-动作对的价值估计)。使用均方误差或其他适当的损失函数来训练评论家网络,使其近似Q值函数。更新目标评论家网络,通常采用软更新策略(例如,使用滑动平均值来更新目标网络参数)。

(5)更新演员和策略网络

使用评论家网络的Q值估计来计算策略梯度。使用策略梯度算法来更新演员网络的参数,以最大化期望累积奖励和策略熵的加权和。更新目标演员网络和目标策略网络,通常采用软更新策略。

(6)自动调整目标熵

在训练过程中,自动调整目标熵以平衡探索和利用。如果策略的熵低于目标熵,增加目标熵的值,以鼓励更多的探索。

(7)重复:重复步骤2至步骤6,直到达到预定的训练轮数或其他停止条件。

(8)评估策略:在训练结束后,可以使用演员网络的最终参数来评估策略的性能。通常,这涉及在测试环境中运行策略,并计算平均奖励或其他性能指标。

(9)保存模型(可选):保存训练后的演员、评论家和策略网络模型,以备将来使用。

SAC的训练过程是一个迭代的过程,演员和评论家网络相互协作,通过反馈信号来改进策略。通过最大化期望累积奖励和策略熵的加权和,SAC能够有效地处理连续动作空间和高维状态空间的问题,同时保持多样性和探索性。

10.3.4  SAC算法实战 

下面是一个使用SAC算法的简单例子,该例子创建了一个简单的虚拟环境,其中一个虚拟机器人尝试在不碰到障碍物的情况下远离原点。 SAC代理通过训练来学习如何选择动作以最大化总奖励。

实例10-3:使用SAC算法训练一个强化学习代理(源码路径:daima\10\sac.py

实例文件sac.py的主要实现代码如下所示:

# 定义虚拟环境
class Environment:
    def __init__(self):
        self.state_dim = 2  # 状态维度为2(x坐标和y坐标)
        self.action_dim = 1  # 动作维度为1(推进或后退)
        self.state = np.array([0.0, 0.0])  # 初始状态为原点坐标

    def step(self, action):
        # 模拟虚拟机器人的动作
        velocity = action[0]  # 动作表示速度
        self.state[0] += velocity  # 更新x坐标

        # 计算奖励,目标是使机器人尽量远离原点
        reward = -np.abs(self.state[0])

        # 检查是否达到终止条件(机器人离原点太远)
        done = np.abs(self.state[0]) > 10.0

        return self.state, reward, done


# 创建连续动作空间的SAC代理
class SACAgent:
    def __init__(self, state_dim, action_dim):
        self.state_dim = state_dim
        self.action_dim = action_dim

        # 创建策略网络和Q网络
        self.actor = self.build_actor()
        self.q1 = self.build_critic()
        self.q2 = self.build_critic()

        # 定义优化器
        self.actor_optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
        self.critic_optimizer = tf.keras.optimizers.Adam(learning_rate=0.002)

    def build_actor(self):
        input_layer = Input(shape=(self.state_dim,))
        x = Dense(64, activation='relu')(input_layer)
        x = Dense(64, activation='relu')(x)
        mean = Dense(self.action_dim, activation='tanh')(x)
        log_stddev = Dense(self.action_dim)(x)
        actor = Model(inputs=input_layer, outputs=[mean, log_stddev])
        return actor

    def build_critic(self):
        input_layer = Input(shape=(self.state_dim + self.action_dim,))
        x = Dense(64, activation='relu')(input_layer)
        x = Dense(64, activation='relu')(x)
        q_value = Dense(1)(x)
        critic = Model(inputs=input_layer, outputs=q_value)
        return critic

    def select_action(self, state):
        mean, log_stddev = self.actor.predict(np.array([state]))
        stddev = tf.math.exp(log_stddev)
        action_distribution = tfp.distributions.Normal(loc=mean, scale=stddev)
        action = action_distribution.sample()
        return action.numpy()[0]

    def update(self, state, action, reward, next_state, done):
        with tf.GradientTape(persistent=True) as tape:
            mean, log_stddev = self.actor(np.array([state]))
            stddev = tf.math.exp(log_stddev)
            action_distribution = tfp.distributions.Normal(loc=mean, scale=stddev)
            log_prob = action_distribution.log_prob(action)

            # 将action从一维数组转换为二维数组
            action = np.array([action])

            # 连接state和action
            q1_value = self.q1(np.concatenate([np.array([state]), action], axis=-1))

            q2_value = self.q2(np.concatenate([np.array([state]), action], axis=-1))

            next_mean, next_log_stddev = self.actor(np.array([next_state]))
            next_stddev = tf.math.exp(next_log_stddev)
            next_action_distribution = tfp.distributions.Normal(loc=next_mean, scale=next_stddev)
            next_action = next_action_distribution.sample()
            next_log_prob = next_action_distribution.log_prob(next_action)

            target_q_value = tf.minimum(q1_value, q2_value) - log_prob + next_log_prob
            target_q_value = tf.stop_gradient(target_q_value)

            critic_loss1 = tf.reduce_mean(tf.square(target_q_value - q1_value))
            critic_loss2 = tf.reduce_mean(tf.square(target_q_value - q2_value))

            actor_loss = tf.reduce_mean(log_prob - tf.minimum(q1_value, q2_value))

        actor_gradients = tape.gradient(actor_loss, self.actor.trainable_variables)
        critic_gradients1 = tape.gradient(critic_loss1, self.q1.trainable_variables)
        critic_gradients2 = tape.gradient(critic_loss2, self.q2.trainable_variables)

        self.actor_optimizer.apply_gradients(zip(actor_gradients, self.actor.trainable_variables))
        self.critic_optimizer.apply_gradients(zip(critic_gradients1, self.q1.trainable_variables))
        self.critic_optimizer.apply_gradients(zip(critic_gradients2, self.q2.trainable_variables))

        del tape


# 训练SAC代理
def train_sac_agent():
    env = Environment()
    agent = SACAgent(env.state_dim, env.action_dim)

    max_episodes = 1000
    for episode in range(max_episodes):
        state = env.state
        total_reward = 0

        while True:
            action = agent.select_action(state)
            next_state, reward, done = env.step(action)
            agent.update(state, action, reward, next_state, done)

            total_reward += reward
            state = next_state

            if done:
                break

        print(f"Episode: {episode}, Total Reward: {total_reward}")


if __name__ == "__main__":
    train_sac_agent()

上述代码的实现流程如下所示:

  1. 首先定义了一个虚拟环境Environment,该环境具有状态维度为2(x坐标和y坐标)和动作维度为1(推进或后退)。虚拟机器人的目标是尽量远离原点。
  2. 创建一个SAC代理SACAgent,该代理包括策略网络(actor)和两个Q网络(critics)。策略网络用于生成动作,Q网络用于估计动作的价值。代理使用Adam优化器进行参数更新。
  3. 策略网络和Q网络的结构在build_actor和build_critic方法中定义。策略网络生成均值和对数标准差,用于构建动作的概率分布。Q网络用于估计状态-动作对的Q值。
  4. select_action方法用于根据策略网络选择动作,并返回动作的样本。
  5. update方法实现了SAC算法的更新步骤,包括计算动作的log概率、Q值的估计、目标Q值等,并使用梯度下降更新策略网络和Q网络的参数。
  6. 在train_sac_agent函数中,代理与环境互动,执行多个训练周期。在每个周期内,代理根据策略选择动作,与环境互动,然后更新策略和Q网络的参数。最后,打印每个周期的总奖励。
  7. 主程序检查是否是直接运行脚本,并调用train_sac_agent函数来训练SAC代理。

未完待续

  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值