【读书笔记】【机器学习实战】第十六章:强化学习

阅读书籍为《Hands-On Machine Learning with Scikit-Learn & TensorFlow》王静源等翻译的中文译版《机器学习实战,基于 Scikit-Learn 和 TensorFlow》,本文中所有图片均来自于书籍相关部分截图。

强化学习(RL)

2013年英国一家创业公司DeepMind开发出来一个可以自动学习玩游戏的系统。这个系统可以从头学习Atari公司的所有游戏,仅以原始的像素点作为输入,在不知道游戏规则的情况下获得比大部分人都好的成绩;2016年AlphaGo大败李世石,更是让强化学习名声大噪。

  1. 强化学习(再励学习, 评价学习)Reinforce Learning:来源于心理学上的行为主义理论;
  2. 行为主义理论基本观点如下:学习是刺激与反映之间的连结;学习的过程是一种渐进的“尝试与错误”直至最后成功的过程;强化是学习成功的关键;
  3. 简单来说就是:个体在环境中行动,“好”行为获取激励,“坏”行为获取惩罚,在不断尝试的过程中学会“好坏”的标准,个体以激励最大化为目标;
  4. 强化学习中会将每个结点看成一个智能体,学习过程中,智能体自由做出决策,系统根据决策给智能体以奖励或惩罚。通过不断学习,智能体将会作出可以获得奖励做多的行为反应。
    关于强化学习更书面简洁的解释如下:https://blog.csdn.net/wangh0802/article/details/84849241

技术点

策略搜索

策略:决定智能体行为的算法被称为策略;所以策略可以是一个生成行为决定的网络,任何可以产生行为决策的都可以被称为策略;
场景带入:假如我们现在有一个吸尘器机器人,在工作中有概率P向前移动,1-p的概率向左或向右旋转随机角度[-r,r]。机器人的回报就是30分钟内收集的灰尘量(越多越好)。我们应该如何寻找最佳的策略(一定时间内收集灰尘量最多)?在此我们的参数有P和r;
我们的搜索任务是找出最佳的p r组合使得一定时间内吸尘器收集到的灰尘最多;有如下三种解决办法:

  1. 暴力搜索
    在参数的取值范围内,逐一试验对比结果;显然暴力算法在数据量大时并不满足;
  2. 遗传算法
    随机的创造N = 100个策略,遍历这100个策略找出最好的20个;为这20个策略添加随机变化,使每个产生4个新策略;
    目前我们有了第二代的(20+20*4 = 100个)策略;重复上述迭代过程直到发现一个好的策略;
  3. 策略梯度PG(将会在下文中重点讲解)
    这是一种基于神经网络的优化算法:对策略参数的回报梯度的评估使用优化技术,然后根据获得较高回报的梯度(梯度上升)调整这些参数;
    神经网络策略原理如下:
    在这里插入图片描述
    这里有个我觉得很有趣的点:神经网络每次的结果都是依据概率而产生,之所以不直接依据最大值是因为要给其他决策产生的机会。作者举了去餐馆吃饭的例子十分巧妙:去陌生餐厅吃到一个好吃的菜,会使下次你继续点它的概率提升,但不会到100%,如果到100%每次都只点这一道菜,就永远无法尝试其他菜。

评估行为:信用分配问题

相比于监督学习的实时指导,强化学习中的指导一般是稀疏且延迟的。这就引出了信用分配问题;
信用分配问题:信用分配问题:当代理获得回报时,很难知道哪些行为对该结果有正面或者负面影响。可以想象一只小狗表现
好的几个小时后获得了奖励;它会明白它为什么获得奖励吗?(我的理解是智能体只会“记忆”单次行为的好坏,但并不会考虑行为之间的联系,比如当前行为可能很好,但如果选择下一步的惩罚会更大,或者当前惩罚比较大,但下一步的奖励也可能更多。)
常见的解决方法是根据奖励的总和来评估一个行为,并在每步之后乘以折扣率(折扣率并不是固定值,下图只作为原理说明),如下图所示:
当前向右的行为虽然奖励了10分,但是两步后就会被惩罚50分。所以当前的行为回报值就不能还是10分,而应该是10+00.8+0.80.8*(-50) = -22分,所以如果有其他选择比如奖励只有5分但“长期回报”是-10分或者更高智能体就应该知道向右并不是一个好行为;
在这里插入图片描述

策略梯度

