强化学习从基础到进阶-案例与实践[4.1]:深度Q网络-DQN项目实战CartPole-v0

news2025/1/16 2:49:07

在这里插入图片描述
【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现

在这里插入图片描述
专栏详细介绍:【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现

对于深度强化学习这块规划为:

  • 基础单智能算法教学(gym环境为主)
  • 主流多智能算法教学(gym环境为主)
    • 主流算法:DDPG、DQN、TD3、SAC、PPO、RainbowDQN、QLearning、A2C等算法项目实战
  • 一些趣味项目(超级玛丽、下五子棋、斗地主、各种游戏上应用)
  • 单智能多智能题实战(论文复现偏业务如:无人机优化调度、电力资源调度等项目应用)

本专栏主要方便入门同学快速掌握强化学习单智能体|多智能体算法原理+项目实战。后续会持续把深度学习涉及知识原理分析给大家,让大家在项目实操的同时也能知识储备,知其然、知其所以然、知何由以知其所以然。

声明:部分项目为网络经典项目方便大家快速学习,后续会不断增添实战环节(比赛、论文、现实应用等)

  • 专栏订阅(个性化选择):

    • 强化学习原理+项目专栏大合集-《推荐订阅☆☆☆☆☆》

    • 强化学习单智能体算法原理+项目实战《推荐订阅☆☆☆☆》

    • 强化学习多智能体原理+项目实战《推荐订阅☆☆☆☆☆》

    • 强化学习相关技巧(调参、画图等《推荐订阅☆☆☆》)

    • tensorflow_gym-强化学习:免费《推荐订阅☆☆☆☆》

    • 强化学习从基础到进阶-案例与实践:免费《推荐订阅☆☆☆☆☆》

强化学习从基础到进阶-案例与实践[4.1]:深度Q网络-DQN项目实战CartPole-v0

1、定义算法

相比于Q learning,DQN本质上是为了适应更为复杂的环境,并且经过不断的改良迭代,到了Nature DQN(即Volodymyr Mnih发表的Nature论文)这里才算是基本完善。DQN主要改动的点有三个:

  • 使用深度神经网络替代原来的Q表:这个很容易理解原因
  • 使用了经验回放(Replay Buffer):这个好处有很多,一个是使用一堆历史数据去训练,比之前用一次就扔掉好多了,大大提高样本效率,另外一个是面试常提到的,减少样本之间的相关性,原则上获取经验跟学习阶段是分开的,原来时序的训练数据有可能是不稳定的,打乱之后再学习有助于提高训练的稳定性,跟深度学习中划分训练测试集时打乱样本是一个道理。
  • 使用了两个网络:即策略网络和目标网络,每隔若干步才把每步更新的策略网络参数复制给目标网络,这样做也是为了训练的稳定,避免Q值的估计发散。想象一下,如果当前有个transition(这个Q learning中提过的,一定要记住!!!)样本导致对Q值进行了较差的过估计,如果接下来从经验回放中提取到的样本正好连续几个都这样的,很有可能导致Q值的发散(它的青春小鸟一去不回来了)。再打个比方,我们玩RPG或者闯关类游戏,有些人为了破纪录经常Save和Load,只要我出了错,我不满意我就加载之前的存档,假设不允许加载呢,就像DQN算法一样训练过程中会退不了,这时候是不是搞两个档,一个档每帧都存一下,另外一个档打了不错的结果再存,也就是若干个间隔再存一下,到最后用间隔若干步数再存的档一般都比每帧都存的档好些呢。当然你也可以再搞更多个档,也就是DQN增加多个目标网络,但是对于DQN则没有多大必要,多几个网络效果不见得会好很多。

1.1 定义模型

import paddle
import paddle.nn as nn
import paddle.nn.functional as F
!pip uninstall -y parl
!pip install parl
import parl
from parl.algorithms import DQN

