MDP & Q-learning

1.基本概念

  • 强化学习讨论的问题:一个智能体(agent)如何在一个复杂的环境(environment)中去极大化它所获得的奖励。通过感知环境的状态state)对动作(action)的反应(reward),来指导更好的动作,从而获得最大的收益(return)。以上过程称为在交互中学习,这样的学习方法称为强化学习。
  • 一个例子:机器人走网格
image-20250415110836537
  • state:agent相对于environment的一个status,在上面的例子中,state就是指位置,使用s1-s9表示这些位置状态;环境决定状态

  • state space(状态空间):即所有状态的集合

  • Action:agent在某一时刻根据当前state做出决策/选择的行为,在上面例子中,在一个位置状态下,可能有5个action(向上下左右走&原地不动)

  • 离散的动作空间:动作的选择是可数的,比如这里的上下左右,常用算法:Q-learning

    • 连续的动作空间:动作类型是一个实数区间(无限可能),比如手臂的力度
  • Action space of state:当前状态下所有的action集合 - 每个状态都有一个action space

  • state transition:agent在某一state下执行某种action,转移到另一个state

    • 如果action触碰到边界,则相当于agent原地不动
    • 如果action进入到forbidden area,有两种情况:①forbidden area可进入,则直接进入;②forbidden area不可进入,则相当于原地不动
image-20250415110900821
  • state transition probability:借助条件概率去描述状态转移

  • 比如当前在s1,执行动作a2后,若P(s2|s1,a2) = 1,则说明一定会到达s2

    • 若P(s2|s1,a2) = 0.5,P(s5|s1,a2) = 0.5,则说明有50%的概率到达s2,有50%的概率到达s5
  • policy(策略):根据当前state,决定采取什么action

  • Reward(奖励):环境给agent一个反馈,用来指导agent是不是做得好,比如机器人走到出口 + 1分,机器人撞墙 - 1分

  • Episode(回合):agent从起点出发,直到任务结束,称为一轮

  • Trajectory(轨迹):agent一轮行动中,形如(状态,动作,奖励)这样的序列

  • Return(回报):从当前开始,到终点累计的总奖励(Reward之和),强化学习的目标就是最大化期望的Return,Return可以用来评估Policy的好坏

  • 模拟机器人寻路代码

import numpy as np
import time
class GridWorld:
    def __init__(self,size = 3):
        self.size = size
        self.start = (0,0)
        self.goal = (self.size - 1,self.size - 1)
        self.reset()

    def reset(self):
        self.state = self.start #回到起点
        return self.state

    def step(self,action):
        x,y = self.state
        if action == "up" : x = max(0,x - 1)
        elif action == "down" : x = min(self.size - 1,x + 1)
        elif action == "left" : y = max(0,y - 1)
        elif action == "right" : y = min(self.size - 1,y + 1)

        self.state = (x,y)
        reward = 1.0 if self.state == self.goal else -0.1
        done = self.state == self.goal #判断游戏是否结束
        time.sleep(1)  # 每步暂停 1 秒
        return self.state, reward,done

    def get_actions(self):
        return ["up","down","left","right"]

    def render(self):
        grid = [["⬜" for _ in range(self.size)] for _ in range(self.size)] #_仅表示一个占位符,grid是一个二维列表
        x,y = self.state
        gx,gy = self.goal
        grid[gx][gy] = "🏁"
        grid[x][y] = "🟩"
        for row in grid:
            print(" ".join(row)) #" ".join(row) 会把这几个元素用空格 " " 连接成一个字符串
        print() #打印一个空行

if __name__ == "__main__":
    env = GridWorld()
    state = env.reset()
    env.render() #生成网格图

    for _ in range (20):
        action = np.random.choice(env.get_actions())
        next_state,reward,done = env.step(action)
        print(f"Action:{action},Next state:{next_state},Reward:{reward}")
        env.render() #绘制步数之后的网格表
        if done:
            print("✔️successful pass!")
            break

2.马尔科夫决策过程(MDP)

