强化学习DQN代码解析——自动驾驶

        本次将介绍一下强化学习当中的DQN (Deep Q-Learning) 算法,并通过一个自动驾驶的案例(pytorch)来具体说明,代码如下,包含代码运行过程中会出现的一些错误的解决方法。

一、DQN算法

        关于DQN算法的原理介绍,网上有很多详细的解读,在此不具体展开,只提及一些个人认为有助于理解和比较关键的点。

        首先,理解DQN算法之前,我们可以先了解一下强化学习和Q-learning算法的实现原理。强化学习可以理解为与环境的交互学习。其中,最关键的即是:

        状态(state)、策略(policy)、动作(action)、reward(奖励)

        对于每一个状态,我们会给出一个策略,智能体会执行一个动作,并得到相应的奖励。我们训练的目的则是使得累计的奖励最大。

    Q-learning算法:简单来说,是在策略的选择上进行展开的。该算法中存在一张表格,记录着每一个状态下执行每一个动作所得到的Q值,在选取动作时会进行表格的查阅,然后选取Q值最大的动作。

        DQN算法:了解完Q-learning算法后,我们再来考虑DQN算法出现的初衷。当状态和动作为连续的、无限的,我们通过表格的方式记录就不合理了。这时我们采用神经网络来替代表格,输入为状态,输出为动作。这其中涉及几个关键的改变:

        1、经验池的储存:既然存在神经网络,我们就需要有样本进行训练,由于输入是状态输出是动作,那么我们可以将每一次的【当前状态,动作,奖励,下一状态】储存进经验池,当经验池满容量时,进行训练,此后有新的样本将不断替换经验池中的原有样本,反复训练。

        2、双网络:在DQN中存在两个神经网络,一个是Q-target,一个是Q-eval。其中Q-eval网络会不断进行训练和Q值的更新,而Q-target则是相对固定,只有经过固定训练轮次后才会与Q-eval同步。损失函数就是两个网络的Q值之差,我们需要使其差值越来越小。如果两个网络都在改变,很有可能会“错过”,当固定一个网络时,网络的训练会更有目标性,即更容易收敛。

        3、e-greedy算法:上述我们在选择动作时,一般是选择Q值最大的那个动作。但如果有的执行动作不被选择过,它的Q值将始终是0,因此可能永远都不会被选择。所以我们引入动作选择时的随机性,使其具有一定概率去随机选择动作而不是完全依靠Q值的大小选择最优动作。

二、自动驾驶环境配置

        我们使用Edouard Leurent发布在github上的highway-env包。

        包下载链接:项目目录预览 - highway-env - GitCode  

import torch
import torch.nn as nn
from torch.autograd import Variable
from torch import FloatTensor, LongTensor, ByteTensor
from collections import namedtuple
import random
import gymnasium as gym
import highway_env
from matplotlib import pyplot as plt
import numpy as np

首先导入所需要的库。

2.1 初始化环境

# 初始化环境
config = \
    {
        "observation":
            {
                "type": "Kinematics",
                "vehicles_count": 5,
                "features": ["presence", "x", "y", "vx", "vy", "cos_h", "sin_h"],
                "features_range":
                    {
                        "x": [-100, 100],
                        "y": [-100, 100],
                        "vx": [-20, 20],
                        "vy": [-20, 20]
                    },
                "absolute": False,
                "order": "sorted"
            },
        "simulation_frequency": 8,  # [Hz]
        "policy_frequency": 2,  # [Hz]
    }
