强化学习Q-learning算法——Python实现

Q-learning是一种基于值迭代的强化学习(Reinforcement Learning, RL)算法,主要用于在给定环境中学习一个策略,使得智能体(agent)能够在与环境交互的过程中获得最大累计奖励。它通过学习一个状态-动作值函数(Q函数)来指导智能体的行为选择,适用于各种离散状态和动作的任务环境。Q-learning在各种应用领域中都有显著表现,包括机器人控制、游戏AI、交通系统优化以及金融市场分析等。通过不断改进和扩展,Q-learning在未来将有望在更多复杂的实际任务中发挥重要作用,特别是在结合其他机器学习技术和多智能体系统的研究中。

一、Q-learning算法

强化学习是做出最佳决策的科学。它可以帮助我们制定活的物种所表现出的奖励动机行为。比方说,你想让一个孩子坐下来学习考试。要做到这一点非常困难,但是如果每次完成一章/主题时都给他一块巧克力,他就会明白,如果他继续学习,他会得到更多的巧克力棒。所以他会有一些学习考试的动机。这里孩子代表着Agent代理。奖励制度和考试代表了Environment环境。今天的题目是类似于强化学习的States状态。所以,孩子必须决定哪些话题更重要(即计算每种行为的价值)。这将是我们的工作的 Value-Function价值方程,每次他从一个章(States)到另一章(States)浏览复习时,他都会得到Reward奖励,他用来在时间内完成主题的方法就是我们的Policy决策。

1.1 Q-learning计算步骤

Q-Learning算法是一种无模型的强化学习技术,用于学习代理在环境中采取动作的最佳策略。在Q-Learning中,我们通常使用两个主要的矩阵来表示和更新信息:奖励矩阵(R)和Q值矩阵(Q)。

  • 奖励矩阵(R)
    奖励矩阵是一个表格,其中每一行代表一个状态(State),每一列代表一个动作(Action)。矩阵中的每个元素 Rs,a 表示在特定状态s 下采取特定动作a时,从环境中获得的即时奖励(Reward)。奖励可以是正的(获得奖励),负的(受到惩罚),或零。
  • Q值矩阵(Q)
    Q值矩阵是一个表格,同样每一行代表一个状态,每一列代表一个动作。矩阵中的每个元素Qs,a 表示在状态 s 下采取动作 a并遵循最佳策略所能获得的预期回报。这个值是通过Q-Learning算法不断更新得到的。Q值反映了从当前状态开始,采取某个动作,并在未来遵循最佳策略所能获得的累积奖励的估计。

Q-learning的核心思想是通过不断地更新状态-动作值(Q值)来逼近最优Q函数(Q),从而得到最优策略。我们可以构建一个矩阵 Q, 它用来表示 agent 已经从经验中学到的知识。矩阵 QR 是同阶的, 其行表示状态,列表示行为,R(s,a)表示在状态s是采取行为a获得的奖励。由于刚开始时 agent 对外界环境一无所知, 因此矩阵 Q 应初始化为零矩阵,对于状态数目未知的情形, 我们可以让 Q 从一个元素出发, 每次发现一个新的状态时就可以在 Q 中增加相应的行列。
Q-learning 算法的转移规则比较简单, 如下式所示:

(1)Q(s,a)=R(s,a)+γmaxa~{Q(s~,a~)}

其中 s,a 表示当前的状态和行为, s~,a~ 表示 s 的下一个状态及行为, 式 (1) 中的 γ称为学习系数, 满足 0γ<1,如果γ=0,对立即的奖励更有效;如果接近1,整个系统会更考虑将来的奖励。
在没有老师的情况下, 我们的 agent 将通过经验进行学习 (也称为无监督学习)。它不断从一个状态转至另一状态进行探索, 直到到达目标。我们将 agent 的每一次探索称为一个 episode。在每一个 episode 中, agent 从任意初始状态到达目标状态。当 agent 达到目标状态后, 一个 episode 即结束, 接着进入另一个 episode。下面给出整个 Q-learning 算法的计算步骤:

Step 1 给定参数 γ 和 reward 矩阵 R
Step 2 今 Q:=0
Step 3 For each episode:
3.1 随机选择一个初始的状态 s
3.2 若未达到目标状态, 则执行以下几步
(1) 在当前状态 s 的所有可能行为中选取一个行为 a
(2) 利用选定的行为 a, 得到下一个状态 s~
(3) 按照公式(1),计算 Q(s,a)
(4) 令 s:=s~.

