强化学习记录一DQN

感谢科科科老师的课程
感谢莫凡大佬的课程

强烈推荐先过一遍科科科老师的dqn

首先说一下为什么会出现DQN

在 Q learning 或者 SARSA 中
我们可以用一个 Q 表 来存储我们的行为与状态
在这里插入图片描述

但是当state的状态为连续空间的时候
就会出现无数个state
如果把连续的state离散化会导致精度丢失

如果有一个东西丢给它一个state就会给出一个action就好了
而神经网络恰好就符合我们的需求

2013年发表的论文
Playing Atari with Deep Reinforcement Learning
这是第一次成功的强化学习和深度学习的结合
知道你们不想看英文,上面的链接是中文的

凡事都是有利有弊
引入神经网络的弊端:
弊端1. 用神经网络训练的时候所有状态都是连续的,具有高度相关性
弊端2. 训练神经网络时,Q_target是在一直变化的,
因为画圈圈的部分也是经过神经网络得到的,所以很难拟合Q_target

举个例子
监督学习的终点就在前方五十米,你只需要慢慢接近终点就行了
dqn的终点在前方五十米,你前进了二十米,终点又移动到了你后方五十米
在这里插入图片描述

解决1的办法:经验回放
搞一个经验池,随机抽取经验训练
解决2的办法:固定目标
使用两个神经网络,一个网络参数每次都更新,一个网络固定次数更新参数
用这个每次都更新参数的网络 拟合 固定次数更新参数的网络
这个地方你或许会有疑问,固定次数更新参数会有什么不一样?
直接看图吧

请添加图片描述
请添加图片描述

分模块代码解析

基本的神经网络框架

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 在CartPole-v0中状态是用四个浮点数表示
		# N_STATES就是4
        self.l1 = nn.Linear(N_STATES, 64)
        self.l2 = nn.Linear(64, 32)
    	# 在CartPole-v0中动作是用两个数表示
		# N_ACTIONS就是2
        self.predict = nn.Linear(32, N_ACTIONS)

    def forward(self, x):
        out = self.l1(x)
        out = F.relu(out)
        out = self.l2(out)
        out = F.relu(out)
        return self.predict(out)

在这里插入图片描述

经验回放所使用的类

class ReplayMemory():
    def __init__(self, max_size, batch_size):
    	# 存放经验的最大容量
        self.max_size = max_size
        # 每次从经验池中随机取多少条经验
        self.batch_size = batch_size
        # 定义一个双向队列存放经验,坑1,会讲一下这个双向队列
        self.buffer = collections.deque(maxlen=max_size)
        
    def append(self, exp):
    	# 把一条经验放入经验池
        self.buffer.append(exp)

    def sample(self):
        # 从经验池随机抽样得到batch_size条经验
        mini_batch = random.sample(self.buffer, self.batch_size)
        # 声明4个空数组分别存放状态、动作、奖励、下次的状态
        # s 表示 state,a 表示 action,r 表示 reward,ss 表示 下次的state
        s_mini_batch, a_mini_batch, r_mini_batch, ss_mini_batch = [], [], [], []
        for exp in mini_batch:
            s, a, r, ss = exp
            s_mini_batch.append(s)
            a_mini_batch.append(a)
            r_mini_batch.append(r)
            ss_mini_batch.append(ss)
		# 坑2,会讲一下返回的都是什么东西
        return torch.FloatTensor(np.array(s_mini_batch)), \
        				# 坑3,这个地方真的很坑,我自己搞了好久
               torch.LongTensor(np.array(a_mini_batch)).view(-1,1), \
               torch.FloatTensor(np.array(r_mini_batch)).view(-1,1), \
               torch.FloatTensor(np.array(ss_mini_batch))
    def __len__(self):
        return len(self.buffer)

坑1:
既然队列有max_size,那么达到max_size后,经验该怎么存放
看下面的代码

在这里插入图片描述
当你追加经验超过它的max_size后,它会自动把最先进入队列的经验扔掉
这种机制可以保证在经验池里的都是最新的经验
也不用担心当经验池满了之后如何覆盖

坑2:
这里就是把list转成了我们想要形状的张量
view(-1,1)的参数可以看作行和列
上面这个就是要把形状变成(-1行,1列)
其中-1就是我不确定,让程序自己算
在这里插入图片描述
坑3:
为什么其他的都是float类型的张量,而这个是long类型的张量

