强化学习(1)-- 介绍

1. 强化学习的基础架构

强化学习是一种人工智能领域的学习方法,它的目标是让智能体(agent)通过与环境交互来最大化累积的奖励(reward)。

在强化学习中,智能体通过不断与环境交互,从中获取反馈信号,以调整自身的行为策略,使得未来获得的奖励最大化。智能体在环境中执行一个动作,环境根据该动作返回一个奖励信号以及下一时刻的状态,智能体根据当前的状态以及奖励信号来选择下一个动作,如此不断地交互下去,直到任务完成。
在这里插入图片描述
强化学习中的主要元素包括:

  • 环境(environment):智能体与之交互的外部世界,它包含了状态、动作和奖励等信息。
  • 状态(state):环境的一种表示,描述了环境的某个特定时刻的状态。
  • 智能体(agent):决策者,它的目标是从环境中学习一个最优的行为策略,使得未来获得的累积奖励最大化。
  • 策略(policy):智能体的行为策略,用于选择下一步要执行的动作,以最大化未来的奖励。
  • 动作(action):智能体可以执行的操作,用于改变环境的状态。
  • 奖励(reward):智能体根据执行某个动作而获得的反馈信号,通常是一个数值表示执行该动作的好坏。

2. 强化学习的主流方法

2.1 Q-learning

2.1.1 算法介绍

Q-learning是一种基于动作-值函数(Q函数)的强化学习方法,它通过不断更新Q函数来学习最优的行为策略。

所谓Q函数本质上是一个表格,表示在某个状态下执行某个动作所能获得的累积奖励,Q-Learning的核心是不断更新这个Q Table 来完成动作-值的更新。在这里插入图片描述

Q-Learning的主要流程如下:

  1. 初始化Q函数:在开始学习之前,需要初始化Q函数,通常将Q函数的值设为0或者随机值。
  2. 环境交互:智能体与环境交互,执行一个动作,并且获取环境返回的奖励和下一个状态。
  3. 更新Q函数:根据当前的状态、动作、奖励和下一个状态,更新Q函数的值。具体的更新公式为:Q(s,a) = Q(s,a) + alpha * (reward + gamma * max[Q(s’,a’)] - Q(s,a))
  4. 其中,s表示当前状态,a表示当前动作,s’表示下一个状态,a’表示下一个状态下的最优动作,alpha表示学习率(控制每次更新的步长),gamma表示折扣因子(控制未来奖励的重要程度)。
  5. 选择下一个动作:根据当前状态和Q函数,选择下一个动作,通常采用epsilon-greedy策略,即以一定的概率随机选择动作(探索),以一定的概率选择当前Q值最大的动作(利用)。

循环执行2-4步,直到学习结束。

2.1.2 算法流程举例

假设我们要使用Q-Learning学习一个迷宫游戏:

(1)首先,我们需要定义迷宫的状态空间和动作空间。假设迷宫是一个5x5的网格,智能体可以执行4个动作:上、下、左、右。我们可以用一个5x5的矩阵表示状态空间,0表示空格子,1表示障碍物,2表示终点。例如,下面是一个迷宫的状态空间:

0 0 0 1 0
0 1 0 1 0
0 1 0 1 0
0 1 0 1 0
0 0 0 2 0

(2)接下来,我们需要初始化Q函数。假设我们使用一个5x5x4的数组来表示Q函数,第一维表示状态,第二维表示动作,第三维表示Q值。初始时,我们可以将所有的Q值设为0。
(3)环境交互。智能体从起点开始,不断执行动作,直到到达终点。每次执行一个动作,智能体会得到一个奖励(如果到达了终点,奖励为1,否则奖励为0),并进入下一个状态。例如,智能体可以从(0, 0)出发,先执行向右的动作,然后到达(0, 1)。假设这一步的奖励为0,那么智能体当前的状态就是(0, 1)。
(4)更新Q函数。根据当前状态、执行的动作、奖励和下一个状态,可以使用Q-Learning更新Q函数的值。例如,假设智能体在状态(0, 0)执行了向右的动作,到达了状态(0, 1),奖励为0,下一个最优动作是向下。那么我们可以使用下面的公式来更新Q函数:

Q(0, 0, 1) = Q(0, 0, 1) + alpha * (0 + gamma * max[Q(0, 1, 0), Q(0, 1, 1), Q(0, 1, 2), Q(0, 1, 3)] - Q(0, 0, 1))

其中,最后一维的0,1,2,3分别代表上右下左四个动作
(5)选择下一个动作。根据当前状态和Q函数,可以使用epsilon-greedy策略来选择下一个动作。即以一定的概率随机选择一个动作,以一定的概率选择当前Q值最大的动作。
可以设置一个递减的epsilon值,让随机选择动作的概率随着训练次数的增加而减小。例如,开始时可以将epsilon设置为1,然后每训练一次就将epsilon减小一个小的值,直到最后变成一个很小的值。

2.1.3 简化版代码实现

import numpy as np

# 定义状态空间和动作空间
maze = np.array([
    [0, 0, 0, 1, 0],
    [0, 1, 0, 1, 0],
    [0, 1, 0, 1, 0],
    [0, 1, 0, 1, 0],
    [0, 0, 0, 2, 0]
])
n_states = 5 * 5
n_actions = 4

# 初始化Q函数
Q = np.zeros((n_states, n_actions))

# 定义超参数
alpha = 0.1
gamma = 0.9
epsilon = 1.0
epsilon_decay = 0.001

# 定义epsilon-greedy策略
def choose_action(state):
    if np.random.uniform(0, 1) < epsilon:
        action = np.random.choice(n_actions)
    else:
        action = np.argmax(Q[state])
    return action

# 循环训练
n_episodes = 1000
for episode in range(n_episodes):
    # 重置状态
    state = 0
    
    # 环境交互
    while maze.flat[state] != 2:
        action = choose_action(state)
        next_state = np.clip({
            0: state - 5,
            1: state + 1,
            2: state + 5,
            3: state - 1
        }[action], 0, 24)
        reward = int(maze.flat[next_state] == 2)
        
        # 更新Q函数
        Q[state][action] = Q[state][action] + alpha * (reward + gamma * np.max(Q[next_state]) - Q[state][action])
        
        # 转移状态
        state = next_state
        
    # 调整epsilon值
    epsilon = max(0.1, epsilon - epsilon_decay)