①基本概念

  • 为什么需要MDP:强化学习中,agent需要在一个环境中不断做决策,获得奖励,从而学习“好行为”,为了严谨的描述决策 - 反馈 - 学习的过程,就引入MDP

  • MDP是对完全可观测环境进行描述的,观测到的状态内容完整决定了决策的需要的特征

    • 你不需要依赖历史信息或者猜测隐藏变量——当前的状态就足够做出合理的动作选择(不关心「怎么来到这个状态」(没有“前序”))
    • 例如机器人走迷宫就是完全可观测,不需要知道“你从哪儿来”、“你之前走了几步”——当前状态已经够用了
  • 马尔科夫性:未来只依赖于当前状态和当前动作,与过去无关,因为当前的状态就包含了过去的信息

    • 使用状态转移概率:$$p_{ss'} = P[S_{t + 1 = s'} | S_t = s]$$描述马尔科夫性,即在当前状态为s的条件下,下一时刻状态转移到s’的概率,这个概率就被称为状态转移概率
      • $S_t$为t时刻的状态
      • $P_{ss'}$为当前状态为s,下一时刻转移到s‘的概率
    • 状态转移矩阵定义了所有状态的转移概率,矩阵中每行元素的和为1

$$
\begin{bmatrix}
p_{11} ... p_{1n}\
\vdots \
p_{n1} ... p_{nn}

\end{bmatrix}
$$

  • 马尔科夫过程:又称为马尔科夫链,是一个无记忆的随机过程,可以用一个元素<S,P>表示,其中S表示状态集,P表示状态转移矩阵

  • 示例:学生马尔科夫链:每个圆圈表示状态,箭头表示在当前状态下转移到目标状态的概率,根据此图不难画出状态转移矩阵

img

②马尔科夫奖励过程 Markov Reward Process & 回报Return

  • 即在马尔科夫过程的基础上,增加了奖励R衰减系数$\gamma$,故可以用<S,P,R,$\gamma$>表示
    • 奖励即当前时刻t在状态s下,在下一个时刻能获得的奖励期望,即$$R_S = E[R_{t+1}|S_t = s]$$
    • 衰减系数:是一个[0,1]的数,表示未来奖励的重要程度
      • 什么是未来奖励:比如做日结,每天100元,共7天
      • 现实生活中,大多人认为今天拿到的钱 > 未来拿到的钱,所以引入了衰减因子,弱化将来的奖励,即越远的奖励,价值越低
  • 回报Return:即从当前时刻t开始,到结束所有奖励的总和

联系第一部分的概念

Reward(奖励):环境给agent一个反馈,用来指导agent是不是做得好,比如机器人走到出口 + 1分,机器人撞墙 - 1分

Return(回报):从当前开始,到终点累计的总奖励(Reward之和),强化学习的目标就是最大化期望的Return,Return可以用来评估Policy的好坏

  • 未来奖励计算 $$G_t = R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + ... \gamma^n R_{n+1}$$
    • $\gamma$偏向0,则表明趋向于“近视”性评估
    • $\gamma$偏向1,则表明偏重考虑远期的利益
  • 可以推导得$G_t = R_{t+1} + \gamma G_{t+1}$(提一个$\gamma$出来)

③价值函数 Value Function

  • 价值函数给出某一state或action的长期价值
  • 某一状态的价值函数$$v(s) = E[G_t|S_t = s]$$,即从当前状态开始,马尔科夫链收获(Return)的期望(期望能获得的未来所有回报的期望值)
  • 当$\gamma$ = 0时,各状态的即时奖励同各状态的价值相同(也就是上面说的近视)
  • 当$\gamma$ != 0时,各状态的价值就需要通过计算得到

image-20250418075206069

  • 上图是$\gamma$ = 0.9的情况,可以简单理解一下,比如状态为class3时,即时奖励R = -2,但因为衰减因子为0.9,因此会偏重考虑远期的利益,而class3有0.6的概率会进入pass,而pass的及时奖励为10,故经计算可得class3的价值为4.1(意思就是因为考虑了远期利益,class3的价值相比即时价值大大增加)
  • Return和价值的区别
    • Return是对单个Trajectory所求的
    • state Value是针对多个Trajectory的Return,求这些Return的期望

④贝尔曼公式

  • 尝试对上述的某一状态的价值函数进行化简

$$
\begin{align}
v(s) &= E[G_t|S_t = s]\
&= E[R_{t+1} + \gamma R_{t+2} + \gamma ^2 R_[t+3] +...|S_t = s]\
&= E[R_{t+1} + \gamma(R_{t+2} + \gamma R_{t+3} +...)|S_t = s]\
&= E[R_{t+1} + \gamma G_{t+1}|S_t = s]\
&= E[R_{t+1} + \gamma v(S_{t+1})|S_t = s]
\end{align}
$$

  • 前面3行就是递归,重点理解最后一行$G_{t+1} = v(S_{t +1})$,由上述可知
    • $G_{t+1} = R_{t+2} + \gamma R_{t+3} + \gamma^2 R_{t+4} + ... \gamma^n R_{n+1}$,表示t+1时刻开始,未来可获得的总奖励,也就是收获Return
    • $v(s_{t+1}) = E[G_{t+1}|S_{t+1} = s]$,表示t+1时刻开始,不同trajectory的Return的期望
    • 暂且理解:在你还不知道将来会走到哪个状态的时候,就可以用对 $S_{t+1}$ 的期望来近似$G_{t+1}$
  • 所以价值函数可以拆解为$$v(s) = E[R_{t+1}|S_t = s] + E[\gamma v(S_{t+1})|S_t = s)]$$
    • 前一部分为当前时刻的即时奖励
    • 后一部分为下一时刻状态的价值期望
  • 因此贝尔曼方程提供了一种递归的方式来计算每个状态的价值
  • 价值迭代:基于动态规划,反复应用贝尔曼方程来计算每个状态的最优价值
    • 策略评估:给定一个策略,计算每个状态的价值
    • 策略改进:在评估策略之后,更新策略为当前状态下选择价值最大的action
  • 实现价值迭代:在 GridWorld 中计算每个位置的价值并最终得到最优策略。
