1、交叉熵理解
https://www.cnblogs.com/kyrieng/p/8694705.html (信息量为什么取log)
2、前向传播 反向传播 复习
英文版:http://neuralnetworksanddeeplearning.com/chap2.html
中文版 https://blog.csdn.net/qq_16137569/article/details/81449209(全连接)
https://blog.csdn.net/qq_16137569/article/details/81477906 (卷积)
https://www.cnblogs.com/pinard/p/6494810.html (卷积)
2.2 RNN复习
https://zhuanlan.zhihu.com/p/28054589(RNN变种图解)
https://www.cnblogs.com/pinard/p/6519110.html https://blog.csdn.net/zhaojc1995/article/details/80572098(lstm详解)
tensorflow实现RNN char:https://zhuanlan.zhihu.com/p/28196873
tensorflow实现lstm minist手写数字分类:https://blog.csdn.net/u010858605/article/details/78696325
3、强化学习复习
《python强化学习实战 印度苏达桑》代码地址:https://github.com/PacktPublishing/Deep-Reinforcement-Learning-Hands-On
英文版原书地址(含代码):https://github.com/sudharsan13296/Hands-On-Reinforcement-Learning-With-Python
一、策略迭代
初始化策略并计算该策略下的状态值函数(策略评估),根据每个状态值函数改进策略(策略改善),重复策略评估和策略改善步骤,直到策略收敛。
# 策略评估
def policy_evaluation(self, agent, max_iter=-1):
"""
:param obj agent: 智能体
:param int max_iter: 最大迭代数
"""
iteration = 0
while True:
iteration += 1
new_value_pi = agent.value_pi.copy()
# 对每个state计算v(s)
for i in range(1, agent.s_len):
ac = agent.pi[i]
transition = agent.p[ac, i, :]
value_sa = np.dot(transition, agent.r + agent.gamma * agent.value_pi)
new_value_pi[i] = value_sa
# 前后2次值函数的变化小于一个阈值,结束
diff = np.sqrt(np.sum(np.power(agent.value_pi - new_value_pi, 2)))
if diff < 1e-6:
break
else:
agent.value_pi = new_value_pi
if iteration == max_iter:
break
# 策略提升
def policy_improvement(self, agent):
"""
:param obj agent: 智能体
"""
# 初始化新策略
new_policy = np.zeros_like(agent.pi)
for i in range(1, agent.s_len):
for j in range(0, agent.a_len):
# 计算每一个状态行动值函数
agent.value_q[i, j] = np.dot(agent.p[j, i, :], agent.r + agent.gamma * agent.value_pi)
# 选出每个状态下的最优行动
max_act = np.argmax(agent.value_q[i, :])
new_policy[i] = max_act
if np.all(np.equal(new_policy, agent.pi)):
return False
else:
agent.pi = new_policy
return True
# 策略迭代
def policy_iteration(self, agent):
"""
:param obj agent: 智能体
"""
iteration = 0
while True:
iteration += 1
self.policy_evaluation(agent)
ret = self.policy_improvement(agent)
if not ret:
break
print('Iter {} rounds converge'.format(iteration))
二、值迭代
对每一个当前状态 s ,对每个可能的动作 a 都计算一下采取这个动作后到达的下一个状态的期望价值。看看哪个动作可以到达的状态的期望价值函数最大,就将这个最大的期望价值函数作为当前状态的价值函数。循环执行这个步骤,直到价值函数收敛。(值迭代是根据状态期望值选择动作,而策略迭代是先估计状态值然后修改策略)
class ValueIteration(object):
def value_iteration(self, agent, max_iter=-1):
"""
:param obj agent: 智能体
:param int max_iter: 最大迭代数
"""
iteration = 0
while True:
iteration += 1
# 保存算出的值函数
new_value_pi = np.zeros_like(agent.value_pi)
for i in range(1, agent.s_len):
value_sas = []
for j in range(0, agent.a_len):
# 对每一个状态s和行动a,计算值函数
value_sa = np.dot(agent.p[j, i, :], agent.r + agent.gamma * agent.value_pi)
value_sas.append(value_sa)
# 从每个行动中,选出最好的值函数
new_value_pi[i] = max(value_sas)
# 前后2次值函数的变化小于一个阈值,结束
diff = np.sqrt(np.sum(np.power(agent.value_pi - new_value_pi, 2)))
if diff < 1e-6:
break
else:
agent.value_pi = new_value_pi
if iteration == max_iter:
break
print('Iter {} rounds converge'.format(iteration))
for i in range(1, agent.s_len):
for j in range(0, agent.a_len):
# 计算收敛后值函数的状态-行动值函数
agent.value_q[i, j] = np.dot(agent.p[j, i, :], agent.r + agent.gamma * agent.value_pi)
# 取出最大的行动
max_act = np.argmax(agent.value_q[i, :])
agent.pi[i] = max_act
值迭代与策略迭代相同、不同之处:
1、两个方法最终都求出策略函数和值函
2、策略迭代是等值函数稳定以后再改进策略,值迭代每一次都在改进策略并且更新值函数,一直到值函数收敛。
三、基于蒙特卡洛和基于TD_error
前者经过多次由起始到终止的实验,利用经验平均值估计状态值函数;后者根据本次和下一次(或者下好多次)的差值更新状态值函数
蒙特卡洛:
TD_error: ,arfa为步长
时序差分法和蒙特卡洛方法的区别主要有:
1)蒙特卡洛方法要等到最后结果才能学习,而时序差分法可以在任一阶段进行学习,具有更快速更灵活的更新状态进行估值。
2)时序差分法是基于即时奖励和下一状态的预估价值来替代当前状态在状态序列结束时可能得到的回报,是当前状态价值的有偏估计。而蒙特卡洛法则使用实际的收获来更新状态价值,是某一状态价值的无偏估计(注:其实这里的本质上是是否求期望的问题,蒙特卡洛通过采样多条序列来求期望,可以代替总体样本的无偏估计,但是因为序列的多样性导致方差较大,而时序差分法是直接预估下一状态的值来更新当前状态的值函数,这种预估本身就和真实值存在一定的偏差,但也因为只用到了下一状态的值,因此随机性较小,方差相对来说更小)。
3)时序差分法的方差较蒙特卡洛方法更小,且对初始值敏感,但比蒙特卡洛方法更高效。时序差分法不止是有单步序列,还可以扩展到N步,当N足够大,甚至趋于无穷时,就等价于蒙特卡洛方法了。时序差分法可以分为在线策略(on-policy,也可称为同策略)和 离线策略(off-policy,也可称为异策略)。目前在线策略最常见的算法是SARSA算法,离线策略最常见的是Q-Learning算法。
四、DQN
DQN对Q-Learning的修改主要有三个方面:1)DQN利用深度卷积神经网络逼近值函数。2)DQN利用了经验回放 训练强化学习的学习过程。3)DQN设置了目标网络单独处理TD_error,该网络的参数每隔几步才会更新。
Q网络参数更新:均方误差损失函数,梯度反向传播
参考:知乎大佬https://zhuanlan.zhihu.com/p/26052182
在第[12]行,利用了经验回放;在[13]行利用了独立的目标网络;第[15]行,更新动作值函数逼近网络参数;第[17]行更新目标网络参数. 翻译如下:
参考:https://www.cnblogs.com/jiangxinyang/p/10112381.html
代码来自https://github.com/ljpzzz/machinelearning/tree/master/reinforcement-learning
其中,对于经验回放在代码中的具体用法:在算法刚开始跑的时候,经验回放池的数据从无到有,这时是不会去训练Q网络的,只有当经验回放池的数据量大于BATCH_SIZE,才开始训练。此后由于经验回放池的数据会一直增多,回放池的大小是REPLAY_SIZE,如果池子满了,而且一条新的交互数据到了,我们就从回放池里面删除一条最老的数据。然后只要经验回放池里的数据量大于我们用于需要采样的样本量BATCH_SIZE,就去训练Q网络。这段时间开始,每多一条新的环境交互数据,就会训练Q网络一次。
五、Double DQN
选择动作和动作评估(用来更新值函数)选用两个Q网络,以防止估计值函数大于实际值。
#DoubleDQN
# Step 2: calculate y
y_batch = []
current_Q_batch = self.Q_value.eval(feed_dict={self.state_input: next_state_batch})
max_action_next = np.argmax(current_Q_batch, axis=1)
target_Q_batch = self.target_Q_value.eval(feed_dict={self.state_input: next_state_batch})
for i in range(0,BATCH_SIZE):
done = minibatch[i][4]
if done:
y_batch.append(reward_batch[i])
else :
target_Q_value = target_Q_batch[i, max_action_next[i]]
##这是与DQN不同的地方,在Q网络中选择使Q(S')最大的action,
然后在目标Q'网络中找到该action对应的Q'(S')值来更新y
y_batch.append(reward_batch[i] + GAMMA * target_Q_value)
##DQN
# Step 2: calculate y
y_batch = []
Q_value_batch = self.target_Q_value.eval(feed_dict={self.state_input:next_state_batch})
for i in range(0,BATCH_SIZE):
done = minibatch[i][4]
if done:
y_batch.append(reward_batch[i])
else :
y_batch.append(reward_batch[i] + GAMMA * np.max(Q_value_batch[i]))
注意:(1)Double DQN是先在Q网络中找最大的Q(S')对应的action_max ,然后在目标Q’网络中找到action_max对应的Q'(S')值去更新y。(2)Q'目标网络的参数是从Q网络中定期(代码中是episode%10==0时)复制更新。但是经验池中有足够的经验以后,Q网络会不停地更新,Q’保持不变。然后每10episode再从Q网络复制更新一次Q'。
我的疑问: Q‘目标网络的参数是从Q网络中定期(代码中是episode%10==0时)复制更新的,那它们此时计算出来的Q值是一样的对吗?只不过在其他9次episode中,Q网络不停地更新,而Q'保持不变。
那这样的话,大体上Q'和Q参数变化趋势还是一样的,为什么就解决了过估计呢?
另外,为什么Q'网络参数要从Q网络中复制而来?为什么不是自己更新呢?
刘建平老师:这个要考虑Q’网络的作用,是解耦,尽可能的保证一定的独立性,同时参数总体趋势还是和Q一致。期望通过延时更新达到独立的目的。如果Q‘也和Q一样进行训练的话,那么复制参数没有意义,会把Q‘的训练搞乱,达不到独立的目的。
多个Q网络各个训练在其他场景也是可以做的,比如A3C里面,就是多个网络独立训练。它的目的是加快训练,达到并行的目的,而不是独立的目的。
六、优先回放(Prioritized Replay)DQN
一个理想的标准是智能体学习的效率越高,权重越大。符合该标准的一个选择是TD偏差δδ。TD偏差越大,说明该状态处的值函数与TD目标的差距越大,智能体的更新量越大,因此该处的学习效率越高。
七、Dueling DQN
动作值函数分解为 状态值函数(仅与状态有关)+优势函数(与状态动作都有关系)。因此,一共有两个相同结构的Q网络,每个Q网络都有状态函数和优势函数的定义,以及组合后的Q网络输出。
def create_Q_network(self):
# input layer
self.state_input = tf.placeholder("float", [None, self.state_dim])
# network weights
with tf.variable_scope('current_net'):
W1 = self.weight_variable([self.state_dim,20])
b1 = self.bias_variable([20])
# hidden layer 1
h_layer_1 = tf.nn.relu(tf.matmul(self.state_input,W1) + b1)
# hidden layer for state value
with tf.variable_scope('Value'):
W21= self.weight_variable([20,1])
b21 = self.bias_variable([1])
self.V = tf.matmul(h_layer_1, W21) + b21
# hidden layer for action value
with tf.variable_scope('Advantage'):
W22 = self.weight_variable([20,self.action_dim])
b22 = self.bias_variable([self.action_dim])
self.A = tf.matmul(h_layer_1, W22) + b22
# Q Value layer
self.Q_value = self.V + (self.A - tf.reduce_mean(self.A, axis=1, keep_dims=True))
with tf.variable_scope('target_net'):
W1t = self.weight_variable([self.state_dim,20])
b1t = self.bias_variable([20])
# hidden layer 1
h_layer_1t = tf.nn.relu(tf.matmul(self.state_input,W1t) + b1t)
# hidden layer for state value
with tf.variable_scope('Value'):
W2v = self.weight_variable([20,1])
b2v = self.bias_variable([1])
self.VT = tf.matmul(h_layer_1t, W2v) + b2v
# hidden layer for action value
with tf.variable_scope('Advantage'):
W2a = self.weight_variable([20,self.action_dim])
b2a = self.bias_variable([self.action_dim])
self.AT = tf.matmul(h_layer_1t, W2a) + b2a
# Q Value layer
self.target_Q_value = self.VT + (self.AT - tf.reduce_mean(self.AT, axis=1, keep_dims=True))
其余代码,和DQN的一样。https://github.com/ljpzzz/machinelearning/tree/master/reinforcement-learning
八、策略梯度法
在基于值函数方法中,我们用参数近似表示V值或者Q值,迭代使其变大;在基于策略的方法中,我们用参数近似表示策略,用蒙特卡洛方法近似得到V(迭代使得在策略下的累计回报变大)。
def create_softmax_network(self):
#最常用的策略函数就是softmax,它主要应用于离散空间中,
softmax策略使用描述状态和行为的特征与参数θ的线性组合来权衡一个行为发生的机率
# network weights
W1 = self.weight_variable([self.state_dim, 20])
b1 = self.bias_variable([20])
W2 = self.weight_variable([20, self.action_dim])
b2 = self.bias_variable([self.action_dim])
# input layer
self.state_input = tf.placeholder("float", [None, self.state_dim])
self.tf_acts = tf.placeholder(tf.int32, [None, ], name="actions_num")
self.tf_vt = tf.placeholder(tf.float32, [None, ], name="actions_value")
# hidden layers
h_layer = tf.nn.relu(tf.matmul(self.state_input, W1) + b1)
# softmax layer
self.softmax_input = tf.matmul(h_layer, W2) + b2
#softmax output
self.all_act_prob = tf.nn.softmax(self.softmax_input, name='act_prob')
self.neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.softmax_input,
labels=self.tf_acts)
self.loss = tf.reduce_mean(self.neg_log_prob * self.tf_vt)
# softmax交叉熵损失函数和状态价值函数的乘积,用交softmax是为了求真实值和实际结果的分布
#平均交叉熵应该是先计算batch中每一个样本的交叉熵后取平均计算得到的,而利用tf.reduce_mean函数其实计算的是整个矩阵的平均值
self.train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(self.loss)
def choose_action(self, observation):#用softmax网络计算的概率选择action
prob_weights = self.session.run(self.all_act_prob, feed_dict={self.state_input: observation[np.newaxis, :]})
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel()) # select action w.r.t the actions prob
return action
def learn(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 * 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)
#rs 表示一组状态序列中的归一化回报吧?不是Q值
# train on episode
self.session.run(self.train_op, feed_dict={
#这里的feed_dict是每个step存下来的一条从起点到终点的轨迹 的状态,action,累计回报
self.state_input: np.vstack(self.ep_obs),
self.tf_acts: np.array(self.ep_as),
self.tf_vt: discounted_ep_rs,
})
self.ep_obs, self.ep_as, self.ep_rs = [], [], [] # empty episode data
参考:https://www.cnblogs.com/pinard/p/10137696.html
九、Actor-Critic
在策略梯度方法中,策略函数就是Actor,用来更新策略、执行动作选择,而V值(或Q值)是用蒙特卡洛的方法,累计回报取平均得到的。在AC中,增加一个评估函数,用新的网络参数近似V值(Q值),就像DQN一样,评价策略更新得好不好。
我对此的疑问:在AC里面,策略更新有专门的更新公式,但是评估更新用的是loss。为什么不能反过来?或者是,两个都用loss?两个网络的更新方式不同,原因是什么? 师兄解答:其实都是用梯度下降法优化参数,只是对应的目的不同。
actor策略梯度更新公式:
critic 更新:均方差损失函数
####首先,choose_action()的结果来自Actor网络:用actor网络最后一层输出值经过softmax选择action,env.step后得到r和nextState。
h_layer = tf.nn.relu(tf.matmul(self.state_input, W1) + b1)
# softmax layer
self.softmax_input = tf.matmul(h_layer, W2) + b2
# softmax output
self.all_act_prob = tf.nn.softmax(self.softmax_input, name='act_prob')
def choose_action(self, observation):
prob_weights = self.session.run(self.all_act_prob, feed_dict={self.state_input: observation[np.newaxis, :]})
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel()) # select action w.r.t the actions prob
return action
####Q_value由critic网络得到。把由Actor求到的action、r、nextState输入critic,得到td_error。
####critic网络根据td_error 更新参数,目的是减小TDerror
def create_training_method(self):
self.next_value = tf.placeholder(tf.float32, [1,1], "v_next")
self.reward = tf.placeholder(tf.float32, None, 'reward')
with tf.variable_scope('squared_TD_error'):
self.td_error = self.reward + GAMMA * self.next_value - self.Q_value
self.loss = tf.square(self.td_error)
with tf.variable_scope('train'):
self.train_op = tf.train.AdamOptimizer(self.epsilon).minimize(self.loss)
def train_Q_network(self, state, reward, next_state):
s, s_ = state[np.newaxis, :], next_state[np.newaxis, :]
v_ = self.session.run(self.Q_value, {self.state_input: s_})
td_error, _ = self.session.run([self.td_error, self.train_op],
{self.state_input: s, self.next_value: v_, self.reward: reward})
return td_error
####Actor网络根据td_error和策略更新参数,目的是大化当前策略的价值
self.neg_log_prob = tf.nn.softmax_cross_entropy_with_logits(logits=self.softmax_input,
labels=self.tf_acts)
self.exp = tf.reduce_mean(self.neg_log_prob * self.td_error)
#这里需要最大化当前策略的价值,因此需要最大化self.exp,即最小化-self.exp
self.train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(-self.exp)
疑问: AC 算法存在难以收敛的问题。(为什么呢?)
critic_policy本身的问题:1) 比较难应用到随机型策略(stochastic policy)和连续的动作空间。2) value function的微小变化会引起策略变化巨大,从而影响训练收敛性。尤其是引入函数近似(function approximation,FA)后,虽然算法泛化能力提高了,但也引入了bias,从而使得训练的收敛性更加难以保证。
actor_policy的问题:通过将策略参数化,从而直接学习策略。这样做的好处是与前者相比拥有更好的收敛性,以及适用于高维连续动作空间及stochastic policy。但缺点包括梯度估计的变化幅度比较高,且容易收敛到非最优解。另外因为每次梯度的估计不依赖以往的估计,意味着无法充分利用老的信息,因此数据利用率低。
actor_critic :一方面actor学习策略,而策略更新依赖critic估计的value function;另一方面critic估计value function,而value function又是策略的函数。Policy和value function互为依赖,相互影响,因此更不容易收敛了。
改进方法:在DQN中,为了方便收敛使用了经验回放的技巧。A3C利用多线程的方法,同时在多个线程里面分别和环境进行交互学习,每个线程都把学习的成果汇总起来,整理保存在一个公共的地方。并且,定期从公共的地方把大家的齐心学习的成果拿回来,指导自己和环境后面的学习交互。通过这种方法,A3C避免了经验回放相关性过强的问题,同时做到了异步并发的学习模型。
A3C的优化主要有3点,分别是异步训练框架,网络结构优化,Critic评估点的优化
参考:https://www.cnblogs.com/pinard/p/10334127.html
十、A3C:Asynchronous Advantage Actor-critic 异步优势策略评估
由于AC算法难以收敛的问题,A3C采用DQN中经验回放的技巧,并且为了避免回放池经验数据相关性太强,用于训练的时候效果很可能不佳的问题,A3C采用多线程的方法,同时在多个线程里面分别和环境进行交互学习,每个线程都把学习的成果汇总起来,整理保存在一个公共的地方。并且,定期从公共的地方把大家的齐心学习的成果拿回来,指导自己和环境后面的学习交互。
因此,A3C比AC做出的优化是:
1、异步训练
Global Network是所有线程共享的公共部分,包括Actor和Critic两个功能。下面有n个worker线程,每个线程里有和公共的神经网络一样的网络结构,每个线程会独立的和环境进行交互得到经验数据,计算自己线程里的损失函数和梯度,去更新公共网络的参数(此时并不更新自己的网络参数),每隔一段时间,从公共网络复制参数为自己的参数。
2、网络结构优化
AC中分别有actor 和critic两个网络,而A3C把它们合并了,也就是用一个网络可以同时输出policy和value。
3、critic优化
AC中的优势函数只用到了本次reward、本次V值和下一状态的V值。公式:
在A3C中,使用了n步采样,以加速收敛。采样值由经验池提供。这样A3C中使用的优势函数表达为:
十一、DDPG:Deep Deterministic Policy Gradient 深度确定性策略梯度
Deep: 采用神经网络拟合策略和评价函数
Deterministic: actor 策略输出只有一个结果,每次只确定一个action。(而不像普通AC,得到的是很多个action的概率)
Policy Gradient: 采用策略梯度下降,分别有actor和critic 当前网络和目标网络共四个网络,目标网络参数定期从当前网络复制更新。
1. Actor当前网络:负责策略网络参数θ 的迭代更新,负责根据当前状态S选择当前动作A,用于和环境交互生成S′,R 。
2. Actor目标网络:负责根据经验回放池中采样的下一状态S′选择最优下一动作A' 。网络参数θ′定期从θ复制。
3. Critic当前网络:负责价值网络参数w的迭代更新,负责计算当前Q值Q(S,A,w) 。目标Q值yi=R+γQ′(S′,A′,w′)
4. Critic目标网络:负责计算目标Q值中的Q′(S′,A′,w′) 部分。网络参数w′定期从w复制。
actor策略梯度计算:
##### main 中
a = ddpg.choose_action(s) #经过actor当前网络,得到在action连续范围内的一个数
a = np.clip(np.random.normal(a, var), -2, 2)
#numpy.clip(a, a_min, a_max, out=None)把数组值限制在一个范围内
#numpy.random.normal(loc=0.0, scale=1.0, size=None),从均值为loc,方差为scale的正态分布,输出的shape,默认为None,只输出一个值
# add randomness to action selection for exploration 用正态分布的形式增加探索action
s_, r, done, info = env.step(a)
ddpg.store_transition(s, a, r / 10, s_)
if ddpg.pointer > MEMORY_CAPACITY:
var *= .9995 # decay the action randomness
ddpg.learn()
s = s_
ep_reward += r
###四个网络
with tf.variable_scope('Actor'):
self.a = self._build_a(self.S, scope='eval', trainable=True)#由actor当前网络得到a
a_ = self._build_a(self.S_, scope='target', trainable=False)#由actor目标网络得到s'的a'
with tf.variable_scope('Critic'):
# assign self.a = a in memory when calculating q for td_error,
# otherwise the self.a is from Actor when updating Actor
q = self._build_c(self.S, self.a, scope='eval', trainable=True)#由critic当前网络得到q
q_ = self._build_c(self.S_, a_, scope='target', trainable=False)#critic目标网络得到q_
###网络更新
q_target = self.R + GAMMA * q_
# in the feed_dic for the td_error, the self.a should change to actions in memory
td_error = tf.losses.mean_squared_error(labels=q_target, predictions=q)#用q q_计算tderror,更新critic网络
self.ctrain = tf.train.AdamOptimizer(LR_C).minimize(td_error, var_list=self.ce_params)
a_loss = - tf.reduce_mean(q) # maximize the q
self.atrain = tf.train.AdamOptimizer(LR_A).minimize(a_loss, var_list=self.ae_params)#更新actor网络以增大q