注意,上述代码中没有考虑障碍物对智能体的影响。如果智能体走到了一个障碍物,那么它应该停留在原地,不应该转移到下一个状态。可以根据具体情况对代码进行修改。

2.1.4 算法评价

Q-Learning的优点在于可以学习任何形式的策略,并且具有良好的收敛性质。但是,它也有一些局限性,例如需要离散化状态空间和动作空间、难以处理连续状态空间和动作空间等问题。

2.2 策略梯度(Policy Gradient)

策略梯度(Policy Gradient)是一种基于梯度优化的强化学习算法,它的目标是直接学习策略函数(policy function),而不是学习值函数(value function)。策略函数是一个映射,将当前状态映射到一个动作的概率分布。策略梯度算法的目标是最大化期望累积回报(expected cumulative reward),这个期望是在策略函数下采样得到的。
在这里插入图片描述
策略梯度的算法框架如下:

  1. 初始化策略函数的参数。
  2. 生成一批轨迹,即从起始状态开始,通过执行策略函数生成动作序列,直到到达终止状态,将得到的回报作为这个轨迹的价值。
  3. 对所有轨迹的回报进行标准化处理,使其均值为0,标准差为1。
  4. 计算所有轨迹中每个状态-动作对的梯度,并对所有梯度进行加权平均,得到策略梯度的方向。
  5. 使用梯度上升法更新策略函数的参数,使得策略函数的期望累积回报最大化。

重复步骤2~5,直到策略函数的性能收敛或者达到最大迭代次数。
在这里插入图片描述
策略梯度算法的核心是计算梯度方向,即如何更新策略函数的参数以优化预期的回报。梯度方向的计算通常使用策略梯度定理(Policy Gradient Theorem),其公式如下:

∇ θ J ( θ ) ≈ 1 N ∑ i = 1 N ∑ t = 0 T ∇ θ log ⁡ π θ ( a t ( i ) ∣ s t ( i ) ) R ( i ) \nabla_{\theta} J(\theta) \approx \frac{1}{N} \sum_{i=1}^N \sum_{t=0}^T \nabla_{\theta} \log \pi_{\theta}(a_t^{(i)}|s_t^{(i)}) R^{(i)} θJ(θ)N1i=1Nt=0Tθlogπθ(at(i)st(i))R(i)

其中, ∇ θ J ( θ ) \nabla_{\theta} J(\theta) θJ(θ) 表示要更新的参数的梯度方向, J ( θ ) J(\theta) J(θ) 表示预期的回报, π θ ( a t ( i ) ∣ s t ( i ) ) \pi_{\theta}(a_t^{(i)}|s_t^{(i)}) πθ(at(i)st(i)) 表示在状态 s t ( i ) s_t^{(i)} st(i) 下,根据策略函数 π θ \pi_{\theta} πθ 采取动作 a t ( i ) a_t^{(i)} at(i) 的概率, R ( i ) R^{(i)} R(i) 表示第 i i i 条采样轨迹的总回报。

上式中, log ⁡ π θ ( a t ( i ) ∣ s t ( i ) ) \log \pi_{\theta}(a_t^{(i)}|s_t^{(i)}) logπθ(at(i)st(i)) 表示采取动作 a t ( i ) a_t^{(i)} at(i) 的概率的对数,而 ∇ θ log ⁡ π θ ( a t ( i ) ∣ s t ( i ) ) \nabla_{\theta} \log \pi_{\theta}(a_t^{(i)}|s_t^{(i)}) θlogπθ(at(i)st(i)) 表示对应的梯度向量。这个梯度向量可以使用自动微分或符号微分方法进行计算。

需要注意的是,上述公式是使用蒙特卡洛采样的策略梯度算法(也称为REINFORCE算法)的公式,它的更新方向具有较大的方差,不太稳定。在实际中,为了增加计算的效率和更新的稳定性,可以采用基于价值函数或基于策略梯度定理的Actor-Critic等策略梯度算法的改进版本,以获取更好的训练效果。

2.2.1 简化代码实现

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

class Policy(nn.Module):
    def __init__(self, n_states, n_actions):
        super(Policy, self).__init__()
        self.fc1 = nn.Linear(n_states, 10)
        self.fc2 = nn.Linear(10, n_actions)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.softmax(self.fc2(x), dim=0)
        return x

class PolicyGradientAgent:
    def __init__(self, env, learning_rate=0.01, reward_decay=0.95):
        self.env = env
        self.n_actions = env.action_space.n
        self.n_states = env.observation_space.shape[0]
        self.lr = learning_rate
        self.gamma = reward_decay
        
        # 创建策略网络
        self.policy_network = Policy(self.n_states, self.n_actions)
        self.optimizer = optim.Adam(self.policy_network.parameters(), lr=self.lr)
        
    def choose_action(self, state):
        # 根据当前状态选择一个动作
        state = torch.from_numpy(state).float().unsqueeze(0)
        action_probs = self.policy_network(state)[0]
        action = np.random.choice(range(self.n_actions), p=action_probs.detach().numpy())
        return action
    
    def store_transition(self, state, action, reward):
        # 存储每一步的状态、动作和回报
        self.states.append(state)
        self.actions.append(action)
        self.rewards.append(reward)
    
    def learn(self):
	    # 计算折扣回报
	    discounted_rewards = self.discount_and_norm_rewards()
	
	    # 将状态、动作、回报转换为tensor
	    states = torch.tensor(self.states)
	    actions = torch.tensor(self.actions)
	    rewards = torch.tensor(discounted_rewards)
	
	    # 计算损失
	    log_probs = self.policy_network(states).gather(1, actions.view(-1, 1))
	    loss = -torch.mean(log_probs * rewards)
	
	    # 梯度下降更新策略网络
	    self.optimizer.zero_grad()
	    loss.backward()
	    self.optimizer.step()
	
	    # 清空经验池
	    self.states, self.actions, self.rewards = [], [], []
	
	def discount_and_norm_rewards(self):
	    # 计算折扣回报
	    discounted_rewards = np.zeros_like(self.rewards)
	    running_add = 0
	    for t in reversed(range(len(self.rewards))):
	        running_add = running_add * self.gamma + self.rewards[t]
	        discounted_rewards[t] = running_add
	
	    # 归一化处理
	    discounted_rewards -= np.mean(discounted_rewards)
	    discounted_rewards /= np.std(discounted_rewards)
	
	    return discounted_rewards

