【学习强化学习】十一、Soft Actor-Critic

参考资料

  1. 论文链接
  2. 论文链接2
  3. https://zhuanlan.zhihu.com/p/70360272
  4. https://blog.csdn.net/qq_38587510/article/details/104970837
  5. https://zhuanlan.zhihu.com/p/75937178

前言

  • Soft Actor-Critic (SAC)是面向Maximum Entropy Reinforcement learning 开发的一种off policy算法,和DDPG相比,Soft Actor-Critic使用的是随机策略stochastic policy,相比确定性策略具有一定的优势。Soft Actor-Critic兼具稳定性高和样本利用率高的优点。
  • SAC的一个主要特征是entropy regularization正则化). policy可以被训练去最大程度上权衡期望回报和熵。熵是策略随机性的一个衡量。 增加熵也就说明策略的随机性增强,所以会增加更多的探索,从而可以加快后续的学习速度。可以防止policy过早的收敛到局部最优值。

1. 介绍

Soft Actor-Critic(SAC)算法学习的目标是最大化熵正则化的累积奖励而不只是累计奖励, 从而鼓励更多的探索。
max ⁡ π θ E [ ∑ t γ t ( r ( S t , A t ) + α H ( π θ ( ⋅ ∣ S t ) ) ) ] \max _{\pi_{\theta}} \mathbb{E}\left[\sum_{t} \gamma^{t}\left(r\left(S_{t}, A_{t}\right)+\alpha \mathcal{H}\left(\pi_{\theta}\left(\cdot \mid S_{t}\right)\right)\right)\right] πθmaxE[tγt(r(St,At)+αH(πθ(St)))]
这里 α \alpha α 是正则化系数。

2. Soft Policy Iteration

柔性策略迭代(Soft Policy Iteration)是一个有理论保证的学习最优最大化熵策略的算法。和策略迭代类似,柔性策略迭代也分为两步:柔性策略评估(Soft policy evaluation)和柔性策略提高(Soft policy improvment)。


V π ( s ) = E [ ∑ t γ t ( r ( S t , A t ) + α H ( π ( ⋅ ∣ S t ) ) ) ] , V^{\pi}(s)=\mathbb{E}\left[\sum_{t} \gamma^{t}\left(r\left(S_{t}, A_{t}\right)+\alpha \mathcal{H}\left(\pi\left(\cdot \mid S_{t}\right)\right)\right)\right], Vπ(s)=E[tγt(r(St,At)+αH(π(St)))],
其中 s 0 = s s_{0}=s s0=s, 令
Q ( s , a ) = r ( s , a ) + γ E [ V ( s ′ ) ] Q(s, a)=r(s, a)+\gamma \mathbb{E}\left[V\left(s^{\prime}\right)\right] Q(s,a)=r(s,a)+γE[V(s)]
这里假设 s ′ ∼ Pr ⁡ ( ⋅ ∣ s , a ) s^{\prime} \sim \operatorname{Pr}(\cdot \mid s, a) sPr(s,a) 是下一个状态。可以很容易地验证以下式子成立。
V π ( s ) = E a ∼ π [ Q ( s , a ) − α log ⁡ ( a ∣ s ) ] . V^{\pi}(s)=\mathbb{E}_{a \sim \pi}[Q(s, a)-\alpha \log (a \mid s)] . Vπ(s)=Eaπ[Q(s,a)αlog(as)].
柔性策略评估时, 定义的贝尔曼回溯算子 T \mathcal{T} T
T π Q ( s , a ) = r ( s , a ) + γ E [ V π ( s ′ ) ] . \mathcal{T}^{\pi} Q(s, a)=r(s, a)+\gamma \mathbb{E}\left[V^{\pi}\left(s^{\prime}\right)\right] . TπQ(s,a)=r(s,a)+γE[Vπ(s)].

和策略评估类似, 我们可以证明对于任何映射 Q 0 : S × A → R , Q k = T π Q k − 1 Q^{0}: \mathcal{S} \times \mathcal{A} \rightarrow \mathbb{R}, Q^{k}=\mathcal{T}^{\pi} Q^{k-1} Q0:S×AR,Qk=TπQk1 会收敛到 π \pi π 的柔性 Q Q Q 值。

