DQN变体:Nature DQN

这篇文章主要讨论DQN的变体,Nature DQN。

解决问题

它主要解决了DQN算法的一个问题,也就是相关依赖问题,收敛速度比较慢。 还记得计算当前目标 Q Q Q y j y_j yj的公式是:
y j = { R j    i s _ e n d j   i s   t r u e R j + γ m a x a ′ Q ( ϕ ( S j ′ ) , A j ′ , w )    i s _ e n d j   i s   f a l s e y_j=\left\{ \begin{aligned} R_j && \ \ is\_end_j\ is\ true\\ R_j+γmax_{a^′}Q(ϕ(S^′_j),A^′_j,w) && \ \ is\_end_j\ is\ false\\ \end{aligned} \right. yj={RjRj+γmaxaQ(ϕ(Sj),Aj,w)  is_endj is true  is_endj is false
然后要用目标 y j y_j yj与当前Q值使用均方差计算损失函数 1 m ∑ j = 1 m = 1 m ( y j − Q ( ϕ ( S j ) , A j , w ) ) 2 \frac{1}{m}∑\limits_{j=1}^m=\frac{1}{m}(y_j−Q(ϕ(S_j),A_j,w))^2 m1j=1m=m1(yjQ(ϕ(Sj),Aj,w))2。可以清楚的看到目标 y j y_j yj与当前Q值使用的是同一个Q网络,所以会产生相关依赖性,从而造成收敛变慢。

所以为了解决这一问题,我们就构造两个同样的Q网络:当前Q网络和目标Q网络。目标Q网络的网络参数不需要迭代更新,而是每隔一段时间从当前Q网络Q复制过来,即延时更新,这样可以减少目标Q值和当前的Q值相关性。

算法流程

注意:当前 Q Q Q网络结构没有 ′ ' ,目标 Q ′ Q^′ Q ′ '

网络结构
以下是算法流程:
算法输入:迭代轮数 T T T,状态特征维度 n n n, 动作集 A A A, 步长 α α α,衰减因子 γ γ γ, 探索率 ϵ ϵ ϵ, 当前 Q Q Q网络结构, 目标 Q ′ Q^′ Q网络结构,批量梯度下降的样本数 m m m
输出: Q Q Q网络参数

  1. 随机初始化当前 Q Q Q网络的所有参数 w w w,基于 w w w初始化所有的状态和动作对应的价值 Q Q Q,机初始化目标 Q ′ Q^′ Q网络的所有参数 w ′ w^′ w。清空经验回放的集合 D D D
  2. for i i i from 1 to T T T,进行迭代。
    a ) a) a) 初始化 S S S为当前状态序列的第一个状态, 拿到其特征向量 ϕ ( S ) ϕ(S) ϕ(S)
    b ) b) b) Q Q Q网络中使用 ϕ ( S ) ϕ(S) ϕ(S)作为输入,得到 Q Q Q网络的所有动作对应的 Q Q Q值输出。用 ϵ − ϵ− ϵ贪婪法在当前 Q Q Q值输出中选择对应的动作 A A A
    c ) c) c) 在状态 S S S执行当前动作A,得到新状态 S ′ S^′ S对应的特征向量 ϕ ( S ′ ) ϕ(S^′) ϕ(S)和奖励 R R R,是否终止状态 i s _ e n d is\_end is_end,没有下一个状态
    d ) d) d) ϕ ( S ) , A , R , ϕ ( S ′ ) , i s _ e n d {ϕ(S),A,R,ϕ(S^′),is\_end} ϕ(S),A,R,ϕ(S),is_end这个五元组存入经验回放集合D
    e ) S = S ′ e) S=S^′ e)S=S
    f ) f) f) 从经验回放集合 D D D中采样 m m m个样本 ϕ ( S j ) , A j , R j , ϕ ( S j ′ ) , i s _ e n d j , j = 1 , 2. , , , m {ϕ(S_j),A_j,R_j,ϕ(S^′_j),is\_end_j},j=1,2.,,,m ϕ(Sj),Aj,Rj,ϕ(Sj),is_endj,j=1,2.,,,m计算当前目标 Q Q Q y j y_j yj
    y j = { R j    i s _ e n d j   i s   t r u e R j + γ m a x a ′ Q ′ ( ϕ ( S j ′ ) , A j ′ , w ′ )    i s _ e n d j   i s   f a l s e y_j=\left\{ \begin{aligned} R_j && \ \ is\_end_j\ is\ true\\ R_j+γmax_{a^′}Q^′(ϕ(S^′_j),A^′_j,w^′) && \ \ is\_end_j\ is\ false\\ \end{aligned} \right. yj={RjRj+γmaxaQ(ϕ(Sj),Aj,w)  is_endj is true  is_endj is false
    g) 使用均方差损失函数 1 m ∑ j = 1 m = 1 m ( y j − Q ( ϕ ( S j ) , A j , w ) ) 2 \frac{1}{m}∑\limits_{j=1}^m=\frac{1}{m}(y_j−Q(ϕ(S_j),A_j,w))^2 m1j=1m=m1(yjQ(ϕ(Sj),Aj,w))2,通过神经网络的梯度反向传播来更新 Q Q Q网络的所有参数 w w w,目标 Q ′ Q^′ Q网络不变,不参与反向传播
    h) 如果T%C=1,则更新目标Q网络参数w′=w
     i) 如果S′是终止状态,当前轮迭代完毕,否则转到步骤b)

可以从上述算法中清楚的看到,和普通的DQN基本没有什么不同。只是在 y j y_j yj的计算方面使用到了目标 Q ′ Q^′ Q。更新由当前 Q Q Q网络实现。这也让标签与数据的相关性不是太大。

