案例:从CartPole-v1迁移到MountainCar-v0
- 在源环境(CartPole-v1)中训练模型
首先,我们使用DQN算法在CartPole-v1环境中训练一个强化学习模型。以下是代码示例:
python
import gym
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import random
from collections import deque
# 定义 Q 网络
class QNetwork(nn.Module):
def __init__(self, state_dim, action_dim):
super(QNetwork, self).__init__()
self.fc1 = nn.Linear(state_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, action_dim)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
return self.fc3(x)
# Replay buffer,用于存储经验
class ReplayBuffer:
def __init__(self, capacity):
self.buffer = deque(maxlen=capacity)
def add(self, transition):
self.buffer.append(transition)
def sample(self, batch_size):
transitions = random.sample(self.buffer, batch_size)
states, actions, rewards, next_states, dones = zip(*transitions)
states = np.stack(states)
next_states = np.stack(next_states)
actions = np.array(actions, dtype=np.int64)
rewards = np.array(rewards, dtype=np.float32)
dones = np.array(dones, dtype=np.float32)
return states, actions, rewards, next_states, dones
def size(self):
return len(self.buffer)
# 选择动作
def select_action(state, policy_net, epsilon, action_dim):
if random.random() < epsilon:
return random.choice(np.arange(action_dim))
else:
with torch.no_grad():
state = torch.FloatTensor(state).unsqueeze(0)
q_values = policy_net(state)
return q_values.argmax().item()
# Q-learning 更新
def update_model(policy_net, target_net, optimizer, replay_buffer, batch_size, gamma):
if replay_buffer.size() < batch_size:
return
states, actions, rewards, next_states, dones = replay_buffer.sample(batch_size)
states = torch.FloatTensor(states)
actions = torch.LongTensor(actions)
rewards = torch.FloatTensor(rewards)
next_states = torch.FloatTensor(next_states)
dones = torch.FloatTensor(dones)
q_values = policy_net(states).gather(1, actions.unsqueeze(1)).squeeze(1)
next_q_values = target_net(next_states).max(1)[0]
target_q_values = rewards + gamma * next_q_values * (1 - dones)
loss = (q_values - target_q_values.detach()).pow(2).mean()
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 训练模型
def train_dqn(env_name, num_episodes=500, gamma=0.99, epsilon_start=1.0, epsilon_end=0.01, epsilon_decay=0.995, batch_size=64):
env = gym.make(env_name)
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
policy_net = QNetwork(state_dim, action_dim)
target_net = QNetwork(state_dim, action_dim)
target_net.load_state_dict(policy_net.state_dict())
target_net.eval()
optimizer = optim.Adam(policy_net.parameters())
replay_buffer = ReplayBuffer(10000)
epsilon = epsilon_start
for episode in range(num_episodes):
state, _ = env.reset()
total_reward = 0
while True:
action = select_action(state, policy_net, epsilon, action_dim)
next_state, reward, done, _, __ = env.step(action)
replay_buffer.add((state, action, reward, next_state, done))
state = next_state
total_reward += reward
update_model(policy_net, target_net, optimizer, replay_buffer, batch_size, gamma)
if done:
print(f"Episode {episode + 1}/{num_episodes}, Total Reward: {total_reward}")
break
epsilon = max(epsilon_end, epsilon_decay * epsilon)
if episode % 10 == 0:
target_net.load_state_dict(policy_net.state_dict())
return policy_net
# 在源环境(CartPole)中训练模型
policy_net_cartpole = train_dqn(env_name='CartPole-v1')
接下来,我们将CartPole-v1环境中训练好的模型迁移到MountainCar-v0环境中,并进行微调。以下是代码示例:
python
# 定义函数以匹配网络的结构,进行部分权重的迁移
def transfer_weights(policy_net, target_env_state_dim, target_env_action_dim):
# 获取预训练的网络
pretrained_dict = policy_net.state_dict()
# 创建新网络,适应目标环境的状态和动作维度
new_policy_net = QNetwork(target_env_state_dim, target_env_action_dim)
# 获取新网络的权重
new_dict = new_policy_net.state_dict()
# 仅保留在预训练网络和新网络中都有的层(即隐藏层的参数)
pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in new_dict and 'fc1' not in k and 'fc3' not in k}
# 更新新网络的权重
new_dict.update(pretrained_dict)
# 将更新后的字典加载到新模型中
new_policy_net.load_state_dict(new_dict)
return new_policy_net
# 微调模型
def fine_tune_dqn(policy_net, env_name, num_episodes=200, gamma=0.99, epsilon_start=0.1, epsilon_end=0.01, epsilon_decay=0.995, batch_size=64):
env = gym.make(env_name)
target_state_dim = env.observation_space.shape[0]
target_action_dim = env.action_space.n
# 调用 transfer_weights 函数,将权重从 CartPole 模型迁移到新的 MountainCar 模型
policy_net = transfer_weights(policy_net, target_state_dim, target_action_dim)
target_net = QNetwork(target_state_dim, target_action_dim)
target_net.load_state_dict(policy_net.state_dict())
target_net.eval()
optimizer = optim.Adam(policy_net.parameters())
replay_buffer = ReplayBuffer(10000)
epsilon = epsilon_start
for episode in range(num_episodes):
state, _ = env.reset()
total_reward = 0
while True:
action = select_action(state, policy_net, epsilon, target_action_dim)
next_state, reward, done, _, __ = env.step(action)
replay_buffer.add((state, action, reward, next_state, done))
state = next_state
total_reward += reward
update_model(policy_net, target_net, optimizer, replay_buffer, batch_size, gamma)
if done:
print(f"Episode {episode + 1}/{num_episodes}, Total Reward: {total_reward}")
break
epsilon = max(epsilon_end, epsilon_decay * epsilon)
if episode % 10 == 0:
target_net.load_state_dict(policy_net.state_dict())
return policy_net
# 微调源环境训练的策略网络到目标环境 MountainCar
fine_tuned_policy_net = fine_tune_dqn(policy_net_cartpole, env_name='MountainCar-v0')
强学迁移中哪些没有变?
在强化学习的迁移学习过程中,即使经过微调,也存在一些保持不变的部分,这些部分是迁移学习能够有效工作的关键。以下是保持不变的主要内容:
-
隐藏层的结构和部分权重
在迁移学习中,通常会保留预训练模型的隐藏层结构和部分权重。这些隐藏层在源任务中已经学习到了一些通用的特征表示,这些特征在目标任务中可能仍然有用。例如:
隐藏层的权重:在预训练模型中,隐藏层的权重已经通过大量的数据训练得到了优化。这些权重在迁移到新任务时会被保留,作为新模型的初始化权重。虽然在微调过程中这些权重可能会发生一些变化,但它们的初始值仍然是预训练模型中的值。
隐藏层的结构:隐藏层的结构(如层数、每层的神经元数量等)通常保持不变,因为这些结构在源任务中已经被证明是有效的。
-
学习算法和框架
迁移学习过程中,学习算法和整体框架通常保持不变。这意味着:
算法类型:使用的强化学习算法(如DQN、PPO等)在迁移过程中保持不变。这是因为算法的核心思想和机制适用于多个任务。
框架和超参数:虽然某些超参数(如学习率、折扣因子等)可能需要根据新任务进行调整,但整体的算法框架和大部分超参数保持不变。
-
通用的特征表示
隐藏层学习到的特征表示在迁移过程中保持相对稳定。这些特征表示是数据的通用特征,能够在多个任务中发挥作用。例如:
低级特征:在视觉任务中,卷积神经网络的低级层通常学习到边缘、纹理等通用特征,这些特征在不同的视觉任务中都可能有用。
状态空间的通用表示:在强化学习中,隐藏层可能学习到状态空间的通用表示,这些表示在不同的任务中仍然可以提供有用的信息。
-
目标函数的形式
虽然具体的目标函数(如奖励函数)可能因任务而异,但目标函数的形式通常保持不变。强化学习的目标是最大化累积奖励,这一目标形式在迁移学习中仍然适用。例如:
最大化累积奖励:无论是源任务还是目标任务,强化学习的目标都是通过学习策略来最大化累积奖励。这一目标形式在迁移过程中保持不变。
奖励函数的形式:虽然奖励的具体定义可能不同,但奖励函数的形式(如即时奖励与累积奖励的关系)通常保持一致。
-
交互机制
强化学习的核心是通过与环境的交互来学习策略。这种交互机制在迁移学习中保持不变:
环境交互:在源任务和目标任务中,智能体都需要通过与环境的交互来获取反馈(如奖励和状态转移)。这种交互机制在迁移过程中保持一致。
探索与利用的平衡:在迁移学习中,智能体仍然需要在探索新的策略和利用已知的策略之间进行平衡。这种平衡机制在迁移过程中保持不变。
-
策略网络的输出层结构
虽然输出层的权重可能会根据目标任务进行调整,但输出层的结构(如输出维度)通常保持不变。这是因为输出层的结构是由任务的性质决定的,例如动作空间的维度。
举例说明
假设我们在CartPole-v1环境中训练了一个DQN模型,并将其迁移到MountainCar-v0环境中进行微调。以下是保持不变的部分:
隐藏层结构和部分权重:隐藏层的结构和部分权重从CartPole-v1模型迁移到MountainCar-v0模型中。
DQN算法框架:使用的DQN算法框架保持不变,包括经验回放、目标网络等机制。
通用特征表示:隐藏层学习到的通用特征表示(如状态的低级特征)在MountainCar-v0环境中仍然有用。
目标函数形式:最大化累积奖励的目标函数形式保持不变。
交互机制:智能体通过与环境的交互来学习策略的机制保持不变。
通过保持这些部分不变,强化学习的迁移学习能够利用预训练模型的经验,加速在新任务中的学习过程,并提高学习效率和性能。