【强化学习】A3C代码注释版本

##########################################
# A3C做出的改进:
# 解决AC难以收敛的问题
# 不一样的地方:
#import threading
# import tensorflow as tf
import tensorflow.compat.v1 as tftf.compat.v1.disable_eager_execution()
import numpy as np
import gym
import os
import shutil
import matplotlib.pyplot as pltGAME = 'CartPole-v0'  # 游戏名称
OUTPUT_GRAPH = True
LOG_DIR = '../log'  # 一个文件名
N_WORKERS = 3  # 有3个并行的worker
MAX_GLOBAL_EP = 3000  # 最大执行回合数
GLOBAL_NET_SCOPE = 'Global_Net'  # object名称
UPDATE_GLOBAL_ITER = 100  # 每UPDATE_GLOBAL_ITER 步 或者回合完了,进行sync操作
GAMMA = 0.9  # 折扣因子
ENTROPY_BETA = 0.001
LR_A = 0.001  # learning rate for actor  Actor的学习率
LR_C = 0.001  # learning rate for critic   Actor的学习率
GLOBAL_RUNNING_R = []  # 游戏整体的这个游戏里面一共进行了多少步,这个数组长度就是多少
GLOBAL_EP = 0  # 整体进行的回合数
STEP = 3000  # Step limitation in an episode   TODO 这里应该改成200,因为200步游戏就达到终止条件了
TEST = 10  # 实验次数每100回合测试一次 TODO 这里应该是100吧?env = gym.make(GAME)
N_S = env.observation_space.shape[0]  # 状态维度(?,4)
N_A = env.action_space.n  # 动作维度(2)# 这个 class 可以被调用生成一个 global net.
# 也能被调用生成一个 worker 的 net, 因为他们的结构是一样的,
# 所以这个 class 可以被重复利用.
class ACNet(object):def __init__(self, scope, globalAC=None):  # 这里的scope是网络名称# 当创建worker网络的时候,我们传入之前创建的globalAC给这个worker# 判断当下建立的网络是 local 还是 globalif scope == GLOBAL_NET_SCOPE:  # 如果建立的网络是Global网络# 关于tf.name_scope和tf.variable_scopewith tf.variable_scope(scope):self.s = tf.placeholder(tf.float32, [None, N_S], 'S')  # 定义一组状态# 这里的a_params具体代表求导时的那个分母,也就是对什么求导self.a_params, self.c_params = self._build_net(scope)[-2:]  # 取这个数组从第一个到倒数第三个,最后两个数被丢弃了else:  # local net, calculate losseswith tf.variable_scope(scope):# 接着计算 critic loss 和 actor loss# 用这两个 loss 计算要推送的 gradientsself.s = tf.placeholder(tf.float32, [None, N_S], 'S')  # 定义一组状态self.a_his = tf.placeholder(tf.int32, [None, ], 'A')  # 定义了一组动作self.v_target = tf.placeholder(tf.float32, [None, 1], 'Vtarget')  # 目标价值self.a_prob, self.v, self.a_params, self.c_params = self._build_net(scope)# 接着计算 critic loss 和 actor loss# 用这两个 loss 计算要推送的 gradients# 张量减法运算:tf.subtract函数是一个算术运算符, 用于表示减法, 返回x - y的元素td = tf.subtract(self.v_target, self.v, name='TD_error')  # td_error = v_target - vwith tf.name_scope('c_loss'):  # c网络loss函数self.c_loss = tf.reduce_mean(tf.square(td))  # td_error先求平方然后取平均with tf.name_scope('a_loss'):  # 下面这些操作都封装到一个叫a_loss的作用域中,用来计算a_loss,方便可视化# 下面这些应该是在实现a_loss的那个公式log_prob = tf.reduce_sum(tf.log(self.a_prob + 1e-5) * tf.one_hot(self.a_his, N_A, dtype=tf.float32),axis=1, keep_dims=True)# tf.stop_gradient: 停止梯度运算,当在一个图中执行时, 这个op按原样输出它的输入张量。# 当构建ops来计算梯度时,该op会阻止将其输入贡献考虑在内。exp_v = log_prob * tf.stop_gradient(td)entropy = -tf.reduce_sum(self.a_prob * tf.log(self.a_prob + 1e-5),axis=1, keep_dims=True)  # encourage explorationself.exp_v = ENTROPY_BETA * entropy + exp_v  # expect valueself.a_loss = tf.reduce_mean(-self.exp_v)  # actor_losswith tf.name_scope('local_grad'):# tf.gradients 实现a_loss对a_params求导  梯度self.a_grads = tf.gradients(self.a_loss, self.a_params)self.c_grads = tf.gradients(self.c_loss, self.c_params)with tf.name_scope('sync'):  # 同步with tf.name_scope('pull'):  # 调用pull,这个worker就会从global_net中获取到最新的参数# assign相当于连线,一般是将一个变量的值不间断地赋值给另一个变量,就像把这两个变量连在一起,所以习惯性的当做连线用,比如把一个模块的输出给另一个模块当输入。self.pull_a_params_op = [l_p.assign(g_p) for l_p, g_p in zip(self.a_params, globalAC.a_params)]self.pull_c_params_op = [l_p.assign(g_p) for l_p, g_p in zip(self.c_params, globalAC.c_params)]with tf.name_scope('push'):  # 调用push,这个worker就会将自己的个人更新推送去global_netself.update_a_op = OPT_A.apply_gradients(zip(self.a_grads, globalAC.a_params))self.update_c_op = OPT_C.apply_gradients(zip(self.c_grads, globalAC.c_params))def _build_net(self, scope):  # 这里搭建Actor和Critic网络w_init = tf.random_normal_initializer(0., .1)  # 生成一组符合标准正态分布的 tensor 对象,初始化张量with tf.variable_scope('actor'):  # 搭建一个actor网络"""这个actor网络有两层,第一层输入是状态,输出一个维度为200的东西,第一层的输出l_a是第二层的输入,第一层的激活函数和第二层不一样"""# https://blog.csdn.net/yangfengling1023/article/details/81774580/# 全连接层,曾加了一个层,全连接层执行操作 outputs = activation(inputs.kernel+bias) 如果执行结果不想进行激活操作,则设置activation=None# self.s:输入该网络层的数据  200:输出的维度大小,改变inputs的最后一维    tf.nn.relu6:激活函数,即神经网络的非线性变化# kernel_initializer=w_init:卷积核的初始化器    name:层的名字l_a = tf.layers.dense(self.s, 200, tf.nn.relu6, kernel_initializer=w_init, name='la')a_prob = tf.layers.dense(l_a, N_A, tf.nn.softmax, kernel_initializer=w_init, name='ap')with tf.variable_scope('critic'):  # 搭建critic网络"""这个网络也是有两层,第一层有100个输出,第二层只有一个输出,"""l_c = tf.layers.dense(self.s, 100, tf.nn.relu6, kernel_initializer=w_init, name='lc')v = tf.layers.dense(l_c, 1, kernel_initializer=w_init, name='v')  # state value# tf.get_collection 用来获取一个名称是‘key’的集合中的所有元素,返回的是一个列表,列表的顺序是按照变量放入集合中的先后;# scope参数可选,表示的是名称空间(名称域),如果指定,就返回名称域中所有放入‘key’的变量的列表,不指定则返回所有变量。# TODO 这个含义看不懂。。。TRAINABLE_VARIABLES:将由优化程序训练的Variable对象的子集a_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=scope + '/actor')c_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=scope + '/critic')return a_prob, v, a_params, c_params  # 返回均值、方差,v:state_value TODO a_params, c_params 这两个参数什么意思啊def update_global(self, feed_dict):  # run by a local  进行push操作SESS.run([self.update_a_op, self.update_c_op], feed_dict)  # 将本地def pull_global(self):  # run by a local     #进行pull操作SESS.run([self.pull_a_params_op, self.pull_c_params_op])def choose_action(self, s):  # run by a local    根据s选择动作prob_weights = SESS.run(self.a_prob, feed_dict={self.s: s[np.newaxis, :]})action = np.random.choice(range(prob_weights.shape[1]),p=prob_weights.ravel())  # select action w.r.t the actions prob   根据概率选择动作return actionclass Worker(object):def __init__(self, name, globalAC):self.env = gym.make(GAME).unwrapped  # 创建自己的环境self.name = name  # 自己的名字self.AC = ACNet(name, globalAC)  # 自己的local net,并绑定上globalACdef work(self):  # worker网络的主体部分# s,a,r 的缓存,用于n_steps更新global GLOBAL_RUNNING_R, GLOBAL_EPtotal_step = 1  # 初始化步数buffer_s, buffer_a, buffer_r = [], [], []    # state,action,和reward的缓存# 当 COORD不需要停止的时候,或者当GLOBAL_EP比MAX_GLOBAL_EP小的时候while not COORD.should_stop() and GLOBAL_EP < MAX_GLOBAL_EP:  # MAX_GLOBAL_EP最大训练EPs = self.env.reset()  # 重置环境ep_r = 0  # 统计ep的总rewardwhile True:# if self.name == 'W_0':#     self.env.render()a = self.AC.choose_action(s)  # 选择动作s_, r, done, info = self.env.step(a)  # 与环境互动if done: r = -5  # 把reward-100的时候,改为-5ep_r += r  # 保存数据   ep_r 总的rewardbuffer_s.append(s)  # 添加各种缓存buffer_a.append(a)buffer_r.append(r)# 每UPDATE_GLOBAL_ITER 步 或者回合完了,进行sync操作,也就是学习的步骤if total_step % UPDATE_GLOBAL_ITER == 0 or done:  # update global and assign to local net# 获得用于计算TD_error 的下一state的value# 下面对应价值函数的那个公式,如果是terminal state,则对未来的期望等于0if done:v_s_ = 0  # terminalelse:v_s_ = SESS.run(self.AC.v, {self.AC.s: s_[np.newaxis, :]})[0, 0]buffer_v_target = []  # 下 state value的缓存,用于计算TD# 计算每个state的V(s')# print(a[::-1]) ### 取从后向前(相反)的元素[1 2 3 4 5]-->[ 5 4 3 2 1 ]# (莫烦说)对MDP的一个反向的计算,对未来的reward的一个递解的步骤for r in buffer_r[::-1]:v_s_ = r + GAMMA * v_s_buffer_v_target.append(v_s_)buffer_v_target.reverse()  # 先反转填充再转回来# np.vstack:按垂直方向(行顺序)堆叠数组构成一个新的数组buffer_s, buffer_a, buffer_v_target = np.vstack(buffer_s), np.array(buffer_a), np.vstack(buffer_v_target)# feed_dict的作用是给使用placeholder创建出来的tensor赋值feed_dict = {self.AC.s: buffer_s,self.AC.a_his: buffer_a,self.AC.v_target: buffer_v_target,}self.AC.update_global(feed_dict)  # 推送更新去globalACbuffer_s, buffer_a, buffer_r = [], [], []  # 清空缓存self.AC.pull_global()  # 获取globalAC的最新参数s = s_  # 更新状态total_step += 1  # 整体部署+1if done:# 达到游戏终止条件,更新rewardif len(GLOBAL_RUNNING_R) == 0:  # 如果是第一回合GLOBAL_RUNNING_R.append(ep_r)else:  # 不是第一回合GLOBAL_RUNNING_R.append(0.99 * GLOBAL_RUNNING_R[-1] + 0.01 * ep_r)print(self.name,"Ep:", GLOBAL_EP,"| Ep_r: %i" % GLOBAL_RUNNING_R[-1],)GLOBAL_EP += 1  # 加一回合break  # 结束这回合if __name__ == '__main__':SESS = tf.Session()  # 创建一个会话# 下面是真正的重点!!worker并行计算# 使用 tf.device() 指定模型运行的具体设备,可以指定运行在GPU还是CPU上,以及哪块GPU上。with tf.device("/cpu:0"):  # 以下部分,都在CPU0完成# tf.train.RMSPropOptimizerSHI是一种优化算法,有很多种优化算法,具体见下面这个文档,有空好好学习下# https://www.cnblogs.com/bigcome/p/10084220.htmlOPT_A = tf.train.RMSPropOptimizer(LR_A, name='RMPropA')  # 定义了一个actor优化器OPT_C = tf.train.RMSPropOptimizer(LR_C, name='RMPropC')  # 定义了一个Critic优化器GLOBAL_AC = ACNet(GLOBAL_NET_SCOPE)  # we only need its params 定义了一个总的AC网络workers = []  # 定义workersfor i in range(N_WORKERS):  # 创建worker,之后再并行i_name = 'W_%i' % i  # worker的名字workers.append(Worker(i_name, GLOBAL_AC))  # 每个worker都有共享这个global AC# 调用 tf.train.Coordinator() 来创建一个线程协调器,用来管理之后在Session中启动的所有线程;COORD = tf.train.Coordinator()  # Tensorflow用于并行的工具SESS.run(tf.global_variables_initializer())# 执行到这儿的意思是,OUTPUT_GRAPH这个参量只出现了一次,而且是true,也就是说,删除path这个路径里面的LOG_DIR文件,然后保存一张新的进去,也就是咱们收敛情况的图if OUTPUT_GRAPH:# os.path.exists()就是判断括号里的文件是否存在的意思,path代表路径,括号内的可以是文件名。if os.path.exists(LOG_DIR):  # 这句是说,如果LOG_DIR这个文件存在,就将它删除# 在python文件中,使用代码删除文件夹以及里面的文件,可以使用shutil.rmtree,递归地删除文件夹以及里面的文件。shutil.rmtree(LOG_DIR)# tf.summary.FileWriter 指定一个文件用来保存图 ,指定LOG_DIR这个文件来保存SESS.graph这个图tf.summary.FileWriter(LOG_DIR, SESS.graph)# 开启tf线程worker_threads = []for worker in workers:  # 执行每一个workerjob = lambda: worker.work()  # 有一个工人(worker),有一个方法(work),这句是说让这个工人worker去执行work这个方法t = threading.Thread(target=job)  # 添加一个工作线程t.start()  # 开始这个线程worker_threads.append(t)  # 把这个线程添加到worker_threads中COORD.join(worker_threads)  # 当所有的worker都运行完了才会进行下面的步骤,如果没有这一句,那么每一个worker运行完就会进行下面的步骤testWorker = Worker("test", GLOBAL_AC)  # 创建一个测试worker   TODO 不知道这个worker有什么作用testWorker.AC.pull_global()  # 对testworker执行pull操作# 下面应该是测试部分total_reward = 0for i in range(TEST):state = env.reset()  # 初始化状态for j in range(STEP):env.render()  # env.render()函数用于渲染出当前的智能体以及环境的状态action = testWorker.AC.choose_action(state)  # 选择一个测试动作state, reward, done, _ = env.step(action)total_reward += reward  # reward叠加if done:  # 达到终止条件就跳出循环breakave_reward = total_reward / TEST# 打印内容:整个过程一共进行了多少回合,平均奖励是多少print('episode: ', GLOBAL_EP, 'Evaluation Average Reward:', ave_reward)plt.plot(np.arange(len(GLOBAL_RUNNING_R)), GLOBAL_RUNNING_R)  # TODO GLOBAL_RUNNING_R这个参量再研究一下plt.xlabel('step')plt.ylabel('Total moving reward')plt.show()

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

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

