pytorch 笔记: DQN(experience replay)

1 理论知识

DQN 笔记 State-action Value Function(Q-function)_UQI-LIUWJ的博客-CSDN博客

强化学习笔记 experience replay 经验回放_UQI-LIUWJ的博客-CSDN博客

2 导入库

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import gym

3 定义参数

BATCH_SIZE = 32             
# 从experience buffer 每一次提取,用于训练的batch大小

LR = 0.01                   
# 学习率

EPSILON = 0.9               
# 贪婪策略指数,Q-learning的一个指数,用于指示是探索还是利用。

GAMMA = 0.9                 
# 折扣回报

TARGET_REPLACE_ITER = 100   
# target network的更新频率
#在100轮以内固定target network,每100轮更新一次

MEMORY_CAPACITY = 2000
#replay buffer 大小

env = gym.make('CartPole-v0')

N_ACTIONS = env.action_space.n
#2
#每一个state时可以有几个动作

N_STATES = env.observation_space.shape[0]
#4
#每一个state是几维的向量

4 用于计算action 权重的神经网络

        Net的作用是输入某一时刻的state向量,输出是各个action的值

        这里相比于policy network,这里输出的时候不用softmax,因为这里是需要Q最大的action就可以了

class Net(nn.Module):
    def __init__(self, ):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(N_STATES, 50)
        self.out = nn.Linear(50, N_ACTIONS)

    def forward(self, x):
        x = self.fc1(x)
        x = F.relu(x)
        actions_value = self.out(x)
        return actions_value

5 创建Q-learning 模型

5.1 __init__

# 创建Q-learning的模型
class DQN(object):
    def __init__(self):
        # 两张网结构上是一样的,不过就是target_net是每100次更新一次,eval_net每次都更新
        self.eval_net, self.target_net = Net(), Net()
        #eval_net:实时更新的Q的network
        #target_net:每TARGET_REPLACE_ITER轮更新一次的target network

        self.learn_step_counter = 0                                     
        # 如果次数到了,更新target_net,那么target_net更新的轮数重置
        
        self.memory_counter = 0                                         
        # relay buffer 计数器

        self.memory = np.zeros((MEMORY_CAPACITY, N_STATES * 2 + 2))     
        # relay buffer

        self.optimizer = torch.optim.Adam(self.eval_net.parameters(), lr=LR)
        self.loss_func = nn.MSELoss()

5.2 选择动作

# 选择动作
    def choose_action(self, x):
        
        
        x = torch.unsqueeze(torch.FloatTensor(x), 0)
        #print(state.shape)  
        #torch.size([1,4])
        #当前状态s对应的向量,通过unsqueeze操作变成[1,4]维的向量

        
        #之前设置了EPSILON,用于控制是使用还是探索
        if np.random.uniform() < EPSILON:   
            # 贪婪策略

            actions_value = self.eval_net(x)
            #eval_net是一个Net 类
            #actions_value 是每个action的Q值 (即Q(s,a)))

            action = torch.max(actions_value, 1)[1].data.numpy()
            #表示Q值最大的那个action的index

            action = action[0]
            # return the argmax index,在本例中,就是0或者1

        else:  
            # random
            action = np.random.randint(0, N_ACTIONS)
            #此时使用的是探索
        return action 

5.3 存储记忆

将transition的结果存入experience buffer中

 def store_transition(self, s, a, r, s_):
        #s和s_是两个ndarray(当前状态,使用了某个action之后的状态)
        # a和r是两个数,action和reward
        transition = np.hstack((s, [a, r], s_)) 
        # 将参数打包起来,成为一个长一元ndarray
        
        index = self.memory_counter % MEMORY_CAPACITY
        #当前的transition需要存入/覆盖的位置

        self.memory[index, :] = transition
        self.memory_counter += 1 
        #更新memory