Agent 利用上述算法从经验中进行学习。每一个 episode 相当于一个 training session,在一个 traiming session 中, agent探索外界环境,并接收外界环境的 reward,直到达到目标状态。训练的目的是要强化 agent 的 “大脑” (用 Q 表示)。训练得越多, 则 Q 被优化得更好,当矩阵 Q 被训练强化后, agent 便很容易找到达到目标状态的最快路径了。利用训练好的矩阵 Q, 我们可以很容易地找出一条从任意状态 s0 出发达到目标状态的行为路径, 具体步骤如下:

令当前状态 s:=s0
确定 a, 它满足 Q(s,a)=maxa~{Q(s,a~)}
令当前状态 s:=s~(s~ 表示 a 对应的下一个状态 )
重复执行步 2 和步 3 直到 s 成为目标状态.

通过不断迭代更新 Q(s,a) 的值,Q-Learning算法可以学习到最优策略 π 下的状态-动作对的价值函数Q(s,a)。这个过程不需要环境的动态模型,因此Q-Learning是一种无模型的强化学习算法。

1.2 Q-Learning解的推导

贝尔曼方程是动态规划中的核心原理,它将一个状态的价值分解为即时奖励和未来价值的总和。对于状态价值函数 Vπ(s) 和动作价值函数 Qπ(s,a),贝尔曼方程可以表示为:

Vπ(s)=aAπ(a|s)[R(s,a)+γsSP(s|s,a)Vπ(s)]

Qπ(s,a)=R(s,a)+γsSP(s|s,a)Vπ(s)

其中:

  • S 是状态空间;A 是动作空间
  • π(a|s)s$ 下采取动作a 的策略
  • R(s,a) 是在状态 s 下采取动作 a 的即时奖励
  • P(s|s,a)是从状态s 采取动作a 转移到状态s 的概率
  • γ 是折扣因子,0γ<1

Q-Learning算法的目标是找到一个策略π,使得Q(s,a) 最大化,即:

π=argmaxπQπ(s,a)

Q-Learning的更新规则如下:

Qt+1(st,at)Qt(st,at)+α[Rt+1+γmaxaQt(st+1,a)Qt(st,at)]

其中:

  • Qt(st,at)是时间 t下状态st 和动作 atQ
  • Rt+1 是在 (st,at)下的即时奖励
  • α是学习率,0α<1
  • γ 是折扣因子

假设我们有一个策略 π,其对应的Q值函数为 Qπ。根据贝尔曼动态规划方程,我们有:

Qπ(s,a)=R(s,a)+γsSP(s|s,a)Vπ(s)

其中P(s|s,a)是状态转移概率,由于 Vπ(s)=maxaQπ(s,a),我们可以将 Vπ(s)替换为 maxaQπ(s,a)

Qπ(s,a)=R(s,a)+γsSP(s|s,a)maxaQπ(s,a)

如果我们取 π为最优策略 π,那么我们得到:

Q(s,a)=R(s,a)+γsSP(s|s,a)maxaQ(s,a)

这个方程表明,最优值Q(s,a) 可以通过对未来状态的最优Q值的加权和来计算,这就是Q-Learning算法的马尔科夫解。

二、Q-learning算例

一只小白鼠在迷宫里面,目的是找到出口,如果他走出了正确的步子,就会给它正反馈(糖),否则给出负反馈(点击),那么,当它走完所有的道路后,无论把它放到哪儿,它都能通过以往的学习找到通往出口最正确的道路。假设迷宫有5间房,如图1所示,这5间房有些房间是想通的,我们分别用0-4进行了标注,其中5代表了出口。

图1 迷宫 图2 状态转移奖励

在这个游戏里,我们的目标是能够走出房间,就是到达5的位置,为了能更好的达到这个目标,我们为每一个门设置一个奖励。如果以state为行,action为列,则上图又可转化为如下的reward矩阵:比如如果能立即到达5,那么我们给予100的奖励,5因为也可以到它自己,所以也是给100的奖励;对于其他状态之间如果没法到5的我们不给予奖励,奖励是0了;在Q-learning中,目标是奖励值累加的最大化,所以一旦达到5,它将会一直保持在这儿,如图3所示。

图3 奖励图 图4 奖励矩阵R

2.1 RQ矩阵