相关文章

用python做人脸识别的程序怎么做_手把手教你用Python实现人脸识别

作者&#xff1a;Kangvcar简书专栏&#xff1a;http://www.jianshu.com/u/d9c480744afd环境要求&#xff1a;Ubuntu17.10Python 2.7.14环境搭建&#xff1a;1. 安装 Ubuntu17.10 > 安装步骤在这里2. 安装 Python2.7.14 (Ubuntu17.10 默认Python版本为2.7.14)3. 安装 git 、c…

【强化学习】PPO代码注释版本

# PPO主要通过限制新旧策略的比率&#xff0c;那些远离旧策略的改变不会发生# import tensorflow as tf import tensorflow.compat.v1 as tf tf.compat.v1.disable_eager_execution() import numpy as np import matplotlib.pyplot as plt import gym# 定义一些超级参量 EP_MAX…

【强化学习】可视化学习tensorboard

tensorflow定义了一个图结构&#xff1a; 代码&#xff1a; a tf.constant(3.0, name"a")b tf.constant(4.0, name"b")c tf.add(a, b, name"add")var tf.Variable(tf.random_normal([2, 3], mean0.0, stddev1.0), name"variable"…

面向初学者的JSF 2.0教程

1.什么是JSF&#xff1f; JSF是Java Server Faces的首字母缩写。 它是一种服务器端处理技术&#xff0c;它允许将服务器端代码嵌入到网页中。 由于可以将服务器端处理和呈现代码嵌入网页本身&#xff0c;因此使项目的整体编码更加简单。 减少总体数量以及文件大小。 JSF包含2个…