def value_iteration(env,gamma = 0.9,threshold = 0.01):
    value_table = np.zeros((env.size,env.size)) #状态价值表
    while (1):
        delta = 0
        for x in range(env.size):
            for y in range(env.size):
                state = (x,y)
                if state == env.goal:
                    continue
                action_values = [] #动作价值
                for action in env.get_actions():
                    next_state,reward,done = env.step(state,action)
                    action_value = reward + gamma * value_table[next_state[0],next_state[1]] #贝尔曼公式
                    action_values.append(action_value)
                max_action_value = max(action_values)
                delta = max(delta, abs(value_table[x, y] - max_action_value)) #价值更新的差值
                value_table[x, y] = max_action_value
        if delta < threshold:#如果一次迭代中,所有状态的价值变化都很小(小于 threshold),就认为已经“收敛”了,也就是说差不多已经是最优解了,就可以 停止迭代。
            break
    return value_table

if __name__ == "__main__":
    env = GridWorld()
    values = value_iteration(env)
    print("最终状态价值表:")
    print(np.round(values, 2))

⑤马尔科夫决策过程

  • 就是在马尔科夫奖励过程的基础上加一个decision过程,对比奖励过程多了一个动作集合,使用<S,A,P,R,$\gamma$>来表示

    • S - 状态空间
    • A - 动作空间
    • P - 状态转移概率,$P_{ss'}$为当前状态为s,下一时刻转移到s‘的概率
    • R - 奖励函数Reward
    • $\gamma$ - 衰减因子
  • 策略(policy):使用π表示策略的集合,π(a|s)表示状态为s时,采取行动a的概率

  • 目标:找到一个策略π,使得长期累积奖励(Return)最大化 - 即上面的$G_t = R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + ... \gamma^n R_{n+1}$

  • 策略提取:对每个状态,尝试所有动作,计算最优价值

#提取每个状态的最优动作(即策略)
def extract_policy(value_table,env,gamma = 0.9):
    policy = np.full((env.size,env.size)," ",dtype=object) #存储最优策略
    for x in range(env.size):
        for y in range(env.size):
            state = (x,y)
            if state == env.goal:
                continue
            action_values = []
            for action in env.get_actions():
                next_state,reward,done = env.step(state,action)
                action_value = reward + gamma * value_table[next_state[0], next_state[1]]
                action_values.append(action_value)
            best_action = env.get_actions()[np.argmax(action_values)] #action_values是价值列表,取价值最大的action的序号i,get_actions()[i]对应的动作就是最优动作
            policy[x,y] = best_action
    return policy
  • 可视化 + agent应用最优策略
#打印最优策略
def render_policy(policy, env):
    arrow_map = {
        "up": "⬆",
        "down": "⬇",
        "left": "⬅",
        "right": "➡",
        " ": " "  # 空白用于目标状态
    }
    print("最优策略图:")
    for x in range(env.size):
        row = ""
        for y in range(env.size):
            if (x, y) == env.goal:
                row += "🏁 "
            else:
                row += arrow_map[policy[x, y]] + " "
        print(row)

#最优策略应用到agent
def run_agent(env,policy):
    state = env.reset()
    env.render()
    steps = 0
    while state != env.goal:
        action = policy[state]
        state, reward, done = env.step(state,action)
        env.render()
        steps += 1
        if done:
            print("✔️successful pass!")
            break
  • 完整代码
import numpy as np
import time
class GridWorld:
    def __init__(self,size = 3):
        self.size = size
        self.start = (0,0)
        self.goal = (self.size - 1,self.size - 1)
        self.reset()

    def reset(self):
        self.state = self.start #回到起点
        return self.state

    def step(self,state,action):
        x,y = state
        if action == "up" : x = max(0,x - 1)
        elif action == "down" : x = min(self.size - 1,x + 1)
        elif action == "left" : y = max(0,y - 1)
        elif action == "right" : y = min(self.size - 1,y + 1)

        self.state = (x,y)
        reward = 1.0 if self.state == self.goal else -0.1
        done = self.state == self.goal #判断游戏是否结束
        #time.sleep(1)  # 每步暂停 0.3 秒
        return self.state, reward,done

    def get_actions(self):
        return ["up","down","left","right"]

    def render(self):
        grid = [["⬜" for _ in range(self.size)] for _ in range(self.size)] #_仅表示一个占位符,grid是一个二维列表
        x,y = self.state
        gx,gy = self.goal
        grid[gx][gy] = "🏁"
        grid[x][y] = "🟩"
        for row in grid:
            print(" ".join(row)) #" ".join(row) 会把这几个元素用空格 " " 连接成一个字符串
        print() #打印一个空行
# 用来模拟“某个状态执行动作后的新状态”


def value_iteration(env,gamma = 0.9,threshold = 0.01):
    #如果一次迭代中,所有状态的价值变化都很小(小于 threshold),就认为已经“收敛”了,也就是说差不多已经是最优解了,就可以 停止迭代。
    value_table = np.zeros((env.size,env.size)) #状态价值表
    while (1):
        delta = 0
        for x in range(env.size):
            for y in range(env.size):
                state = (x,y)
                if state == env.goal:
                    continue
                action_values = [] #动作价值
                for action in env.get_actions():
                    next_state,reward,done = env.step(state,action)
                    action_value = reward + gamma * value_table[next_state[0],next_state[1]] #贝尔曼公式
                    action_values.append(action_value)
                max_action_value = max(action_values)
                delta = max(delta, abs(value_table[x, y] - max_action_value)) #价值更新的差值
                value_table[x, y] = max_action_value
        if delta < threshold:
            break
    return value_table
#提取每个状态的最优动作(即策略)
def extract_policy(value_table,env,gamma = 0.9):
    policy = np.full((env.size,env.size)," ",dtype=object) #存储最优策略
    for x in range(env.size):
        for y in range(env.size):
            state = (x,y)
            if state == env.goal:
                continue
            action_values = []
            for action in env.get_actions():
                next_state,reward,done = env.step(state,action)
                action_value = reward + gamma * value_table[next_state[0], next_state[1]]
                action_values.append(action_value)
            best_action = env.get_actions()[np.argmax(action_values)] #action_values是价值列表,取价值最大的action的序号i,get_actions()[i]对应的动作就是最优动作
            policy[x,y] = best_action
    return policy

#打印最优策略
def render_policy(policy, env):
    arrow_map = {
        "up": "⬆",
        "down": "⬇",
        "left": "⬅",
        "right": "➡",
        " ": " "  # 空白用于目标状态
    }
    print("最优策略图:")
    for x in range(env.size):
        row = ""
        for y in range(env.size):
            if (x, y) == env.goal:
                row += "🏁 "
            else:
                row += arrow_map[policy[x, y]] + " "
        print(row)

#最优策略应用到agent
def run_agent(env,policy):
    state = env.reset()
    env.render()
    steps = 0
    while state != env.goal:
        action = policy[state]
        state, reward, done = env.step(state,action)
        env.render()
        steps += 1
        if done:
            print("✔️successful pass!")
            break

if __name__ == "__main__":
    env = GridWorld()
    values = value_iteration(env)
    print("最终状态价值表:")
    print(np.round(values, 2))
    policy = extract_policy(values, env)
    render_policy(policy, env)
    print("智能体开始执行最优策略:")
    run_agent(env, policy)
  • 输出
最终状态价值表:
[[0.46 0.62 0.8 ]
 [0.62 0.8  1.  ]
 [0.8  1.   0.  ]]
最优策略图:
⬇ ⬇ ⬇ 
⬇ ⬇ ⬇ 
➡ ➡ 🏁 
智能体开始执行最优策略:
🟩 ⬜ ⬜
⬜ ⬜ ⬜
⬜ ⬜ 🏁

⬜ ⬜ ⬜
🟩 ⬜ ⬜
⬜ ⬜ 🏁

⬜ ⬜ ⬜
⬜ ⬜ ⬜
🟩 ⬜ 🏁

⬜ ⬜ ⬜
⬜ ⬜ ⬜
⬜ 🟩 🏁

⬜ ⬜ ⬜
⬜ ⬜ ⬜
⬜ ⬜ 🟩

✔️successful pass!

进程已结束,退出代码为 0

3.Q-learning

  • 上面的MDP中策略提取部分,就是在一个已知环境,根据贝尔曼方程,计算出最优的状态价值V(s),再推导出最优策略,这是动态规划的方法,前提是我们知道环境的所有信息(状态、动作、转移概率)
  • Q-learning 的目标是学习一个“评分表”(称为 Q-table),告诉你在某个特定位置(状态 s)选择某个特定方向(动作 a) 最终能获得多少总分(累积奖励)。这个分数就是 Q 值,记为Q(s,a)
  • 引入RL的核心思想:在未知环境下智能体通过交互学会最优策略,Q-learning是一种无模型基于值的强化学习算法

$$
Q(s_t,a_t)\leftarrow Q(s_t,a_t) + \alpha[r_{t+1} + \gamma \cdot \max_{a’}(Q(S_{t+1},a^‘) - Q(s_t,a_t)) ]
$$

  • 上述为核心公式,其中Q(s,a) :在状态s下采取动作a取得的回报 | α:学习率 | $r_{t+1}$:在时刻t执行动作a后的即时奖励 | $\gamma$:衰减因子

  • 公式含义:通过对当前Q值进行更新,使当前Q值向目标值靠近

    • 目标值:贝尔曼方程计算得出,即当前动作带来的即时奖励$r_{t+1}$ + 下一状态得到的最大期望未来回报,即$\gamma \cdot \max_{a’}(Q(S_{t+1},a^‘)$
    • 理解:当前的Q值可能是猜的,或者是初始化值(因为qlearning是在未知环境下),而目标值是看到了奖励+猜测下一状态的最大收益
  • 学习的过程 - 学习是如何进行的

    • 每次选择动作 - 探索 or 利用

      • 探索:随机选择一个动作执行
      • 利用:选择当前Q值最大的动作
    • 如何更新Q值

      • 目标值怎么来
        • 在一次次尝试中得到即时奖励r和下一个状态s'
        • 从已有的Q表中查s'的最大Q值(初始可能为0),这个值就作为目标值,用于更新当前的Q
      • Q值更新
        • 有了目标值,更新Q值的过程就是从当前值往目标值靠,即$Q_{s_t,a_t} \leftarrow Q_{s_t,a_t} + \alpha \cdot(目标值 - 当前估计)$
      best_next_q = max([Q.get((next_state,a),0.0)for a in env.get_actions()])
      current_q = Q.get((state,action),0.0)
      Q[state,action] = current_q + alpha * (reward + gamma * best_next_q - current_q) 
      
  • 代码实现:初始化一个Q表,每个元素是一个键值对,key为(state,action),Value为Q值,设定一个训练轮数episodes开始训练,每轮训练都是从起点开始,重复执行:有10%的概率“探索”,即随机选择一个动作,然后执行,更新状态和q值,有90%的概率执行“利用”,即选择当前状态下可获得最大q值的动作,然后执行,更新状态和q值,直到到达终点,结束本轮训练。

#Q-learning
#episodes:训练总回合数,epsilon:探索率,智能体随机选择动作的概率,0.1即10%的时间随机探索,90%的时间利用已知信息
def q_learning(env,episodes = 500,alpha = 0.1, gamma = 0.9, epsilon = 0.1):
    #Q-table 存储了在特定状态(state)下执行特定动作(action)的预期未来总回报(Q值)
    Q = {} #Q表初始化,key:(state,action),value:Q值
    for ep in range(episodes):
        state = env.reset()
        while (1):
            if (np.random.rand() < epsilon):
                action = np.random.choice(env.get_actions()) #探索(10%的时间) - 随机选择一个动作
            else:
                q_vals = [Q.get((state,a),0.0) for a in env.get_actions()]
                action = env.get_actions()[np.argmax(q_vals)] #利用 - 选择Q值最大的动作
            #执行动作
            next_state,reward,done = env.step(state,action)
            #更细Q值
            best_next_q = max([Q.get((next_state,a),0.0)for a in env.get_actions()])#查找在next_state下哪个动作最佳 - 用于以后的“利用”
            current_q = Q.get((state,action),0.0)
            Q[state,action] = current_q + alpha * (reward + gamma * best_next_q - current_q) #qlearning公式

            state = next_state
            if done:
                break #停止当前轮循环,开启下一轮,相当于一个训练的过程
    return Q
  • 也可以将q表打印出来
def print_q_table(Q, env):
    data = []
    for x in range(env.size):
        for y in range(env.size):
            state = (x, y)
            row = {'state': state}
            for action in env.get_actions():
                row[action] = round(Q.get((state, action), 0.0), 2)
            data.append(row)

    df = pd.DataFrame(data)
    print("\n📋 Q 表(每个状态对应的动作 Q 值):")
    print(df.to_string(index=False))

Q = q_learning(env)
print_q_table(Q, env)
image-20250422155649538
  • 应用Q-learning
#从训练后的Q表中提取最优策略
def extract_policy_from_q(Q,env):
    policy = {}
    for x in range(env.size):
        for y in range(env.size):
            state = (x,y)
            if state == env.goal:
                continue
            q_vals = [Q.get((state,a),0.0) for a in env.get_actions()]
            best_action = env.get_actions()[np.argmax(q_vals)]
            policy[state] = best_action
    return policy #policy是一个二维矩阵

#agent使用qlearning
def run_agent_with_q(env,policy):
    state = env.reset()
    env.render()
    steps = 0
    while state != env.goal:
        action = policy[state]
        state,reward,done = env.step(state,action)
        env.render()
        steps += 1
        if done:
            print(f"到终点辣,用了 {steps}步")
            break

if __name__ == "__main__":
    env = GridWorld()
    Q = q_learning(env) #得到训练后的Q表
    policy = extract_policy_from_q(Q,env)
    run_agent_with_q(env,policy)
posted @ 2025-04-20 15:14  mumumu1  阅读(61)  评论(0)    收藏  举报