Q-Learning算法的目标是学习一个策略,该策略能够最大化从任何状态开始的预期回报。在每个时间步骤,小白鼠采取一个动作,观察即时奖励和下一个状态,然后更新Q值矩阵。随着时间的推移,Q值矩阵将逐渐收敛到一个状态-动作对的最优Q值,小白鼠可以通过选择具有最高Q值的动作来采取最佳策略。
想象一下小白鼠(或虚拟机器人),它对环境一无所知,但它需要通过自我学习知道怎么样到外面,就是到达5的位置。好啦,现在可以引出Q-learning的概念了,“状态”以及“动作”,我们可以将每个房间看成一个state,从一个房间到另外一个房间的动作叫做action,state是一个节点,而action是用一个剪头表示。现在假设在状态2,从状态2可以到状态3,而无法直接到状态0、1、4、5,因为2没法直接到0、1、4;从状态3,可以到1、4或者2,而无法到达0、5;而4可以到0、3、5,而无法到达1、2;其它依次类推。所以我们能够把这些用一个矩阵来表示,这个矩阵结构就是提到的RQ矩阵了,这个矩阵的列表示的是当前状态,而行标表示的则是下一个状态。如R矩阵的第三行的行标是2,如果取第四列,列标为3,就表示从状态2转移到状态3。鉴于状态2可以转移到状态3,这时记这个2->3的转移奖励为0(不奖不罚);如果从状态2转移不到状态4,就记2->4的转移收益是-1(负激励,尽量不这样走),如果从某个状态转移到了目标状态5(达成目标,正激励),其奖励就记为100,就得到奖励矩阵R,如图4所示。
Q矩阵初始化的时候全为0,这里总的状态数是6,因为后面要求最大值,所以初始化都为0。如果我们并不知道有多少个状态,那么就从某个状态开始,一旦发现新的状态,那么为这个矩阵添加上新的行和列。
我们的小白鼠(或虚拟机器人)将通过环境来学习,机器人会从一个状态跳转到另一个状态,直到我们到达最终目标状态。我们把从开始状态开始一直达到最终目标状态的这个过程称之为一个场景episode,小白鼠会从一个随机的开始场景出发,直到到达最终状态完成一个场景,然后立即重新初始化到一个开始状态,从而进入下一个场景。

2.2 计算迭代episode

为进一步理解上一节中介绍的 Q-learning 算法是如何工作的, 下面我们一步一步地迭代几个 episode。
首先取学习系数 γ=0.8,初始状态为房间 1,并将 Q 初始化为一个零矩阵。观察矩阵 R 的第二行 (对应房间 1 或状态 1 ), 它包含两个非负值, 即当前状态 1 的下一步行为有两种可能: 转至状态 3 或转至状态 5。 随机地, 我们选取转至状态 5。

初始Q矩阵 收益矩阵

想象一下, 当我们的 agent 位于状态 5 以后, 会发生什么事情呢? 观察矩阵 R 的第 6 行 (对应状态 5 ), 它对应三个可能的行为: 转至状态 1,4 或 5 ,根据公式 (1), 我们有

Q(1,5)=R(1,5)+0.8max{Q(5,1),Q(5,4),Q(5,5)}=100+0.8max{0,0,0}=100

现在状态 5 变成了当前状态。因为状态 5 即为目标状态, 故一次 episode 便完成了, 至此, 小白鼠的 “大脑” 中的 Q 矩阵刷新为

第一次 episode 后的 Q 矩阵 第二次 episode 后的 Q 矩阵

接下来, 进行下一次 episode 的迭代, 首先随机地选取一个初始状态, 这次我们选取状态 3 作为初始状态。
观察矩阵 R 的第四行 (对应状态 3 ), 它对应三个可能的行为: 转至状态 1,2 或 4。 随机地, 我们选取转至状态 1 ,因此观察矩阵 R 的第二行 (对应状态 1 ), 它对应两个可能的行为: 转至状态 3 或 5,根据公式 (1),我们有

Q(3,1)=R(3,1)+0.8max{Q(1,3),Q(1,5)}=0+0.8max{0,100}=80

若我们执行更多的episode 迭代,最终将得到一个收敛矩阵(理论上可以证明这个矩阵一定存在),见下图所示。

状态转移图 Q收敛矩阵
规范化Q矩阵(用矩阵元素最大值除所有元素) 最优的转移行为