篡改referer_HTTP_REFERER的用法及伪造

引言在php中&#xff0c;可以使用$_SERVER[‘HTTP_REFERER’]来获取HTTP_REFERER信息&#xff0c;关于HTTP_REFERER&#xff0c;php文档中的描述如下&#xff1a;“引导用户代理到当前页的前一页的地址(如果存在)。由 user agent 设置决定。并不是所有的用户代理都会设置该项&a…

华为nova7保密柜_华为发布nova8系列新品 轻松拍出Vlog黄金脸占比

2020年12月23日&#xff0c;华为nova8系列手机正式发布。作为深受年轻人喜爱的手机品牌之一&#xff0c;华为nova8系列此次延续其强大的前置视觉实力、强悍的设计性能以及独特的设计美学&#xff0c;带来业内首款专为Vlog拍摄设计的5G手机。随着短视频和Vlog的崛起&#xff0c;…

从拉格朗日乘数法到KKT条件

从拉格朗日乘数法到KKT条件 最近看论文遇到了Karush–Kuhn–Tucker &#xff08;KKT&#xff09;条件&#xff0c;想搞清楚这是个什么东东&#xff0c;因此就把这个东西认真学习一下并且分享出来&#xff0c;希望对大家有用。学习KKT就不得不先学习一下拉格朗日乘数法&#xf…