策略梯度算法PG主要思想是通过在梯度后面跟随更高的回报来优化策略参数。
具体实现步骤如下:
1.首先,运行神经网络,计算每一次更有可能被选择的行为的梯度,但并不实施。
2.运行即便后,计算每个行为的得分呢(使用上述方法)。
3.如果一个行为的得分是正数,意味着行为是好的,我们希望使用之前计算的这些梯度,使得该行为在未来更有可能被选择。反之如果是负数,则为不好,我们会希望使用相反的梯度使得该行为在未来不被采用。具体解决的办法是简单的将每个梯度向量乘以每个行为的分数。
4.计算所有梯度的平均值来执行最终的梯度下降步骤。

TF实现如下:

import gym
import numpy as np
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected

env = gym.make("CartPole-v0")

n_inputs = 4
n_hidden = 4
n_outputs = 1
initializer = tf.contrib.layers.variance_scaling_initializer()
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
hidden = fully_connected(X, n_hidden, activation_fn=tf.nn.elu, weights_initializer=initializer)
logits = fully_connected(hidden, n_outputs, activation_fn=None, weights_initializer=initializer)
outputs = tf.nn.sigmoid(logits)
p_left_and_right = tf.concat(axis=1, values=[outputs, 1 - outputs])
action = tf.multinomial(tf.log(p_left_and_right), num_samples=1)
y = 1. - tf.to_float(action)
cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(
 labels=y, logits=logits)
optimizer = tf.train.AdamOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(cross_entropy)
gradients = [grad for grad, variable in grads_and_vars]
gradient_placeholders = []
grads_and_vars_feed = []
for grad, variable in grads_and_vars:
    gradient_placeholder = tf.placeholder(tf.float32, shape=grad.get_shape())
    gradient_placeholders.append(gradient_placeholder)
    grads_and_vars_feed.append((gradient_placeholder, variable))

training_op = optimizer.apply_gradients(grads_and_vars_feed)
init = tf.global_variables_initializer()
saver = tf.train.Saver()


def discount_rewards(rewards, discount_rate):
    discounted_rewards = np.empty(len(rewards))
    cumulative_rewards = 0
    for step in reversed(range(len(rewards))):
        cumulative_rewards = rewards[step] + cumulative_rewards * discount_rate
        discounted_rewards[step] = cumulative_rewards
    return discounted_rewards


def discount_and_normalize_rewards(all_rewards, discount_rate):
    all_discounted_rewards = [discount_rewards(rewards, discount_rate) for rewards in all_rewards]
    flat_rewards = np.concatenate(all_discounted_rewards)
    reward_mean = flat_rewards.mean()
    reward_std = flat_rewards.std()
    return [(discounted_rewards - reward_mean)/reward_std
            for discounted_rewards in all_discounted_rewards]


print(discount_rewards([10, 0, -50], discount_rate=0.8))
print(discount_and_normalize_rewards([[10, 0, -50], [10, 20]], discount_rate=0.8))

n_iterations = 250 # number of training iterations
n_max_steps = 1000 # max steps per episode
n_games_per_update = 10 # train the policy every 10 episodes
save_iterations = 10 # save the model every 10 training iterations
discount_rate = 0.95
with tf.Session() as sess:
    init.run()
    for iteration in range(n_iterations):
        all_rewards = [] # all sequences of raw rewards for each episode
        all_gradients = [] # gradients saved at each step of each episode
        for game in range(n_games_per_update):
            current_rewards = [] # all raw rewards from the current episode
            current_gradients = [] # all gradients from the current episode
        obs = env.reset()
        for step in range(n_max_steps):
            action_val, gradients_val = sess.run([action, gradients], feed_dict={X: obs.reshape(1, n_inputs)}) # one obs
            obs, reward, done, info = env.step(action_val[0][0])
            current_rewards.append(reward)
            current_gradients.append(gradients_val)
            if done:
                break
        all_rewards.append(current_rewards)
        all_gradients.append(current_gradients)
    all_rewards = discount_and_normalize_rewards(all_rewards, discount_rate=0.95)
    feed_dict = {}
    for var_index, grad_placeholder in enumerate(gradient_placeholders):
        # multiply the gradients by the action scores, and compute the mean
        mean_gradients = np.mean([reward * all_gradients[game_index][step][var_index]
                                 for game_index, rewards in enumerate(all_rewards) for step, reward in enumerate(rewards)], axis=0)
        feed_dict[grad_placeholder] = mean_gradients
    sess.run(training_op, feed_dict=feed_dict)
    if iteration % save_iterations == 0:
        saver.save(sess, "my_policy_net_pg.ckpt")