最终策略:在状态 0 下,选择动作 4 作为最优策略;在状态 1 下,选择动作 5 作为最优策略;在状态 2 下,选择动作 3 作为最优策略;在状态 3 下,选择动作 4 作为最优策略;在状态 4 下,选择动作 5 作为最优策略;在状态 5 下,选择动作 5 作为最优策略。所以从状态2出发的最优路径序列为2-3-4-5。

三、算法Python程序

3.1 示例1

import numpy as np

# 定义参数
gamma = 0.8  # 折扣因子
alpha = 0.1  # 学习率
epsilon = 0.1  # 探索概率
episodes = 10000  # 学习的总回合数

# 定义收益矩阵
R = np.array([
    [-1, -1, -1, -1, 0, -1],
    [-1, -1, -1, 0, -1, 100],
    [-1, -1, -1, 0, -1, -1],
    [-1, 0, 0, -1, 0, -1],
    [0, -1, -1, 0, -1, 100],
    [-1, 0, -1, -1, 0, 100]
])

# 初始化Q值矩阵
Q = np.zeros_like(R, dtype=float)

# Q-learning算法
for episode in range(episodes):
    # 随机选择初始状态
    state = np.random.randint(0, R.shape[0])
    
    while True:
        # 选择动作:采用ε-贪婪策略
        if np.random.rand() < epsilon:
            action = np.random.randint(0, R.shape[1])
        else:
            action = np.argmax(Q[state])

        # 执行动作,获取即时奖励和下一个状态
        next_state = action
        reward = R[state, action]
        
        # 只在奖励不为 -1 时进行 Q 值更新
        if reward != -1:
            # Q值更新
            Q[state, action] = Q[state, action] + alpha * (reward + gamma * np.max(Q[next_state]) - Q[state, action])

            # 状态转移
            state = next_state
        
        # 如果达到了终止状态,结束本回合
        if reward == 100:
            break

# 对 Q 矩阵进行规范化,使最大值为100
max_value = np.max(Q)
if max_value > 0:
    Q = (Q / max_value) * 100

# 保留两位小数
Q = np.round(Q, 2)

# 输出最终的Q值矩阵
print("最终的Q值矩阵(规范化后):")
print(Q)

# 输出最终策略
policy = np.argmax(Q, axis=1)
print("最终策略:")
print(policy)

# 解释最终策略
for state in range(len(policy)):
    print(f"在状态 {state} 下,选择动作 {policy[state]} 作为最优策略。")

3.2 示例2

如下图所示的一个游戏表格,初始位置为黄色的0,目标终点为绿色的6,想要通过策略学习找到从0到6的最短路径,其中红色的7和8是禁止通过的位置,Agent经过这两个位置会game over。以下通过强化学习的Q-Learning方法来学习策略,找到最优的路径方案。状态空间S 即为0~8;动作空间A 可以向左、向下和向右运动,设置为0~2,根据状态和动作,预先给定的奖励值在不同动作和状态下的奖励矩阵见下图。

游戏场景图 奖励矩阵R
状态转移矩阵S 状态0 状态 1 状态 2 状态 3 状态 4 状态 5 状态 6 状态 7 状态 8
动作 1 0 0 7 3 3 2 4 7 1
动作 2 7 2 4 3 4 6 6 3 5
动作 3 1 8 5 4 6 5 6 2 8

注意:状态转移矩阵 S 中的值表示从当前状态(行)执行某个动作(列)后转移到的目标状态。例如,从状态1执行动作1将转移到状态0,执行动作2将转移到状态7,执行动作3将转移到状态1。

import numpy as np

# 奖励矩阵
R = [[-1000, -100, 0], [-5, 0, -100], [-100, 0, 0], [-1000, -1000, 0],
     [-5, -1000, 100], [-5, 100, -1000], [0, -1000, -1000], [-1000, 0, 0], [0, 0, -1000]]

print(R)
# 状态转移矩阵
S = [[0, 7, 1], [0, 2, 8], [7, 4, 5], [3, 3, 4], [3, 4, 6],
     [2, 6, 5], [4, 6, 6], [7, 3, 2], [1, 5, 8]]

print(S)
# 初始化Q表
Q = np.zeros([9, 3])

# 折扣因子和学习率
gamma = 0.9
alpha = 0.9

# 迭代次数
epoch = 10000

# 初始化变量
cnt = 0
done = False
action = 0
state = 0
rt = 0
road = [0]  # 构建一个数组存放路径,0表示默认位置