class MLP(parl.Model):
    """ Linear network to solve Cartpole problem.
    Args:
        input_dim (int): Dimension of observation space.
        output_dim (int): Dimension of action space.
    """

    def __init__(self, input_dim, output_dim):
        super(MLP, self).__init__()
        hidden_dim1 = 256
        hidden_dim2 = 256
        self.fc1 = nn.Linear(input_dim, hidden_dim1)
        self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
        self.fc3 = nn.Linear(hidden_dim2, output_dim)

    def forward(self, state):
        x = F.relu(self.fc1(state))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

1.2 定义经验回放

from collections import deque
class ReplayBuffer:
    def __init__(self, capacity: int) -> None:
        self.capacity = capacity
        self.buffer = deque(maxlen=self.capacity)
    def push(self,transitions):
        '''_summary_
        Args:
            trainsitions (tuple): _description_
        '''
        self.buffer.append(transitions)
    def sample(self, batch_size: int, sequential: bool = False):
        if batch_size > len(self.buffer):
            batch_size = len(self.buffer)
        if sequential: # sequential sampling
            rand = random.randint(0, len(self.buffer) - batch_size)
            batch = [self.buffer[i] for i in range(rand, rand + batch_size)]
            return zip(*batch)
        else:
            batch = random.sample(self.buffer, batch_size)
            return zip(*batch)
    def clear(self):
        self.buffer.clear()
    def __len__(self):
        return len(self.buffer)

1.3 定义智能体

from random import random
import parl
import paddle
import math
import numpy as np


class DQNAgent(parl.Agent):
    """Agent of DQN.
    """

    def __init__(self, algorithm, memory,cfg):
        super(DQNAgent, self).__init__(algorithm)
        self.n_actions = cfg['n_actions']
        self.epsilon = cfg['epsilon_start']
        self.sample_count = 0  
        self.epsilon_start = cfg['epsilon_start']
        self.epsilon_end = cfg['epsilon_end']
        self.epsilon_decay = cfg['epsilon_decay']
        self.batch_size = cfg['batch_size']
        self.global_step = 0
        self.update_target_steps = 600
        self.memory = memory # replay buffer

    def sample_action(self, state):
        self.sample_count += 1
        # epsilon must decay(linear,exponential and etc.) for balancing exploration and exploitation
        self.epsilon = self.epsilon_end + (self.epsilon_start - self.epsilon_end) * \
            math.exp(-1. * self.sample_count / self.epsilon_decay) 
        if  random.random() < self.epsilon:
            action = np.random.randint(self.n_actions)
        else:
            action = self.predict_action(state)
        return action

    def predict_action(self, state):
        state = paddle.to_tensor(state , dtype='float32')
        q_values = self.alg.predict(state) # self.alg 是自带的算法
        action = q_values.argmax().numpy()[0]
        return action

    def update(self):
        """Update model with an episode data
        Args:
            obs(np.float32): shape of (batch_size, obs_dim)
            act(np.int32): shape of (batch_size)
            reward(np.float32): shape of (batch_size)
            next_obs(np.float32): shape of (batch_size, obs_dim)
            terminal(np.float32): shape of (batch_size)
        Returns:
            loss(float)
        """
        if len(self.memory) < self.batch_size: # when transitions in memory donot meet a batch, not update
            return
        
        if self.global_step % self.update_target_steps == 0:
            self.alg.sync_target()
        self.global_step += 1
        state_batch, action_batch, reward_batch, next_state_batch, done_batch = self.memory.sample(
            self.batch_size)
        action_batch = np.expand_dims(action_batch, axis=-1)
        reward_batch = np.expand_dims(reward_batch, axis=-1)
        done_batch = np.expand_dims(done_batch, axis=-1)

        state_batch = paddle.to_tensor(state_batch, dtype='float32')
        action_batch = paddle.to_tensor(action_batch, dtype='int32')
        reward_batch = paddle.to_tensor(reward_batch, dtype='float32')
        next_state_batch = paddle.to_tensor(next_state_batch, dtype='float32')
        done_batch = paddle.to_tensor(done_batch, dtype='float32')
        loss = self.alg.learn(state_batch, action_batch, reward_batch, next_state_batch, done_batch) 

2. 定义训练