def train(env, agent, max_episodes, max_episode_steps):
	scores = []
	for i in range(max_episodes):
	    state = env.reset()
	    score = 0
	    for j in range(max_episode_steps):
	        action = agent.choose_action(state)
	        next_state, reward, done, _ = env.step(action)
	        agent.store_transition(state, action, reward)
	        state = next_state
	        score += reward
	        if done or j == max_episode_steps - 1:
	            agent.learn()
	            scores.append(score)
	            print('Episode: {} | Score: {:.2f}'.format(i, score))
	            break
	return scores

2.3 Actor-Critic

Actor-Critic是一种结合了策略梯度和值函数的强化学习方法,它既能学习最优的行为策略,又能学习价值函数,从而更加稳定和高效地学习。

Actor-Critic算法中的“Actor”指的是策略网络,它类似于策略梯度算法中的神经网络,负责选择动作。而“Critic”指的是值函数网络,用来估计当前状态的价值,并提供一个基准来指导策略的更新。

Actor-Critic算法有很多变体,如N步Actor-Critic算法、异步Actor-Critic算法、连续动作空间的Actor-Critic算法等等。其中最基本的版本称为“单步Actor-Critic算法”,它的具体实现如下:

  1. 初始化策略网络和值函数网络;
  2. 对于每个回合,执行以下操作:
    • 初始化环境和状态;
    • 在当前策略下选择一个动作;
    • 执行动作并观察下一个状态和奖励;
    • 计算当前状态的价值,即使用值函数网络估计当前状态的状态值或动作值;
    • 计算当前状态的优势函数,即当前状态的价值减去基准价值;
    • 使用当前状态、动作、优势函数和奖励来计算损失函数;
    • 使用损失函数对策略网络和值函数网络进行反向传播更新参数;
  3. 重复执行步骤2,直到训练结束。

在这里插入图片描述
Actor-Critic算法的数学原理可以用下面的公式描述:

Δ θ = α ∇ θ log ⁡ π θ ( s , a ) ( Q π ( s , a ) − V π ( s ) ) \Delta\theta=\alpha\nabla_{\theta}\log\pi_{\theta}(s,a)(Q^{\pi}(s,a)-V^{\pi}(s)) Δθ=αθlogπθ(s,a)(Qπ(s,a)Vπ(s))

其中, Δ θ \Delta\theta Δθ表示策略的改进量, α \alpha α表示学习率, ∇ θ log ⁡ π θ ( s , a ) \nabla_{\theta}\log\pi_{\theta}(s,a) θlogπθ(s,a)表示策略在状态 s s s下采取行动 a a a的概率的梯度, Q π ( s , a ) Q^{\pi}(s,a) Qπ(s,a)表示在状态 s s s下采取行动 a a a的回报期望, V π ( s ) V^{\pi}(s) Vπ(s)表示在状态 s s s下采取最优行动的回报期望。公式的意思是:Actor根据当前状态 s s s和采取的行动 a a a,计算出策略的梯度,并根据Critic评估的 Q π ( s , a ) Q^{\pi}(s,a) Qπ(s,a) V π ( s ) V^{\pi}(s) Vπ(s),更新策略的参数 θ \theta θ,从而使得Actor的策略更加接近于最优策略。

2.3.1 简化版代码实现

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from collections import deque

class ActorCritic(nn.Module):
    def __init__(self, input_size, output_size, hidden_size=128):
        super(ActorCritic, self).__init__()
        self.actor = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size),
            nn.Softmax(dim=-1)
        )
        self.critic = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, 1)
        )
    
    def forward(self, x):
        return self.actor(x), self.critic(x)

def train(env, ac, gamma=0.99, num_episodes=1000, lr=0.001):
    optimizer = optim.Adam(ac.parameters(), lr=lr)
    criterion = nn.SmoothL1Loss()

    for episode in range(num_episodes):
        obs = env.reset()
        done = False
        ep_reward = 0
        while not done:
            action_prob, value = ac(torch.from_numpy(obs).float())
            action = np.random.choice(len(action_prob), p=action_prob.detach().numpy())
            next_obs, reward, done, _ = env.step(action)
            ep_reward += reward

            _, next_value = ac(torch.from_numpy(next_obs).float())

            if done:
                target = torch.Tensor([reward])
            else:
                target = reward + gamma * next_value.detach()

            td_error = target - value

            actor_loss = -torch.log(action_prob[action]) * td_error.detach()
            critic_loss = criterion(value, target)
            loss = actor_loss + critic_loss

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            obs = next_obs
        
        print("Episode {}: Reward = {}".format(episode+1, ep_reward))

在训练期间,我们还需要维护一个经验回放缓冲区。每个时间步,我们将当前状态、动作、奖励和下一个状态存储在回放缓冲区中。当我们训练策略和值函数网络时,我们可以从回放缓冲区中随机采样一批数据,并使用它们来更新网络。

2.4 深度强化学习(Deep Reinforcement Learning)

将深度学习方法应用到强化学习中的一种方法,通过使用深度神经网络来表示策略函数或值函数,从而更好地处理高维状态空间和连续动作空间的问题。

2.4.1 DQN(Deep Q-Networks)

深度强化学习中最经典的算法之一,其核心思想是使用深度神经网络来近似Q值函数,通过策略改进的方式不断提高策略的性能。

DQN(Deep Q-Networks)是一种深度强化学习算法,是利用神经网络近似Q-learning的一种方法。DQN主要解决了Q-learning算法在高维状态空间下无法有效应用的问题。

DQN算法的主要思路是使用一个神经网络来逼近Q函数,将状态作为神经网络的输入,输出为每个动作的Q值。在训练时,使用经验回放机制和目标网络来提高训练效果。

具体来说,DQN算法使用一个神经网络来近似Q函数,将状态作为输入,输出每个动作的Q值。神经网络的训练目标是最小化Q值与目标Q值之间的均方误差,其中目标Q值由当前状态和下一状态的最大Q值计算得出。为了提高训练效率和稳定性,DQN算法使用了两个技术:经验回放和目标网络。

经验回放是一种存储和重用过去的经验的方法。在训练时,DQN算法将智能体与环境的交互过程中的经验存储在一个经验回放池中,并从中随机选择一批经验进行训练。这样做的好处是可以使数据更充分地利用,并且减少数据之间的相关性。

