学习记录#2:超小白地,入门强化学习吧
首先,正经学RL,去看书或者大佬的博客,然后动手跟着案例去做,比较好。
我写这个学习记录还有一大原因,目前许多国内各种教程,总是上来甩给观众各种数学概念,然后学生懵懵地把概念啃完,跟着实操做几步,背几步,能把考试题啊面试题啊什么的都做对,就自认为学会了。但是,我认为这不是一个正常的学习过程。我眼中的正常学习过程,是遇到一个个问题,然后不断寻找问题的解决方案,各个知识点的出现有一定的逻辑过程/发展过程。
人工智能有两大基础部分,神经网络和机器学习,前者是一个实在的结构,后者是前者获取智能的方法。机器学习主要分为三种:监督学习、无监督学习和强化学习。
- 监督学习有点像刷题:我们出一些互相没有显著关联的题目,智能体给出答案,我们将这个答案和正确答案比对,以此训练智能体。
- 无监督学习有点像寻宝:智能体在大量数据中挖掘潜在结构与内在规律,无监督学习的典型的问题是聚类问题。
- 强化学习有点像游戏:每次智能体给出操作,我们考虑这个操作的后果与期望的目标,以此训练智能体。
这可能值得强调,强化学习是一种学习方法,强化学习这个概念与使用什么智能体或什么神经网络是相独立的。
下面我们主要讨论的是强化学习:
这里有一个经典的强化学习在干什么的概念图:智能体根据环境做出动作,环境给予智能体反馈,奖罚智能体,并告诉智能体当前世界的状态。