highway_env.register_highway_envs()  # 解决Environment `highway` doesn't exist.
env = gym.make("highway-v0", render_mode='rgb_array')
env.configure(config)

 其中,highway_env.register_highway_envs()可以解决由于gym版本带来的以下问题:          (Environment `highway` doesn't exist.)

如果出现:OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.

可以参考:关于OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.错误解决方法 - 知乎 (zhihu.com)

2.2 网络的建立

# DQN网络模型基本参数
Tensor = FloatTensor
GAMMA = 0.9
TARGET_NETWORK_REPLACE_FREQ = 50  # target网络更新频率
MEMORY_CAPACITY = 200  # 经验库容量
BATCH_SIZE = 100  # 批量训练
LR = 0.01  # 学习率


# DQNNET的建立
class DQNNet(nn.Module):
    def __init__(self):
        super(DQNNet, self).__init__()
        self.linear1 = nn.Linear(35, 35)
        self.linear2 = nn.Linear(35, 5)

    def forward(self, s):
        s = torch.FloatTensor(s)
        s = s.view(s.size(0), 1, 35)
        s = self.linear1(s)
        s = self.linear2(s)
        return s

        此小节定义了网络参数以及网络结构。在此我们只定义了两层线性全连接层,当然也可以根据自己的需求进行修改。 

2.3  DQN算法

class DQN(object):
    def __init__(self):
        self.net, self.target_net = DQNNet(), DQNNet()
        self.learn_step_counter = 0
        self.memory = []
        self.position = 0
        self.capacity = MEMORY_CAPACITY
        self.optimizer = torch.optim.Adam(self.net.parameters(), lr=LR)
        self.loss_func = nn.MSELoss()

    # 动作选择函数,动作的选择遵循e-greedy算法
    def choose_action(self, s, e):
        x = np.expand_dims(s, axis=0)  # 拓展数组的形状和维度,使其满足输入格式
        if np.random.uniform() < 1 - e:  # 从(0,1)范围内随机取值
            actions_value = self.net.forward(x)  # 向前传播
            action = torch.max(actions_value, -1)[1].data.numpy()
            action = action.max()  # 选取Q值最大的一个动作
        else:
            action = np.random.randint(0, 5)  # 随机选取动作
        return action

    # 放入经验库
    def push_memory(self, s, a, r, s_):
        if len(self.memory) < self.capacity:
            self.memory.append(None)
        self.memory[self.position] = Transition(torch.unsqueeze(torch.FloatTensor(s), 0),
                                                torch.unsqueeze(torch.FloatTensor(s_), 0),
                                                torch.from_numpy(np.array([a])),
                                                torch.from_numpy(np.array([r], dtype='float32')))  #
        self.position = (self.position + 1) % self.capacity
        # 如果有超过经验库容量的样本,则从头开始替换经验库里的样本

    # 随机选取batch个样本进行训练
    def get_sample(self, batch_size):
        sample = random.sample(self.memory, batch_size)
        return sample

    # 训练
    def learn(self):
        if self.learn_step_counter % TARGET_NETWORK_REPLACE_FREQ == 0:  # 检查是否到达更新频率
            self.target_net.load_state_dict(self.net.state_dict())  # 将eval网络参数同步到target网络
        self.learn_step_counter += 1

        transitions = self.get_sample(BATCH_SIZE)  # 选择批量训练样本
        batch = Transition(*zip(*transitions))

        b_s = Variable(torch.cat(batch.state))
        b_s_ = Variable(torch.cat(batch.next_state))
        b_a = Variable(torch.cat(batch.action))
        b_r = Variable(torch.cat(batch.reward))

        q_eval = self.net.forward(b_s).squeeze(1).gather(1, b_a.unsqueeze(1).to(torch.int64))
        q_next = self.target_net.forward(b_s_).detach()  #
        q_target = b_r + GAMMA * q_next.squeeze(1).max(1)[0].view(BATCH_SIZE, 1).t()
        loss = self.loss_func(q_eval, q_target.t())
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()
        return loss

        此小节定义了DQN类。其中包含动作的选择、经验库的增加、训练样本的提取、训练过程。 

 2.4 模型训练

Transition = namedtuple('Transition', ('state', 'next_state', 'action', 'reward'))

# 训练模型
dqn = DQN()
count = 0

sum_reward = 0
all_reward = []

all_loss = []

for episode in range(100):  # 100次碰撞
    done = False
    s = env.reset()[0]  # 初始化环境,得到初始状态
    reward = []

    while not done:

        e = np.exp(-count / 300)  # 随机选择action的概率,随着训练次数增多逐渐降低
        a = dqn.choose_action(s, e)  # 动作选择
        s_, r, done, info, _ = env.step(a)  # 环境交互,得到下一步的状态
        env.render()  # 渲染
        dqn.push_memory(s, a, r, s_)  # 将该行为放入经验池

        if (dqn.position != 0) & (dqn.position % 99 == 0):  # if 经验池容量已满或者全部重新更新
            loss_ = dqn.learn()  # 训练、损失函数
            all_loss.append(loss_)
            count += 1
            print('trained times:', count)

        s = s_  # 更新状态
        reward.append(r)

    sum_reward = np.sum(reward)
    all_reward.append(sum_reward)

plt.plot(all_reward, 'b*--', alpha=0.5, linewidth=1, label='acc')
plt.show()

a1 = torch.Tensor(all_loss)
plt.plot(a1.detach().numpy(), 'b*--', alpha=0.5, linewidth=1, label='acc')
plt.show()

env.step()返回值的含义:
        观察(Observation): 新的观察表示汽车的新位置、速度等,即状态。这些信息会用于决定下一步的动作;
        奖励(Reward): 如果汽车成功避开了障碍物并靠近了目的地,你可能会得到正的奖励。反之,如果碰到了障碍物,可能是负的奖励;
        完成标记(Done): 如果汽车到达目的地或者碰到障碍物导致任务失败,done会被设为 True,表示这一回合结束;
        额外信息(Info): 这里可能包含模拟器内部的一些额外信息,比如是否违反了交通规则。但这通常不用于模型训练;

其中,s = env.reset()    →   s = env.reset()[0]

可以解决问题 :expected sequence of length 5 at dim 1 (got 4).

s_, r, done, info= env.step(a)    →   s_, r, done, info, _ = env.step(a)

可以解决问题 :too many values to unpack (expected 4).

最后画出了累计奖励和损失函数图。后续我们可以自行调参训练,本文参数无参考意义。大部分代码问题和版本有关,如果运行本代码还是有问题,可以尝试将本文做的一些修改给改回去。

附上原文链接,但跑原文代码时遇到些许问题,在此分享个人的一些解决方法和理解。

仅供学习用途,侵权删。

原链接

  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
MATLAB强化学习DQN(深度Q网络)代码是一种实现深度强化学习的方法,通常用于解决控制问题和决策问题。DQN通过结合深度神经网络和Q学习算法来实现对环境的学习和决策。以下是MATLAB中实现DQN算法的简要代码示例: 首先,需要定义一个深度神经网络模型来拟合Q值函数。可以使用MATLAB的Neural Network Toolbox来构建一个适合于解决强化学习问题的神经网络模型,例如多层感知器(MLP)或卷积神经网络(CNN)。 其次,需要定义Q学习算法的参数,包括学习率、贪心策略的选择、回放缓冲区的大小等。 然后,需要定义DQN算法的训练过程。在每一步中,Agent(智能体)根据当前的状态选择动作,并观察环境返回的奖励和下一个状态。Agent将这些信息存储到回放缓冲区中,并周期性地从中随机抽样一批数据用于训练神经网络。 最后,可以使用训练好的DQN模型来进行决策。Agent在每个时间步根据当前状态使用训练好的神经网络模型来选择动作,并与环境交互。 需要注意的是,以上只是一个简要的示例,实际的DQN代码可能还涉及到一些具体问题的处理,比如环境的建模、奖励函数的设计、超参数的调优等。另外,为了更好地理解DQN算法的原理和代码实现,建议阅读相关的文献和资料,例如DeepMind团队的原始论文《Playing Atari with Deep Reinforcement Learning》以及MATLAB官方提供的强化学习工具箱的文档和示例代码

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值