ML2.1 Deep Q Learning 学习和实践

1. 资料

2. Q Learning 的思路

强化学习的目标是:获得一个智能体 Agent ,当把环境信息告知 Agent 以后,Agent 可以做出”好”的决策。

有一些关键词:环境信息、“好”决策

  • 为了建模环境信息和交互影响,可以简单粗暴利用马尔科夫链来建模整个交互场景。
    • 每个状态 state 都可以被独立描述(不依赖历史和未来状态)
    • 特定 state 下做出特定的行为 action 后,会概率转移到某其他 state
    • 基于马尔科夫链可以极大简化建模的难度。虽然也有其限制
      • 很多现实问题的环境并非是独立的,要强行独立则需要给 state 加入历史信息,可能会导致状态空间非常大
  • 而为了评估”好”决策,则需要有价值评分机制

基于马尔科夫链和价值度量,我们可以建立一个 Q 值函数,用以度量各个 state 下各个 action 的 期望价值

\(Q(s, a) = \mathbb{E} \left[ \sum_{t=0}^{\infty} \gamma^t r_{t+1} \mid s_t = s, a_t = a \right]\)

  • s 表示状态
  • a 表示动作
  • \(r_{t+1}\) 是下一时刻的奖励
  • \(\gamma\) 是折扣因子,用来平衡当前奖励和未来奖励的重要性,0-1 之间,越小越看重当前奖励

不过我们最后更关心的 Q 值是在最优策略选择下的 \(Q^* \),如果获得了这么一个 \(Q^* \),那对于任意 state 我们只需要选择使 \(Q^* (s,a)\) 最大的那个 action 即可。即

\(Q^*(s, a) = \max_{\pi} \mathbb{E} \left[ \sum_{t=0}^{\infty} \gamma^t r_{t+1} \mid s_t = s, a_t = a, \pi \right]\)

  • 其中 \(\pi\) 表示 Agent 的策略,对应于在各个 state 下会选择的 action。\(a=\pi(s)\)

由于我们并不知道 \(Q^* \) 函数的真实函数形式,我们需要去寻找它。参考机器学习思路:

  1. 先建立一个带参数的 Q 函数 \(Q(s,a,\theta)\) ,其中 \(\theta\) 是待定的参数(组)。
  2. 通过类似随机梯度下降的方法去更新参数 \(\theta\),使 \(Q(s,a,\theta)\) 逼近 \(Q^*(s, a) \)
  3. 更新方法:\(Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left( r_t + \gamma \max_{a’} Q(s_{t+1}, a’) – Q(s_t, a_t) \right)\)
    • 不断用微小步伐向样本目标靠近(随机梯度下降)

3. Table Q Learning

3.1 思路说明

Q 本身是一个 状态,动作 -> 价值 (s,a -> v) 的函数,如果 s 和 a 的可能性都足够少,那完全可以建立一个 (s,a -> v) 的表去直接记录映射,如

States \ Actionsa1a2a3
s1v11v12v13
s2v21v22v23
s3v31v32v33

其参数组 \(\theta\) 最简单粗暴的设定方式,就是和表大小一致,每个 v 对应一个 \(\theta\)

即 \(Q(s,a,\theta) = \theta(s,a)\)

对应的参数更新方法 \(\theta(s_t, a_t) = \theta(s_t, a_t) + \alpha \left( r_t + \gamma \max_{a’} \theta(s_{t+1}, a’) – \theta(s_t, a_t) \right)\)

  • 需要不停和环境交互获得 \(s_t, a_t, r_t, s_{t+1}\) 的经验信息
  • 在尝试并获得经验过程中,临时的 action 选择策略也很重要,常用有
    • ε-greedy 策略:这是最常见的选择策略之一。在 ε-greedy 策略中,智能体以 ε 的概率选择随机动作(探索),以 1− ε 的概率选择当前最优的动作(利用)。ε 通常从较高的值(如 1.0)开始,逐渐降低到一个较低的值(如 0.01),以便在训练初期有更多探索,后期逐步收敛到利用。
    • Softmax 策略:将 Q 值转换成概率分布来选择动作,Q 值越高被选中概率越大。
    • Thompson Sampling(汤普森采样):估计各个动作的回报的概率分布,并基于分布进行采样。
    • … 其他策略

3.2 实践

用 OpenAI Gym 的经典强化学习环境 冰湖 来尝试 Table Q Learning。

简而言之:控制小人在 4*4 的冰湖上走动。目标是走到右下角拿奖励。冰湖上有洞,掉洞就死。可以选择上下左右走,但选择并不一定对应于上下左右,可能由于滑动导致行为和结果不一致(有干扰)。

我们在让 Agent 学习的过程中,采用 ε-greedy 策略进行探索。

详细实现过程的 Jupyter Notebook 见:https://github.com/Raytto/my_ml_study/blob/main/deep_q_study/s1_regular_q_agents.ipynb

训练结果:

基本能稳定在 65% 左右的胜率,考虑到冰湖场景下由于滑动天然就非必胜,基本上 70% 左右的成功率就是最好策略了。

4. 基本的 Deep Q Learning

4.1 思路说明

当 Q 函数的参数都是离散型且很有限变量时,用上面的 table 方式表示 Q 函数就非常直观方便。

但是如果 state 和 action 的可能性都非常多,取两个值域的笛卡尔积可能构成非常庞大的集合,甚至如果 state 或 action 是连续变量时,构成的集合是无限大的。这种情况下则无法用 table 表示了。