目标网络是另外一个神经网络,它的参数与主网络参数相同,但是在训练过程中不会被更新,而是定期从主网络复制一份参数。这样做的好处是可以使目标Q值更加稳定,减少训练过程中的波动。

总之,DQN算法通过使用深度神经网络逼近Q函数,使用经验回放和目标网络来提高训练效率和稳定性,能够有效地解决高维状态空间下的强化学习问题。它是深度强化学习中一种非常有效的算法,在许多复杂任务上取得了成功。
在这里插入图片描述

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import random
from collections import deque

class DQN(nn.Module):
    def __init__(self, state_dim, action_dim, hidden_dim=64):
        super(DQN, self).__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, action_dim)
        )
    
    def forward(self, state):
        return self.net(state)

class ReplayBuffer:
    def __init__(self, capacity):
        self.buffer = deque(maxlen=capacity)
    
    def push(self, state, action, reward, next_state, done):
        self.buffer.append((state, action, reward, next_state, done))
    
    def sample(self, batch_size):
        state, action, reward, next_state, done = zip(*random.sample(self.buffer, batch_size))
        return np.array(state), np.array(action), np.array(reward), np.array(next_state), np.array(done)
    
    def __len__(self):
        return len(self.buffer)

class DQNAgent:
    def __init__(self, state_dim, action_dim, lr=0.001, gamma=0.99, epsilon=1.0, epsilon_decay=0.995, epsilon_min=0.01, target_update=10, batch_size=64, memory_capacity=10000):
        self.state_dim = state_dim
        self.action_dim = action_dim
        self.gamma = gamma
        self.epsilon = epsilon
        self.epsilon_decay = epsilon_decay
        self.epsilon_min = epsilon_min
        self.target_update = target_update
        self.batch_size = batch_size
        self.memory = ReplayBuffer(memory_capacity)
        
        self.q_net = DQN(state_dim, action_dim)
        self.target_net = DQN(state_dim, action_dim)
        self.target_net.load_state_dict(self.q_net.state_dict())
        self.target_net.eval()
        
        self.optimizer = optim.Adam(self.q_net.parameters(), lr=lr)
        self.loss_fn = nn.MSELoss()
        self.total_reward = 0
    
    def get_action(self, state):
        if np.random.rand() < self.epsilon:
            return np.random.randint(self.action_dim)
        with torch.no_grad():
            q_values = self.q_net(torch.tensor(state, dtype=torch.float))
            return q_values.argmax().item()
    
    def train(self):
        if len(self.memory) < self.batch_size:
            return
        
        states, actions, rewards, next_states, dones = self.memory.sample(self.batch_size)
        self.total_rewards += rewards
        
        with torch.no_grad():
            max_q_values = self.target_net(torch.tensor(next_states, dtype=torch.float)).max(dim=1)[0]
            target_q_values = rewards + self.gamma * max_q_values * (1 - dones)
        
        q_values = self.q_net(torch.tensor(states, dtype=torch.float)).gather(1, torch.tensor(actions, dtype=torch.long).unsqueeze(1)).squeeze()
        
        loss = self.loss_fn(q_values, target_q_values)
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()
        
        if self.epsilon > self.epsilon_min:
            self.epsilon *= self.epsilon_decay
        
        if self.target_update > 0 and self.memory.__len__() % self.target_update == 0:
            self.target_net.load_state_dict(self.q_net.state_dict())
            
        return self.total_rewards
    
    def store_transition(self, state, action, reward, next_state, done):
        self.memory.push(state, action, reward, next_state, done)

def main():
    env = gym.make('CartPole-v0')
    batch_size = 32
    gamma = 0.99
    dqn_agent = DQNAgent(state_dim=env.observation_space.shape[0], action_dim=env.action_space.n, lr=1e-3, gamma=gamme, batch_size=batch_size)
    num_episodes = 1000
    for episode in range(num_episodes):
        episode_reward = dqn_agent.train()
        print("Episode:", episode, "Reward:", episode_reward)
    env.close()

在DQN算法中,我们使用两个神经网络来估计 Q 值函数:一个是当前估计的 Q 网络,另一个是目标 Q 网络。这是为了解决 Q 学习算法中使用同一个 Q 网络估计目标值和估计值时的不稳定性问题。

Q 网络的参数 θ \theta θ 更新使用的是以下公式:
θ t + 1 = θ t + α ( r + γ Q θ t ( s ′ , a ′ ) − Q θ t ( s , a ) ) ∇ θ Q θ t ( s , a ) \theta_{t+1} = \theta_{t} + \alpha(r+ \gamma Q_{\theta{t}}(s', a') - Q_{\theta{t}}(s, a))\nabla_{\theta}Q_{\theta{t}}(s, a) θt+1=θt+α(r+γQθt(s,a)Qθt(s,a))θQθt(s,a)
其中, α \alpha α 是学习率, r r r 是当前状态下的奖励值, γ \gamma γ 是折扣因子,表示对于未来奖励的考虑程度, s s s 是当前状态, a a a 是在 s s s 状态下的动作, s ′ s' s 是执行动作 a a a 后的下一个状态, max ⁡ a ′ Q θ t ( s ′ , a ′ ) \max_{a'} Q_{\theta_t}(s',a') maxaQθt(s,a) 表示下一个状态 s ′ s' s 下所有可能的动作 a ′ a' a 的 Q 值中的最大值, Q θ t ( s , a ) Q_{\theta_t}(s,a) Qθt(s,a) 表示当前状态和动作的 Q 值。

目标 Q 网络的参数 θ ′ \theta' θ 的更新使用的是以下公式:
θ t ′ = τ θ t + ( 1 − τ ) θ t ′ \theta_{t}^{'} = \tau\theta_{t} + (1-\tau)\theta_{t}^{'} θt=τθt+(1τ)θt
其中, τ \tau τ 是一个小于 1 的参数,用于平滑地更新目标网络的参数,通常取 0.01 或更小的值。

可以将目标 Q 网络理解为固定不变的“银行家”,它给出一个“稳妥”的估计,而 Q 网络则充当“投资人”,不断调整自己的估计。在训练过程中,Q 网络的参数会不断地被更新,而目标 Q 网络的参数只是慢慢地向 Q 网络的参数靠近,这样可以避免过度的拟合和震荡,提高训练的稳定性。