策略提高阶段, 我们用当前的 Q Q Q 值求解以下最大化熵正则化奖励的优化问题。
π ( ⋅ ∣ s ) = arg ⁡ max ⁡ π E a ∼ π [ Q ( s , a ) + α H ( π ) ] \pi(\cdot \mid s)=\arg \max _{\pi} \mathbb{E}_{a \sim \pi}[Q(s, a)+\alpha \mathcal{H}(\pi)] π(s)=argπmaxEaπ[Q(s,a)+αH(π)]

求解以上这个优化问题后可以得到的解为
π ( ⋅ ∣ s ) = exp ⁡ ( 1 α Q ( s , ⋅ ) ) Z ( s ) . \pi(\cdot \mid s)=\frac{\exp \left(\frac{1}{\alpha} Q(s, \cdot)\right)}{Z(s)} . π(s)=Z(s)exp(α1Q(s,)).
这里 Z ( s ) Z(s) Z(s) 是归一化常数, 也即 Z ( s ) = ∑ a exp ⁡ ( 1 α Q ( s , a ) ) Z(s)=\sum_{a} \exp \left(\frac{1}{\alpha} Q(s, a)\right) Z(s)=aexp(α1Q(s,a)) 。如果采用的策略模型无法表达最优的策略 π \pi π, 我们可以进一步求解
π ( ⋅ ∣ s ) = arg ⁡ min ⁡ π ∈ Π D K L ( π ( ⋅ ∣ s ) ∥ exp ⁡ ( 1 α Q ( s , ⋅ ) ) Z ( s ) ) . \pi(\cdot \mid s)=\arg \min _{\pi \in \Pi} D_{\mathrm{KL}}\left(\pi(\cdot \mid s) \| \frac{\exp \left(\frac{1}{\alpha} Q(s, \cdot)\right)}{Z(s)}\right) . π(s)=argπΠminDKL(π(s)Z(s)exp(α1Q(s,))).
我们可以证明在学习过程, 上面描述的柔性策略提高阶段也有单调提高的性质。即使在使用 KL-散 度投影到 Π \Pi Π 之后这个性质也是成立的。最后, 我们可以证明柔性策略迭代和策略迭代类似收敛到最优解, 如以下定理所示。

定理 6.1 6.1 6.1: 让 π 0 ∈ Π \pi_{0} \in \Pi π0Π 为初始策略。假设在柔性策略迭代算法下, π 0 \pi_{0} π0 会收敛到 π ∗ \pi * π, 那么对任意的 ( s , a ) ∈ S × A (s, a) \in \mathcal{S} \times \mathcal{A} (s,a)S×A 和任意的 π ∈ Π , Q π ∗ ( s , a ) ⩾ Q π ( s , a ) \pi \in \Pi, Q^{\pi *}(s, a) \geqslant Q^{\pi}(s, a) πΠ,Qπ(s,a)Qπ(s,a)

3. SAC

SAC 进一步把柔性策略迭代拓展到更实用的函数近似设定下, 它采用在价值函数和策略函数之间进行交替优化的方式来学习, 而不只是通过估计策略 π \pi π Q Q Q 值来提升策略。