torch.LongTensor(np.array(a_mini_batch)).view(-1,1)

说到这就不得不提 torch.gather(input,dim,index)
其中index只接收int64类型也就是long类型,
你直接给它1都不行,必须是long(1)
一般报错是这样的
在这里插入图片描述
至于gather()的作用下面会再讲

最主要的类DQN

class DQN():
    def __init__(self):
    	# 创建两个神经网络,为什么是两个,见弊端2
        self.eval_net, self.target_net = Net(), Net()
        
        self.learn_step_counter = 0  # 记录学习的次数
        self.Target_replace_iter = 100  # 每学习100次更新target网络的参数
        
        # 优化器
        self.optimizer = optim.Adam(self.eval_net.parameters(), lr=lr)
        # 均方差
        self.loss_func = nn.MSELoss()

    def choose_action(self, s):
    	# 这是是把s转换成了张量
        s = torch.FloatTensor(s)
        # 随机产生一个0-1之间的数,如果小于greedy就选择价值最高的行为,greedy是全局参数,没有定义在这个类中
        if np.random.uniform() < greedy:
        	# 给eval_net一个状态,得到各个行为的价值
            actions_value = self.eval_net.forward(s)
            # 选择其中价值最大的动作
            action = torch.max(actions_value, 0)[1].data.numpy()  
        else:
        	# 随机选择行为(探索精神)
            action = np.random.randint(0, N_ACTIONS)
        return action

    def learn(self):
        # 每隔Target_replace_iter步更新一下target网络的参数
        if self.learn_step_counter % self.Target_replace_iter == 0:
            self.target_net.load_state_dict(self.eval_net.state_dict())
        # 记录学习的次数
        self.learn_step_counter += 1
        
        # 随机抽样得到batch_size条经验
        s, a, r, ss = memory.sample()
        
		# 强化学习的精髓,这三行下面详细讲
        q_eval = self.eval_net(s).gather(1, a)
        q_next = self.target_net(ss).detach()
        q_target = r + gamma * q_next.max(1)[0].view(batch_size, 1)

        # 计算q_eval与q_target值的差距
        loss = self.loss_func(q_eval, q_target)
        # 梯度归零
        self.optimizer.zero_grad()
        # 反向传播
        loss.backward()
        # 更新权重参数
        self.optimizer.step()

坑3那里说的gather()的作用也在这里讲了

q_eval = self.eval_net(s).gather(1, a)

self.eval_net(s)就是使用当前的神经网络eval_net进行一次前向传播得到各个行为的价值
在这里我们使用的是CartPole-v0的环境
在这里插入图片描述

为了方便观察我这里把batch_size调成4看一下效果
下面就是动作0和动作1分别对应的价值
在这里插入图片描述
那后面的gather(1, a)是干什么的呢
a就是在状态s下所采取的行为
在这里插入图片描述
那把行为所对应的价值取出来是不是就得到了在状态s下采取行为a的价值
gather(1, a)就是干这个的
看一下gather(dim,index)是如何做到的
在这里插入图片描述
最终q_eval就是在状态s下采取行为a的价值
在这里插入图片描述

q_next = self.target_net(ss).detach()

这行代码是target_net进行一次前向传播得到下一个state的价值
而后面的.detach()就是不进行反向传播
因为我们不是要固定target_net的参数吗

这两行代码我们已经得到了
在状态s下采取行为a的价值
下一个状态各个行为对应的价值
在这里插入图片描述
看最后一行核心代码

q_target = r + gamma * q_next.max(1)[0].view(batch_size, 1)

q_next.max(1)[0].view(batch_size, 1)这一部分就是把下一个状态中行为的最大值取出来
在这里插入图片描述

q_target = r + gamma * max(q_next)
q_target 就是我们这一步的奖励加上打折未来可能获取的最大奖励
类比
今天我说现在给你100块
今天我说一年后给你100块
你的心情肯定是不一样的,这就是他们的奖励相同但价值不同
gamma 就是对未来的奖励做了一个折扣
在这里插入图片描述
这种方法会先更新得到奖励或惩罚的那一步
然后更新得到奖励或惩罚的那一步的前一步
然后更新前前一步
请添加图片描述
完整代码地址
https://gitee.com/VVeaker/ai/blob/master/DeepReinforcementLearning/DQN_CartPole_v0.py

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值