26.0.0-alpha1_WildFly 8.0.0.Alpha1的发布和一些历史

26.0.0-alpha1自从我们发布WildFly 8.0.0.Alpha1版本以来&#xff0c; 已经过去了大约2周。 该下载位于WildFly下载页面上 。 我敢肯定&#xff0c;你们中的许多人可能会想知道WildFly是什么&#xff0c;而其中一些知道它是什么的人可能不会知道已经发布了。 我将尝试回答其中的…

单机最大负载_分布式高可靠之负载均衡,今天看了你肯定会

到目前为止&#xff0c;我已经为你介绍了分布式起源、分布式协调与同步、分布式资源管理与负载调度、分布式计算技术、分布式通信技术和分布式数据存储。可以说&#xff0c;掌握了这些内容&#xff0c;基本上就掌握了分布式的关键技术。然而&#xff0c;只有可靠的分布式系统才…

【强化学习】一文带你理清强化学习

整理不易&#xff0c;希望留个赞再走哦&#xff01;&#xff01; 学习路线 这个图描述的比较清晰&#xff0c;蓝框里是整个强化学习的一些概念基础了&#xff0c;橙色是一些学习方法&#xff0c;可以针对性的选择一些&#xff0c;废话不多说&#xff0c;接下来就按照这个路线图…