Q ϕ ( s , a ) Q_{\phi}(s, a) Qϕ(s,a) 表示 Q Q Q 值函数, π θ \pi_{\theta} πθ 表示策略函数。这里我们考虑连续动作的设定并假设 π θ \pi_{\theta} πθ 的 输出为一个正态分布的期望和方差。 Q Q Q 值函数可以通过最小化柔性 Bellman 残差来学习:
J Q ( ϕ ) = E [ ( Q ( S t , A t ) − r ( S t , A t ) − γ E S t + 1 [ V ϕ ~ ( S t + 1 ) ] ) 2 ] J_{Q}(\phi)=\mathbb{E}\left[\left(Q\left(S_{t}, A_{t}\right)-r\left(S_{t}, A_{t}\right)-\gamma \mathbb{E}_{S_{t+1}}\left[V_{\tilde{\phi}}\left(S_{t+1}\right)\right]\right)^{2}\right] JQ(ϕ)=E[(Q(St,At)r(St,At)γESt+1[Vϕ~(St+1)])2]
这里 V ϕ ~ ( s ) = E π θ [ Q ϕ ~ ( s , a ) − α log ⁡ π θ ( a ∣ s ) ] , Q ϕ ~ V_{\tilde{\phi}}(s)=\mathbb{E}_{\pi_{\theta}}\left[Q_{\tilde{\phi}}(s, a)-\alpha \log \pi_{\theta}(a \mid s)\right], Q_{\tilde{\phi}} Vϕ~(s)=Eπθ[Qϕ~(s,a)αlogπθ(as)],Qϕ~ 表示参数 ϕ ~ \tilde{\phi} ϕ~ Q Q Q 值函数的参数 ϕ \phi ϕ 的指数移 动平均数得到的目标 Q Q Q 值网络。策略函数 π θ \pi_{\theta} πθ 可以通过最小化以下的 KL-散度得到。
J π ( θ ) = E s ∼ D [ E a ∼ π θ [ α log ⁡ π θ ( a ∣ s ) − Q ϕ ( s , a ) ] ] J_{\pi}(\theta)=\mathbb{E}_{s \sim \mathcal{D}}\left[\mathbb{E}_{a \sim \pi_{\theta}}\left[\alpha \log \pi_{\theta}(a \mid s)-Q_{\phi}(s, a)\right]\right] Jπ(θ)=EsD[Eaπθ[αlogπθ(as)Qϕ(s,a)]]
实际中, SAC 也使用了两个 Q Q Q 值函数(同时还有两个 Q Q Q 值目标函数)来处理 Q Q Q 值估计的 偏差问题, 也就是令 Q ϕ ( s , a ) = min ⁡ ( Q ϕ 1 ( s , a ) , Q ϕ 2 ( s , a ) ) Q_{\phi}(s, a)=\min \left(Q_{\phi_{1}}(s, a), Q_{\phi_{2}}(s, a)\right) Qϕ(s,a)=min(Qϕ1(s,a),Qϕ2(s,a)) 。注意到 J π ( θ ) J_{\pi}(\theta) Jπ(θ) 中的期望也依赖于策略 π θ \pi_{\theta} πθ, 我们可以使用似然比例梯度估计的方法来优化 J π ( θ ) J_{\pi}(\theta) Jπ(θ)

在连续动作空间的设 定下, 我们也可以用策略网络的再参数化来优化。这样往往能够减少梯度估计的方差。再参数化 的做法将 π θ \pi_{\theta} πθ 表示成一个使用状态 s s s 和标准正态样本 ϵ \epsilon ϵ 作为其输入的函数直接输出动作 a a a :
a = f θ ( s , ϵ ) . a=f_{\theta}(s, \epsilon) . a=fθ(s,ϵ).
代入 J π ( θ ) J_{\pi}(\theta) Jπ(θ) 的式子中
J π ( θ ) = E s ∼ D , ϵ ∼ N [ α log ⁡ π θ ( f θ ( s , ϵ ) ∣ s ) − Q ϕ ( s , f θ ( s , ϵ ) ) ] J_{\pi}(\theta)=\mathbb{E}_{s \sim \mathcal{D}, \epsilon \sim \mathcal{N}}\left[\alpha \log \pi_{\theta}\left(f_{\theta}(s, \epsilon) \mid s\right)-Q_{\phi}\left(s, f_{\theta}(s, \epsilon)\right)\right] Jπ(θ)=EsD,ϵN[αlogπθ(fθ(s,ϵ)s)Qϕ(s,fθ(s,ϵ))]
式中 N \mathcal{N} N 表示标准正态分布, π θ \pi_{\theta} πθ 现在被表示为 f θ f_{\theta} fθ

最后, SAC 还提供了自动调节正则化参数 α \alpha α 方法。该方法通过最小化以下损失函数实现。
J ( α ) = E a ∼ π θ [ − α log ⁡ π θ ( a ∣ s ) − α κ ] J(\alpha)=\mathbb{E}_{a \sim \pi_{\theta}}\left[-\alpha \log \pi_{\theta}(a \mid s)-\alpha \kappa\right] J(α)=Eaπθ[αlogπθ(as)ακ]

这里 κ \kappa κ 是一个可以理解为目标熵的超参数。这种更新 α \alpha α 的方法被称为自动熵调节方法。其背后的原理是在给定每一步平均熵至少为 κ \kappa κ 的约束下, 原来的策略优化问题的对偶形式。

再参数化技巧!!!

