莫烦Python代码实践(五)——REINFORCE基础算法工程化解析

更新时间:2023-06-02 19:17:05 阅读: 评论:0

莫烦Python代码实践(五)——REINFORCE基础算法⼯程化
解析
提⽰:转载请注明出处,若⽂本⽆意侵犯到您的合法权益,请及时与作者联系。
莫烦Python代码实践(五)——REINFORCE基础算法⼯程化解析
声明
本⽂是作者学习莫烦Python的代码笔记总结,如想深⼊可移步。
⼀、REINFORCE算法与DQN算法的区别
REINFORCE是基于Policy的RL算法中最经典、最基础的算法,可以说是学习基于基于Policy的RL算法的⼊门必备。
与作为基于Value的RL算法⼊门必备的DQN算法相⽐,两个算法的主要区别如下:
类别DQN算法REINFORCE
算法种类value-bad Policy-bad,
更新机制单步更新回合更新
拟合策略函数,输出在某个状态选择某个动作的概率函数拟合拟合Q函数,输出在某个状态选择某个动作可以得到的预期
价值
损失函数q_target和q_eval的均⽅误差预期概率分布和⽬标概率分布的交叉熵损失
经验使⽤⽤来采样计算q_target,以训练⽹络⽤来计算各个状态的长期回报G等,以训练⽹络
注:可能⼀般意义上并不认为REINFORCE存在经验回放机制,这⾥我们将存储过去的状态信息的机制都⼀律称为经验池。
关于⽹络的区别可以参考下图:
REINFORCE算法的主要流程如下:
同样上述流程相对抽象,我们使⽤基于⾯向对象思想详细说明下如上流程:
在将上述算法流程进⾏⼯程化的过程中,主要可以分为以下⼏个阶段:
1. 算法的整体流程
2. 经验池的初始化与清空
3. 初始化策略⽹络
方案英语
4. 运⾏策略⽹络选择动作
5. 学习并训练策略⽹络
接下来我们就依次描述这5个阶段,讲解如何实现上述算法的⼀些细节。
3、REINFORCE的代码实现
1、算法的整体流程
现在假设我们在Gym中的CartPole-v0环境中测试该算法代码,并且我们已经搭建了Agent_REINFORCE类,那么我们的主要流程初始化代码如下:
# 初始化环境
env = gym.make('CartPole-v0')
env = env.unwrapped
env.ed(1)
# 初始化agent
STATE_FEATURES = env.obrvation_space.shape[0]
ACTION_SPACE = env.action_space.n
agent = Agent_REINFORCE(
n_features = STATE_FEATURES,
n_actions = ACTION_SPACE,
learning_rate=0.02,
中国近代史简介
reward_decay=0.99,
小素
output_graph=Fal,
)
# 进⾏仿真训练
train(agent, num_episode=250)
train()函数中记录了整个算法的运⾏流程,这⾥并不复杂,对照上述算法流程即可理解:
def train(agent,num_episode=3000,is_Render = Fal):
for i in range(num_episode):
running_reward = 0 # 运⾏的回合奖励
obrvation = () # 初始化环境状态
while True:
if is_Render: der() # 是否渲染环境,渲染会变慢
action = agent.choo_action(obrvation) # 选择动作
obrvation_, reward, done, info = env.step(action)
感人文章agent.store_in_memory(obrvation, action, reward)
obrvation = obrvation_ # 切换观察值
if done:
vt = agent.learn()  # 该训练回合结束时才开始学习
running_reward = sum(agent.ep_rs)#获取本回合累计奖励
# 打印信息
print("episode:",i, "reward:",int(running_reward))
break
接下来我们详细介绍上述每个关键细节的实现部分。
2、经验池的初始化与清空
在REINFORCE中的经验池每次存储的都是本回合各个step的(s,a,r),然后在每回合结束时,⼀下⼦将本回合的所有(s,a,r)输⼊策略⽹络进⾏learn,learn之后再清空经验池来存储下回合的信息,这种特点就是回合更新。这和我们之前实现DQN时每个step进⾏learn有很⼤区分。因为在learn时,我们是分别输⼊s,a,r,⽽经验池存在存⼊和清空两个操作,所以该算法中的经验池机制实现⾮常简单,使⽤以下三个列表即可实现:
def store_in_memory(lf, s, a, r):
lf.ep_obs.append(s)
lf.ep_as.append(a)
lf.ep_rs.append(r)
def __clear_memory(lf):
lf.ep_obs, lf.ep_as, lf.ep_rs = [], [], []
工作学习总结3、初始化策略⽹络
搭建策略⽹络是该算法中相对较难的⼀点。因为我们的RL任务⽐较简单,我们这⾥使⽤两层全连接⽹络搭建,为了让代码减少全连接层的细节影响,我们直接使⽤封装得tf.layer来搭建这个策略⽹络。
⾸先我们来看下这个⽹络的简单结构说明:
上图中的“?”表⽰这个数不确定,其是每回合训练的最⼤step数。
策略⽹络的输⼊是⼀个回合的所有状态序列(本质上就是⼀个shape=(?,n_features)的⼆维数组),输出是预测的动作的概率分布(本质上就是⼀个shape=(?,n_actions)的⼆维数组),为了训练这个⽹络,
我们搭建的loss需要我们输⼊⼀个回合的所有动作序列(本质上就是⼀个shape=(?)的⼀维数组)和所有累计回报序列(本质上就是⼀个shape=(?)的⼀维数组)。
这⾥需要说明的是,假如我们输⼊⼀个状态值,这个状态值具备n_features=3个特征信息的,那么s=[0.5,0.6,0.7],这时训练较好的策略⽹络的输出应该是⼀个具备n_actions=3个动作个数的[0.2,0.1,0.7],这表⽰agent选择第1个动作的概率为0.2,第2个动作的概率为0.1,第三个动作的概率为0.7。
在训练时,我们并不会⼀个⼀个输⼊状态值,这样训练效率较低,我们⼀下⼦输⼊整个回合的所有状态值,所以此时的输⼊输出都变成了⼆维,本质没变。
现在我们先定义计算图的三个变量输⼊:
with tf.name_scope('inputs'):
lf.tf_obs = tf.placeholder(tf.float32, [None, lf.n_features], name="obrvations") # 输⼊状态序列
lf.tf_acts = tf.placeholder(tf.int32, [None, ], name="actions") # 输⼊动作序列
lf.tf_vt = tf.placeholder(tf.float32, [None, ], name="reward") # 输⼊累计回报序列
接着搭建⽹络结构:
# fc1 ⽣成⼀个全连接层
layer = tf.layers.den(
inputs=lf.tf_obs, # 输⼊数据
units=10, # 神经元数量
anh,  # tanh 激活函数
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),# 权重矩阵的初始化器
bias_stant_initializer(0.1),# 偏置项的初始化器
name='fc1'
)
# fc2 ⽣成⼀个全连接层
all_act = tf.layers.den(
拆迁办inputs=layer,
units=lf.n_actions,
activation=None,
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
bias_stant_initializer(0.1),
name='fc2'
)
lf.all_act_prob = tf.nn.softmax(all_act, name='act_prob')
接着定义损失计算:
with tf.name_scope('loss'):
neg_log_prob = tf.reduce_sum(-tf.log(lf.all_act_prob)*tf.one_hot(lf.tf_acts, lf.n_actions), axis=1)# 求和
loss = tf.reduce_mean(neg_log_prob * lf.tf_vt)  #求平均值
with tf.name_scope('train'):
下⾯我们解释下上述损失函数的设计,我们这⾥使⽤的是交叉熵损失函数:
其中p(x)是我们的真实分布,q(x)是我们的预测分布,接下来我们再来看这⾏代码:
neg_log_prob = tf.reduce_sum(-tf.log(lf.all_act_prob)*tf.one_hot(lf.tf_acts, lf.n_actions), axis=1)
在上述代码中-tf.log()容易理解,求对数的负数,也符合我们的公式,lf.all_act_prob就是我们策略⽹路的输出,其shape=
镇江镇(?,n_actions),即⽹络预测分布。
lf.tf_acts是我们输⼊的动作序列,其shape=(?),这样⼦的形式是⽆法直接使⽤的,所以我们使⽤tf.one_hot(lf.tf_acts,
lf.n_actions)
将其变为one-hot编码,此时shape=(?,n_actions)。
孕妇能吃汉堡包吗tf.reduce_sum()是⼀个给⾼维数组制定axis进⾏求和的函数,我们使⽤axis=1指明我们仅对数组的第⼆维进⾏求和,因为我们输⼊的都是⼆维数组,第⼀维的数字表⽰有多个这样的序列⽽已。这样⼦就使⽤了上述的损失函数公式的代码实现。
需要注意到,我们之后⼜进⾏了loss = tf.reduce_mean(neg_log_prob * lf.tf_vt),这是因为我们现在计算到的neg_log_prob仍然是⼀个⼆维数组,我们要先将其与lf.tf_vt相乘,这样⼦就相当于给每个step的损失值加了⼀个权重,权重越⼤的地⽅越需要我们进⾏训
练,tf.reduce_mean()求其平均值,降为了⼀维。
4、运⾏策略⽹络选择动作
这⼀部分⽐较简单,直接上代码:
def choo_action(lf, obrvation):
prob_weights = lf.ss.run(lf.all_act_prob, feed_dict={lf.tf_obs: waxis, :]})
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel())
return action
因为obrvation是⼀个⼀维数组,⽽我们的⽹络输⼊需要⼆维,所以我们使⽤waxis, :]来将其升为⼆维数组,其输⼊⽹络后得到shape=(1,n_actions)的prob_weights。
在上述代码中最关键的是⼀个np.random.choice(),它帮助我们实现按照概率分布进⾏输出,⾸先我们⽤prob_weights.shape[1]获取prob_weights第⼆维的个数,即n_actions,然后使⽤range()将其构造成0-n_actions-1的数组,prob_weights.ravel()来进⾏降维,直接降为⼀维,shape=(1,n_actions)变成了shape=(n_actions),按照这个概率从range()⽣成的数组中采样。
5、学习并训练策略⽹络
1、学习和训练⽹络

本文发布于:2023-06-02 19:17:05,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/968384.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:算法   动作   代码   函数   回合
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图