python:一个超简单的DQN模型

模型描述

MDP模型:

  • 只考虑状态、动作、回报
  • 不考虑转移函数和衰减因子

模型代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/5/17 14:56
# @Author  : Liu Lihao
# @File    : DQN.py

import torch
import torch.nn as nn
import torch.nn.functional as F
import random
import collections
from torch import optim
import numpy as np


'''
参数设置
'''
learning_rate = 0.001 #0.0001
N_episode = 100  # 训练回合数

train_threshold = 2000
batch_size = 20
buffer_limit = 50000  # 经验池最大容量
gamma = 0  # 预测的下一状态的最大动作值折合到reward中的衰减程度



'''
构建Q网络
使用全连接神经网络
策略采用 epsilon 贪婪策略,有 epsilon 的概率选择随机动作, 1-epsilon 的概率选择贪婪动作。
'''
class Qnet(nn.Module):
    def __init__(self, state_dim, action_num):
        super(Qnet, self).__init__()
        self.fc1 = nn.Linear(state_dim, 128)
        self.fc2 = nn.Linear(128, 128)
        self.fc3 = nn.Linear(128, 128)
        self.fc4 = nn.Linear(128, action_num)
        self.action_num = action_num

    def forward(self, x):
        x = F.leaky_relu(self.fc1(x))
        x = F.leaky_relu(self.fc2(x))
        x = F.leaky_relu(self.fc3(x))
        x = self.fc4(x)
        return x

    # 选择动作
    # in: state
    # out: action
    def sample_action(self, obs, epsilon):
        out = self.forward(obs)
        coin = random.random()  # 生成0和1之间的随机浮点数
        if coin < epsilon:
            # 随机返回一个动作
            return random.randint(0, self.action_num-1)
        else:
            # 返回最大Q值对应的动作
            return out.argmax().item()


'''
定义经验回放池
经验回放池实际上是一个队列,当经验回放池满时,会抛弃旧的经验值,加入新采样的经验值。
采样时,从经验回放池中随机抽取batch_size个经验值作为一个transition返回给训练机进行学习。
'''
class ReplayBuffer():
    def __init__(self):
        self.buffer = collections.deque(maxlen=buffer_limit)  # 双端队列

    # 插入经验池
    def put(self, transition):
        self.buffer.append(transition)

    # 从经验池中抽样
    '''
    shape:
        s_lst: [batch_size,input_size]
        a_lst: [batch_size,1]
        r_lst: [batch_size,1]
    '''
    def sample(self, n):
        mini_batch = random.sample(self.buffer, n)  # 随机抽取n个样本
        s_lst, a_lst, r_lst = [], [], []

        for transition in mini_batch:
            s, a, r = transition
            s_lst.append(s)
            a_lst.append([a])
            r_lst.append([r])

        return torch.tensor(s_lst, dtype=torch.float),\
               torch.tensor(a_lst),\
               torch.tensor(r_lst, dtype=torch.float)

    # 返回buffer长度
    def size(self):
        return len(self.buffer)



'''
定义智能体
参数:
    state_dim:状态的维度
    action_dim:动作的维度
    save_path:模型保存路径
'''
class DQNModel:
    def __init__(self, state_dim, action_num, save_path):
        self.q = Qnet(state_dim, action_num)
        self.memory = ReplayBuffer()
        self.optimizer = optim.Adam(self.q.parameters(), lr=learning_rate)
        self.save_path = save_path

    '''神经网络前传+选择动作'''
    def get_policy(self, s, epsilon):
        # 从已经训练的模型中载入参数
        try:
            checkpoint = torch.load(self.save_path)
            self.q.load_state_dict(checkpoint['q_state_dict'])
            # print("载入模型成功...")
        except Exception as e:
            print(e)
            # print("模型参数随机初始化...")
        # 根据state选择动作
        action = self.q.sample_action(torch.Tensor(s).float(), epsilon)
        return action
    
    '''存储经验样本'''
    def store_memory(self, state, action, reward):
        self.memory.put((state, action, reward))

    '''
    训练Q_net
    训练N_episode次
    每次抽取batch_size个经验样本
    '''
    def update(self):
        LOSS = []
        if self.memory.size() > batch_size:
            for i in range(N_episode):
                s, a, r = self.memory.sample(batch_size)
                q_out = self.q(s)  # 将状态s输入神经网络得到输出
                q_a = q_out.gather(1, a)  # 在状态s处执行a动作对应的q值
                loss = F.smooth_l1_loss(q_a, r)  # 计算loss
                LOSS.append(loss.item())
                '''
                shape:
                q_out:[batch_size, action_number]
                q_a:[batch_size, 1]
                loss:[]
                '''
                self.optimizer.zero_grad()
                loss.backward()
                self.optimizer.step()

            # 保存网络参数
            torch.save(
                {
                    'q_state_dict': self.q.state_dict(),
                    'optim_state_dict': self.optimizer.state_dict()
                }, self.save_path)

        return np.mean(LOSS)

DQN的Q值网络模型一个深度神经网络,它的输入是当前状态,输出是每个动作对应的Q值。在DQN中,我们使用一个带有固定目标网络的Q值网络和一个经验回放池来进行离线学习。 下面是一个简单DQN的Q值网络模型Python实现,使用Keras作为深度学习库: ```python import numpy as np from keras.models import Sequential from keras.layers import Dense class DQNAgent: def __init__(self, state_size, action_size): self.state_size = state_size self.action_size = action_size self.memory = [] self.gamma = 0.95 # discount rate self.epsilon = 1.0 # exploration rate self.epsilon_min = 0.01 self.epsilon_decay = 0.995 self.learning_rate = 0.001 self.model = self._build_model() self.target_model = self._build_model() def _build_model(self): model = Sequential() model.add(Dense(24, input_dim=self.state_size, activation='relu')) model.add(Dense(24, activation='relu')) model.add(Dense(self.action_size, activation='linear')) model.compile(loss='mse', optimizer='adam') return model def remember(self, state, action, reward, next_state, done): self.memory.append((state, action, reward, next_state, done)) def act(self, state): if np.random.rand() <= self.epsilon: return np.random.choice(self.action_size) act_values = self.model.predict(state) return np.argmax(act_values[0]) def replay(self, batch_size): minibatch = random.sample(self.memory, batch_size) for state, action, reward, next_state, done in minibatch: target = reward if not done: target = (reward + self.gamma * np.amax(self.target_model.predict(next_state)[0])) target_f = self.model.predict(state) target_f[0][action] = target self.model.fit(state, target_f, epochs=1, verbose=0) if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay def update_target_model(self): self.target_model.set_weights(self.model.get_weights()) ``` 在上面的代码中,我们定义了一个DQNAgent类,它包含了一个用于存储经验回放池的memory列表,以及一些参数和两个Keras模型一个用于学习的model和一个用于生成目标值的target_model。在构建model时,我们使用了三个全连接层,其中前两个层使用ReLU激活函数,最后一层使用线性激活函数。在compile函数中,我们使用均方误差作为损失函数,使用Adam优化器进行优化。 在act函数中,我们使用epsilon-greedy策略来选择动作,其中epsilon是用于控制探索程度的参数。 在replay函数中,我们从memory中随机抽取一批经验数据,并使用Bellman方程计算目标值。然后,我们将目标值与当前模型输出的Q值进行比较,并使用mse损失函数进行训练。 最后,在update_target_model函数中,我们将model的权重复制到target_model中,以保持目标值的稳定。 需要注意的是,这只是一个简单DQN的Q值网络模型的实现,实际应用中还需要进行一些改进和调整。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值