5.4 模型学习

    def learn(self):
        # target parameter update
        if self.learn_step_counter % TARGET_REPLACE_ITER == 0:
            self.target_net.load_state_dict(self.eval_net.state_dict())
        #如果到了该更新target network的时候了,那么把eval_net此时的参数赋给target_net 
            
        self.learn_step_counter += 1 
        # target network的计数
        
        sample_index = np.random.choice(MEMORY_CAPACITY, BATCH_SIZE)
        #从relay buffer中选择一个batch的样本index(可以重复)
        
        b_memory = self.memory[sample_index, :]
        #通过上一行的index,从relay buffer中提取出这一个batch的记录

        b_s = torch.FloatTensor(b_memory[:, :N_STATES])
        b_a = torch.LongTensor(b_memory[:, N_STATES:N_STATES+1].astype(int))
        b_r = torch.FloatTensor(b_memory[:, N_STATES+1:N_STATES+2])
        b_s_ = torch.FloatTensor(b_memory[:, -N_STATES:]) 
        #分别对应了这一个batch中的原始状态,该状态采取的动作,该状态的reward,该状态采取该动作之后的状态
        
        
        q_eval = self.eval_net(b_s).gather(1, b_a)  
        #  (batch, 1)
        #  self.eval_net(b_s)的输出是一个(batch,2)的tensor,相应的Q(s,a)
        # 这里gather的操作是根据b_a是0还是1选择每一行的0还是1,也就是选择maxQ(s,a)

        q_next = self.target_net(b_s_).detach()     
        # detach的作用就是不反向传播去更新,因为target的更新在前面定义好了的,每100轮更新一次
        #q_nexts 是Q(s_t+1,a),由于target network不进行更新,所以将q_next detach出来

        q_target = b_r + GAMMA * q_next.max(1)[0].view(BATCH_SIZE, 1)   
        #r_t+Γ max Q(s_t+1,a)
        # shape (batch, 1)
        
        loss = self.loss_func(q_eval, q_target) 
        
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step() 
        #pytorch老三样

6 使用&训练DQN

dqn = DQN()
#创建一个DQN network
 
print('\nCollecting experience...')
for i_episode in range(400):
    #采集400个序列

    s = env.reset() 
    # 重置当前环境状态。
    #s表示初始化这一个episode的环境
    #array([ 2.3993547 ,  0.35676894,  0.00279927, -0.2451453 ], dtype=float32)

    ep_r = 0
    # ep_r表示每个episode中的reward
    
    for t in range(1000):
        #一个epsiode里面有1000步

        #env.render()
        #渲染环境,如果你是再服务器上跑的,只想出结果,不想看动态推杆过程的话,可以注释掉
        
        a = dqn.choose_action(s) 
        #根据当前的状态s,选择当前回合合适的action
        
        s_, r, done, info = env.step(a) 
        # 四个返回的内容是state,reward,done(是否重置环境),info
        
        x, x_dot, theta, theta_dot = s_
        #s的四个状态 

        r1 = (env.x_threshold - abs(x)) / env.x_threshold - 0.8
        # r1代表车的 x水平位移 与 x最大边距 的距离差的得分
        r2 = (env.theta_threshold_radians - abs(theta)) / env.theta_threshold_radians - 0.5
        # r2代表杆的 与垂直线之间的夹角
        r = r1 + r2 
        #修改reward,因为如果使用默认的reward的话可能不好训练,所以这里使用了修改后的reward
        #杆越竖直,越靠近中心,奖励应该越大
        
        dqn.store_transition(s, a, r, s_) 
        #状态,该状态使用的action,该状态使用这个action之后的奖励,之后的状态
        #这些信息存储起来,放入relay buffer中
        
        ep_r += r
        #当前这轮episode的reward++
        
        if dqn.memory_counter > MEMORY_CAPACITY:
            #表明relay buffer中已经满了,也即是已经采样了一些样本,之后再进行training,那么就可以一边采样一边训练了
            dqn.learn()

            if done:
                print('Ep: ', i_episode, '| Ep_r: ', round(ep_r, 2)) 

        if done:
            break
        s = s_
'''

Collecting experience...
Ep:  203 | Ep_r:  1.29
Ep:  204 | Ep_r:  1.67
Ep:  205 | Ep_r:  1.36
Ep:  206 | Ep_r:  8.36
Ep:  207 | Ep_r:  3.23
Ep:  208 | Ep_r:  2.25
Ep:  209 | Ep_r:  1.12
Ep:  210 | Ep_r:  1.82
Ep:  211 | Ep_r:  3.1
Ep:  212 | Ep_r:  4.04
Ep:  213 | Ep_r:  1.26
'''

loss function 如下:

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UQI-LIUWJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值