DQN的完全实现

DQN的完全实现

DQN是强化学习中经典的离线学习算法,本项目实现了Dueling-DQN和多进程游戏交互,同时默认添加了三种游戏的训练配置。完整代码详见此处

项目结构

E:..gitignore
│  ReadMe.md
│  requirements.txt
├─config
│  │  flapper_train_setting.py	flapper_bird的训练配置
│  │  mario_train_setting.py	马里奥的训练配置
│  │  test_train_setting.py		cartpole的训练配置
├─dqn 训练相关的代码
│  │  dqn_trainner.py
│  │  log_wrapper.py
│  │  replay_buffer.py
├─game 不同游戏的实现
│  │  flapper_game.py
│  │  mario_game.py
│  │  test_game.py
│  │  vec_game_wrapper.py  多进程游戏的主要实现代码
├─model 不同游戏的模型架构,model_wrapper 主要是对torch模型的封装
│  │  flapper_model_arch.py
│  │  mario_model_arch.py
│  │  model_wrapper.py
│  │  test_model_arch.py
├─result 所有相关游戏结果都将储存在该位置
│  ├─log tensorboard_log保存 reward和loss
│  │	│  .gitkeep
│  ├─model 所有训练中保存的模型
│  │   	│  .gitkeep
│  └─monitor 每个游戏的监控结果
│      	│  .gitkeep
│
└─test 测试代码
        env_test.py 单环境测试代码
        new_trainTest.py 训练测试代码
        vec_envTest.py 多进程游戏测试

项目依赖

训练依赖于torch以及tqdm,numpy

如果需要查看tensorboard则需下载tensorboard。

部分游戏render需要使用opencv-python,

游戏全部依赖于gymnasium。

实现特性

  • vec_game主要由pipe通信完成

    def game_worker(game, monitor_file_path, conn2):
        one_game = game(monitor_file_path = monitor_file_path)
        while True:
            msg, arg = conn2.recv()
            if msg == 'step':
                state, reward, done, truncated, info = one_game.step(arg)
                conn2.send((state, reward, done, truncated, info))
            if msg == 'reset_step':
                state, reward, done, truncated, info = one_game.reset_step(arg)
                conn2.send((state, reward, done, truncated, info))
            elif msg == 'reset':
                state, info = one_game.reset(arg)
                conn2.send((state, info))
            elif msg == 'render':
                one_game.render()
    
  • replay_buffer由numpy完成,所以这里会引入一部分时间消耗

def __init__(self, buffer_size, obs_shape_dict):
        self.buffer_size = buffer_size
        self.size = 0
        self.index = 0
        self.obs_shape_dict = deepcopy(obs_shape_dict)
        
        self.states = {}
        self.next_states = {}
        
        for key, value in self.obs_shape_dict.items():
            state_shape = [self.buffer_size]
            state_shape.extend(value)
            self.states[key] = np.zeros(state_shape, dtype = np.float32)
            self.next_states[key] = np.zeros(state_shape, dtype = np.float32)
        
        self.actions = np.zeros((self.buffer_size, ), dtype=np.int64)
        self.rewards = np.zeros((self.buffer_size, ), dtype=np.float32)
        self.dones = np.zeros((self.buffer_size, ), dtype=np.float32)
        self.truncateds = np.zeros((self.buffer_size, ), dtype=np.float32)

部分结果

在配置为12th Gen Intel® Core™ i7-12700H 2.30 GHz 和3070ti的电脑上,

关于cartpole, 十分钟就可以训练出较好结果:

Pictures/image/202403102237152.gif at main · yinpeilin/Pictures (github.com)

(因为opencv颜色通道显示的关系,部分颜色出现错位,但不影响结果)

关于flapper_bird, 基于图像约要三十分钟左右:

Pictures/image/202403102221708.gif at main · yinpeilin/Pictures (github.com)

使用方法

如需要使用自己的游戏,需要依照config中的文件进行配置,在game文件夹下写完完整的游戏,在model文件夹下添加模型架构,然后依照test/new_trainTest.py进行训练。

TIPS

因为gym_super_mario_bros是以gym(gymnasium的早期版本,早已经不再维护)来实现的,所以为了适配gymnasium,我们需要一些额外的配置。

  • 手动将gym_super_mario_bros库中的所有import gym 改为import gymnasium as gym
  • 因为gym库和gymnasium库相比,step和reset的参数略有不同,我们还要到D:\anaconda\envs\game_rl\Lib\site-packages\nes_py(这个目录与你电脑python的配置相关)中nes_env.py中做下列修改:
def reset(self, seed=None):
    -->
def reset(self, seed=None, options=None, return_info=None):
    
def step(self, action):
    ...
	return self.screen, reward, self.done, info
	-->
    return self.screen, reward, self.done, False, info

TODO

  • vec_game的协程模式
  • DDPG的完全实现
  • log_wrapper的实现
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
DQN(Deep Q-Network)是一种强化学习算法,结合了深度神经网络和Q-learning算法,用于解决离散动作空间下的决策问题。下面是DQN的代码实现Python + PyTorch): ```python import random import numpy as np import torch import torch.nn as nn import torch.optim as optim # 定义DQN网络 class DQN(nn.Module): def __init__(self, state_dim, action_dim): super(DQN, self).__init__() self.fc1 = nn.Linear(state_dim, 64) self.fc2 = nn.Linear(64, 64) self.fc3 = nn.Linear(64, action_dim) def forward(self, x): x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) x = self.fc3(x) return x # 定义经验回放缓存 class ReplayBuffer: def __init__(self, capacity): self.capacity = capacity self.buffer = [] self.position = 0 def push(self, state, action, reward, next_state, done): if len(self.buffer) < self.capacity: self.buffer.append(None) self.buffer[self.position] = (state, action, reward, next_state, done) self.position = (self.position + 1) % self.capacity def sample(self, batch_size): batch = random.sample(self.buffer, batch_size) state, action, reward, next_state, done = map(np.stack, zip(*batch)) return state, action, reward, next_state, done def __len__(self): return len(self.buffer) # 定义DQN智能体 class DQNAgent: def __init__(self, state_dim, action_dim, lr, gamma, epsilon, buffer_capacity, batch_size): self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") self.state_dim = state_dim self.action_dim = action_dim self.lr = lr self.gamma = gamma self.epsilon = epsilon self.batch_size = batch_size self.buffer = ReplayBuffer(buffer_capacity) self.policy_net = DQN(state_dim, action_dim).to(self.device) self.target_net = DQN(state_dim, action_dim).to(self.device) self.target_net.load_state_dict(self.policy_net.state_dict()) self.target_net.eval() self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr) self.loss_fn = nn.SmoothL1Loss() def act(self, state): if np.random.rand() < self.epsilon: return random.randrange(self.action_dim) with torch.no_grad(): state = torch.FloatTensor(state).to(self.device) q_values = self.policy_net(state) action = q_values.argmax().item() return action def update(self): if len(self.buffer) < self.batch_size: return state, action, reward, next_state, done = self.buffer.sample(self.batch_size) state = torch.FloatTensor(state).to(self.device) action = torch.LongTensor(action).to(self.device) reward = torch.FloatTensor(reward).to(self.device) next_state = torch.FloatTensor(next_state).to(self.device) done = torch.FloatTensor(done).to(self.device) q_values = self.policy_net(state).gather(1, action.unsqueeze(-1)).squeeze(-1) next_q_values = self.target_net(next_state).max(1)[0] expected_q_values = reward + self.gamma * next_q_values * (1 - done) loss = self.loss_fn(q_values, expected_q_values.detach()) self.optimizer.zero_grad() loss.backward() self.optimizer.step() def update_target(self): self.target_net.load_state_dict(self.policy_net.state_dict()) def save(self, path): torch.save(self.policy_net.state_dict(), path) def load(self, path): self.policy_net.load_state_dict(torch.load(path)) self.target_net.load_state_dict(self.policy_net.state_dict()) self.target_net.eval() ``` 该实现包括以下组件: 1. DQN网络:定义了一个三层全连接神经网络,输入为状态,输出为每个动作的Q值。 2. 经验回放缓存:用于存储智能体与环境交互的经验,以便于随机采样。 3. DQN智能体:包括动作选择、经验回放、网络更新等功能。 在训练中,智能体与环境进行交互,得到状态、动作、奖励、下一个状态和是否终止的信息,将其存储到经验回放缓存中。然后,智能体从经验回放缓存中随机采样一批数据,计算Q值的误差并更新网络参数。同时,定期将策略网络的参数复制给目标网络,以稳定训练过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值