CompModCogPsy
  • 全书介绍和写作计划
  • 第一章 计算认知科学导论
    • 前言
    • 1.1 交叉学科三角
    • 1.2 认知科学的特点
    • 1.3 认知科学的发展历史
    • 1.4 我们为什么需要计算认知
      • 1.4.1 认知科学的基础假设:信息处理理论
      • 1.4.2 挑战与“诞生”
      • 1.4.3 计算认知的必要性
  • 第二章 计算模型基础
    • 2.1 什么是计算模型?
    • 2.2 模型选择
    • 2.3 模型拟合
    • 2.4 模型准确度
    • 2.5 模型可信度
  • 第三章 概率推断和贝叶斯理论
    • 3.1 概率基础
    • 3.2 概率推断
      • 3.2.1 似然函数
      • 3.2.2 最大似然估计
    • 3.3 贝叶斯理论
    • 3.4 拓展阅读:p值
    • 3.5 编程练习-最大似然估计
  • 第四章 心理物理学和信号检测论
    • 心理物理学基础
    • 心理物理曲线
      • 几种常见的心理物理曲线
      • 拟合心理物理曲线
    • 信号检测论
      • dprime
      • 决策标准
      • receiver operating curve (ROC)曲线和area under curve (AUC)
      • dprime和AUC的关系
      • 2AFC的应用
      • Page
    • 展望
  • 第五章 近似推断
    • 马尔科夫链蒙特卡洛采样
      • Metropolis-Hasting算法
    • 变分推断
    • 展望
  • 第六章 知觉决策
    • 模拟一个简单知觉决策
    • 模拟决策和反应时
    • 权衡反应时和正确率
    • 6.4 经典漂移扩散模型
    • 漂移扩散模型的应用
      • 基于价值的决策
      • 精神疾病的应用
      • 社会认知
    • 展望
  • 第七章 价值决策
    • 人类决策基础
    • 前景理论
    • 风险决策
    • 展望
  • 第八章 强化学习
    • 机器学习强化学习基础
      • 动态规划
      • 时间差分学习
      • 基于模型和无模型强化学习
    • 心理学的强化学习
    • 强化学习的交叉关系
    • 强化学习模型和参数估计
    • Rescorlar-wagner模型
    • 二阶段任务
    • 展望
  • 第九章 社会决策和社会学习
    • 社会决策
    • 社会学习
    • 展望
  • 第十章 神经网络
    • 神经网络和心理学引言
    • 神经网络基础
      • 多层感知机
      • 卷积神经网络
      • 循环神经网络
    • 神经网络和人脑加工的关系
      • 感知觉的编解码
      • 工作记忆
      • 长时记忆
      • 学习和决策
    • 展望
由 GitBook 提供支持
在本页
  1. 第八章 强化学习
  2. 机器学习强化学习基础

时间差分学习