再参数化技巧 (Reparameterization Trick) 是将一个条件高斯概 率密度 p ( y ∣ x ) = N ( μ ( x ) , σ 2 ( x ) ) p(y \mid x)=\mathcal{N}\left(\mu(x), \sigma^{2}(x)\right) p(yx)=N(μ(x),σ2(x)) 写作函数 y ( x ) = μ ( x ) + σ ( x ) ϵ , ϵ ∼ N ( 0 , 1 ) y(x)=\mu(x)+\sigma(x) \epsilon, \epsilon \sim \mathcal{N}(0,1) y(x)=μ(x)+σ(x)ϵ,ϵN(0,1)

因而我们可以按程 序生成样本, 先采样 ϵ \epsilon ϵ 再以一种确定性的方式得到 y y y, 这使得对随机性策略的采样过程进行梯度 追踪。实际上根据同样的过程也可以得到从动作价值函数到策略间的反向传播梯度。为了像 DPG 那样通过价值函数来得到随机性策略的梯度, SAC使用了这个再参数化技巧, 并且对随机噪声取 了额外的期望值,从而可以使用随机性策略进行连续控制。

比如, 在 SAC 中, 随机性策略被一个均值和一个方差, 以及一个从正态分布(Normal Distribution)中采样的噪声项再参数化。SAC 中的优化目标有一个额外的熵相关项:
π ∗ = arg ⁡ max ⁡ π E τ ∼ π [ ∑ t = 0 ∞ γ t ( R ( S t , A t , S t + 1 ) + α H ( π ( ⋅ ∣ S t ) ) ) ] \pi^{*}=\arg \max _{\pi} \mathbb{E}_{\tau \sim \pi}\left[\sum_{t=0}^{\infty} \gamma^{t}\left(R\left(S_{t}, A_{t}, S_{t+1}\right)+\alpha H\left(\pi\left(\cdot \mid S_{t}\right)\right)\right)\right] π=argπmaxEτπ[t=0γt(R(St,At,St+1)+αH(π(St)))]

因此, 价值函数和 Q \mathrm{Q} Q 值函数间的关系变为
V π ( s ) = E a ∼ π [ Q π ( s , a ) ] + α H ( π ( ⋅ ∣ s ) ) = E a ∼ π [ Q π ( s , a ) − α log ⁡ π ( a ∣ s ) ] \begin{aligned} V^{\pi}(s) &=\mathbb{E}_{a \sim \pi}\left[Q^{\pi}(s, a)\right]+\alpha H(\pi(\cdot \mid s)) \\ &=\mathbb{E}_{a \sim \pi}\left[Q^{\pi}(s, a)-\alpha \log \pi(a \mid s)\right] \end{aligned} Vπ(s)=Eaπ[Qπ(s,a)]+αH(π(s))=Eaπ[Qπ(s,a)αlogπ(as)]
S A C \mathrm{SAC} SAC 中使用的策略是一个 Tanh 归一化高斯分布, 这与传统设置不同。SAC 中的动作表示可以使用如下再参数化技巧:
a θ ( s , ϵ ) = tanh ⁡ ( μ θ ( s ) + σ θ ( s ) ⋅ ϵ ) , ϵ ∼ N ( 0 , I ) a_{\theta}(s, \epsilon)=\tanh \left(\mu_{\theta}(s)+\sigma_{\theta}(s) \cdot \epsilon\right), \epsilon \sim \mathcal{N}(0, I) aθ(s,ϵ)=tanh(μθ(s)+σθ(s)ϵ),ϵN(0,I)

