模仿学习(Imitation Learning,IL)算法是强化学习领域的一个分支,它关注于让智能体通过模仿专家的行为来学习任务。模仿学习通常用于学习复杂任务,尤其是当通过传统的强化学习算法直接学习效率较低或成本较高时。以下是一些常见的模仿学习算法:
行为克隆(Behavioral Cloning, BC):
逆强化学习(Inverse Reinforcement Learning, IRL):
模仿学习(Apprenticeship Learning):
对抗性模仿学习(Adversarial Imitation Learning):
专家演示再参数化(Reparameterization of Expert Demonstrations):
最大熵模仿学习(Maximum Entropy Inverse Reinforcement Learning):
序列级模仿学习(Sequence-Level Imitation Learning):
元模仿学习(Meta-Learning for Imitation Learning):
模仿学习算法的关键优势在于它们可以利用专家的知识来加速学习过程,减少试错的次数。然而,这些算法也面临一些挑战,例如如何确保学习到的策略在未见过的状态上也能表现良好,以及如何处理专家演示中的噪声或次优行为。
模仿学习在自动驾驶、机器人控制、游戏AI等领域有广泛的应用。通过模仿专家的行为,智能体可以在这些复杂任务中快速获得有效的策略。
import gym
from matplotlib import pyplot as plt
%matplotlib inline
#创建环境
env = gym.make('CartPole-v1')
env.reset()
#打印游戏
def show():
plt.imshow(env.render(mode='rgb_array'))
plt.show()
定义ppo模型
class PPO:
def __init__(self, env, model, model_td):
self.env = env
self.model = model
self.model_td = model_td
self.optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
self.optimizer_td = torch.optim.Adam(model_td.parameters(), lr=1e-2)
self.loss_fn = torch.nn.MSELoss()
def get_action(self, state):
state = torch.FloatTensor(state).reshape(1, 4)
prob = self.model(state)
action = random.choice(range(2), weights=prob[0].tolist(), k=1)[0]
return action
def get_data(self):
states, rewards, actions, next_states, overs = [], [], [], [], []
state = self.env.reset()
over = False
while not over:
action = self.get_action(state)
next_state, reward, over, _ = self.env.step(action)
states.append(state)
rewards.append(reward)
actions.append(action)
next_states.append(next_state)
overs.append(over)
state = next_state
return (torch.FloatTensor(states).reshape(-1, 4),
torch.FloatTensor(rewards).reshape(-1, 1),
torch.LongTensor(actions).reshape(-1, 1),
torch.FloatTensor(next_states).reshape(-1, 4),
torch.LongTensor(overs).reshape(-1, 1))
def test(self, play=False):
state = self.env.reset()
reward_sum = 0
over = False
while not over:
action = self.get_action(state)
state, reward, over, _ = self.env.step(action)
reward_sum += reward
if play and random.random() < 0.2:
display.clear_output(wait=True)
# 假设有一个函数 show() 用于显示游戏状态
# show()
return reward_sum
def train(self, epochs=1000):
for epoch in range(epochs):
states, rewards, actions, next_states, overs = self.get_data()
values = self.model_td(states)
with torch.no_grad():
targets = self.model_td(next_states)
targets = targets * 0.98
targets = (1 - overs) * targets + rewards
deltas = targets - values
advantages = self.get_advantages(deltas)
advantages = torch.FloatTensor(advantages).reshape(-1, 1)
old_probs = self.model(states).gather(dim=1, index=actions)
for _ in range(10):
new_probs = self.model(states)
ratios = new_probs / old_probs
surr1 = ratios * advantages
surr2 = torch.clamp(ratios, 1 - 0.1, 1 + 0.1) * advantages
loss = -torch.min(surr1, surr2).mean()
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
# 更新价值网络
loss_td = self.loss_fn(self.model_td(states), targets)
self.optimizer_td.zero_grad()
loss_td.backward()
self.optimizer_td.step()
if epoch % 100 == 0:
test_result = sum([self.test(play=False) for _ in range(10)]) / 10
print(epoch, test_result)
定义teacher模型
teacher = PPO()
teacher.train(*teacher.get_data())
teacher.get_action([1,2,3,4]),teacher.test(play=Fasle)
训练teacher模型
#训练teacher模型
for i in range(500):
teacher.train(*teacher.get_data())
if i%50==0:
test_result = sum([teacher.test(play=False)for _ in range(10)])/10
print(i,test_reslut)
teacher.test(play=False)
获取教师数据,并且删除教师
#获取教师数据,并且删除教师
#使用训练好的模型获取一批教师数据
teacher_states, _,teacher_actions, _, _=teacher.get_date()
#删除教师,只留下教师的数据就可以了
del teacher
teacher_states.shape,teacher_actions.shape
student = PPO()
#定义鉴别器网络,它的任务是鉴定一批数据是出自teaccher还是student
class Discriminator(torch.nn.Module):
def __init__(self):
super().__init__()
self.sequential = torch.nn.Sequential(
torch.nn.Linear(6,128),
torch.nn.ReLU(),
torch.nn.Linear(128,1),
torch.NN.Sigmoid(),
)
def forward(self,states,actions):
one_hot = torch.nn.functional.one_hot(actions.squeeze(dim = 1),num_classes = 2)
cat = torch.cat([states,one_hot],dim=1)
return self.sequential(cat)
discriminator = Discriminator()
discriminator(torch,randn(2,4),torch.ones(2,1).log())
torch.nn.functional.one_hot
函数用于将离散的索引转换成 one-hot 编码形式。这种编码在处理分类问题或需要表示每个类别的独立特征时非常有用。
您提供的代码 one_hot = torch.nn.functional.one_hot(actions.squeeze(dim=1), num_classes=2)
执行以下操作:
actions.squeeze(dim=1)
:这个调用首先将 actions
张量在第二个维度(dim=1)上进行挤压,移除维度为 1 的单维度。这通常用于准备 one-hot 编码,确保 actions
张量在进行 one-hot 编码之前是一维的。
torch.nn.functional.one_hot(...)
:接着,对挤压后的 actions
张量应用 one-hot 编码。编码的类别数由 num_classes=2
指定,这意味着每个索引将被转换成一个长度为 2 的 one-hot 向量。
one_hot
:这是编码后得到的张量的变量名。对于每个索引值,它将在对应索引的位置上有一个 1,其余位置为 0。
例如,如果 actions
是一个包含 [0, 1] 的张量,one_hot
将是一个长度为 2 的张量,其中第一个元素是 [1, 0]
,第二个元素是 [0, 1]
。
One-hot 编码在强化学习中的策略梯度方法中很有用,特别是当你需要从连续的动作概率中采样动作时,它可以帮助你将概率转换为动作的 one-hot 编码表示。在某些实现中,这种方法可以用于计算策略网络输出的概率与实际采取的动作之间的差异。
def copy_learn():
optimizer = torch.optim.Adam(discriminator.parameters(),lr = 1e-3)
bce_loss = torch.nn.BCELoss()
for i in range(500):
#使用学生模型获取一局游戏的数据,不需要reward
states,_,actions,next_states,overs = student.get_data()
#使用鉴别器坚定两批数据是来自教师还是学生
prob_teacher = discriminator(teacher_states,teacher_actions)
prob_student = discriminator(states,actions)
#老师的用0表示,学生的用1表示,计算二分类loss
loss_teacher = bec_loss(prob_teacher,torch.zeros_like(prob_teacher))
loss_student = bec_loss(prob_student,torch.ones_like(prob_student))
#调整鉴别器的loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
#使用一批数据来自学生的概率作为reward,取loss再符号取反
#因为鉴别器会把学生数据的概率贴近1,所以目标是让鉴别器无法分辨,这是一种对抗网络的思路
reward =-prob_student.log().detach()
#更新学生模型参数,使用PPO模型本身的更新方式
student.train(states,rewards,actions,next_states,overs)
if i%50==0:
test_result =sum([student.test(play=False)
for _ in range(10)])/10
print(i,test_result)