【深度强化学习】(3) Policy Gradients 模型解析,附Pytorch完整代码

news2024/11/28 11:57:50

大家好,今天和各位分享一下基于策略的深度强化学习方法,策略梯度法是对策略进行建模,然后通过梯度上升更新策略网络的参数。我们使用了 OpenAI 的 gym 库,基于策略梯度法完成了一个小游戏。完整代码可以从我的 GitHub 中获得:

https://github.com/LiSir-HIT/Reinforcement-Learning/tree/main/Model


1. 基于策略的深度强化学习

针对智能体在大规模离散动作下无法建模的难题,在基于值函数的深度强化学习中,利用神经网络对 Q 值函数近似估计,使深度学习与强化学习得到完美融合。

但是基于值函数的深度强化学习有一定的不足之处:

(1) 无法处理连续动作的任务。DQN 系列的算法可以较好地解决强化学习中大规模离散动作空间的任务,但在连续动作的任务中,难以实现利用深度神经网络对所有状态-动作的 Q 值函数近似表达。

(2) 无法处理环境中状态受到限制的问题。在基于值函数深度强化学习更新网络参数时,损失函数会依赖当前状态和下一个状态的值函数,当智能体在环境中观察的状态有限或建模受到限制时,就会导致实际环境中两个不同的状态有相同的价值函数进而导致损失函数为零,出现梯度消失的问题。

(3) 智能体在环境中的探索性能较低。基于值函数的深度强化学习方法中,目标值都是从动作空间中选取一个最大价值的动作,导致智能体训练后的策略具有确定性,而面对一些需要随机策略进行探索的问题时,该方法就无法较好地解决。 

由于基于值函数的深度强化学习存在上述的一些局限性,需要新的方法来解决这些问题,于是基于策略的深度强化学习被提出。该方法中将智能体当前的策略参数化,并且使用梯度的方法进行更新。 


2. 策略梯度法

强化学习中策略梯度算法是对策略进行建模,然后通过梯度上升更新策略网络的参数。Policy Gradients 中无法使用策略的误差来构建损失函数,因为参数更新的目标是最大化累积奖励的期望值,所以策略更新的依据是某一动作对累积奖励的影响,即增加使累积回报变大的动作的概率,减弱使累积回报变小的动作的概率。 

下图代表智能体在当前策略下,完成一个回合后构成的状态、动作序列 r=\left \{ s_1,a_1,s_2,a_2,s_2,...s_T,a_T \right \},其中,Actor 是策略网络每个回合结束后的累计回报为每个状态下采取的动作的奖励之和

R = \sum_{t=1}^{T}r_t

智能体在环境中执行策略 \pi_ \theta 后状态转移概率

P_{\theta }(\tau )=P(s_1)P(r_1,s_2|s_1,a_1)\cdots P_\theta (a_T,s_T)P(r_{T+1},s_T|s_T,a_T)

P_{\theta }(\tau ) = \prod_{t=1}^{T} ( P_\theta (a_t|s_t) P(r_t,s_{t+1} | s_t,a_t) )

回合累计回报的期望

\bar{R}_\theta = \sum _{\tau } R(\tau ) P_\theta (\tau ) = E_{\tau \sim P_\theta (\tau )} [R(\tau )]

通过微分公式可以得到累计回报的梯度为:

\bigtriangledown \bar{R} _{\theta } = \sum_{\tau} R(\tau ) P_{\theta }(\tau) \bigtriangledown log P_\theta (\tau ) \approx \frac{1}{N} \sum_{n=1}^{N}\sum_{t=1}^{T} R(\tau ^n) \bigtriangledown log P_\theta (a_t^n | s_t^n)

利用累计回报的梯度更新策略网络的参数:

\theta ^ {new} \leftarrow \theta ^{old} + \beta \bigtriangledown \bar{R}_{\theta ^{old}}

其中, \beta 为梯度系数。通过上式的策略迭代可得,如果智能体在某个状态下采取的动作使累积回报增加,网络参数就会呈梯度上升趋势,该动作的概率就会增加,反之,梯度为下降趋势,减小该动作的概率。为了防止环境中所有的奖励都是正值,实现对于一些不好动作有一个负反馈,可以在总回报处减去一个基线。


3. 代码实现

策略函数 \pi (a|s) 是一个概率密度函数,用于控制智能体的运动。\pi (a|s)输入状态 s,输出每个动作 a 的概率分布。策略网络是指通过训练一个神经网络来近似策略函数。策略网络的参数 \theta 可通过策略梯度算法进行更新,从而实现对策略网络 \pi (a|s;\theta ) 的训练。 

