📢本篇文章是博主强化学习RL领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在👉强化学习专栏:

       【强化学习】(21)---《【离线强化学习:行为规范Actor Critic (BRAC) 算法》

离线强化学习:行为规范Actor Critic (BRAC) 算法

目录

1. 介绍

2. 算法背景

3. 算法实现

4. BRAC 的两种变体

[Python] BRAC算法实现

BRAC算法伪代码实现:

BRAC算法pytorch实现:

BRAC算法在CartPole中实现:

1. 环境与离线数据生成

2. BRAC-P 策略与价值网络实现

3. 测试 BRAC-P 策略

[Results] 运行结果

[Notice] 实现流程

1. 环境初始化与离线数据生成

2. BRAC-P策略与价值网络实现

3. 策略训练

4. 策略测试

6. 实验与效果

7. 总结


1. 介绍

        离线强化学习(Offline Reinforcement Learning)旨在从静态数据集中学习策略,而无须与环境进行交互。传统的强化学习方法依赖大量环境交互,这在某些情况下是不切实际或昂贵的。离线强化学习通过利用已有的数据,降低了这些需求。

        行为规范Actor Critic 算法(Behavior Regularized Actor Critic,BRAC) 是一种专门为离线强化学习设计的算法,其主要目标是通过行为正则化(Behavior Regularization)来解决由于数据分布偏差导致的策略退化问题。 BRAC 算法是由Yifan Wu et al. 在 2019 年的论文“Behavior Regularized Offline Reinforcement Learning”中提出的。论文中讨论了在连续控制场景下,通过一种乐观保守的策略迭代方法来实现策略改进和探索。

如果你对原文感兴趣,可以通过下面链接查看文献全文:

Behavior Regularized Offline Reinforcement Learning


2. 算法背景

        在离线强化学习中,模型只能依赖固定的历史数据集进行学习,而不能通过与环境的交互来探索新策略。由于数据是由某个行为策略(behavior policy)收集的,这个行为策略未必是最优策略,因此所学到的策略容易偏离数据中的实际分布,导致性能不理想。这个现象称为分布偏差问题(distributional shift)。

        BRAC 算法的核心思想是通过限制策略优化过程中策略与行为策略的偏离,来减轻分布偏差带来的影响。这种方法通过引入行为正则化,使得学到的策略不会过度偏离生成离线数据的行为策略,从而保持策略的稳定性和鲁棒性。


3. 算法实现

        BRAC 通过在策略优化过程中加入正则化项,将学习的策略限制在行为策略的附近。其基本架构可以看作是传统 Actor-Critic 方法的扩展,其中引入了一个基于行为策略的正则化机制,以控制策略的变化幅度。

