策略网络和估值网络是强化学习两种重要的方法。强化学习和有监督学习、无监督学习不同,它的学习目标是变化的、不明确的,甚至可能不存在绝对正确的标签。AlphaGo就是结合了策略网络,估值网络和蒙特卡洛搜索树实现对战程序的。
强化学习中有环境状态(Environment State)、动作(Action)和奖励(Reward)。模型的载体是Agent。
策略网络的实质是建立一个神经网络模型,通过观察环境状态预测出目前应该执行的策略,执行这个策略,并获取可以获得最大的奖励是多少。和普通的监督学习不同,策略网络不是通过feature预测label,而是根据对观察的环境状态进入模型,得到action,执行这个action后得到reward,通过reward的加权衰减并叠加后计算梯度,通过梯度优化网络参数。
强化学习与其他两类学习还有一个很不同的点——它需要一个环境生成工具,生成问题对应的环境和模型载体Agent。Gym是OpenAI退出的开源的强化学习环境。通过pip install gym即可安装。
选择gym下的Cartpole问题来完成强化学习初体验。
CartPole的环境中有一辆小车,在一个一维无阻力轨道上行驶,在车上连接着不太结实的杆,这个杆会左右摇晃,我们的任务是让车不偏离中心超过2.4个单位距离并且杆的倾角小于15度,否则,表示任务失败。环境和agent如下:
环境信息Observation是一个4值的数组,包含小车的位置、速度、杆的角度等。
在运行过程中,小车每坚持一步,reward加1,每次失败后,reward清0. 当reward大于200后任务完成,退出学习。
以带有一个隐含层的MLP为神经网络模型,完成本次任务。整个计算图如下
其中,虚线框内是圣经网络计算子图,绿色部分是小车每动作一步都会计算的,橙色是一次迭代(从开始运行到任务失败算一次迭代)执行一次,红色是一个batch执行一次。
Input_y = 1-action;
epr = reward * discount
advantage = epr – mean(epr) / std(epr), mean为均值,std为标准差。
程序如下:
#coding:utf-8
import numpy as np
import tensorflow as tf
import gym
env = gym.make('CartPole-v0')
H = 50
batch_size = 25
learning_rate = 1e-1
D = 4
gamma = 0.99
"""
以observation为输入,建立MLP。
一个隐藏层用relu激活,一个全连接层接输出,因为是action,所以,输出只有一个。
输出用sigmoid激活
"""
observations = tf.placeholder(tf.float32, [None, D], name = "input_x")
W1 = tf.get_variable('W1', shape = [D, H], initializer = tf.contrib.layers.xavier_initializer())
layer1 = tf.nn.relu(tf.matmul(observations, W1))
W2 = tf.get_variable('W2', shape = [H, 1], initializer = tf.contrib.layers.xavier_initializer())
score = tf.matmul(layer1, W2)
probability = tf.nn.sigmoid(score)
"""
reward的逐步衰减。
"""
def discount_rewasrd(r):
discounted_r = np.zeros_like(r)
running_add = 0
for t in reversed(range(r.size)):
running_add = running_add * gamma + r[t]
discounted_r[t] = running_add
return discounted_r
"""
"""
input_y = tf.placeholder(tf.float32, [None, 1], name="input_y")
advantages = tf.placeholder(tf.float32, name="reward_singal")
#实际上是对action做对数似然
loglik = tf.log(input_y * (input_y - probability) + (1 - input_y) * (input_y + probability))
loss = -tf.reduce_mean(loglik * advantages)
tvars = tf.trainable_variables()
newGrads = tf.gradients(loss, tvars)
"""
梯度更新模块
每个Batch计算完成后才做一次更新
apply_gradients的更新原理?
"""
adam = tf.train.AdamOptimizer(learning_rate = learning_rate)
W1Grad = tf.placeholder(tf.float32, name = "batch_grade1")
W2Grad = tf.placeholder(tf.float32, name = "batch_grade2")
batchGrad = [W1Grad, W2Grad]
updateGrads = adam.apply_gradients(zip(batchGrad, tvars))
"""
"""
xs, ys, drs = [], [], [] #xs是环境信息的观察列表, ys是label列表, drs是每一个action的reward
reward_sum = 0
episode_number = 1
total_episodes = 10000
with tf.Session() as sess:
rendering = False
init = tf.global_variables_initializer()
sess.run(init)
observation = env.reset()
gradBuffer = sess.run(tvars)
for ix,grad in enumerate(gradBuffer):
gradBuffer[ix] = grad * 0
#因为循环层次不同,整个计算图被分成几个子图执行。
while episode_number <= total_episodes:
if reward_sum/batch_size > 100 or rendering == True:
env.render()
rendering = True
x = np.reshape(observation, [1,D])
tfprob = sess.run(probability, feed_dict={observations: x})
action = 1 if np.random.uniform() < tfprob else 0
xs.append(x)
y = 1 - action
ys.append(y)
observation, reward, done, info = env.step(action)
reward_sum += reward
drs.append(reward)
if done:
episode_number += 1
epx = np.vstack(xs)
epy = np.vstack(ys)
epr = np.vstack(drs)
xs, ys, drs = [], [], []
discounted_epr = discount_rewasrd(epr)
discounted_epr -= np.mean(discounted_epr)
discounted_epr /= np.std(discounted_epr)
tGrad = sess.run(newGrads, feed_dict={observations: epx, input_y: epy, advantages: discounted_epr})
for ix, grad in enumerate(tGrad):
gradBuffer[ix] += grad
if episode_number % batch_size == 0:
#运行图中的优化子图,将梯度缓存中一个batch的梯度输入到图中。
sess.run(updateGrads, feed_dict={W1Grad: gradBuffer[0], W2Grad: gradBuffer[1]})
for ix, grad in enumerate(gradBuffer):
gradBuffer[ix] = grad * 0 #一个batch结束后,将所有的梯度缓存清空。
print "Average reward for episode %d : %f." % (episode_number, reward_sum / batch_size)
if reward_sum / batch_size > 200:
print "Task solved in", episode_number, 'episodes!'
break
reward_sum = 0
observation = env.res
执行结果很尴尬的是,reward最大值刚好只能达到200.000,一直都不退出。