由于 SAC 中策略的随机性, 策略梯度可以在最大化期望价值函数时使用再参数化技巧得到, 即:
max ⁡ θ E a ∼ π θ [ Q π θ ( s , a ) − α log ⁡ π θ ( a ∣ s ) ] = max ⁡ θ E ϵ ∼ N [ Q π θ ( s , a ( s , ϵ ) ) − α log ⁡ π θ ( a ( s , ϵ ) ∣ s ) ] \begin{aligned} &\max _{\theta} \mathbb{E}_{a \sim \pi_{\theta}}\left[Q^{\pi_{\theta}}(s, a)-\alpha \log \pi_{\theta}(a \mid s)\right] \\ &=\max _{\theta} \mathbb{E}_{\epsilon \sim \mathcal{N}}\left[Q^{\pi_{\theta}}(s, a(s, \epsilon))-\alpha \log \pi_{\theta}(a(s, \epsilon) \mid s)\right] \end{aligned} θmaxEaπθ[Qπθ(s,a)αlogπθ(as)]=θmaxEϵN[Qπθ(s,a(s,ϵ))αlogπθ(a(s,ϵ)s)]
因而, 梯度可以经过 Q \mathrm{Q} Q 网络到策略网络, 与 D P G \mathrm{DPG} DPG 类似, 即:
∇ θ 1 ∣ B ∣ ∑ S t ∈ B ( Q π θ ( S t , a ( S t , ϵ ) ) − α log ⁡ π θ ( a ( S t , ϵ ) ∣ S t ) ) \nabla_{\theta} \frac{1}{|\mathcal{B}|} \sum_{S_{t} \in \mathcal{B}}\left(Q^{\pi_{\theta}}\left(S_{t}, a\left(S_{t}, \epsilon\right)\right)-\alpha \log \pi_{\theta}\left(a\left(S_{t}, \epsilon\right) \mid S_{t}\right)\right) θB1StB(Qπθ(St,a(St,ϵ))αlogπθ(a(St,ϵ)St))
其使用一个采样batch B \mathcal{B} B 来更新策略, 而 a ( S t , ϵ ) a\left(S_{t}, \epsilon\right) a(St,ϵ) 通过再参数化技巧来从随机性策略中采样。在这种 情况下, 再参数化技巧使得随机性策略能够以一种类似于 DPG 的方式来更新。