先前介绍的动态规划(Dynamic programming)的求解方法是基于模型(model-based)的,也就是当我们知道环境 Env{S,A,P,R}Env\{S,A,P,R\}Env{S,A,P,R} 的情况下。但是现实生活中的状态空间 SSS 和动作空间 AAA 往往是无限的,并且我们很少知道当前状态 ss s 与下一个状态 s′s's′ 之间的转换函数 P(s′∣s,a)P(s'\mid s, a)P(s′∣s,a)与下一个状态的奖励 R(s′)R(s')R(s′),我们只能从经验中进行学习推导,由此引出一种算法——“时间差分学习”来解决问题。

时间差分学习(Temporal Difference Learning,简称TD学习)是一种强化学习算法,用于通过经验来预测未来奖励或值的估计。TD学习结合了蒙特卡罗方法和动态规划的优点,在不需要模型的情况下进行学习。它通过更新当前估计值,以使其更接近于下一个时刻的估计值,从而逐步改进对价值的预测。

举个例子来方便我们理解时间差分学习算法是怎样工作的。假设你需要预测星期六的天气,并且手头上正好有相关的模型。按照一般的方法,你只有到星期六才能根据结果对你的模型进行调整。然而,当到了星期五时,你应该对星期六的天气有很好的判断。因此在星期六到来之前,你就能够调整你的模型以预测星期六的天气。

让我们回到前面所讲述的贝尔曼期望方程(Bellman equation),这个方程的含义是在给定策略 π\piπ的情况时价值函数 VVV在策略指引下所采轨迹上的期望。在我们不知道转换函数 P(s′∣s,a)P(s'\mid s, a) P(s′∣s,a)的情况下,我们可以尝试很多次来对价值函数 VVV进行估计。

Vπ(st)=∑aπ(at∣st)∑st+1p(st+1∣st,at)[rt+γVπ(st+1)]=Eat,st+1∼π,p[rt+γVπ(st+1)]≈1N∑iNrti+γVπ(st+1i)V^\pi\left(s_t\right)= \sum_a \pi\left(a_t \mid s_t\right) \sum_{s_{t+1}} p\left(s_{t+1} \mid s_t, a_t\right)\left[r_t+\gamma V^\pi\left(s_{t+1}\right)\right]\\ \begin{align} & =\mathbb{E}_{a_t, s_{t+1} \sim \pi, p}\left[r_t+\gamma V^\pi\left(s_{t+1}\right)\right] \\ & \approx \frac{1}{N} \sum_i^N r_t^i+\gamma V^\pi\left(s_{t+1}^i\right) \end{align}Vπ(st​)=a∑​π(at​∣st​)st+1​∑​p(st+1​∣st​,at​)[rt​+γVπ(st+1​)]​=Eat​,st+1​∼π,p​[rt​+γVπ(st+1​)]≈N1​i∑N​rti​+γVπ(st+1i​)​​

那能不能更简单一点呢?——将 NNN假设为1带入。其中 {rt+γVπ(st+1i)−Vπ(st)}\{r_t + \gamma V^\pi\left(s_{t+1}^i\right) - V^\pi\left(s_t\right)\}{rt​+γVπ(st+1i​)−Vπ(st​)}这一项即为更新量

Vπ(st)≈rt + γVπ(st+1) = Vπ(st) + rt + γVπ(st+1) − Vπ(st)\begin{align} V^\pi\left(s_t\right) \approx r_t  + \gamma V^\pi\left(s_{t+1}\right) = V^\pi\left(s_t\right) + r_t + \gamma V^\pi\left(s_{t+1}\right) - V^\pi\left(s_t\right) \end{align}Vπ(st​)≈rt​ + γVπ(st+1​) = Vπ(st​) + rt​ + γVπ(st+1​) − Vπ(st​)​​

显然直接带入 N=1N=1N=1 是不准确的估算,所以需要利用学习率 α\alphaα 来调节更新的多少。其中 rt+γVπ(st+1)r_t + \gamma V^\pi\left(s_{t+1}\right)rt​+γVπ(st+1​)这一项就为TD target, rt+γVπ(st+1)−Vπ(st)r_t + \gamma V^\pi\left(s_{t+1}\right) - V^\pi\left(s_t\right)rt​+γVπ(st+1​)−Vπ(st​)为更新量,即预测误差(prediction error)。

Vπ(st)= Vπ(st) + α(rt + γVπ(st+1) − Vπ(st))\begin{align} V^\pi\left(s_t\right) = V^\pi\left(s_t\right) + \alpha(r_t + \gamma V^\pi\left(s_{t+1}\right) - V^\pi\left(s_t\right)) \end{align}Vπ(st​)= Vπ(st​) + α(rt​ + γVπ(st+1​) − Vπ(st​))​​

推出这个公式有什么用呢?在这个公式下,我们的目标是让模型对状态 ss s 的评估更为准确,即状态价值函数 VVV 更准确。当agent根据策略policy选择动作 ata_tat​时,环境会给出下一个状态 st+1s_{t+1}st+1​与之相应的奖赏 rt+1r_{t+1}rt+1​,利用状态价值函数计算给出当前状态 st+1s_{t+1}st+1​的价值,最后计算error以此优化状态价值函数VVV 。

对于一个强化学习的任务,我们需要agent面对状态 sts_{t} st​ 做出最优的动作 ata_tat​ 。当目标是训练agent在面对状态时做出更好的动作action时,我们应该利用评估动作的动作价值函数 QQ Q。

下面是贝尔曼方程的动作价值函数 QQ Q 形式公式(5),与之前的推导一致,我们获得Q learning的公式(6)。

Qπ(st,at)=∑st+1p(st+1∣st,at)[rt+γmax⁡aQπ(st+1,a)]\begin{align} Q^\pi\left(s_t, a_t\right)=\sum_{s_{t+1}} p\left(s_{t+1} \mid s_t, a_t\right)\left[r_t+\gamma \max _a Q^\pi\left(s_{t+1}, a\right)\right] \end{align}Qπ(st​,at​)=st+1​∑​p(st+1​∣st​,at​)[rt​+γamax​Qπ(st+1​,a)]​​
Qπ(st,at)=Qπ(st,at)+α(r+γmax⁡aQπ(st+1,a)−Qπ(st,at))\begin{align} Q^\pi\left(s_t, a_t\right)=Q^\pi\left(s_t, a_t\right)+\alpha\left(r+\gamma \max _a Q^\pi\left(s_{t+1}, a\right)-Q^\pi\left(s_t, a_t\right)\right) \end{align}Qπ(st​,at​)=Qπ(st​,at​)+α(r+γamax​Qπ(st+1​,a)−Qπ(st​,at​))​​

接下来,我们以frozen_lake为例, 结合代码完整理解Q-learning:

首先,初始化环境

seed = 1234 # 设置随机种子 
env = frozen_lake(seed=seed) # 以种子初始化冰湖   
env.reset() # 环境重制 
fig, ax = plt.subplots(1, 1, figsize=(4, 4))
env.render(ax) # 渲染

如图为渲染后的初始化的冰湖游戏,图中O为起始点,G为终点,蓝色方块为障碍物。

首先建立需要用到的参数

q = np.random.randn(env.nS, env.nA) 
# 动作价值函数:代表在一个状态下做出一个动作的价值 
# 在冰湖游戏里为64个格子(长8*宽8) * 4中动作(上,下,左,右)
rng = np.random.RandomState(seed)
# 通过种子固定随机数生成器

策略policy我们就利用最简单的贪心策略,即agent在做选择时,只考虑当前选择的最优选。

def e_greedy(q, rng, env, eps):
    # epsilon greedy decision policy
    # q: 动作价值(Q值)列表或数组,表示每个可能动作的预期回报。注意虽然上述q为64*4,
    # 但在选择时,我们只对于一个状态做选择,即放入函数的q为1*4
    # rng: 随机数生成器(通常是 np.random 或者带有随机状态的生成器)。
    # env: 环境对象,应该包含动作空间的大小(env.nA 表示动作的总数)。
    # eps: epsilon 值,表示探索的概率(即选择随机动作的概率)
    
    a_max = np.argwhere(q==np.max(q)).flatten()
    # np.argwhere(q==np.max(q)) 返回一个包含最大 Q 值对应索引的数组。
    # .flatten() 将多维数组压平为一维数组。
    
    policy = np.sum([np.eye(env.nA)[i] for i in a_max], axis=0) / len(a_max)
    # 这里构建了一个策略(policy),其中每个最大的 Q 值动作被赋予相同的选择概率。
    
    # np.eye(env.nA) 创建一个大小为 env.nA 的单位矩阵,4*4
    # array([[1., 0., 0., 0.],
    #        [0., 1., 0., 0.],
    #        [0., 0., 1., 0.],
    #        [0., 0., 0., 1.]])
    # 每一行代表一个动作的 one-hot 编码。
    
    # [np.eye(env.nA)[i] for i in a_max] 选择与 a_max 中索引对应的动作,
    # 生成这些动作的 one-hot 编码。
    # np.sum(..., axis=0) / len(a_max) 对所有这些 one-hot 编码进行平均,
    # 以便将每个最大 Q 值动作的选择概率平均分配。

    if rng.rand() < 1-eps:
        a = rng.choice(env.nA, p=policy) # 按照policy的概率来选 policy为1*4的列表
    else:
        a = rng.choice(env.nA) # 随机来选
    return a 

初始策略决定好,进入Q_learning函数的编写

def Q_learning(env, alpha=.1, eps=.1, gamma=.99, max_epi=2000, 
               seed=1234, theta=1e-4, show_update=False, Q=None):
    # rng
    rng = np.random.RandomState(seed)
    # initialize Q
    if Q is None: Q = np.zeros([env.nS, env.nA])
    for epi in range(max_epi):
        s, r, done = env.reset()
        t = 0 
        q_old = Q.copy()
        G = 0
        while True:
            # sample At, observe Rt, St+1
            # 采样t时刻的action At,观察回报Rt 和 下一个状态St+1
            a = e_greedy(Q[s, :], rng, env, eps) 
            # a = rng.choice(env.A, p=pi)
            s_next, r, done = env.step(a) # move to the next step
            
            # the key step of Q learning
            # 贝尔曼方程
            Q[s, a] = Q[s, a]+ alpha*(r + gamma*(1-done)*(Q[s_next, :]).max() - Q[s, a])
            
            
            s = s_next 
            t += 1
            G += r
            
            if done:
                break 
            
            if show_update:
            #    渲染结果
                Pi = np.eye(env.nA)[np.argmax(Q, axis=1)]
                V = Q.max(1)
                fig, axs = plt.subplots(1, 3, figsize=(11, 4))
                clear_output(True)
                ax = axs[0]
                env.render(ax)
                #print(env.state, s, a)
                ax = axs[1]
                env.show_v(ax, V)
                ax = axs[2]
                env.show_pi(ax, Pi)
                time.sleep(.05)
                plt.show()
                
        if (np.abs(q_old - Q)<theta).all():
            # 采样到收敛
            break
    pi = np.eye(env.nA)[np.argmax(Q, axis=1)] 
    # 选择最优的动作
    return Q, pi

最终的结果

最后更新于8个月前

图1 冰湖游戏全局状态
图2 Q learning算法在冰湖游戏中模拟的最终结果