2.4.2 DDPG(Deep Deterministic Policy Gradient)

主要解决连续动作空间问题,使用Actor-Critic结构,将连续的动作空间转化为连续的动作值,并通过反馈更新Actor和Critic的权重参数。

DDPG算法使用一个Actor网络和一个Critic网络,其中Actor网络是一个策略函数,它接收状态作为输入,输出对应动作的概率分布;Critic网络是一个价值函数,它接收状态和动作作为输入,输出对应的Q值。这两个网络都是深度神经网络,因此可以处理高维、复杂的状态空间和动作空间。

DDPG算法中,Actor网络的目标是最大化Q值,即最大化期望回报。为了实现这一点,Actor网络采用了一种叫做确定性策略梯度(Deterministic Policy Gradient,DPG)的方法,它直接输出最优动作,而不是动作的概率分布。Critic网络的目标是学习Q值函数,用于评估Actor网络输出的动作。

DDPG算法使用经验回放机制,这样可以使样本更加独立和随机,从而更好地训练神经网络。DDPG算法还引入了一个Target网络,用于计算目标Q值。Target网络是Critic网络的一个副本,但是它的参数是通过软更新方式得到的,即定期将Critic网络的参数赋值给Target网络。

在每一步中,DDPG算法使用Actor网络选择动作,然后执行动作并观察环境的反馈,将反馈(奖励和下一个状态)存储在经验回放缓冲区中。在训练过程中,DDPG算法从经验回放缓冲区中随机采样一批经验,然后使用它们来更新Actor和Critic网络的参数。

具体地说,更新Critic网络的参数时,DDPG算法使用类似于DQN中的Bellman方程的更新公式,其中目标Q值使用Target网络计算。更新Actor网络的参数时,DDPG算法使用策略梯度算法,其中梯度使用Critic网络计算。此外,为了防止过拟合和提高训练效果,DDPG算法还使用了噪声策略,使得Actor网络的输出更具随机性,从而更好地探索动作空间。

在这里插入图片描述

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
import random

# 经验回放定义
class ReplayBuffer:
    def __init__(self, buffer_size, state_dim, action_dim):
        self.buffer_size = buffer_size
        self.state_dim = state_dim
        self.action_dim = action_dim
        self.buffer = np.empty((buffer_size, state_dim * 2 + action_dim + 2)) # state, action, reward, next_state, done
        self.position = 0

    def add(self, state, action, reward, next_state, done):
        data = np.hstack((state, action, [reward], next_state, [done]))
        self.buffer[self.position % self.buffer_size] = data
        self.position += 1

    def sample(self, batch_size):
        if self.position < batch_size:
            raise Exception("not enough data")
        idx = np.random.choice(min(self.position, self.buffer_size), batch_size, replace=False)
        batch = self.buffer[idx]
        state = torch.tensor(batch[:, :self.state_dim], dtype=torch.float)
        action = torch.tensor(batch[:, self.state_dim:self.state_dim+self.action_dim], dtype=torch.float)
        reward = torch.tensor(batch[:, -3], dtype=torch.float)
        next_state = torch.tensor(batch[:, -2-self.state_dim:-2], dtype=torch.float)
        done = torch.tensor(batch[:, -1], dtype=torch.float)
        return state, action, reward, next_state, done

# 噪声生成
class OrnsteinUhlenbeckProcess:
    def __init__(self, size, theta=0.15, sigma=0.2, mu=0.):
        self.size = size
        self.theta = theta
        self.sigma = sigma
        self.mu = mu
        self.reset()

    def reset(self):
        self.state = np.ones(self.size) * self.mu

    def sample(self):
        x = self.state
        dx = self.theta * (self.mu - x) + self.sigma * np.random.randn(len(x))
        self.state = x + dx
        return self.state

# 神经网络定义
class ActorNet(nn.Module):
    def __init__(self, state_dim, action_dim, hidden_dim):
        super(ActorNet, self).__init__()
        self.fc1 = nn.Linear(state_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, hidden_dim)
        self.fc3 = nn.Linear(hidden_dim, action_dim)
        self.dropout = nn.Dropout(p=0.1)

    def forward(self, state):
        x = F.relu(self.fc1(state))
        x = self.dropout(x)
        x = F.relu(self.fc2(x))
        x = self.dropout(x)
        x = torch.tanh(self.fc3(x))
        return x

class CriticNet(nn.Module):
    def __init__(self, state_dim, action_dim, hidden_dim):
        super(CriticNet, self).__init__()
        self.fc1 = nn.Linear(state_dim+action_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, hidden_dim)
        self.fc3 = nn.Linear(hidden_dim, 1)
        self.dropout = nn.Dropout(p=0.1)

    def forward(self, state, action):
        x = torch.cat([state, action], dim=1)
        x = F.relu(self.fc1(x))
        x = self.dropout(x)
        x = F.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)
        return x
        
