2. 状态(State)的定义与表示

状态,说白了就是描述智能体当前所处环境的全部有用信息。
我刚入行时总觉得这个概念太虚,直到在项目里吃过亏才真正理解它有多重要。

2.1 什么是状态

在马尔可夫决策过程中,状态 S 是一个集合,里面包含了智能体做决策所需的一切。每次决策前,智能体看一眼当前状态,然后决定下一步动作。

举一个最简单的例子:你玩贪吃蛇,当前蛇头的位置、食物的位置、蛇身的长度——这些就是状态。少了任何一个,决策就会出错。

核心定义:状态 s ∈ S 是对环境当前情形的完整刻画,满足马尔可夫性——未来只依赖于当前状态,与历史无关。

嗯,这里要注意一个坑:什么叫“完整刻画”?
我曾经在一个机器人导航项目里,只用坐标(x,y)做状态,结果机器人总在同一个地方撞墙——因为我忘了把朝向角度加进去。状态一旦漏掉关键信息,整个策略就废了。

2.2 状态空间的分类

状态空间根据实际情况可以分成两大类,我来跟你聊聊各自的特点。

类型特点典型例子
离散状态空间状态数量有限,可枚举网格世界、棋类游戏、电梯楼层
连续状态空间状态取值连续,不可枚举机器人位置(x,y,θ)、风速、股票价格

实际项目中,离散状态处理起来舒服很多,因为我们可以用表格把每个状态的价值存下来。但现实世界大多是连续的——你想想看,机器人位置怎么可能只落在整数格点上?

我的建议:如果问题允许,先尝试离散化处理。我见过太多新手一上来就上连续算法,结果调参就调了三个月。

2.3 状态的表示方法

状态怎么存?取决于你用什么工具。在 Python 里,我习惯这么干:

# 离散状态:直接用元组或整数索引
state_grid = (2, 3)          # 网格第2行第3列
state_idx = 23               # 展平后的全局索引

# 连续状态:用 NumPy 数组或 namedtuple
import numpy as np
state_continuous = np.array([1.5, 2.7, 0.3])  # x, y, theta

# 结构化状态:用字典(适合带属性)
state_dict = {'pos': (1.2, 3.4), 'velocity': 0.5, 'is_obstacle': False}

我个人习惯把离散状态映射成整数编号,这样在训练 Q 表时能直接当行索引。连续状态则一律用向量,方便喂给神经网络。

曾经踩过的坑:我用浮点数直接做状态 key 存字典,结果因为精度问题匹配不上。后来老老实实用离散索引或 tuple(四舍五入后的数值)。千万别在浮点数上直接做相等比较!

2.4 状态转移:状态如何变化

状态不是静止的。智能体执行一个动作后,环境会进入新状态。这个变化规律叫状态转移函数:

P(s' | s, a) —— 在状态 s 执行动作 a 后,转移到 s' 的概率。

为什么要强调概率?因为环境往往有随机性。比如你让机器人往前走,可能遇到打滑、风阻,最后实际位置跟预期有偏差。

# 简单示例:网格世界的状态转移逻辑
def transition(state, action):
    x, y = state
    if action == 'up':
        next_state = (x, y+1) if y+1 < grid_height else (x, y)
    elif action == 'down':
        next_state = (x, y-1) if y-1 >= 0 else (x, y)
    # ... 其他方向
    return next_state

注意这里的确定性转移。如果要做成随机版本,你可以在代码里加一个概率分支——比如 0.8 概率走预设方向,0.2 概率滑到旁边。

2.5 状态空间的结构化表示

下面这张图展示了经典网格世界中的状态空间结构。每个格子是一个状态,智能体通过上下左右移动在状态间跳转。

网格世界状态空间结构 S₁ S₂ S₃ S₄ S₅ S₆ S₇ S₈ 红色 S₇ 为终止状态(终止状态不进行状态转移) 蓝色为普通状态,智能体可上下左右移动(灰色箭头方向)

每个格子就是一个离散状态。智能体从当前格子走到相邻格子,就是一次状态转移。箭头表示了可能的下一个状态——如果在边界上,转移会留在原地。

状态空间大小:该网格共 2×4 = 8 个状态。实际项目中,状态数可能轻松上千万。例如 100×100 的网格加 4 个朝向,状态数就是 100×100×4 = 40,000。

2.6 实战:状态设计检查清单

我总结了一份清单,每次设计状态时我都会过一遍:

  1. 完整性——所有会影响决策的因素都包含了吗?
  2. 马尔可夫性——只看当前状态,能否独力决定未来演化?
  3. 可观测性——智能体能获取到这些状态信息吗?
  4. 尺度选择——离散化粒度太粗会丢失信息,太细会让状态空间爆炸。
  5. 冗余消除——有没有两个状态其实等价?可以合并吗?

举个例子。我做过一个自动驾驶仿真项目,最初把绝对坐标当状态,结果车在相同位置但不同速度下表现完全不同——后来加入了速度分量。说白了,状态里少一个关键变量,模型就学不会。

避坑指南:我曾经把状态空间搞得过大(1000万个状态),Q 表训练了三天都没收敛。后来通过状态聚合(把相似状态合并成一个)把状态量降到 5000,训练时间从三天降到两小时。设计时一定要预估状态空间大小,别等到跑了才知道。

2.7 总结

状态是 MDP 的起点,也是建模时最容易出错的地方。

  • 离散状态用整数索引,连续状态用向量。
  • 状态转移函数决定了环境如何响应动作。
  • 设计时要兼顾完整性和可行性——信息不能少,但也不能太多。

我的经验是:先从一个简单的离散状态集合开始,跑通流程后再逐步细化。别一开始就想把整个宇宙装进去。