def train(cfg, env, agent):
    ''' 训练
    '''
    print(f"开始训练!")
    print(f"环境:{cfg['env_name']},算法:{cfg['algo_name']},设备:{cfg['device']}")
    rewards = []  # record rewards for all episodes
    steps = []
    for i_ep in range(cfg["train_eps"]):
        ep_reward = 0  # reward per episode
        ep_step = 0
        state = env.reset()  # reset and obtain initial state
        for _ in range(cfg['ep_max_steps']):
            ep_step += 1
            action = agent.sample_action(state)  # sample action
            next_state, reward, done, _ = env.step(action)  # update env and return transitions
            agent.memory.push((state, action, reward,next_state, done))  # save transitions
            state = next_state  # update next state for env
            agent.update()  # update agent
            ep_reward += reward  #
            if done:
                break
        steps.append(ep_step)
        rewards.append(ep_reward)
        if (i_ep + 1) % 10 == 0:
            print(f"回合:{i_ep+1}/{cfg['train_eps']},奖励:{ep_reward:.2f},Epislon: {agent.epsilon:.3f}")
    print("完成训练!")
    env.close()
    res_dic = {'episodes':range(len(rewards)),'rewards':rewards,'steps':steps}
    return res_dic

def test(cfg, env, agent):
    print("开始测试!")
    print(f"环境:{cfg['env_name']},算法:{cfg['algo_name']},设备:{cfg['device']}")
    rewards = []  # record rewards for all episodes
    steps = []
    for i_ep in range(cfg['test_eps']):
        ep_reward = 0  # reward per episode
        ep_step = 0
        state = env.reset()  # reset and obtain initial state
        for _ in range(cfg['ep_max_steps']):
            ep_step+=1
            action = agent.predict_action(state)  # predict action
            next_state, reward, done, _ = env.step(action)  
            state = next_state  
            ep_reward += reward 
            if done:
                break
        steps.append(ep_step)
        rewards.append(ep_reward)
        print(f"回合:{i_ep+1}/{cfg['test_eps']},奖励:{ep_reward:.2f}")
    print("完成测试!")
    env.close()
    return {'episodes':range(len(rewards)),'rewards':rewards,'steps':steps}


3、定义环境

OpenAI Gym中其实集成了很多强化学习环境,足够大家学习了,但是在做强化学习的应用中免不了要自己创建环境,比如在本项目中其实不太好找到Qlearning能学出来的环境,Qlearning实在是太弱了,需要足够简单的环境才行,因此本项目写了一个环境,大家感兴趣的话可以看一下,一般环境接口最关键的部分即使reset和step。

import gym
import paddle
import numpy as np
import random
import os
from parl.algorithms import DQN
def all_seed(env,seed = 1):
    ''' omnipotent seed for RL, attention the position of seed function, you'd better put it just following the env create function
    Args:
        env (_type_): 
        seed (int, optional): _description_. Defaults to 1.
    '''
    print(f"seed = {seed}")
    env.seed(seed) # env config
    np.random.seed(seed)
    random.seed(seed)
    paddle.seed(seed)
    
def env_agent_config(cfg):
    ''' create env and agent
    '''
    env = gym.make(cfg['env_name']) 
    if cfg['seed'] !=0: # set random seed
        all_seed(env,seed=cfg["seed"]) 
    n_states = env.observation_space.shape[0] # print(hasattr(env.observation_space, 'n'))
    n_actions = env.action_space.n  # action dimension
    print(f"n_states: {n_states}, n_actions: {n_actions}")
    cfg.update({"n_states":n_states,"n_actions":n_actions}) # update to cfg paramters
    model = MLP(n_states,n_actions)
    algo = DQN(model, gamma=cfg['gamma'], lr=cfg['lr'])
    memory =  ReplayBuffer(cfg["memory_capacity"]) # replay buffer
    agent = DQNAgent(algo,memory,cfg)  # create agent
    return env, agent

4、设置参数

到这里所有qlearning模块就算完成了,下面需要设置一些参数,方便大家“炼丹”,其中默认的是笔者已经调好的~。另外为了定义了一个画图函数,用来描述奖励的变化。

