【学习笔记】RL算法 Q learning +SARSA + DQN
个人学习笔记,自用。代码仓库:https://github.com/lansinuote/More_Simple_Reinforcement_Learning
1 Q learning、SARSA、改进的SARSA、蒙特卡洛
前置基础:时序差分
思路:定义环境、初始化Q表格、进行一场游戏并记录数据、数据池、开始训练
Q learning:
#Q矩阵当前估计的state下action的价值
value = Q[state, action]
#实际玩了之后得到的reward+下一个状态的价值*0.9
target = reward + Q[next_state].max() * 0.9
SARSA:
#Q矩阵当前估计的state下action的价值
value = Q[state, action]
#求下一个动作,和Q学习唯一的区别点
next_action = Q[next_state].argmax() # argmax()-返回当前状态即所在的行的最大值的列索引
#实际玩了之后得到的reward+下一个状态的价值*0.9
target = reward + Q[next_state, next_action] * 0.9
同策略的SARSA(理论上不能用池化理论上不能用池化,所以每次训练现玩游戏):
每次训练都要现玩一局游戏
#玩一局游戏并得到数据
for (state, action, reward, next_state, over) in play()[0]:
多步的TD算法(蒙特卡洛 ):
#玩一局游戏,得到数据
state, action, reward, next_state, over, _ = play()
for i in range(len(state)):
#计算value
value = Q[state[i], action[i]]
#计算target
#累加未来N步的reward,越远的折扣越大
#这里是在使用蒙特卡洛方法估计target
reward_s = 0
for j in range(i, min(len(state), i + 5)):
reward_s += reward[j] * 0.9**(j - i)
#计算最后一步的value,这是target的一部分,按距离给折扣
target = Q[next_state[j]].max() * 0.9**(j - i + 1)
#如果最后一步已经结束,则不需要考虑状态价值
#最后累加reward就是target
target = target + reward_s
2 DQN及改进DQN
QLearning使用表格估计Q函数,不便于扩展.
所以使用神经网络估计Q函数.
DQN:
# 1. 定义模型,评估状态下每个动作的价值
model = torch.nn.Sequential(
torch.nn.Linear(4, 64),
torch.nn.ReLU(),
torch.nn.Linear(64, 64),
torch.nn.ReLU(),
torch.nn.Linear(64, 2),
)
# 2. 玩一局游戏并记录数据
action = model(torch.FloatTensor(state).reshape(1, 4)).argmax().item()
# 3. 获取一批数据样本
def sample(self):
data = random.sample(self.pool, 64)
state = torch.FloatTensor([i[0] for i in data]).reshape(-1, 4)
action = torch.LongTensor([i[1] for i in data]).reshape(-1, 1)
reward = torch.FloatTensor([i[2] for i in data]).reshape(-1, 1)
next_state = torch.FloatTensor([i[3] for i in data]).reshape(-1, 4)
over = torch.LongTensor([i[4] for i in data]).reshape(-1, 1)
return state, action, reward, next_state, over
# 4. 训练
def train():
model.train()
optimizer = torch.optim.Adam(model.parameters(), lr=2e-4)
loss_fn = torch.nn.MSELoss()
#共更新N轮数据
for epoch in range(1000):
pool.update()
#每次更新数据后,训练N次
for i in range(200):
#采样N条数据
state, action, reward, next_state, over = pool.sample()
#计算value
value = model(state).gather(dim=1, index=action)
#计算target
with torch.no_grad():
target = model(next_state)
target = target.max(dim=1)[0].reshape(-1, 1)
target = target * 0.99 * (1 - over) + reward
loss = loss_fn(value, target)
loss.backward()
optimizer.step()
optimizer.zero_grad()
双模型DQN:
双模型使用两个不同的模型计算value和target,缓解了自举造成的过高估计.
#定义模型,评估状态下每个动作的价值
model = torch.nn.Sequential(
torch.nn.Linear(4, 64),
torch.nn.ReLU(),
torch.nn.Linear(64, 64),
torch.nn.ReLU(),
torch.nn.Linear(64, 2),
)
#延迟更新的模型,用于计算target
创建model_delay,等同model
#计算value
value = model(state).gather(dim=1, index=action)
#计算target
with torch.no_grad():
target = model_delay(next_state)
#复制参数
if (epoch + 1) % 5 == 0:
model_delay.load_state_dict(model.state_dict())
加权数据池:
给高loss的数据加权,提高这些数据被采样中的概率.
为了缓解过拟合,降低这些数据的lr,这里以削减loss的方式实现.
数据池中添加
# 维护概率表
self.prob.extend([1.0] * len(data))
idx = torch.FloatTensor(self.prob).clamp(0.1, 1.0).multinomial(num_samples=64, replacement=False)
Dueling DQN:
Q(state,action) = state下最优action分数 + 误差
为了限制误差的范围,可以对它去均值,相当于去基线
Q(state,action) = state下最优action分数 + 误差 - mean(误差)
理解起来有点困难,所以可以这么理解
Q(state,action) = state分数 + action分数 - mean(action分数)
Noise DQN:
给模型参数增加随机性