实例

下面我们用一个具体的例子来演示DQN的应用。仍然使用了OpenAI Gym中的CartPole-v0游戏来作为我们算法应用。CartPole-v0游戏的介绍参见这里。它比较简单,基本要求就是控制下面的cart移动使连接在上面的pole保持垂直不倒。这个任务只有两个离散动作,要么向左用力,要么向右用力。而state状态就是这个cart的位置和速度, pole的角度和角速度,4维的特征。坚持到200分的奖励则为过关。

以下为Pytorch实现:

# -*- coding: utf-8 -*-
"""
Created on Thu Dec  5 15:38:07 2019

@author: asus
"""

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


GAMMA = 0.9
INITIAL_EPSILON = 0.5
FINAL_EPSILON = 0.01
REPLAY_SIZE = 10000
BATCH_SIZE = 32
ENV_NAME = 'CartPole-v0'
EPISODE = 3000 # Episode limitation
STEP = 300 # Step limitation in an episode
TEST = 10 # The number of experiment test every 100 episode


class MODEL(torch.nn.Module):
    def __init__(self, env):
        super(MODEL, self).__init__()
        self.state_dim = env.observation_space.shape[0]
        self.action_dim = env.action_space.n
        self.fc1 = torch.nn.Linear(self.state_dim, 20)
        self.fc1.weight.data.normal_(0, 0.8)
        self.fc2 = torch.nn.Linear(20, self.action_dim)
        self.fc2.weight.data.normal_(0, 0.2)
    
    def create_Q_network(self, x):
        x = F.relu(self.fc1(x))
        Q_value = self.fc2(x)
        return Q_value
    
    def forward(self, x, action_input):
        Q_value = self.create_Q_network(x)
        Q_action = torch.mul(Q_value, action_input).sum(dim=1)
        return Q_action
    
class DQN():
    def __init__(self, env):
        self.target_Q_net = MODEL(env)
        self.current_Q_net = MODEL(env)
        self.replay_buffer = deque()
        self.time_step = 0
        self.epsilon = INITIAL_EPSILON
        self.optimizer = torch.optim.Adam(params=self.current_Q_net.parameters(), lr=0.0001)
        self.loss = torch.nn.MSELoss()
        
    def perceive(self,state,action,reward,next_state,done):
        one_hot_action = np.zeros(self.current_Q_net.action_dim)
        one_hot_action[action] = 1
        self.replay_buffer.append((state,one_hot_action,reward,next_state,done))
        if len(self.replay_buffer) > REPLAY_SIZE:
            self.replay_buffer.popleft()
        if len(self.replay_buffer) > BATCH_SIZE:
            self.train_Q_network()
    
    def train_Q_network(self):
        self.time_step += 1
        # Step 1: obtain random minibatch from replay memory
        minibatch = random.sample(self.replay_buffer,BATCH_SIZE)
        state_batch = [data[0] for data in minibatch]
        action_batch = [data[1] for data in minibatch]
        reward_batch = [data[2] for data in minibatch]
        next_state_batch = torch.FloatTensor([data[3] for data in minibatch])

        # Step 2: calculate y
        y_batch = []
        
        Q_value_batch = self.target_Q_net.create_Q_network(next_state_batch)
        
        for i in range(0,BATCH_SIZE):
            done = minibatch[i][4]
            if done:
                y_batch.append(reward_batch[i])
            else:
                y_batch.append(reward_batch[i] + GAMMA * torch.max(Q_value_batch[i]))
                
        y = self.current_Q_net(torch.FloatTensor(state_batch), torch.FloatTensor(action_batch))
        y_batch = torch.FloatTensor(y_batch)
        cost = self.loss(y, y_batch)
        self.optimizer.zero_grad()
        cost.backward()
        self.optimizer.step()
            
    def egreedy_action(self,state):
        Q_value = self.current_Q_net.create_Q_network(torch.FloatTensor(state))
        if random.random() <= self.epsilon:
            self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
            return random.randint(0, self.current_Q_net.action_dim - 1)
        else:
            self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
            return torch.argmax(Q_value).item()     
                
    def action(self,state):
        return torch.argmax(self.target_Q_net.create_Q_network(torch.FloatTensor(state))).item()
               
    def update_target_params(self):
        torch.save(self.current_Q_net.state_dict(), 'net_params.pkl')
        self.target_Q_net.load_state_dict(torch.load('net_params.pkl'))
        
def main():
  # initialize OpenAI Gym env and dqn agent
  env = gym.make(ENV_NAME)
  agent = DQN(env)

  for episode in range(EPISODE):
    # initialize task
    state = env.reset()
    # Train
    for step in range(STEP):
      action = agent.egreedy_action(state) # e-greedy action for train
      next_state,reward,done,_ = env.step(action)
      # Define reward for agent
      reward = -1 if done else 0.1
      agent.perceive(state,action,reward,next_state,done)
      state = next_state
      if done:
        break
    # Test every 100 episodes
    if episode % 100== 0:
      total_reward = 0
      for i in range(TEST):
        state = env.reset()
        for j in range(STEP):
#          env.render()
          action = agent.action(state) # direct action for test
          state,reward,done,_ = env.step(action)
          total_reward += reward
          if done:
            break
      ave_reward = total_reward/TEST
      print ('episode: ',episode,'Evaluation Average Reward:',ave_reward)
    agent.update_target_params()
if __name__ == '__main__':
  main()

参考别人的tensorflow代码

参考文献:
1.https://www.cnblogs.com/pinard/p/9756075.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值