while cnt < epoch:
    state = 0
    done = False
    while not done:
        # 贪心算法,确保不会一直按照已知的路走,有10%概率探索未知的方法
        if np.random.rand() < 0.9:
            action = np.argmax(Q[state, :])  # 获取当前状态下的最大Q值动作
        else:
            # 随机去探索
            action = np.random.randint(0, 3)
        
        # 执行action切换到新的state
        state_new = S[state][action]
        
        # 更新Q表
        rt = R[state][action] + gamma * np.max(Q[state_new])
        Q[state, action] = Q[state, action] + alpha * (rt - Q[state, action])
        
        # 记录走过的路径
        road.append(state)
        
        # 检查是否到达终止状态
        if state_new in [6, 7, 8]:
            done = True
        
        # 更新状态
        state = state_new

    cnt += 1
    print("Epoch:", cnt, "Path:", road)
    road = [0]  # 清空当前路径,开始下一次迭代

# 输出最终的Q表
print("Final Q-table:")
print(Q)
Final Q-table:
[[-934.39       -100.           72.9  ]
 [  60.61         81.         -100.   ]
 [-100.           90.           90.   ]
 [-919.         -919.           90.   ]
 [  76.         -910.          100.   ]
 [  76.          100.         -909.99 ]
 [   0.            0.            0.   ]
 [   0.            0.            0.   ]
 [   0.            0.            0.   ]]

对于状态0,最佳动作是动作2(索引从0开始),因为它有最高的预期回报72.9;对于状态1,最佳动作是动作1,预期回报为60.61;对于状态2,最佳动作是动作1或动作2,因为它们都提供了90的预期回报,且值相同;对于状态3,最佳动作是动作2,预期回报为90;对于状态4,最佳动作是动作2,预期回报为90;对于状态5,最佳动作是动作2,预期回报为100;对于状态6(目标状态),最佳动作是不动,预期回报为0;对于状态7和状态8,Q表中的值都是0,这意味着在这些状态下没有进行足够的探索或者这些状态在迭代过程中没有被访问过,因此没有学习到有效的策略,在这里是因为这两个是禁止到的位置。
请注意,Q学习是一个迭代过程,随着迭代次数的增加,Q表的值会逐渐稳定下来。在这个例子中,迭代次数可能还不够,导致某些状态的Q值没有被正确更新。此外,由于探索概率设置为10%,这意味着大部分时间都是在进行贪心选择,这可能导致某些状态或动作没有得到足够的探索。

总结

Q-learning是一种基于价值迭代的强化学习算法,其主要特点是不需要知道环境的动态模型。智能体通过与环境的直接交互来学习策略,适用于各种不确定和复杂的环境。Q-learning采用离线更新方法,在每次交互后立即更新Q值,使得算法在训练过程中不断优化。Q-learning的基本思想是通过更新状态-动作值函数(Q值),来评估每个动作在每个状态下的价值。具体而言,智能体在每个状态选择一个动作,接收到环境的反馈(奖励)和转移到的新状态,然后使用Bellman方程更新Q值。该过程持续进行,直到Q值函数收敛,最终得到最优策略。
Q-learning在许多领域取得了显著成功,如游戏、机器人控制和自动化。然而,它也存在一些挑战和局限性。首先,在处理高维状态空间和连续动作空间时,Q-learning的表现不佳。其次,在大型复杂环境中,Q-learning的学习效率较低,需要大量时间和计算资源来收敛。为克服这些挑战,未来研究方向包括:结合深度学习的方法,例如深度Q网络(DQN),以处理高维和复杂任务;多智能体系统中的Q-learning算法,研究智能体之间的协作和竞争;迁移学习,将一个任务中学到的知识迁移到另一个相关任务,提高学习效率和泛化能力;确保Q-learning算法在无人驾驶、医疗等高风险领域的安全性和鲁棒性。

参考资料

  1. A Painless Q-learning Tutorial (一个 Q-learning 算法的简明教程)
  2. 《深入浅出机器学习》之强化学习
  3. 强化学习:Q-learning由浅入深:简介1
  4. 强化学习之Q-learning简介
  5. 基于Q-learning的强化学习案例(附python代码)
posted @   郝hai  阅读(2542)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
历史上的今天:
2022-05-21 R语言文本数据挖掘(四)
2022-05-21 R语言文本数据挖掘(三)
点击右上角即可分享
微信分享提示