了解Java缓冲池

了解Java缓冲池 缓冲池空间位于垃圾收集器管理的内存之外。 这是分配本地堆外内存的一种方法。 使用缓冲池有什么好处&#xff1f; 为了回答这个问题&#xff0c;让我们首先了解什么是字节缓冲区。 字节缓冲区 非直接缓冲区 ByteBuffer类附带了java.nio包。 它允许我们分配直接…

java 循环拼接字符串用分号隔开_Java 8中字符串拼接新姿势:StringJoiner

在为什么阿里巴巴不建议在for循环中使用””进行字符串拼接一文中&#xff0c;我们介绍了几种Java中字符串拼接的方式&#xff0c;以及优缺点。其中还有一个重要的拼接方式我没有介绍&#xff0c;那就是Java 8中提供的StringJoiner &#xff0c;本文就来介绍一下这个字符串拼接…

module ‘tensorflow_core.compat.v1‘ has no attribute ‘contrib‘问题的完美解决

问题描述&#xff1a; Instructions for updating: Use keras.layers.Dense instead. Traceback (most recent call last):File "run_cnn.py", line 200, in <module>model TextCNN(config)File "D:\MY DATA\学习资料\研究生\深度学习\text-classificat…

doctrine find的对象转换成数组_「ES6基础」Array数组的新方法(上)