import argparse
import seaborn as sns
import matplotlib.pyplot as plt
def get_args():
    """ 
    """
    parser = argparse.ArgumentParser(description="hyperparameters")      
    parser.add_argument('--algo_name',default='DQN',type=str,help="name of algorithm")
    parser.add_argument('--env_name',default='CartPole-v0',type=str,help="name of environment")
    parser.add_argument('--train_eps',default=200,type=int,help="episodes of training") # 训练的回合数
    parser.add_argument('--test_eps',default=20,type=int,help="episodes of testing") # 测试的回合数
    parser.add_argument('--ep_max_steps',default = 100000,type=int,help="steps per episode, much larger value can simulate infinite steps")
    parser.add_argument('--gamma',default=0.99,type=float,help="discounted factor") # 折扣因子
    parser.add_argument('--epsilon_start',default=0.95,type=float,help="initial value of epsilon") #  e-greedy策略中初始epsilon
    parser.add_argument('--epsilon_end',default=0.01,type=float,help="final value of epsilon") # e-greedy策略中的终止epsilon
    parser.add_argument('--epsilon_decay',default=200,type=int,help="decay rate of epsilon") # e-greedy策略中epsilon的衰减率
    parser.add_argument('--memory_capacity',default=200000,type=int) # replay memory的容量
    parser.add_argument('--memory_warmup_size',default=200,type=int) # replay memory的预热容量
    parser.add_argument('--batch_size',default=64,type=int,help="batch size of training") # 训练时每次使用的样本数
    parser.add_argument('--targe_update_fre',default=200,type=int,help="frequency of target network update") # target network更新频率
    parser.add_argument('--seed',default=10,type=int,help="seed") 
    parser.add_argument('--lr',default=0.0001,type=float,help="learning rate")
    parser.add_argument('--device',default='cpu',type=str,help="cpu or gpu")  
    args = parser.parse_args([])                
    args = {**vars(args)}  # type(dict)         
    return args
def smooth(data, weight=0.9):  
    '''用于平滑曲线,类似于Tensorboard中的smooth

    Args:
        data (List):输入数据
        weight (Float): 平滑权重,处于0-1之间,数值越高说明越平滑,一般取0.9

    Returns:
        smoothed (List): 平滑后的数据
    '''
    last = data[0]  # First value in the plot (first timestep)
    smoothed = list()
    for point in data:
        smoothed_val = last * weight + (1 - weight) * point  # 计算平滑值
        smoothed.append(smoothed_val)                    
        last = smoothed_val                                
    return smoothed

def plot_rewards(rewards,cfg,path=None,tag='train'):
    sns.set()
    plt.figure()  # 创建一个图形实例,方便同时多画几个图
    plt.title(f"{tag}ing curve on {cfg['device']} of {cfg['algo_name']} for {cfg['env_name']}")
    plt.xlabel('epsiodes')
    plt.plot(rewards, label='rewards')
    plt.plot(smooth(rewards), label='smoothed')
    plt.legend()

5、训练

# 获取参数
cfg = get_args() 
# 训练
env, agent = env_agent_config(cfg)
res_dic = train(cfg, env, agent)
 
plot_rewards(res_dic['rewards'], cfg, tag="train")  
# 测试
res_dic = test(cfg, env, agent)
plot_rewards(res_dic['rewards'], cfg, tag="test")  # 画出结果
seed = 10
n_states: 4, n_actions: 2
开始训练!
环境:CartPole-v0,算法:DQN,设备:cpu
回合:10/200,奖励:10.00,Epislon: 0.062
回合:20/200,奖励:85.00,Epislon: 0.014
回合:30/200,奖励:41.00,Epislon: 0.011
回合:40/200,奖励:31.00,Epislon: 0.010
回合:50/200,奖励:22.00,Epislon: 0.010
回合:60/200,奖励:10.00,Epislon: 0.010
回合:70/200,奖励:10.00,Epislon: 0.010
回合:80/200,奖励:22.00,Epislon: 0.010
回合:90/200,奖励:30.00,Epislon: 0.010
回合:100/200,奖励:20.00,Epislon: 0.010
回合:110/200,奖励:15.00,Epislon: 0.010
回合:120/200,奖励:45.00,Epislon: 0.010
回合:130/200,奖励:73.00,Epislon: 0.010
回合:140/200,奖励:180.00,Epislon: 0.010
回合:150/200,奖励:167.00,Epislon: 0.010
回合:160/200,奖励:200.00,Epislon: 0.010
回合:170/200,奖励:165.00,Epislon: 0.010
回合:180/200,奖励:200.00,Epislon: 0.010
回合:190/200,奖励:200.00,Epislon: 0.010

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/678907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis 原理