class DDPG:
    def __init__(self, state_dim, action_dim, gamma, tau, buffer_size, batch_size, actor_lr, critic_lr):
        self.state_dim = state_dim
        self.action_dim = action_dim
        self.gamma = gamma
        self.tau = tau
        self.buffer_size = buffer_size
        self.batch_size = batch_size
        self.actor_lr = actor_lr
        self.critic_lr = critic_lr

        self.actor = Actor(self.state_dim, self.action_dim).to(device)
        self.actor_target = Actor(self.state_dim, self.action_dim).to(device)
        self.critic = Critic(self.state_dim, self.action_dim).to(device)
        self.critic_target = Critic(self.state_dim, self.action_dim).to(device)

        self.actor_optimizer = torch.optim.Adam(self.actor.parameters(), lr=self.actor_lr)
        self.critic_optimizer = torch.optim.Adam(self.critic.parameters(), lr=self.critic_lr)

        self.memory = ReplayBuffer(self.buffer_size)

    def act(self, state):
        state = torch.FloatTensor(state).unsqueeze(0).to(device)
        with torch.no_grad():
            action = self.actor(state).cpu().data.numpy().flatten()
        return action

    def update(self):
        if len(self.memory) < self.batch_size:
            return

        states, actions, rewards, next_states, dones = self.memory.sample(self.batch_size)

        states = torch.FloatTensor(states).to(device)
        actions = torch.FloatTensor(actions).to(device)
        rewards = torch.FloatTensor(rewards).unsqueeze(1).to(device)
        next_states = torch.FloatTensor(next_states).to(device)
        dones = torch.FloatTensor(dones).unsqueeze(1).to(device)

        # Update critic
        next_actions = self.actor_target(next_states)
        q_targets_next = self.critic_target(next_states, next_actions)
        q_targets = rewards + (self.gamma * q_targets_next * (1 - dones))
        q_expected = self.critic(states, actions)
        critic_loss = F.mse_loss(q_expected, q_targets)
        self.critic_optimizer.zero_grad()
        critic_loss.backward()
        self.critic_optimizer.step()

        # Update actor
        actions_pred = self.actor(states)
        actor_loss = -self.critic(states, actions_pred).mean()
        self.actor_optimizer.zero_grad()
        actor_loss.backward()
        self.actor_optimizer.step()

        # Update target networks
        self.soft_update(self.critic, self.critic_target)
        self.soft_update(self.actor, self.actor_target)

    def soft_update(self, local_model, target_model):
        for target_param, local_param in zip(target_model.parameters(), local_model.parameters()):
            target_param.data.copy_(self.tau*local_param.data + (1.0-self.tau)*target_param.data)

    def add_to_buffer(self, state, action, reward, next_state, done):
        self.memory.add(state, action, reward, next_state, done)


def train(env, agent, n_episodes=2000, max_t=1000, print_every=100, save_every=500):
    scores_deque = deque(maxlen=print_every)
    scores = []
    for i_episode in range(1, n_episodes+1):
        state = env.reset()
        agent.reset()
        score = 0
        noise = OrnsteinUhlenbeckProcess(env.action_space.shape[0])

        for t in range(max_t):
            action = agent.act(state, noise.sample())
            next_state, reward, done, _ = env.step(action)
            agent.step(state, action, reward, next_state, done)
            state = next_state
            score += reward
            if done:
                break 

        scores_deque.append(score)
        scores.append(score)
        avg_score = np.mean(scores_deque)

        if i_episode % print_every == 0:
            print('\rEpisode {}\tAverage Score: {:.2f}'.format(i_episode, avg_score))

        if i_episode % save_every == 0:
            torch.save(agent.actor_local.state_dict(), 'checkpoint_actor.pth')
            torch.save(agent.critic_local.state_dict(), 'checkpoint_critic.pth')

        if avg_score >= 30.0:
            print('\nEnvironment solved in {:d} episodes!\tAverage Score: {:.2f}'.format(i_episode-100, avg_score))
            torch.save(agent.actor_local.state_dict(), 'checkpoint_actor.pth')
            torch.save(agent.critic_local.state_dict(), 'checkpoint_critic.pth')
            break

    return scores

2.4.3 SAC(Soft Actor-Critic)

对DDPG进行改进,引入最大熵理论,使得策略在探索和利用之间保持平衡,避免过度依赖奖励函数。

SAC (Soft Actor-Critic) 是一种基于最大熵理论的深度强化学习算法,是 Actor-Critic 算法的一种扩展。SAC 在优化策略和值函数的同时,优化策略的熵,使得策略的探索更加充分。相比于 DDPG 等算法,SAC 有更好的表现,并且更加稳定。

SAC算法与其他基于策略梯度的算法不同,它将熵(Entropy)作为一个额外的目标加入到优化中,从而鼓励策略的探索性和多样性,防止策略陷入局部最优解。因此,SAC算法通常被认为是一个强大、高效且稳定的深度强化学习算法。

SAC算法的目标函数包括三个部分:状态值函数(Value function)的均方误差(Mean Squared Error,MSE)项、策略的期望回报(Expected Return)项和策略熵(Policy Entropy)的负值。具体来说,目标函数可以写成以下形式:

J ( θ ) = E s t ∼ ρ ( s ) , a t ∼ π θ ( a ∣ s t ) [ 1 2 ( V ϕ ( s t ) − Q θ ( s t , a t ) ) 2 − α log ⁡ π θ ( a t ∣ s t ) + α ⋅ H ( π θ ( ⋅ ∣ s t ) ) ] J(\theta)=\mathbb{E}{s_t\sim\rho(s),a_t\sim\pi{\theta}(a|s_t)}[\frac{1}{2}(V_{\phi}(s_t)-Q_{\theta}(s_t,a_t))^2-\alpha\log\pi_{\theta}(a_t|s_t)+\alpha\cdot H(\pi_{\theta}(\cdot|s_t))] J(θ)=Estρ(s),atπθ(ast)[21(Vϕ(st)Qθ(st,at))2αlogπθ(atst)+αH(πθ(st))]

其中, s t s_t st a t a_t at分别表示时间步 t t t时的状态和动作, π θ ( a t ∣ s t ) \pi_{\theta}(a_t|s_t) πθ(atst)表示在状态 s t s_t st下选择动作 a t a_t at的概率, V ϕ ( s t ) V_{\phi}(s_t) Vϕ(st)表示状态 s t s_t st的值函数, Q θ ( s t , a t ) Q_{\theta}(s_t,a_t) Qθ(st,at)表示在状态 s t s_t st下选择动作 a t a_t at的Q值, α \alpha α是一个超参数, H ( π θ ( ⋅ ∣ s t ) ) H(\pi_{\theta}(\cdot|s_t)) H(πθ(st))表示策略 π θ \pi_{\theta} πθ在状态 s t s_t st下的熵, ρ ( s ) \rho(s) ρ(s)是状态 s s s的分布。

SAC算法的核心思想是,通过最大化目标函数 J ( θ ) J(\theta) J(θ)来更新策略和状态值函数。具体来说,算法使用随机梯度下降(Stochastic Gradient Descent,SGD)来最小化目标函数的负数,从而更新参数 θ \theta θ ϕ \phi ϕ

∇ θ L ( θ ) = E s t ∼ ρ ( s ) , a t ∼ π θ ( a ∣ s t ) [ ∇ θ log ⁡ π θ ( a t ∣ s t ) ( α log ⁡ π θ ( a t ∣ s t ) − Q θ ( s t , a t ) + V ϕ ( s t ) ) ] \nabla_{\theta}\mathcal{L}(\theta)=\mathbb{E}{s_t\sim\rho(s),a_t\sim\pi{\theta}(a|s_t)}[\nabla_{\theta}\log\pi_{\theta}(a_t|s_t)(\alpha\log\pi_{\theta}(a_t|s_t)-Q_{\theta}(s_t,a_t)+V_{\phi}(s_t))] θL(θ)=Estρ(s),atπθ(ast)[θlogπθ(atst)(αlogπθ(atst)Qθ(st,at)+Vϕ(st))]