伪代码如下

模型构建部分代码如下:

# 基于策略的学习方法,用于数值连续的问题
import numpy as np
import torch
from torch import nn
from torch.nn import functional as F

# ----------------------------------------------------- #
#(1)构建训练网络
# ----------------------------------------------------- #
class Net(nn.Module):
    def __init__(self, n_states, n_hiddens, n_actions):
        super(Net, self).__init__()
        # 只有一层隐含层的网络
        self.fc1 = nn.Linear(n_states, n_hiddens)
        self.fc2 = nn.Linear(n_hiddens, n_actions)
    # 前向传播
    def forward(self, x):
        x = self.fc1(x)  # [b, states]==>[b, n_hiddens]
        x = F.relu(x)
        x = self.fc2(x)  # [b, n_hiddens]==>[b, n_actions]
        # 对batch中的每一行样本计算softmax,q值越大,概率越大
        x = F.softmax(x, dim=1)  # [b, n_actions]==>[b, n_actions]
        return x

# ----------------------------------------------------- #
#(2)强化学习模型
# ----------------------------------------------------- #
class PolicyGradient:
    def __init__(self, n_states, n_hiddens, n_actions, 
                 learning_rate, gamma):
        # 属性分配
        self.n_states = n_states  # 状态数
        self.n_hiddens = n_hiddens
        self.n_actions = n_actions  # 动作数
        self.learning_rate = learning_rate  # 衰减
        self.gamma = gamma  # 折扣因子
        self._build_net()  # 构建网络模型

    # 网络构建
    def _build_net(self):
        # 网络实例化
        self.policy_net = Net(self.n_states, self.n_hiddens, self.n_actions)
        # 优化器
        self.optimizer = torch.optim.Adam(self.policy_net.parameters(), lr=self.learning_rate)

    # 动作选择,根据概率分布随机采样
    def take_action(self, state):  # 传入某个人的状态
        # numpy[n_states]-->[1,n_states]-->tensor
        state = torch.Tensor(state[np.newaxis, :])
        # 获取每个人的各动作对应的概率[1,n_states]-->[1,n_actions]
        probs = self.policy_net(state)
        # 创建以probs为标准类型的数据分布
        action_dist = torch.distributions.Categorical(probs)
        # 以该概率分布随机抽样 [1,n_actions]-->[1] 每个状态取一组动作
        action = action_dist.sample()
        # 将tensor数据变成一个数 int
        action = action.item()
        return action

    # 获取每个状态最大的state_value
    def max_q_value(self, state):
        # 维度变换[n_states]-->[1,n_states]
        state = torch.tensor(state, dtype=torch.float).view(1,-1)
        # 获取状态对应的每个动作的reward的最大值 [1,n_states]-->[1,n_actions]-->[1]-->float
        max_q = self.policy_net(state).max().item()
        return max_q

    # 训练模型
    def learn(self, transitions_dict):  # 输入batch组状态[b,n_states]
        # 取出该回合中所有的链信息
        state_list = transitions_dict['states']
        action_list = transitions_dict['actions']
        reward_list = transitions_dict['rewards']

        G = 0  # 记录该条链的return
        self.optimizer.zero_grad()  # 优化器清0
        # 梯度上升最大化目标函数
        for i in reversed(range(len(reward_list))):
            # 获取每一步的reward, float
            reward = reward_list[i]
            # 获取每一步的状态 [n_states]-->[1,n_states]
            state = torch.tensor(state_list[i], dtype=torch.float).view(1,-1)
            # 获取每一步的动作 [1]-->[1,1]
            action = torch.tensor(action_list[i]).view(1,-1)
            # 当前状态下的各个动作价值函数 [1,2]
            q_value = self.policy_net(state)
            # 获取已action对应的概率 [1,1]
            log_prob = torch.log(q_value.gather(1, action))
            # 计算当前状态的state_value = 及时奖励 + 下一时刻的state_value
            G = reward + self.gamma * G
            # 计算每一步的损失函数
            loss = -log_prob * G
            # 反向传播
            loss.backward()
        # 梯度下降
        self.optimizer.step()

4. 实例演示

下面基于OpenAI 中的 gym 库,完成一个移动小车使得杆子竖直的游戏。状态states共包含 4 个,动作action有2个,向左和向右移动小车。迭代50回合,绘制每回合的 return 以及平均最大动作价值 q_max

代码如下:

import gym
import numpy as np
import matplotlib.pyplot as plt
from RL_brain import PolicyGradient

# ------------------------------- #
# 模型参数设置
# ------------------------------- #