这种情况下最方便的做法是采用深度神经网络(DNN)。

  • 根据通用逼近定理,当 DNN 神经元足够多且层数足够时,可以拟合任意连续函数
  • 当 DNN 结合 ReLU 等非线性函数后,可以拟合任意分段连续的函数

即用 DNN 来拟合我们的 Q 函数,如果 DNN 的各连接参数为 \(\theta\),则可简单表示为 \(Q(s,a,\theta) = N(s,a,\theta)\)

不过由于 DNN 的最后一层可以是多个输出神经元。如果 action 是离散的,则可以让最后一层的神经元和 action 的各个可能性一一对应。

  • 这样仅需要一次 state 的输入即可得到此 state 下各个 action 的 Q 值。由于决策时确实需要知道所有 action 的 Q 值才行。所以这样建模后使用会比较方便

这样一来,则可表示为 \(Q(s,a,\theta) = N(s,\theta)[a]\)

  • 取 action 对应的的那个输出结果

学习时采用同样思路的更新方法:\(N(s_t,\theta)[a] \leftarrow N(s_t, \theta)[a_t] + \alpha \left( r_t + \gamma \max_{a’} N(s_{t+1})[a’] – N(s_t)[a_t] \right)\)

  • 不过不能像之前 Table Q Learning 那样,直接对 \(\theta\) 进行更新了。需要用反向传播,同步更新 DNN 所有参数。
    • 事实上,table Q Learning 可以视作单层单分支的神经网络,反向传播的梯度都全作用到一个 \(\theta\) 参数上。

4.2 实践

这次用 OpenAI Gym 的经典强化学习环境 小车和杆 来尝试 CartPole-v1。

很经典的控制问题:一个小车上面有一根杆,小车只能在一条线上左右移动。目标是尽可能使上面的杆保持平衡不倒,平衡时间越长分越高(500为上限,会强制结束一次测试)

我们在让 Agent 学习的过程中,继续采用 ε-greedy 策略进行探索。不过 Q 函数替换为简单的 DNN 进行拟合。

详细实现过程的 Jupyter Notebook 见:https://github.com/Raytto/my_ml_study/blob/main/deep_q_study/s2_deep_q_agents.ipynb

训练结果:

  • 可见在初期训练中,Agent 实力确实在上涨。但到一个阶段(也仅仅40分,上限500分)后就反而越来越差。

可能的原因

  • 模型是用一个个样本进行训练的,可能导致新的单一样本对模型产生极大的参数干扰

4. 优化 Deep Q Learning 的表现

4.1 尝试添加 DropOut

参考 GPT 实现中使用过的 dropout 方法,训练中随机抛弃一部分神经元。以减少过拟合的可能性,提升模型的稳定性。

详细实现过程的 Jupyter Notebook 见:https://github.com/Raytto/my_ml_study/blob/main/deep_q_study/s3_deep_q_agents_dropout.ipynb

  • 仅修改了 DNN 部分,对每一层神经元都添加了 dropout

训练结果:

可见有 dropout 确实比没 dropout 的基础版好了不少

  • 最高达到120分
  • 没有在某个时间后一直趴在低分区

但依旧不够好

  • 没达到500的最高分左右,代表还是不能驾驭 CartPole 场景
  • 训练曲线不稳定,不收敛

4.2 尝试添加记忆回放

为了进一步增强模型的稳定性。减少经验对参数的严重干扰。一种常用的方式是基于批量的经验进行学习。

  • 对批次中各个样本求参数梯度后求平均。更高概率抵消掉噪音性质的梯度,留下对学习有价值的梯度。

但为了做到这一点,需要把历史经验保存下来,并每次学习的时候随机取一部分历史经验。

详细实现过程的 Jupyter Notebook 见:https://github.com/Raytto/my_ml_study/blob/main/deep_q_study/s4_deep_q_agents%20mem.ipynb

  • Agent 中添加 memory 以记录经验
  • 添加 sample_memory 以每次随机选取一批经验用于学习
  • 修改 learn 方法,每次学习的时候基于一批次的经验(给每个数据添加一个 batch 维度,从单数据变成张量)

训练结果:

可见模型收敛性上确实好了不少,效果明显。但分数依旧不算很高(仅90左右)。

首先能想到的是 DNN 神经元不够,没能拟合场景需要的 Q*

4.3 尝试扩大模型规模

先尝试把模型每一层的神经元数量从128提升到256

详细实现过程的 Jupyter Notebook 见:https://github.com/Raytto/my_ml_study/blob/main/deep_q_study/s5_deep_q_agents%20256.ipynb

训练结果:

模型扩大到256后,结果确实好了一些,但有限。

继续尝试扩大,每一层加到512个神经元,并且加一层 LayerNorm 尝试稳定训练过程。

详细实现过程的 Jupyter Notebook 见:https://github.com/Raytto/my_ml_study/blob/main/deep_q_study/s6_deep_q_agents%20512_norm.ipynb

训练结果:

确实表现又有进一步,但并不太满意:

  • 首先小车平衡单节的杆的问题本身也不算太复杂,却需要依赖如此多神经元,学习和实际推理效率不高。
  • 学习曲线上依旧不是很稳定,甚至随时间还略有变差的趋势

4.4 尝试 Double Q Learning

发表评论