Redis 原理 动态字符串SDS Redis中保存的key时字符串&#xff0c;value往往是字符串或字符串集合&#xff0c;字符串是Redis中常见的数据结构 Redis没有直接使用C语言中的字符串&#xff0c;因为C语言字符串存在很多问题&#xff0c;使用起来不方便 Redis构建了一种新型的字…

Web网页制作-知识点(3)——HTML5新增标签、CSS简介、CSS的引入方式、选择器、字体属性、背景属性、表格属性、关系选择器

目录 HTML5新增标签 CSS简介 CSS概念 CSS的作用 语法 CSS的引入方式 内联样式&#xff08;行内样式&#xff09; 内部样式 外部样式&#xff08;推荐&#xff09; 选择器 全局选择器 元素选择器 类选择器 ID选择器 合并选择器 选择器的优先级 字体属性 …

Linux——文件基础IO的文件描述符和重定向实现理解

目录 前言&#xff1a; 首先来回顾一下open函数&#xff0c;即在进程中同时打开多个文件&#xff1a; Linux底层进程与文件的关系 &#xff1a; 二.重定向的实现 什么是重定向&#xff1f; 方法1&#xff1a; 2.1关闭stdin&#xff1a; 运行结果&#xff1a; ​编辑由结果知…

统计字符串数组中各元素中指定字符串出现的次数numpy.char.count()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 统计字符串数组中各元素中 指定字符串出现的次数 numpy.char.count() [太阳]选择题 下列代码最后输出的结果是&#xff1f; import numpy as np s np.array([I, Love, Python]) print("…

ChatGPT底层架构Transformer技术及源码实现(二)

ChatGPT底层架构Transformer技术及源码实现(二) Gavin大咖微信:NLP_Matrix_Space 3.2 图解Transformer精髓之架构设计、数据训练时候全生命周期、数据在推理中的全生命周期、矩阵运算、多头注意力机制可视化等 如图3-14所示,是Transformer编解码的示意图,中间有个关键内…

LFS11.3在VMware中安装后需要做的准备

参考lfs 11.3和Blfs 11.3 先简单罗列一下要做的步骤&#xff0c;后续有机会再补充一下细节&#xff0c;遇到问题欢迎读者留言。 1、配置vmware中的网络连接 使用vmware net8 net模式&#xff0c;选用VMnet 配置网络连接/etc/sysconfig/ 目录下ifconfig.*** &#xff08;***为…

fanuc机器人安装profinet IO基板产生报警

fanuc机器人安装profinet IO基板产生报警: SYST-302 请关闭电源 PRIO-397 PMIO 固件需要更新 %x %x 问题描述:新的R30iB‐Plus柜的GSDML 文件与R30iB柜的GSDML文件是不同的,GSDML文件与R834固件版本不匹配的话,会无法扫描到R834的卡,导致无法通讯 解决方法:确认 Expecte…

Diffusion Models: 方法和应用的综合调查 【01】Diffusion Models基础

Diffusion Models: 方法和应用的综合调查 【01】Diffusion Models基础 原文链接&#xff1a;Diffusion Models: 方法和应用的综合调查 【01】Diffusion Models基础 GitHub: https://github.com/YangLing0818/Diffusion-Models-Papers-Survey-Taxonomy. Paper&#xff1a; https…

MySQL学习基础篇(一)