n_hiddens = 16  # 隐含层个数
learning_rate = 2e-3  # 学习率
gamma = 0.9  # 折扣因子
return_list = []  # 保存每回合的reward
max_q_value = 0  # 初始的动作价值函数
max_q_value_list = []  # 保存每一step的动作价值函数

# ------------------------------- #
#(1)加载环境
# ------------------------------- #

# 连续性动作
env = gym.make("CartPole-v1", render_mode="human")
n_states = env.observation_space.shape[0]  # 状态数 4
n_actions = env.action_space.n  # 动作数 2

# ------------------------------- #
#(2)模型实例化
# ------------------------------- #

agent = PolicyGradient(n_states=n_states,  # 4
                       n_hiddens=n_hiddens,  # 16
                       n_actions=n_actions,  # 2
                       learning_rate=learning_rate,  # 学习率
                       gamma=gamma)  # 折扣因子

# ------------------------------- #
#(3)训练
# ------------------------------- #

for i in range(100):  # 训练10回合
    # 记录每个回合的return
    episode_return = 0
    # 存放状态
    transition_dict = {
        'states': [],
        'actions': [],
        'next_states': [],
        'rewards': [],
        'dones': [],
    }
    # 获取初始状态
    state = env.reset()[0]
    # 结束的标记
    done = False

    # 开始迭代
    while not done:
        # 动作选择
        action = agent.take_action(state)  # 对某一状态采取动作
        # 动作价值函数,曲线平滑
        max_q_value = agent.max_q_value(state) * 0.005 + max_q_value * 0.995
        # 保存每一step的动作价值函数
        max_q_value_list.append(max_q_value)
        # 环境更新
        next_state, reward, done, _, _ = env.step(action)
        # 保存每个回合的所有信息
        transition_dict['states'].append(state)
        transition_dict['actions'].append(action)
        transition_dict['next_states'].append(next_state)
        transition_dict['rewards'].append(reward)
        transition_dict['dones'].append(done)
        # 状态更新
        state = next_state
        # 记录每个回合的return
        episode_return += reward

    # 保存每个回合的return
    return_list.append(episode_return)
    # 一整个回合走完了再训练模型
    agent.learn(transition_dict)

    # 打印回合信息
    print(f'iter:{i}, return:{np.mean(return_list[-10:])}')

# 关闭动画
env.close()
        
# -------------------------------------- #
# 绘图
# -------------------------------------- #

plt.subplot(121)
plt.plot(return_list)
plt.title('return')
plt.subplot(122)
plt.plot(max_q_value_list)
plt.title('max_q_value')
plt.show()

左图代表每回合的return,右图为平均最大动作价值

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

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

相关文章

原型模式(设计模式详解)

原型模式 描述 原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需从头开始编写代码。 在原型模式中,一个原型对象作为模板,新的对象通过克隆这个原型对象而创…

MySQL OCP888题解048-letter N in slow query log(慢查询日志里的字母N)

文章目录1、原题1.1、英文原题1.2、中文翻译1.3、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3.1、知识点1:mysqldumpslow - 总结缓慢的查询日志文件4、实验4.1、实验14.1.1、实验目的4.1.2、实验前准备4.1.3、实验步骤4.1.4、实验结论5、总结1、原题 1.1…

dva01-初识

背景 React 本身只是一个 DOM 的抽象层,使用组件构建虚拟 DOM。如果开发大应用,还需要解决一个问题。 通信:React 只提供了一种传参手段,后续数据变化非常麻烦,无法适用于大应用。数据流:每次要更新数据&…

ACE C++网络通信框架深入解析ACE_Message_Block消息类

前言 我所见到最好消息包的接口设计莫过于ACE_Message_Block了。 为什么这样说呢? 对于它的API说明,我最初仅想在它的基础上提供注释说明,而不想多言其它,因为无需多言其他。 不过,后来还是补充两个图,以…

后台搭建常用方式及常用的插件

利用脚手架创建项目 create-vite 是一个快速生成主流框架基础模板的工具,安装后启动预设创建项目 使用NPM: npm create vitelatest 使用Yarn: yarn create vite 使用PNPM: pnpm create vite 开始 | Vite 官方中文文档 create-vue,是 Vue 官方的项目脚…

Flume工作原理 安装配置

目录 简介 主要功能 日志收集 数据处理 工作原理 Flume架构 安装 拷贝压缩包 解压 改名 修改配置文件 安装nc(netcat) 安装telnet协议 应用 应用一:实时监听 新建netcat-logger.conf文件 开启端口监听方式一 访问主机 开启…

flume安装与配置