∇ ϕ L ( ϕ ) = E s t ∼ ρ ( s ) , a t ∼ π θ ( a ∣ s t ) , s t + 1 ∼ P ( s t + 1 ∣ s t , a t ) [ ( V ϕ ( s t ) − Q θ ( s t , a t ) ) 2 ] \nabla_{\phi}\mathcal{L}(\phi)=\mathbb{E}{s_t\sim\rho(s),a_t\sim\pi{\theta}(a|s_t),s_{t+1}\sim P(s_{t+1}|s_t,a_t)}[(V_{\phi}(s_t)-Q_{\theta}(s_t,a_t))^2] ϕL(ϕ)=Estρ(s),atπθ(ast),st+1P(st+1st,at)[(Vϕ(st)Qθ(st,at))2]

import torch
import torch.nn.functional as F
import numpy as np
import random
from collections import deque
import gym

# Define the neural network model for the Q-function
class QNetwork(torch.nn.Module):
    def __init__(self, state_dim, action_dim, hidden_size):
        super(QNetwork, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim + action_dim, hidden_size)
        self.fc2 = torch.nn.Linear(hidden_size, hidden_size)
        self.fc3 = torch.nn.Linear(hidden_size, 1)

    def forward(self, state, action):
        x = torch.cat([state, action], dim=1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# Define the neural network model for the policy function
class PolicyNetwork(torch.nn.Module):
    def __init__(self, state_dim, action_dim, hidden_size, log_std_min=-20, log_std_max=2):
        super(PolicyNetwork, self).__init__()
        self.log_std_min = log_std_min
        self.log_std_max = log_std_max
        self.fc1 = torch.nn.Linear(state_dim, hidden_size)
        self.fc2 = torch.nn.Linear(hidden_size, hidden_size)
        self.mean_fc = torch.nn.Linear(hidden_size, action_dim)
        self.log_std_fc = torch.nn.Linear(hidden_size, action_dim)

    def forward(self, state):
        x = F.relu(self.fc1(state))
        x = F.relu(self.fc2(x))
        mean = self.mean_fc(x)
        log_std = self.log_std_fc(x)
        log_std = torch.clamp(log_std, self.log_std_min, self.log_std_max)
        return mean, log_std

    def sample(self, state, epsilon=1e-6):
        mean, log_std = self.forward(state)
        std = log_std.exp()
        normal = torch.distributions.Normal(mean, std)
        z = normal.rsample()
        action = torch.tanh(z)
        log_prob = normal.log_prob(z) - torch.log(1 - action.pow(2) + epsilon)
        log_prob = log_prob.sum(-1, keepdim=True)
        return action, log_prob

    def get_log_prob(self, state, action, epsilon=1e-6):
        mean, log_std = self.forward(state)
        std = log_std.exp()
        normal = torch.distributions.Normal(mean, std)
        z = torch.atanh(action)
        log_prob = normal.log_prob(z) - torch.log(1 - action.pow(2) + epsilon)
        log_prob = log_prob.sum(-1, keepdim=True)
        return log_prob

# Define the replay buffer
class ReplayBuffer:
    def __init__(self, capacity):
        self.buffer = deque(maxlen=capacity)

    def add(self, state, action, reward, next_state, done):
        self.buffer.append((state, action, reward, next_state, done))

    def sample(self, batch_size):
        state_batch, action_batch, reward_batch, next_state_batch, done_batch = zip(*random.sample(self.buffer, batch_size))
        return np.array(state_batch), np.array(action_batch), np.array(reward_batch, dtype=np.float32), np.array(next_state_batch), np.array(done_batch, dtype=np.uint8)

    def __len__(self):
        return len(self.buffer)

class SAC:
    def __init__(self, state_dim, action_dim, max_action, device):
        self.device = device

        self.actor = PolicyNetwork(state_dim, action_dim, max_action).to(self.device)
        self.critic1 = QNetwork(state_dim, action_dim).to(self.device)
        self.critic2 = QNetwork(state_dim, action_dim).to(self.device)
        self.target_critic1 = QNetwork(state_dim, action_dim).to(self.device)
        self.target_critic2 = QNetwork(state_dim, action_dim).to(self.device)

        hard_update(self.target_critic1, self.critic1)
        hard_update(self.target_critic2, self.critic2)

        self.actor_optim = torch.optim.Adam(self.actor.parameters(), lr=3e-4)
        self.critic_optim = torch.optim.Adam(list(self.critic1.parameters()) + list(self.critic2.parameters()), lr=3e-4)

    def train(self, replay_buffer, batch_size=256, discount=0.99, tau=0.005, alpha=0.2):
        # Sample replay buffer
        state, action, next_state, reward, not_done = replay_buffer.sample(batch_size)

        state = torch.FloatTensor(state).to(self.device)
        action = torch.FloatTensor(action).to(self.device)
        next_state = torch.FloatTensor(next_state).to(self.device)
        reward = torch.FloatTensor(reward).to(self.device).unsqueeze(1)
        not_done = torch.FloatTensor(not_done).to(self.device).unsqueeze(1)

        # Update Q-function
        with torch.no_grad():
            # Select action according to policy and add clipped noise
            next_action, next_log_pi = self.actor.sample(next_state)
            noise = torch.randn_like(next_action) * alpha
            noise = noise.clamp(-0.5, 0.5)
            next_action = (next_action + noise).clamp(-1, 1)

            # Compute the target Q value
            target_Q1 = self.target_critic1(next_state, next_action)
            target_Q2 = self.target_critic2(next_state, next_action)
            target_Q = torch.min(target_Q1, target_Q2)
            target_Q = reward + not_done * discount * (target_Q - alpha * next_log_pi)

        current_Q1 = self.critic1(state, action)
        current_Q2 = self.critic2(state, action)

        critic_loss = F.mse_loss(current_Q1, target_Q) + F.mse_loss(current_Q2, target_Q)

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

        # Update policy
        sampled_actions, log_prob = self.actor.sample(state)

        min_Q = torch.min(
            self.critic1(state, sampled_actions),
            self.critic2(state, sampled_actions)
        )

        actor_loss = (alpha * log_prob - min_Q).mean()

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

        # Update target networks
        soft_update(self.target_critic1, self.critic1, tau)
        soft_update(self.target_critic2, self.critic2, tau)

在SAC算法中,PolicyNetwork和QNetwork是通过最小化Q值和策略的KL散度来联合训练的。具体来说,在每次更新中,需要先更新Q网络,然后再更新策略网络。

更新Q网络时,需要计算目标Q值,目标Q值可以用来计算Q值的损失函数。在SAC算法中,目标Q值是由SAC算法中的三个Q网络中的最小值来计算的,即 Q ( s t , a t ) = min ⁡ i = 1 , 2 , 3 Q i ( s t , a t ) Q(s_t, a_t) = \min_{i=1,2,3} Q_i(s_t, a_t) Q(st,at)=mini=1,2,3Qi(st,at),其中 Q i Q_i Qi 是第 i i i 个Q网络。

在更新策略网络时,需要计算策略梯度。由于策略网络的优化目标是最大化期望累积回报,而累积回报是随机变量,因此需要使用重要性采样来估计梯度。具体来说,对于每个时间步 t t t,可以计算出在状态 s t s_t st 时采取动作 a t a_t at 的概率 π ( a t ∣ s t ) \pi(a_t|s_t) π(atst),并根据重要性采样公式计算策略梯度。SAC算法中的策略梯度可以写成以下形式:

∇ θ J = E s t ∼ D , ϵ t ∼ N [ ∇ θ log ⁡ π θ ( a t ∣ s t ) Q min ( s t , a t ) − α log ⁡ π θ ( a t ∣ s t ) ] \nabla_\theta J = \mathbb{E}{s_t\sim D, \epsilon_t\sim \mathcal{N}}[\nabla\theta \log \pi_\theta(a_t|s_t) Q_{\text{min}}(s_t, a_t) - \alpha \log \pi_\theta(a_t|s_t)] θJ=EstD,ϵtN[θlogπθ(atst)Qmin(st,at)αlogπθ(atst)]

其中 D D D 是经验回放缓冲区, ϵ t \epsilon_t ϵt 是高斯噪声。上式中的第一项是重要性采样的梯度,表示策略网络对应动作的Q值的梯度乘以策略的对数概率,第二项是熵的梯度,用于鼓励策略探索更多的动作。

在具体实现中,可以先计算Q值损失,然后使用Q网络计算策略梯度,并更新策略网络和目标熵值 α \alpha α。策略梯度和目标熵值 α \alpha α 的计算可以使用PyTorch的自动微分功能进行。更新Q网络和策略网络时,可以使用Adam优化器来更新网络的权重参数。

重要性采样(Importance Sampling)
一种用于估计期望值的方法,常用于强化学习中的策略评估问题。在策略评估中,我们需要计算某个策略下的状态值函数或状态-动作值函数,但是由于需要计算期望,我们通常无法直接得到准确的值,而需要使用采样的方法来近似计算。

在某些情况下,我们已经有了一个分布p(x),并且我们想要计算某个函数f(x)在这个分布上的期望值,即 E[f(x)]。但是计算这个期望值可能很困难,因为我们不一定知道分布p(x)的具体形式。重要性采样就是一种通过从另一个分布q(x)中抽样来估计E[f(x)]的方法,其中q(x)是我们已知的一个分布。具体来说,我们可以使用以下公式来估计E[f(x)]:
E [ f ( x ) ] ≈ Σ [ f ( x ) ∗ w ( x ) ] / Σ [ w ( x ) ] E[f(x)] \approx \Sigma [f(x) * w(x)] / \Sigma[w(x)] E[f(x)]Σ[f(x)w(x)][w(x)]
其中,w(x)是重要性采样权重,定义为:
w ( x ) = p ( x ) / q ( x ) w(x) = p(x) / q(x) w(x)=p(x)/q(x)
这个权重表示我们从分布q(x)中采样的样本在分布p(x)中的“重要性”,也就是这个样本对期望值的估计贡献。如果q(x)和p(x)的形式很接近,那么估计的准确性就会比较高,反之则可能会有较大的误差。

在强化学习中,重要性采样通常用于在策略评估过程中估计状态值函数或状态-动作值函数。具体来说,当我们想要评估某个策略的值函数时,我们可以使用另一个策略来采样,然后使用重要性采样来估计目标策略的值函数。具体来说,我们可以从另一个策略(例如均匀随机策略)中采样一批状态或状态-动作对,然后根据重要性采样公式来计算目标策略下的状态值函数或状态-动作值函数的估计值。重要性采样可以大大增加我们用来估计目标策略值函数的数据,从而提高估计的准确性。

2.4.4 PPO(Proximal Policy Optimization)

基于Policy Gradient方法,通过限制每次更新的策略参数变化量,提高训练的稳定性,避免过拟合和振荡。

2.4.5 A3C(Asynchronous Advantage Actor-Critic)

A3C算法使用Actor-Critic结构,通过神经网络分别学习策略和价值函数。此外,为了提高训练效率,A3C算法使用异步训练的方式,即在多个并发的Agent中进行训练,每个Agent拥有独立的副本,通过交互式学习来共同优化全局的模型参数。这种方式可以利用多核CPU或多个GPU,加速模型的训练过程,同时也可以增加模型的稳定性,减少模型陷入局部最优解的风险。

2.5 强化学习中的探索(Exploration in Reinforcement Learning)

探索是强化学习中非常重要的一个问题,它涉及到如何在学习过程中充分探索环境,以获取更多的信息和奖励。常见的探索方法包括epsilon-greedy、softmax策略、UCB等。

3. 参考资料

  1. 强化学习(reinforcement learning)有什么好的开源项目、网站、文章推荐一下? - 半情调的回答 - 知乎
  2. 理解Actor-Critic的关键是什么?(附代码及代码分析) - 张斯俊的文章 - 知乎
  3. 一文带你理清DDPG算法(附代码及代码解释) - 张斯俊的文章 - 知乎
  4. SAC(Soft Actor-Critic)阅读笔记 - 茶仙的文章 - 知乎
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值