3.1算法流程

  1. 数据收集

    从历史记录中获得离线数据集 (\mathcal{D} = {(s, a, r, s')})
  2. 价值函数估计

    使用批量数据训练一个价值函数,通常采用Bellman期望方程: [ Q(s, a) = r + \gamma \mathbb{E}_{s' \sim P}[V(s')] ]其中,(V(s') = \max_{a'} Q(s', a'))为状态价值。
  3. 策略更新

    在策略更新阶段,使用一个行为正则项限制策略偏差:[ \pi_{\text{new}} = \arg\max_\pi \mathbb{E}{s \sim \mathcal{D}}\left[\min_a Q(s, a) - \lambda D(\pi || \pi{\beta})\right] ]这里,(D(\pi || \pi_{\beta})) 表示策略偏差度量,常用的如KL散度或MMD距离,(\pi_{\beta})是行为策略。这些正则化项的目标是控制策略偏离的程度,以确保模型的稳定性和对真实环境的泛化能力。
  4. 循环迭代

    重复以上过程,逐步优化策略直至收敛。

4. BRAC 的两种变体

BRAC 提出了两种不同的策略优化方式,分别为 BRAC-P 和 BRAC-V:

  • BRAC-P(Policy Regularization):在策略优化过程中对策略的变化进行直接约束。这种方法通过对 actor 的更新施加正则化项,确保其更新的方向不会过度偏离行为策略。

  • BRAC-V(Value Regularization):主要针对值函数进行正则化处理,限制值函数估计时对未见过的状态-动作对的过度估计。

        两者的区别在于正则化的重点:BRAC-P 侧重于策略的更新,而 BRAC-V 则是通过对值函数的约束来间接影响策略学习。下面主要介绍BRAC-P的实现,BRAC-V主要修改对值函数的约束,大差不差。


[Python] BRAC算法实现

        🔥若是下面代码复现困难或者有问题,欢迎评论区留言;需要以整个项目形式的代码,请在评论区留下您的邮箱📌,以便于及时分享给您(私信难以及时回复)。

 BRAC算法伪代码实现:

        便于理解BRAC算法的基本实现流程。

# BRAC算法的Python伪代码实现

def brac_algorithm(data, policy_network, value_network, beta_policy, num_iterations, lambda_reg):
    """
    data: 离线数据集,格式为 (s, a, r, s')
    policy_network: 策略网络,用于更新策略
    value_network: 价值网络,用于估计价值函数
    beta_policy: 行为策略
    num_iterations: 迭代次数
    lambda_reg: 正则化参数
    """
    
    for _ in range(num_iterations):
        # Step 1: 价值函数估计
        for s, a, r, s_next in data:
            v_next = max(value_network.predict(s_next))
            q_value = r + gamma * v_next
            value_network.update(s, a, q_value)

        # Step 2: 策略更新
        for s in data:
            # 获取当前策略动作分布
            action_distribution = policy_network.get_action_distribution(s)
            
            # 计算行为正则项
            behavior_regularization = compute_divergence(action_distribution, beta_policy.get_action_distribution(s))
            
            # 更新策略网络,最小化负的Q值减去正则项
            policy_network.update(s, lambda a: -value_network.predict(s, a) + lambda_reg * behavior_regularization)

    return policy_network

def compute_divergence(dist1, dist2):
    """
    计算两个分布之间的散度,如KL散度
    """
    divergence = kl_divergence(dist1, dist2)
    return divergence

def kl_divergence(p, q):
    """
    计算KL散度
    """
    return sum(p[i] * math.log(p[i] / q[i]) for i in range(len(p)))

BRAC算法pytorch实现:

        BRAC算法的基本实现流程。离线强化学习的策略更新,并根据具体需求替换行为策略和价值网络的实现。

代码解释

  • BRACPolicy:策略网络,使用PyTorch构建,包含两层隐藏层。网络输出策略的动作分布(使用Softmax归一化)。
  • update:策略网络的更新函数。包括计算价值损失、KL散度正则化,并反向传播更新参数。
  • BetaPolicy:用于模拟行为策略,提供动作分布。
  • ValueNetwork:用于模拟Q值函数,返回伪随机的Q值。

主要部分

  • KL散度计算:使用 torch.nn.functional.kl_div 来计算策略分布和行为策略分布的KL散度。
  • 优化:通过 torch.optim.Adam 优化器来更新策略网络参数。

"""《BRAC算法的Pytorch实现》
    时间:2024.09.25
    环境:No
    作者:不去幼儿园
"""
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

class BRACPolicy(nn.Module):
    def __init__(self, state_dim, action_dim, beta_policy, lambda_reg, learning_rate=0.001):
        super(BRACPolicy, self).__init__()
        self.state_dim = state_dim
        self.action_dim = action_dim
        self.beta_policy = beta_policy
        self.lambda_reg = lambda_reg
        
        # 策略网络结构
        self.policy_network = nn.Sequential(
            nn.Linear(self.state_dim, 64),
            nn.ReLU(),
            nn.Linear(64, 64),
            nn.ReLU(),
            nn.Linear(64, self.action_dim),
            nn.Softmax(dim=-1)
        )
        
        # 优化器
        self.optimizer = optim.Adam(self.policy_network.parameters(), lr=learning_rate)

    def forward(self, states):
        return self.policy_network(states)

    def update(self, states, value_network):
        # 将状态转换为张量
        states = torch.FloatTensor(states)

        # 计算策略的动作分布
        actions_probs = self.forward(states)
        
        # 从行为策略获取动作分布
        with torch.no_grad():
            beta_actions_probs = self.beta_policy.get_action_distribution(states)
        
        # 计算价值损失
        q_values = value_network.predict(states)
        value_loss = -torch.mean(torch.sum(q_values * actions_probs, dim=1))
        
        # 计算正则化项 (KL散度)
        kl_divergence = F.kl_div(actions_probs.log(), beta_actions_probs, reduction='batchmean')
        
        # 总损失
        loss = value_loss + self.lambda_reg * kl_divergence

        # 反向传播和优化
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

    def get_action_distribution(self, states):
        states = torch.FloatTensor(states)
        with torch.no_grad():
            return self.forward(states).numpy()

# 示例用法
state_dim = 10
action_dim = 5
lambda_reg = 0.1

# 假设有一个预训练的行为策略
class BetaPolicy:
    def get_action_distribution(self, states):
        # 返回一些伪随机的动作分布
        return torch.rand(states.shape[0], action_dim)

beta_policy = BetaPolicy()

# 创建BRAC-P策略对象
brac_policy = BRACPolicy(state_dim, action_dim, beta_policy, lambda_reg)

# 假设有一个简单的价值网络
class ValueNetwork:
    def predict(self, states):
        # 返回一些伪随机的Q值
        return torch.rand(states.shape[0], action_dim)

value_network = ValueNetwork()

# 更新策略
states = torch.rand(32, state_dim)  # 示例状态批次
brac_policy.update(states, value_network)

 BRAC算法在CartPole中实现:

        为了将BRAC算法应用于CartPole环境中并测试结果,我们需要结合离线数据和PyTorch实现策略优化流程。以下是实现过程的详细步骤。

环境准备

首先,安装所需的库:

pip install gym torch numpy

完整代码实现

1. 环境与离线数据生成

        我们将使用gymCartPole-v1环境。为了模拟离线数据,先通过一个预训练策略生成一部分离线数据。随后,我们使用BRAC-P算法在此离线数据集上进行策略优化。


"""《BRAC算法的CartPole环境实现》
    时间:2024.09.26
    环境:CartPole 
    作者:不去幼儿园
"""
import gym
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np

# 创建 CartPole 环境
env = gym.make('CartPole-v1')

# 生成离线数据
def generate_offline_data(env, policy, num_episodes=100):
	data = []
	for _ in range(num_episodes):
		state, _ = env.reset()
		done = False
		while not done:
			action = policy(torch.FloatTensor(state)).argmax().item()
			next_state, reward, done, _, __ = env.step(action)
			data.append((state, action, reward, next_state, done))
			state = next_state
	return data

# 随机策略用于生成离线数据
class RandomPolicy(nn.Module):
	def forward(self, state):
		return torch.tensor([0.5, 0.5])  # 50% 概率选择任一动作


# 使用随机策略生成离线数据
print("\033[38;5;11mINFO:\033[0m 随机策略生成离线数据", flush=True)
random_policy = RandomPolicy()
offline_data = generate_offline_data(env, random_policy)
2. BRAC-P 策略与价值网络实现

        下面是策略网络和价值网络的实现,使用与之前类似的结构,但我们添加了BRAC-P中的正则化项。

# BRAC Policy 网络
class BRACPolicy(nn.Module):
	def __init__(self, state_dim, action_dim, beta_policy, lambda_reg, learning_rate=0.001):
		super(BRACPolicy, self).__init__()
		self.state_dim = state_dim
		self.action_dim = action_dim
		self.beta_policy = beta_policy
		self.lambda_reg = lambda_reg

		# 策略网络结构
		self.policy_network = nn.Sequential(
			nn.Linear(self.state_dim, 64),
			nn.ReLU(),
			nn.Linear(64, 64),
			nn.ReLU(),
			nn.Linear(64, self.action_dim),
			nn.Softmax(dim=-1)
		)

		# 优化器
		self.optimizer = optim.Adam(self.policy_network.parameters(), lr=learning_rate)

	def forward(self, states):
		return self.policy_network(states)

	def update(self, states, value_network):
		# 将状态转换为张量
		states = torch.FloatTensor(states)

		# 计算策略的动作分布
		actions_probs = self.forward(states)

		# 从行为策略获取动作分布
		with torch.no_grad():
			beta_actions_probs = self.beta_policy.get_action_distribution(states)

		# 计算价值损失
		q_values = value_network.predict(states)
		value_loss = -torch.mean(torch.sum(q_values * actions_probs, dim=1))

		# 计算正则化项 (KL散度)
		kl_divergence = F.kl_div(actions_probs.log(), beta_actions_probs, reduction='batchmean')

		# 总损失
		loss = value_loss + self.lambda_reg * kl_divergence

		# 反向传播和优化
		self.optimizer.zero_grad()
		loss.backward()
		self.optimizer.step()

	# 更新BRACPolicy类的get_action_distribution方法,转换列表为numpy array
	def get_action_distribution(self, states):
		states = torch.FloatTensor(np.array(states))  # 转换为 numpy array 再转换为 tensor
		with torch.no_grad():
			return self.forward(states).numpy()


# Value 网络
class ValueNetwork(nn.Module):
	def __init__(self, state_dim, action_dim, learning_rate=0.001):
		super(ValueNetwork, self).__init__()
		self.network = nn.Sequential(
			nn.Linear(state_dim, 64),
			nn.ReLU(),
			nn.Linear(64, 64),
			nn.ReLU(),
			nn.Linear(64, action_dim)
		)
		self.optimizer = optim.Adam(self.network.parameters(), lr=learning_rate)

	def forward(self, states):
		return self.network(states)

	def predict(self, states):
		return self.forward(torch.FloatTensor(states))

	def update(self, states, actions, targets):
		q_values = self.forward(torch.FloatTensor(states))
		q_values = q_values.gather(1, torch.LongTensor(actions).unsqueeze(1)).squeeze(1)
		loss = F.mse_loss(q_values, torch.FloatTensor(targets))

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


# 预训练行为策略
class BetaPolicy:
	def get_action_distribution(self, states):
		return torch.rand(states.shape[0], action_dim)


# 初始化网络
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
beta_policy = BetaPolicy()
value_network = ValueNetwork(state_dim, action_dim)
brac_policy = BRACPolicy(state_dim, action_dim, beta_policy, lambda_reg=0.1)


# 训练
def train_brac_policy(data, policy, value_network, num_epochs=100):
	for _ in range(num_epochs):
		for batch in data:
			states, actions, rewards, next_states, dones = zip(*batch)

			# 将列表转换为 numpy arrays
			states = np.array(states)
			next_states = np.array(next_states)
			rewards = np.array(rewards, dtype=np.float32)  # 确保 rewards 为 float 类型
			dones = np.array(dones, dtype=np.float32)  # 确保 dones 为 float 类型

			# 更新价值网络
			q_next = value_network.predict(next_states).max(dim=1)[0].detach()  # 使用 detach() 防止梯度追踪
			targets = rewards + 0.99 * q_next.numpy() * (1 - dones)  # 使用 .numpy() 获取张量的值
			value_network.update(states, actions, targets)

			# 更新策略网络
			policy.update(states, value_network)


# 数据批次处理
batch_size = 32
batches = [offline_data[i:i + batch_size] for i in range(0, len(offline_data), batch_size)]

# 训练策略
print("\033[38;5;11mINFO:\033[0m BRAC算法策略训练中", flush=True)
train_brac_policy(batches, brac_policy, value_network)
 3. 测试 BRAC-P 策略

        训练完成后,我们测试BRAC策略在CartPole环境中的表现。

# 测试BRAC策略
def test_policy(policy, env, num_episodes=10):
	total_rewards = []
	for _ in range(num_episodes):
		state, _ = env.reset()
		done = False
		episode_reward = 0
		while not done:
			action_probs = policy.get_action_distribution([state])

			# 将 numpy.ndarray 转换为 tensor
			action_probs = torch.FloatTensor(action_probs)

			# 选择具有最大概率的动作
			action = torch.argmax(action_probs).item()
			next_state, reward, done, _, __ = env.step(action)
			episode_reward += reward
			state = next_state
		total_rewards.append(episode_reward)
	return total_rewards


# 测试BRAC策略在 CartPole 上的表现
print("\033[38;5;11mINFO:\033[0m BRAC算法策略测试中", flush=True)
test_rewards = test_policy(brac_policy, env)
print(f"\u001b[38;5;2mSUCCESS:\u001b[0m BRAC策略的平均奖励: {np.mean(test_rewards)}", flush=True)

[Results] 运行结果

论文部分运行结果:

在 CartPole-v1环境中运行结果:


[Notice] 实现流程

1. 环境初始化与离线数据生成

我们使用gym库中的CartPole-v1环境来训练和测试策略。为了模拟离线强化学习的场景,我们首先通过一个随机策略生成了一些离线数据,这些数据包含多个状态、动作、奖励、下一个状态和是否终止的五元组。

  • 离线数据生成:使用一个简单的随机策略(RandomPolicy)来与环境交互,生成离线数据。每次在环境中采取动作并记录状态、动作、奖励、下一状态以及是否结束的信息,最终组成一个离线数据集。
2. BRAC-P策略与价值网络实现

BRAC算法的核心是策略网络和价值网络。为了约束策略网络不偏离离线数据中的行为策略,我们在策略更新时引入了正则化项(如KL散度),对策略的变化进行限制。

  • 策略网络(BRACPolicy):通过一个神经网络学习策略。网络输出动作的概率分布,更新时不仅考虑最大化奖励,还考虑正则化项,限制策略与离线行为策略的偏差。

  • 价值网络(ValueNetwork):用于估计每个状态-动作对的Q值。Q值网络会根据离线数据更新,目标是最小化Q值与实际回报的误差。

3. 策略训练

我们将离线数据划分为多个批次(batches),并使用BRAC算法对策略进行迭代优化。每个批次的数据都用来更新价值网络和策略网络。

  • 价值网络更新:对于每个状态-动作对,使用Bellman方程更新价值网络,使其预测的Q值更加准确。

  • 策略网络更新:在更新策略时,除了最大化Q值,还要通过KL散度或其他度量方式,约束策略网络的输出不偏离离线行为策略。

4. 策略测试

训练完成后,我们在CartPole环境中测试BRAC策略的表现。策略根据当前状态输出动作分布,然后选择最大概率的动作,并执行在环境中。我们记录每次测试的总奖励,计算策略的表现。

具体流程总结

  1. 离线数据生成:通过随机策略与环境交互,生成历史数据集。
  2. BRAC算法实现
    • 定义策略网络和价值网络。
    • 策略更新时,加入行为正则化,约束策略不偏离行为策略。
  3. 策略训练:使用离线数据,通过多次迭代优化策略和价值函数。
  4. 策略测试:在CartPole环境中测试BRAC策略,观察策略的效果。

关键算法部分

  • 行为正则化:通过KL散度度量策略与行为策略的差异,确保新学到的策略不与离线数据中的行为策略偏差过大。

  • 价值估计:价值网络通过估计状态-动作对的Q值来指导策略网络的更新。

  • 策略优化:策略不仅最大化价值函数,还通过正则化保持与行为策略的一致性。

实验结果

        最终,通过测试我们可以评估BRAC策略在CartPole环境中的表现,计算该策略在多次实验中的平均奖励,以观察其稳定性和有效性。


6. 实验与效果

        文献中通过一系列实验验证了 BRAC 算法的有效性。实验结果表明,BRAC 在多个离线强化学习基准任务(例如 Mujoco 环境下的控制任务)上取得了优秀的性能,尤其在行为策略表现较差的情况下,BRAC 能够有效避免策略过度偏离,保持较高的鲁棒性。

        与其他离线强化学习算法相比,BRAC 的优势在于其正则化机制使得策略在数据外推时更加保守,从而避免了过度高估值函数的问题。此外,BRAC 对不同的正则化方式表现出较好的适应性,可以根据任务需求选择不同的正则化度量。


7. 总结

        BRAC 算法为离线强化学习中的分布偏差问题提供了一个有效的解决方案。通过引入行为正则化,BRAC 控制了策略优化时与行为策略的偏离程度,确保学习的策略在实际应用中更加稳定。它的两种变体(BRAC-P 和 BRAC-V)分别从不同角度对策略或值函数进行正则化,为离线强化学习中的策略学习提供了新的方向。

        BRAC 的贡献在于其对分布偏差的有效处理和对离线数据的合理利用,是离线强化学习领域的重要进展之一。

相关知识点

  • 行为克隆(Behavior Cloning):通过模仿现有策略直接构建新的策略。在离线RL中,BRAC通过行为正则项引入类似行为克隆的概念,但保留策略优化灵活性。

  • 政策约束(Policy Constraint):限制新策略不偏离已知策略的机制,保证策略的稳定性。

  • 价值平滑(Value Smoothing):通过将策略约束融入到价值估计中,避免过度乐观的价值估计。


     文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者关注VX公众号:Rain21321,联系作者。✨

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