在日常工作中我们经常会与数组打交道&#xff0c;因此需要熟练掌握数组操作的相关方法&#xff0c;ES6中关于数组的操作&#xff0c;又给我们带来了哪些惊喜呢&#xff0c;Array数组操作又添加了哪些新方法&#xff1f;本篇文章将从以下几个方面进行介绍&#xff1a;Array.from…

No module named ‘__main__.common‘; ‘__main__‘ is not a package

今天在调通代码时遇到一个难缠的bug,源代码如下&#xff1a; from .common import _FLOATX, _EPSILON结果报错&#xff1a; 问题原因&#xff1a; 也就是说&#xff0c;这是相对导入&#xff0c;只有在父模块在当前运行环境中被导入过才能用。 这揭示了报错的缘由&#xff0…

spring 异步返回结果_使用Spring Integration聚合异步结果

spring 异步返回结果嗨&#xff0c;我遇到了一个问题&#xff0c;该问题使用Spring Integration解决方案非常好。 很多时候&#xff0c;我们需要一种将消息分发到未知数量的目的地的方案。 为此&#xff0c;我们使用主题方法。 但是有时&#xff0c;我们还希望从收到消息的所有…

ValueError( Shape(4, ?, 1, 20) and () are incompatible

报错&#xff1a; 解决&#xff1a; 将文件中的return tf.concat(axis, tensors) 改为&#xff1a;return tf.concat(tensors, axis)问题完美解决

adadelta算法_对C++用户比较友好的机器学习算法库

由于疫情影响&#xff0c;这几天在家学习编程&#xff0c;整理了基于c语言的机器学习算法库。目前大部分机器学习库都是面向pyhton语言的&#xff0c;尽管很python包的底层语言是c&#xff0c;但c用户使用起来很麻烦&#xff0c;这里整理了一些对c比较优化的机器学习算法库&…

不解析,使用解析对象

将面向对象的后端与外部系统集成的传统方式是通过数据传输对象 &#xff0c;这些对象在外出之前先序列化为JSON&#xff0c;然后在返回时反序列化。 这种方法很流行&#xff0c;而且是错误的。 序列化部分应该由打印机代替&#xff0c;我在前面已经解释过。 这是我对反序列化的…

yaml报错TypeError: load() missing 1 required positional argument: ‘Loader‘

添加一下命令即可&#xff1a; 我的操作&#xff1a; return yaml.load(config)改为 return yaml.full_load(config)问题完美解决