4. 伪代码

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是基于Tensorflow的《Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning with a Stochastic Actor》版本的SAC强化学习算法的Python代码: ```python import tensorflow as tf import numpy as np import gym # Create actor network class Actor(tf.keras.Model): def __init__(self, state_dim, action_dim, max_action): super(Actor, self).__init__() self.layer1 = tf.keras.layers.Dense(256, activation='relu') self.layer2 = tf.keras.layers.Dense(256, activation='relu') self.mu_layer = tf.keras.layers.Dense(action_dim, activation='tanh') self.sigma_layer = tf.keras.layers.Dense(action_dim, activation='softplus') self.max_action = max_action def call(self, state): x = self.layer1(state) x = self.layer2(x) mu = self.mu_layer(x) * self.max_action sigma = self.sigma_layer(x) + 1e-4 return mu, sigma # Create two critic networks class Critic(tf.keras.Model): def __init__(self, state_dim, action_dim): super(Critic, self).__init__() self.layer1 = tf.keras.layers.Dense(256, activation='relu') self.layer2 = tf.keras.layers.Dense(256, activation='relu') self.layer3 = tf.keras.layers.Dense(1, activation=None) def call(self, state, action): x = tf.concat([state, action], axis=1) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) return x # Create Soft Actor-Critic (SAC) Agent class SACAgent: def __init__(self, state_dim, action_dim, max_action): self.actor = Actor(state_dim, action_dim, max_action) self.critic1 = Critic(state_dim, action_dim) self.critic2 = Critic(state_dim, action_dim) self.target_critic1 = Critic(state_dim, action_dim) self.target_critic2 = Critic(state_dim, action_dim) self.max_action = max_action self.alpha = tf.Variable(0.1, dtype=tf.float32, name='alpha') self.gamma = 0.99 self.tau = 0.005 self.optimizer_actor = tf.keras.optimizers.Adam(learning_rate=3e-4) self.optimizer_critic1 = tf.keras.optimizers.Adam(learning_rate=3e-4) self.optimizer_critic2 = tf.keras.optimizers.Adam(learning_rate=3e-4) def get_action(self, state): state = np.expand_dims(state, axis=0) mu, sigma = self.actor(state) dist = tfp.distributions.Normal(mu, sigma) action = tf.squeeze(dist.sample(1), axis=0) return action.numpy() def update(self, replay_buffer, batch_size): states, actions, rewards, next_states, dones = replay_buffer.sample(batch_size) with tf.GradientTape(persistent=True) as tape: # Compute actor loss mu, sigma = self.actor(states) dist = tfp.distributions.Normal(mu, sigma) log_pi = dist.log_prob(actions) q1 = self.critic1(states, actions) q2 = self.critic2(states, actions) q_min = tf.minimum(q1, q2) alpha_loss = -tf.reduce_mean(self.alpha * (log_pi + self.target_entropy)) actor_loss = -tf.reduce_mean(tf.exp(self.alpha) * log_pi * q_min) # Compute critic loss next_mu, next_sigma = self.actor(next_states) next_dist = tfp.distributions.Normal(next_mu, next_sigma) next_actions = tf.clip_by_value(next_dist.sample(1), -self.max_action, self.max_action) target_q1 = self.target_critic1(next_states, next_actions) target_q2 = self.target_critic2(next_states, next_actions) target_q = tf.minimum(target_q1, target_q2) target_q = rewards + self.gamma * (1.0 - dones) * (target_q - tf.exp(self.alpha) * next_dist.entropy()) q1_loss = tf.reduce_mean(tf.square(q1 - target_q)) q2_loss = tf.reduce_mean(tf.square(q2 - target_q)) critic_loss = q1_loss + q2_loss + alpha_loss # Compute gradients and update weights actor_grads = tape.gradient(actor_loss, self.actor.trainable_variables) critic1_grads = tape.gradient(critic_loss, self.critic1.trainable_variables) critic2_grads = tape.gradient(critic_loss, self.critic2.trainable_variables) self.optimizer_actor.apply_gradients(zip(actor_grads, self.actor.trainable_variables)) self.optimizer_critic1.apply_gradients(zip(critic1_grads, self.critic1.trainable_variables)) self.optimizer_critic2.apply_gradients(zip(critic2_grads, self.critic2.trainable_variables)) # Update target networks for w, w_target in zip(self.critic1.weights, self.target_critic1.weights): w_target.assign(self.tau * w + (1 - self.tau) * w_target) for w, w_target in zip(self.critic2.weights, self.target_critic2.weights): w_target.assign(self.tau * w + (1 - self.tau) * w_target) # Update alpha alpha_grad = tape.gradient(alpha_loss, self.alpha) self.alpha.assign_add(1e-4 * alpha_grad) def save(self, filename): self.actor.save_weights(filename + '_actor') self.critic1.save_weights(filename + '_critic1') self.critic2.save_weights(filename + '_critic2') def load(self, filename): self.actor.load_weights(filename + '_actor') self.critic1.load_weights(filename + '_critic1') self.critic2.load_weights(filename + '_critic2') # Create replay buffer class ReplayBuffer: def __init__(self, max_size): self.max_size = max_size self.buffer = [] self.position = 0 def add(self, state, action, reward, next_state, done): if len(self.buffer) < self.max_size: self.buffer.append(None) self.buffer[self.position] = (state, action, reward, next_state, done) self.position = (self.position + 1) % self.max_size def sample(self, batch_size): indices = np.random.choice(len(self.buffer), batch_size, replace=False) states, actions, rewards, next_states, dones = [], [], [], [], [] for idx in indices: state, action, reward, next_state, done = self.buffer[idx] states.append(np.array(state, copy=False)) actions.append(np.array(action, copy=False)) rewards.append(reward) next_states.append(np.array(next_state, copy=False)) dones.append(done) return np.array(states), np.array(actions), np.array(rewards, dtype=np.float32), np.array(next_states), np.array(dones, dtype=np.uint8) # Create environment and agent env = gym.make('Pendulum-v0') state_dim = env.observation_space.shape[0] action_dim = env.action_space.shape[0] max_action = float(env.action_space.high[0]) agent = SACAgent(state_dim, action_dim, max_action) replay_buffer = ReplayBuffer(1000000) # Train agent max_episodes = 1000 max_steps = 500 batch_size = 256 update_interval = 1 target_entropy = -action_dim for episode in range(max_episodes): state = env.reset() total_reward = 0 for step in range(max_steps): action = agent.get_action(state) next_state, reward, done, _ = env.step(action) replay_buffer.add(state, action, reward, next_state, done) if len(replay_buffer.buffer) > batch_size: agent.update(replay_buffer, batch_size) state = next_state total_reward += reward if done: break print("Episode:", episode, "Total Reward:", total_reward) ``` 请注意,以上代码仅供参考,并且需要根据具体环境和参数进行调整和完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CHH3213

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

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

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

打赏作者

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

抵扣说明:

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

余额充值