一言以蔽之,强化学习是这样一个模型:智能体从环境获得 Observation,做出 Action 改变环境,针对智能体做的怎么样,我们给出 Reward 进行评判,智能体要做的,是让 Reward 最大。
准备工作
作为准备工作,python 的 gymnasium 是一个实践强化学习的非常好的库,下面所有内容都基于这个库: https://gymnasium.farama.org/
首先,创建 python 环境,这里使用 python3.12 是因为这是一个稳定的版本,有torch也有tenserflow,方便之后搞事情……。注意这里装的是 gymnasium 因为 gym 已经停止维护了,安装 gymnasium 时可以带上 [all] 装上所有特性,后续使用各种环境的时候不用单独安装了,适合我这种空间足够的小白玩。去掉 [all] 也是可以的,后续可以根据报错安装对应的环境(QwQ。
安装完成后,新建一个 .py 文件,写上这段例程,然后 python ./文件名.py 运行它:
| |
运行后,会出现一个窗口,小车顶着杆子走,很快杆子会落下了,然后结束。
hint:将上面代码中的
action = env.action_space.sample()改成action = int(observation[0]*0.1 + observation[1] + observation[2] + observation[3] > 0)可以获得一个更加智能的智能体。
在这个例子里,我们看到了 gymnasium 的主要特性:使用 gym.make 创建虚拟环境(以 env 为例),使用 env.reset() 重置环境,使用 env.step(action) 执行 Agent 的动作。
在这个例子中,我们实现了平衡着杆子的小车,但要谈到强化学习,我们的旅程才刚刚开始。
Q-Learning
从一个 OIer 的视角,来看看一个问题
为了方便讨论,我们假设智能体从环境得到的 Observation 是离散的,能采取的 Action 也是离散的。在这一节中,我们先以 is_slippery = false 的
https://gymnasium.farama.org/environments/toy_text/frozen_lake/
环境入手,并进行这样的环境配置:
智能体从环境获得 Observation,做出 Action 改变环境,针对智能体做的怎么样,我们给出 Reward 进行评判,智能体要做的,是让 Reward 最大。
对于环境的每一个状态(暂时假定这能在 Observation 中完全体现出来)与给定的 Action,智能体能得到一个针对性的 Reward,这一关系我们可以用一个表格来表示。表格的每一行表示不同的 Observation,每一列表示不同的 Action,表格中的数值表示 Reward。
点我看 Reward 关于 Observation(行)与 Action(列)关系的表格
| Reward | 0(向左) | 1(向下) | 2(向右) | 3(向上) |
|---|---|---|---|---|
![]() | -1 | -1 | -1 | -1 |
![]() | -1 | -10(TR) | -1 | -1 |
![]() | -1 | -1 | -1 | -1 |
![]() | -1 | -10(TR) | -1 | -1 |
![]() | -1 | -1 | -10(TR) | -1 |
![]() | / | / | / | / |
![]() | -10(TR) | -1 | -10(TR) | -1 |
![]() | / | / | / | / |
![]() | -1 | -10 | -1 | -1 |
![]() | -1 | -1 | -1 | -10(TR) |
![]() | -1 | -1 | -10(TR) | -1 |
![]() | / | / | / | / |
![]() | / | / | / | / |
![]() | -10(TR) | -1 | -1 | -1 |
![]() | -1 | -1 | 10(TE) | -1 |
![]() | / | / | / | / |
诶,那还不简单,我们每次做使得我们马上获得的 Reward 最大的 Action 不就行了……了吗?不对啊不对,在智能体工作过程中,每一步 action 马上得到的 reward 几乎都为 0,在学习过程中也可能遇到为了 reward 总和更高暂时拿负 reward 的情况。
但是,我们可以定义,在某一个 Observation 下采取某个 Action,经过若干步后我们“期望”我们的智能体获得的总 Reward,我们称之为 Q。对于目前这个离散非随机案例,Q 可以是进行这一 Action 后能获得的最大 Reward。熟悉 OI 的小伙伴们马上就能发现,这件事情是可以动态规划求解的。
$$Q’(s,a) = W(s,a) + \max_{a’\in \mathbb {A}}Q(s’,a’)$$
实际 Q-learning 的公式中,会在 maxa′∈AQ(s′,a′)a′∈AmaxQ(s′,a′)前面加上一个折扣因子的在 0 到 1 之间系数。这个系数越接近 0,智能体越重视及时回报,越接近 1,智能体越重视长期回报,在最终得分上表现更好,最终公式如下:
$$Q’(s,a) = W(s,a) + \gamma \max_{a’\in \mathbb {A}}Q(s’,a’)$$
总之,我们重复进行这个若干遍来更新 Q,直到达到最大执行步骤数或 Q 表格稳定即可。
$$ \begin{bmatrix} -1& -1& -1& -1&\\ -1& -10& -1& -1&\\ -1& -1& -1& -1&\\ -1& -10& -1& -1&\\ -1& -1& -10& -1&\\ 0& 0& 0& 0&\\ -10& -1& -10& -1&\\ 0& 0& 0& 0&\\ -1& -10& -1& -1&\\ -1& -1& -1& -10&\\ -1& -1& -10& -1&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& -1& -1& -1&\\ -1& -1& 10& -1&\\ 0& 0& 0& 0&\\ \end{bmatrix}\to \begin{bmatrix} -2& -2& -2& -2&\\ -2& -10& -2& -2&\\ -2& -2& -2& -2&\\ -2& -10& -2& -2&\\ -2& -2& -10& -2&\\ 0& 0& 0& 0&\\ -10& -2& -10& -2&\\ 0& 0& 0& 0&\\ -2& -10& -2& -2&\\ -2& -2& -2& -10&\\ -2& 9& -10& -2&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& -2& 9& -2&\\ -2& 9& 10& -2&\\ 0& 0& 0& 0&\\ \end{bmatrix}\to \begin{bmatrix} -3& -3& -3& -3&\\ -3& -10& -3& -3&\\ -3& -3& -3& -3&\\ -3& -10& -3& -3&\\ -3& -3& -10& -3&\\ 0& 0& 0& 0&\\ -10& 8& -10& -3&\\ 0& 0& 0& 0&\\ -3& -10& -3& -3&\\ -3& 8& 8& -10&\\ -3& 9& -10& -3&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& 8& 9& -3&\\ 8& 9& 10& 8&\\ 0& 0& 0& 0&\\ \end{bmatrix} \to \begin{bmatrix} -4& -4& -4& -4&\\ -4& -10& -4& -4&\\ -4& 7& -4& -4&\\ -4& -10& -4& -4&\\ -4& -4& -10& -4&\\ 0& 0& 0& 0&\\ -10& 8& -10& -4&\\ 0& 0& 0& 0&\\ -4& -10& 7& -4&\\ -4& 8& 8& -10&\\ 7& 9& -10& 7&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& 8& 9& 7&\\ 8& 9& 10& 8&\\ 0& 0& 0& 0&\\ \end{bmatrix}\to \begin{bmatrix} -5& -5& -5& -5&\\ -5& -10& 6& -5&\\ -5& 7& -5& 6&\\ 6& -10& -5& -5&\\ -5& 6& -10& -5&\\ 0& 0& 0& 0&\\ -10& 8& -10& 6&\\ 0& 0& 0& 0&\\ 6& -10& 7& -5&\\ 6& 8& 8& -10&\\ 7& 9& -10& 7&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& 8& 9& 7&\\ 8& 9& 10& 8&\\ 0& 0& 0& 0&\\ \end{bmatrix}\to \begin{bmatrix} -6& 5& 5& -6&\\ -6& -10& 6& 5&\\ 5& 7& 5& 6&\\ 6& -10& 5& 5&\\ 5& 6& -10& -6&\\ 0& 0& 0& 0&\\ -10& 8& -10& 6&\\ 0& 0& 0& 0&\\ 6& -10& 7& 5&\\ 6& 8& 8& -10&\\ 7& 9& -10& 7&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& 8& 9& 7&\\ 8& 9& 10& 8&\\ 0& 0& 0& 0&\\ \end{bmatrix} \to \begin{bmatrix} 4& 5& 5& 4&\\ 4& -10& 6& 5&\\ 5& 7& 5& 6&\\ 6& -10& 5& 5&\\ 5& 6& -10& 4&\\ 0& 0& 0& 0&\\ -10& 8& -10& 6&\\ 0& 0& 0& 0&\\ 6& -10& 7& 5&\\ 6& 8& 8& -10&\\ 7& 9& -10& 7&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& 8& 9& 7&\\ 8& 9& 10& 8&\\ 0& 0& 0& 0&\\ \end{bmatrix}\to…\to \begin{bmatrix} 4& 5& 5& 4&\\ 4& -10& 6& 5&\\ 5& 7& 5& 6&\\ 6& -10& 5& 5&\\ 5& 6& -10& 4&\\ 0& 0& 0& 0&\\ -10& 8& -10& 6&\\ 0& 0& 0& 0&\\ 6& -10& 7& 5&\\ 6& 8& 8& -10&\\ 7& 9& -10& 7&\\ 0& 0& 0& 0&\\ 0& 0& 0& 0&\\ -10& 8& 9& 7&\\ 8& 9& 10& 8&\\ 0& 0& 0& 0&\\ \end{bmatrix} $$
求得 Q 表格之后,诶,我们就可以每次做使得我们马上获得的 Q 最大/并列最大的 Action 就行了。然而,更多实际强化学习问题中,Observation 与 Action 可能是连续的,智能体每进行一次 Action 后对环境的改变是有一定随机性的。
上面的解决方法很完美,只不过这是 OI 这还不是强化学习。在人工智能问题中,我们想得到一个能解决问题的智能体,它应该是一个有参数的东西,通过调参能解决一大类问题。而不是对于每个不同的环境/Q,都给出一个新的智能体。
说句题外话,我非常希望得到的,是一个可以通过调参(学习),解决人类所有能够解决的问题的智能体。其结构应当不止是一个固定的神经网络模型,其学习所使用的参数不应该是一个固定的参数……
马尔可夫决策模型
为了能够进一步讨论,我们先明确一下刚才说的这个可以调参的智能体是什么,这是一个函数,一个变量是 观测(和过去观测),返回值是采取各种动作的可能性的函数。(下面先不考虑过去观测的作用)我们把这个函数叫做策略函数(policy),记作 $\pi(s)$,这个 $s$ 代表观测。
这个函数得到采取各种动作的可能性,为了方便表示,我们记 $\pi(a|s)$ 为在观测 $s$ 下采取动作 $a$ 的概率。我们所“学习”的东西,是这个函数的参数,我们通过“强化学习“调整参数,目标是得到一个能解决问题的函数。“学习”是学一个函数,这件事对于监督学习也是一样的。而神经网络算法等,是这个函数的实现方式。
当观测空间与动作空间都是有限的且较小的时候,我们可以使用表格法来表达这个函数。也就是这个函数的所有参数是一个对观测 $s$ 和动作 $a$ 得到概率 $p$ 的表格。
ε-贪婪策略
使用 ε-贪婪策略玩游戏,分为探索和利用两个过程,最开始多探索,到后面多利用。探索就是随机选一个动作执行,利用就是在 Q 值大的动作里选一个做。边做动作边更新 Q 矩阵。
这里基于上面的例子给一个使用 ε-贪婪策略更新 Q 矩阵的智能体的例子:
| |
怎么训练这个智能体呢?这样:
| |
这里获得的 agent.qValues 就是训练的成果了。然后可以将学习参数 ε 设置为 0,对这个模型进行测试:
| |















