彩色为新学习的内容
PolicyNet学习
已知:小车倒立摆环境,状态空间s:[,,,],动作空间:[0,1],动作1时施加正向10N的力,动作为0施加反向10N的力。
神经网络输入 为 小车状态s,是与环境交互得到的,输出是动作的概率分布;作用在小车上的动作a,是根据神经网络的输出进行采样得到的,在训练过程中充当标签;每个动作对应的累计回报是一次训练中由该动作后的回报进行累积并归一化得到的。
迭代85次得到训练好的神经网络,进行10次测试展示。
训练代码:
import gym
from policynet import PolicyGradient
import matplotlib.pyplot as plt
import time
DISPLAY_REWARD_THRESHOLD = 1000 # 当 回合总 reward 大于 400 时显示模拟窗口
RENDER = False # 在屏幕上显示模拟窗口会拖慢运行速度, 我们等计算机学得差不多了再显示模拟
#创建一个环境
env = gym.make('CartPole-v0')
env.seed(1)# 普通的 Policy gradient 方法, 使得回合的 variance 比较大, 所以我们选了一个好点的随机种子
env = env.unwrapped
print(env.action_space)
print(env.observation_space)
print(env.observation_space.high)
print(env.observation_space.low)
RL = PolicyGradient(
n_actions=env.action_space.n,
n_features=env.observation_space.shape[0],
learning_rate=0.02,
reward_decay=0.99,
)
#学习过程
for i_episode in range(85):
observation = env.reset()
while True:
if RENDER: env.render()
#采样动作,探索环境
action = RL.choose_action(observation)
observation_, reward, done, info = env.step(action)
#将观测,动作和回报存储起来
RL.store_transition(observation, action, reward)
if done:
ep_rs_sum = sum(RL.ep_rs)
if 'running_reward' not in globals():
running_reward = ep_rs_sum
else:
running_reward = running_reward * 0.99+ep_rs_sum * 0.01
if running_reward > DISPLAY_REWARD_THRESHOLD: RENDER = True
print("episode:", i_episode, "rewards:", int(running_reward))
#每个episode学习一次
vt = RL.learn()
if i_episode == 50:
plt.plot(vt)
plt.xlabel('episode steps')
plt.ylabel('normalized state-action value')
plt.show()
break
#智能体探索一步
observation = observation_
# #测试过程
for i in range(10):
observation = env.reset()
count = 0
while True:
# 采样动作,探索环境
env.render()
action = RL.greedy(observation)
#action = RL.choose_action(observation)
#action = RL.sample_action(observation)
# print (action)
# print(action1)
observation_, reward, done, info = env.step(action)
if done:
print(count)
break
observation = observation_
count+=1
#time.sleep(0.001)
print (count)
policy-net 代码
import numpy as np
import tensorflow as tf
np.random.seed(1)
#撒种子,使得以后每次使用random生成随机数都一样
tf.set_random_seed(1)
#通过tf.set_random_seed()函数对该图资源下的全局随机数生成种子进行设置,
# 使得不同Session中的random系列函数表现出相对协同的特征,这就是Graph-Level的表现
# b = tf.random_normal([1],seed=-1) 仅对张量b进行了seed设置,可以发现,这是Op-Level的表现,仅在执行张量b的情况下,才会有和Graph1类似的协同效果
class PolicyGradient:
def __init__(
self,
n_actions,
n_features,
learning_rate=0.01,
reward_decay=0.95,
output_graph=False,
):
#动作空间的维数
self.n_actions = n_actions
#状态特征的维数
self.n_features = n_features
#学习速率
self.lr = learning_rate
#回报衰减率
self.gamma = reward_decay
#一条轨迹的观测值,动作值,和回报值
self.ep_obs, self.ep_as, self.ep_rs = [],[],[]
#创建策略网络
self._build_net()
#启动一个默认的会话
self.sess = tf.Session()
if output_graph:
tf.summary.FileWriter("logs/", self.sess.graph)
# 初始化会话中的变量
self.sess.run(tf.global_variables_initializer())
#创建策略网络的实现
def _build_net(self):
with tf.name_scope('input'):
#创建占位符作为输入
self.tf_obs = tf.placeholder(tf.float32, [None, self.n_features], name="observations")
self.tf_acts = tf.placeholder(tf.int32, [None, ], name="actions_num")
self.tf_vt = tf.placeholder(tf.float32, [None, ], name="actions_value")
#第一层 tf.layers.dense() 全连接层
layer = tf.layers.dense(
inputs=self.tf_obs,#输入该网络层的数据
units=10,#输出的维度大小,改变inputs的最后一维
# use_bias:使用bias为True(默认使用),不用bias改成False即可,是否使用偏置项
#trainable=True:表明该层的参数是否参与训练。如果为真则变量加入到图集合中
activation=tf.nn.tanh,# 激活函数
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),# 卷积核初始化器
bias_initializer=tf.constant_initializer(0.1),
name='fc1',
)
#第二层
all_act = tf.layers.dense(
inputs=layer,
units=self.n_actions,
activation=None,
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
bias_initializer=tf.constant_initializer(0.1),
name='fc2'
)
#利用softmax函数得到每个动作的概率
self.all_act_prob = tf.nn.softmax(all_act, name='act_prob')
#定义损失函数
with tf.name_scope('loss'):
neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act,labels=self.tf_acts) #采用交叉熵的方法计算损失值
loss = tf.reduce_mean(neg_log_prob*self.tf_vt)# 降维到一维,概率*行为值函数 ,行为值函数越大,更新幅度越大 的loss
#定义训练,更新参数
with tf.name_scope('train'):
self.train_op = tf.train.AdamOptimizer(self.lr).minimize(loss)
#定义如何选择行为,即状态s处的行为采样.根据当前的行为概率分布进行采样
def choose_action(self, observation):
prob_weights = self.sess.run(self.all_act_prob, feed_dict={self.tf_obs:observation[np.newaxis,:]})
#np.newaxis的作用就是在这一位置增加一个一维,原来是(n),变成(1,n)
#按照给定的概率采样
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel())
return action
# numpy中的ravel()、flatten()、squeeze()
# 都有将多维数组转换为一维数组的功能,区别:
# ravel():如果没有必要,不会产生源数据的副本
# flatten():返回源数据的副本
# squeeze():只能对维数为1的维度降维
# a1 = np.random.choice(a=5, size=3, replace=False, p=None)
# 参数意思分别 是从a 中以概率P,随机选择3个, p没有指定的时候相当于是一致的分布
# replacement 代表的意思是抽样之后还放不放回去,如果是False的话,那么出来的三个数都不一样,如果是
# True的话, 有可能会出现重复的,因为前面的抽的放回去了
def greedy(self, observation):
prob_weights = self.sess.run(self.all_act_prob, feed_dict={self.tf_obs: observation[np.newaxis, :]})
action = np.argmax(prob_weights.ravel())
return action
#定义存储,将一个回合的状态,动作和回报都保存在一起
def store_transition(self, s, a, r):
self.ep_obs.append(s)
self.ep_as.append(a)
self.ep_rs.append(r)
#学习,以便更新策略网络参数,一个episode之后学一回
def learn(self):
#计算一个episode的折扣回报,从状态S0到终止状态
discounted_ep_rs_norm = self._discount_and_norm_rewards()
#调用训练函数更新参数
self.sess.run(self.train_op, feed_dict={
self.tf_obs: np.vstack(self.ep_obs),# 垂直堆叠数组
self.tf_acts: np.array(self.ep_as),
self.tf_vt: discounted_ep_rs_norm,
})
#清空episode数据
self.ep_obs, self.ep_as, self.ep_rs = [], [],[]
return discounted_ep_rs_norm
def _discount_and_norm_rewards(self):
#折扣回报和
discounted_ep_rs =np.zeros_like(self.ep_rs) # 给出一个形状与他一样的数组
running_add = 0
for t in reversed(range(0, len(self.ep_rs))):# 反转迭代,从大到小呗
running_add = running_add * self.gamma + self.ep_rs[t]
discounted_ep_rs[t] = running_add
#归一化
discounted_ep_rs-= np.mean(discounted_ep_rs)
discounted_ep_rs /= np.std(discounted_ep_rs)
return discounted_ep_rs
总结今天的学习:
1、关注了 莫烦知乎,一个很实在的很良心的工程师免费讲机器学习
2、记录一下程序提速的方法,numpy比python快,比pandas快,左边的操作比右边快:
np.concatenate > np.stack,vstack
a_view4 = a.ravel() 展平 ,view a.flatten() 展平,copy
a += 1 a = a + 1
view快 copy慢 因为view没有复制东西,直接取源数据的索引部分,它改变源数据也改变。
a_view1 = a[1:2, 3:6] # 切片 slice a_copy1 = a[[1,4,6], [2,4,6]] # 用 index 选
a_view2 = a[:100] # 同上 a_copy2 = a[[True, True], [False, True]] # 用 mask
a_view3 = a[::2] # 跳步 a_copy3 = a[[1,2], :]
a_copy4 = a[a[1,:] != 0, :]
使用 `np.take()`, 替代用 index 选数据的方法 ( indices = np.random.randint(0, 1000000, size=10000) )
_ = np.take(a, indices, axis=0) > _ = b[indices]
使用 `np.compress()`, 替代用 mask 选数据的方法. ( mask = a[:, 0] < 0.5 )
_ = np.compress(mask, a, axis=0) _ = b[mask]
使用out参数:https://docs.scipy.org/doc/numpy/reference/ufuncs.html#available-ufuncs
np.add(a, 1, out=a) a = np.add(a, 1) , a += 1
3、学习了seed()用法
np.random.seed(1)
#撒种子,使得以后每次使用random生成随机数都一样,改变参数0,1,2...每次都撒不同的种子
tf.set_random_seed(1)
#通过tf.set_random_seed()函数对该图资源下的全局随机数生成种子进行设置,使得不同Session中的random系列函数表现出相对协同的特征,得到同样的随机数
tf.random_normal([1],seed=-1) 仅对张量b进行了seed设置,仅在执行张量b的情况下,才会有协同效果
4、choice()
a1 = np.random.choice(a=5, size=3, replace=False, p=None)
# 参数意思分别 是从a 中以概率P,随机选择3个, p没有指定的时候相当于是一致的分布
# replacement 代表的意思是抽样之后还放不放回去,如果是False的话,那么出来的三个数都不一样,如果是
# True的话, 有可能会出现重复的,因为前面的抽的放回去了
5、降维
# numpy中的ravel()、flatten()、squeeze()
# 都有将多维数组转换为一维数组的功能,区别:
# ravel():如果没有必要,不会产生源数据的副本
# flatten():返回源数据的副本
# squeeze():只能对维数为1的维度降维