一、数据库概述 1. 为什么要使用数据库 持久化(persistence)&#xff1a;把数据保存到可掉电式存储设备中以供之后使用。大多数情况下&#xff0c;特别是企业级应用&#xff0c;数据持久化意味着将内存中的数据保存到硬盘上加以”固化”&#xff0c;而持久化的实现过程大多通…

程序员编程效率的大敌:中断与上下文切换

程序员编程效率的大敌&#xff1a;中断与上下文切换 首先解释一下中断和上下文切换&#xff1a; 中断: 编程时被打断, 比如被聊天软件/电子邮件/电话/当面打断等&#xff1b;上下文切换&#xff1a;即任务的切换&#xff0c;有自己主动切换&#xff0c;有伴随中断的新任务&am…

C# 静态构造函数学习

静态构造函数用于初始化类中的静态数据或执行仅需一次的特定操作&#xff0c;静态构造函数将在创建第一个实例或引用类中的静态成员之前自动调用。 静态构造函数具有以下特点&#xff1a; 静态构造函数不使用访问权限修饰符修饰或不具有参数&#xff1b; 类或结构体中…

Proxmox VE 8 发布 - 开源虚拟化管理平台

Proxmox VE 8 发布 - 开源虚拟化管理平台 请访问原文链接&#xff1a;https://sysin.org/blog/proxmox-ve-8/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 宣布 Proxmox 虚拟环境的主要版本 8.0&#xff01;它基于出色的 De…

SkyWalking--用代码手动获取traceId的方法

原文网址&#xff1a;SkyWalking--用代码手动获取traceId的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java项目如何用代码手动获取SkyWalking的traceId。 引入依赖 <dependency><groupId>org.apache.skywalking</groupId><artifactId>apm-tool…

【PCB专题】如何使用Assign color在 Allegro 中快速区别不同网络?

在PCB Layout中经常要查看网络走线,比如电源路径是否合理,线宽是否合适,网络是否形成环路等等。一般我们使用的是高亮网络来查看。 困扰 如果是单一网络这样做是没有什么问题的,但如果是多条网络,就一种颜色会很难看清。就算不同的网络是不同的条纹,在布线比较密集的时…

JavaScript 手写代码 第三期

文章目录 1. 为什么要手写代码&#xff1f;2. 手写代码2.1 函数柯里化2.1.1 基本使用2.1.2 手写实现 2.2 sleep函数2.2.1 简单使用2.2.2 手写实现 2.3 Object.assign() 方法2.3.1 基本使用2.3.2 具体示例2.3.3 具体思路2.3.4 具体实现 1. 为什么要手写代码&#xff1f; 我们在…

ChatGPT底层架构Transformer技术及源码实现(三)

ChatGPT底层架构Transformer技术及源码实现(三) 贝叶斯Bayesian Transformer数学推导论证过程全生命周期详解及底层神经网络物理机制剖析 Gavin大咖微信:NLP_Matrix_Space 从数学的角度来讲,线性转换 其中函数g联合了所有头的操作结果,每个头的产生是采用一个f_att的…

RedHat红帽认证---RHCE

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; RHCE 1.安装和配置 Ansible 安装和配置 Ansible按照下方所述&#xff0c;在控制节点 control 上安装和配置 Ansible&#xff1a;安装所需的软件包创建名为 /home/gre…

认识区块链

文章目录 前言从交易说起线下交易&线上交易存在的隐患线上交易隐患引发的思考 货币发展史解决线上交易存在的隐患比特币的诞生比特币价值的产生 比特币&区块链 前言 我想大多数的 IT 人&#xff0c;即便不是 IT 人&#xff0c;或多说少都听说过“比特币”“区块链”这…

InceptionNext:当Inception遇到ConvNeXt

摘要 https://arxiv.org/pdf/2303.16900.pdf 受 Vision Transformer 长距离依赖关系建模能力的启发&#xff0c;大核卷积最近被广泛研究和采用&#xff0c;以扩大感受野和提高模型性能&#xff0c;如采用77深度卷积的杰出工作connext。虽然这种深度算子只消耗少量的flop&…