马尔可夫决策过程MDP

  1. 马尔可夫链
    下图展示了一个有四个状态的Markov链:

    1. 状态S0下一步有0.7的概率回到自己,有0.2的概率到状态S1, 有0.1的概率到S3,一旦出去再也无法返回当前状态;
    2. 状态S1下一步有0.9的概率到S2,有0.1的概率到S3,如果到了S3就无法返回当前状态; 状态S2下一步有100%的概率去状态S1;
    3. 状态S3的状态不会改变,最终态;

    马尔可夫链刻画了系统中的状态转移关系,被广泛应用到于热力学、化学、统计学等。
    在这里插入图片描述

  2. 马尔可夫决策
    在马尔可夫链的基础上做了些许改变:下一步的概率依赖于行为的回报;

    1. 状态s0开始,智能体可以在行为a0、a1、a2中间选择一个。如果选择行为a1,它就肯定保持在状态s0,且不获得任何回报。因此,如果愿意,它可以永远保持在该状态。但是如果选择行为a0,它有70%的概率获得+10的回报,并保持在状态s0。然后可以一次又一次地尝试,并获得尽可能多的回报。但是在某时,它会结束s0状态进入s1状态。
    2. s1状态下,它有两种可能的行为:a0或者a1。它可以通过重复地选择行为a1而保持在该状态,或者移动到状态s2并获得-50的回报。
    3. 状态s2,它别无选择地选择行为a1,该行为有很大可能导致该过程回到s0状态,并获得+40的回报。
      在这里插入图片描述
      结点最优值的计算公式如下(贝尔曼最优方程式):
      在这里插入图片描述
      根据这个方程可以推导出一个更为简洁的求解最优值的算法:值迭代算法
      值迭代算法:首先将所有估计的状态值初始化为零,然后使用值迭代算法迭代更新。迭代公式如下:
      在这里插入图片描述
      在此基础上贝尔曼发现了一个更好的方法进行估算:Q值迭代算法
      在这里插入图片描述
      编程实现如上的算法观察效果:
nan = np.nan # represents impossible actions
T = np.array([# shape=[s, a, s']
    [[0.7, 0.3, 0.0], [1.0, 0.0, 0.0], [0.8, 0.2, 0.0]],
    [[0.0, 1.0, 0.0], [nan, nan, nan], [0.0, 0.0, 1.0]],
    [[nan, nan, nan], [0.8, 0.1, 0.1], [nan, nan, nan]],
])
R = np.array([ # shape=[s, a, s']
    [[10., 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
    [[10., 0.0, 0.0], [nan, nan, nan], [0.0, 0.0, -50.]],
    [[nan, nan, nan], [40., 0.0, 0.0], [nan, nan, nan]],
])
possible_actions = [[0, 1, 2], [0, 2], [1]]

Q = np.full((3, 3), -np.inf) # -inf for impossible actions
for state, actions in enumerate(possible_actions):
    Q[state, actions] = 0.0 # Initial value = 0.0, for all possible actions
learning_rate = 0.01
discount_rate = 0.95
n_iterations = 100
for iteration in range(n_iterations):
    Q_prev = Q.copy()
    for s in range(3):
        for a in possible_actions[s]:
            Q[s, a] = np.sum([T[s, a, sp] * (R[s, a, sp] + discount_rate * np.max(Q_prev[sp])) for sp in range(3)])
print(Q)
print(np.argmax(Q, axis=1))

可见经过Q值迭代算法智能体选择了通过火焰这种总代价较小的策略:
这给出了折扣率为0.95时,该MDP的最优策略:状态s0时选择行为a0,状态s1时选择行为a2(通过火焰!),状态s2时选择行为a1(唯一可能的行为)。有趣的是,当折扣率降低为0.9时,最优策略改变为:状态s1时最佳行为变为a0(保持状态,不通过火焰)。这是有道理的,相比于未来如果更看中现在的价值,那么未来回报的前景就不值得立即付出。
在这里插入图片描述

差分学习与Q学习

具有离散行为的强化学习问题通常可以建模为马尔可夫过程进行决策,但是当智能体新开始进入一个环境中时并不知道转换概率是什么,也不知道回报是什么。智能体必须至少经历一遍每一个状态才能指导相关信息。如果相对转换概率进行合理的估计,智能体就需要体验更多次。
时间差分算法与值迭代算法非常相似,但另外考虑了智能体刚进入一个环境时的状况。
在这里插入图片描述
对每个状态s,该算法简单地持续跟踪代理离开该状态时获得的即时回报的平均值,加上期望以后得到的回报(假设行为最佳)。
类似地,Q学习算法是对Q值算法在初始状态时转换概率和回报未知情况下的调整。
在这里插入图片描述

深度Q学习

深度Q学习框架如下:
在这里插入图片描述
成本函数:
在这里插入图片描述
在这里插入图片描述

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页