目录 flume描述 flume用途 flume基本组件 配置flume 搭建环境: 解压flume安装包将其放入到opt/soft目录 在/opt/soft目录下将apache-flume-1.9.0-bin.tar.gz 改名为flume190 到/opt/soft/flume190/conf目录中将临时配置文件flume-env.sh.template拷贝为配置文…

香港酒店模拟分析项目报告--使用tableau、python、matlab

转载请标记本文出处 软件:tableau、pycharm、关系型数据库:MySQL 数据大量分析考虑电脑性能的情况。 文章目录前言一、爬虫是什么?二、使用tableau数据可视化1.引入数据1.1 制作直方图-各地区酒店数量条形图1.2 各地区酒店均价1.3 价格等级堆…

Idea+maven+spring-cloud项目搭建系列--11-3 dubbo限流和熔断

前言: dubbo 作为rpc 通信组件,在使用过程中,如何避免服务提供端被多个消费端撑爆,消费端如何避免因为服务端的故障造成结果响应超时。 1 服务提供端的限流措施: 1.1 使用 :dubbo.protocol.accepts 参数限制服务端同…

面试热点题:回溯算法之组合 组合与组合总和 III

什么是回溯算法? 回溯算法也可以叫回溯搜索算法,回溯是递归的"副产品",回溯的本质是穷举,然后选出我们需要的数据,回溯本身不是特别高效的算法,但我们可以通过"剪枝"来优化它。 理解回溯算法 回溯…

电脑游戏怎么录屏?其实很简单,只需要简单3步

电脑游戏一直是游戏爱好者最热衷的游戏之一。但是,有时候我们想分享我们在游戏中的精彩时刻,或者记录我们的游戏过程以便后续观看和学习。在这种情况下,录屏就成了必不可少的工具。但是,许多人可能不知道电脑游戏怎么录屏。在本文…

逆向分析——壳

你脑海中的壳是什么 壳在自然界是动物的保护壳,软件同样有保护壳,为了防止破解 也许大海给贝壳下的定义是珍珠,也许时间给煤炭下的定义是钻石 ——沙与沫 壳的由来 在DOS时代,壳一般指的是磁盘加密软件中的一段加密程序 后来发展…

APM新添加UAVCAN设备

简介 UAVCAN是一种轻量级协议,旨在通过CAN总线在航空航天和机器人应用中实现可靠通信。要实现通信,最基本需要data_type_ id, signature、数据结构、设备程序初始化。 添加设备数据结构文件(.uavcan格式) 1.在以下路径添加设备数据结构文件,根据设备类…

三体到底是啥?用Python跑一遍就明白了

文章目录拉格朗日方程推导方程组微分方程算法化求解画图动图绘制温馨提示,只想看图的画直接跳到最后一节拉格朗日方程 此前所做的一切三体和太阳系的动画,都是基于牛顿力学的,而且直接对微分进行差分化,从而精度非常感人&#xf…

Web漏洞-CSRF漏洞

CSRF漏洞介绍:CSRF(Cross-Site Request Forgery),中文名称:跨站请求伪造,是一种劫持用户在当前已登录的Web应用程序上执行非本意操作一种攻击.原理:攻击者利用目标用户的身份,执行某…

基于Stackelberg博弈的光伏用户群优化定价模型(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

keras学习之回调函数的使用

回调函数 回调函数是一个对象(实现了特定方法的类实例),它在调用fit()时被传入模型,并在训练过程中的不同时间点被模型调用可以访问关于模型状态与模型性能的所有可用数据模型检查点(model checkpointing)…

【SAP PO】X-DOC:SAP PO 接口配置 REST 服务对接填坑记

X-DOC:SAP PO 接口配置 REST 服务对接填坑记1、背景2、PO SLD配置3、PO https证书导入1、背景 (1)需求背景: SAP中BOM频繁变更,技术人员在对BOM进行变更后,希望及时通知到相关使用人员 (2&…

配天智造自主原创数字工厂:百余名员工人均创收122万

配天智造(832223)2022年度报告显示,报告期内公司实现营业收入1.3亿元,同比增长52%,归属于挂牌公司股东的净利润3867万元,同比增长28.11%。而这家公司全部在职员工仅有107人,人均创收约为122万。…

计算机科学导论笔记(七)

目录 九、程序设计语言 9.1 演化 9.1.1 机器语言 9.1.2 汇编语言 9.1.3 高级语言 9.2 翻译 9.2.1 编译 9.2.2 解释 9.2.3 翻译过程 9.3 编程模式 9.3.1 面向过程模式 9.3.2 面向对象模式 9.3.3 函数式模式 9.3.4 声明式模式 9.4 共同概念 九、程序设计语言 9.1 …