
1. 为什么是现在开始学强化学习——写给同样在入门路上的人我第一次听到“强化学习”这四个字是在一次技术分享会上。当时演示了一个AI从完全不会走路到学会双足行走的过程画面里的小人摔倒了几千次最后却能摇摇晃晃站起来。我当时的第一反应不是“好厉害”而是“这到底是怎么学的”有明确答案吗没有。有标准样例吗也没有。它靠的是不断试错、接受惩罚、积累反馈最终自己摸索出一套策略。这种和传统监督学习完全不一样的学习范式让我一下子入了坑。这篇是“强化学习入门”系列的第一篇。我打算从零开始用一个系列把强化学习最核心的东西讲清楚。第一篇的重点不是跑大模型、不是复现什么SOTA算法而是把“强化学习在解决什么问题”这件事彻底打通。如果你是非科班出身、刚刚接触强化学习、或者已经听过不少术语但总觉得差一口气那这篇文章就是为你准备的。我会尽量用大白话拆解概念再配合一个我自己写过的小例子让你能踏踏实实走完第一遍。很多人都说强化学习难难在数学、难在调参、难在结果不稳定。但这些都是在之后才会遇到的事情。入门阶段真正的难点是大脑里还没有建立起“智能体—环境—奖励”这个闭环的直觉。只要把这个闭环彻底刻进脑子里后面所有的公式和算法都是在这个闭环上长出来的枝叶。所以我强烈建议你在往下读之前先拿一张纸画出“智能体、环境、奖励”这三个词之间的关系。想不清楚也没关系这篇文章接下来就是帮你画清楚它。2. 先搞懂强化学习在解决什么问题2.1 强化学习和监督学习到底差在哪很多人学习强化的第一反应是拿它和分类、回归那套监督学习框架做对比。对比当然可以但必须先踩住一个根本差异监督学习是“输入一条数据告诉你这条数据的标签”强化学习是“输入一个状态让你自己决定下一步动作然后环境给你回一个奖励”。这里没有明确的“标准答案”只有“延迟的奖励”。我自己的体会是把强化学习想象成养狗。你不可能直接告诉狗“坐下”这个动作在所有场景下应该怎么做你只能在它做对的时候给零食做错的时候不给零食。狗慢慢会把“动作—结果”关联起来。监督学习不是这样的监督学习更像是给一张试卷上面每道题都标着标准答案你只要能记住规律、做对更多题目就行。强化学习没有标准答案只有环境反馈。这个区别带来的一个核心后果是强化学习里的数据分布不是固定的。监督学习的训练集是提前采好的强化学习的“训练数据”却是智能体自己行动产生的状态序列。行动改变了数据分布数据分布又影响下一步行动。这种“自举式”的循环是入门时最反直觉的东西。很多初学者包括我自己第一次写Q-learning时都会困惑为什么我明明在训练但是模型却越来越差因为一开始的动作导致后续状态分布偏移需要非常注意稳定性和探索策略。2.2 强化学习的五要素状态、动作、奖励、策略、价值强化学习的标准框架里有五个东西绕不开。你可以把它们当成一部舞台剧的五个角色状态State当前环境给智能体的信息。比如棋局里当前的局面、机器人每个关节的角度、游戏里角色所在的位置。动作Action智能体可以做的决策。比如左转、右转、跳跃、拿起某个东西。奖励Reward环境对动作的反馈。它是一个数字可正可负用来告诉智能体这次做得好不好。策略Policy智能体的决策规则。它决定在某个状态下应该选择哪个动作。价值Value对累积回报的估计。价值函数回答的不是“这次动作得了多少分”而是“从这个位置继续往下走我大概能挣到多少分”。这里有一个非常容易绕晕的区分奖励是即时反馈价值是长期期望。打个比方你现在选择跳槽去另一家公司签约时的安家费是“奖励”但未来几年职业发展、薪资增长的总预期才是“价值”。强化学习的目标是最大化总的价值不是单次奖励。很多初学者会陷入“只看眼前奖励”的陷阱结果训练了半天策略陷入局部最优。后面我会专门说这个问题。2.3 闭环智能体与环境是怎么互动的现在可以把五要素串成一个循环。想象你在一间黑屋子里每走一步才能看到新的画面。整个过程可以拆成下面这四步智能体观察环境得到一个状态s。智能体根据策略π选择一个动作a。环境执行这个动作转移到一个新状态s‘同时给出奖励r。智能体把s, a, r, s记下来用来更新自己的策略然后回到第1步。这个循环看起来很简单但它是整个强化学习大厦的地基。你以后看到的任何算法无论是Q-learning、DQN、PPO还是A3C本质上都是在回答同一个问题在这个循环里如何根据已经得到的反馈更新策略使得总回报最大。我建议你在读任何强化学习算法之前先把上面这个循环手写三遍。不是为了记住是为了建立一种“画面感”。我自己后来做实验时调试模型最先看的不是损失函数曲线而是这个闭环上哪个环节出了问题是奖励给错了是动作空间太大还是状态表示不好如果这个闭环的直觉不够牢固你会在后面陷入无尽的调参地狱。3. 数学基础从回报到贝尔曼方程3.1 为什么要有折扣因子强化学习的核心目标是让“总回报”最大。但这个“总回报”不是简单把每一步的即时奖励加在一起。因为环境是有持续性的今天拿到的奖励和明天拿到的奖励价值是不一样的。于是我们把未来每一步的奖励做折扣得到一个“折扣累积回报”G_t r_{t1} γ r_{t2} γ^2 r_{t3} ... Σ_{k0}^∞ γ^k r_{tk1}这里的γgamma叫做折扣因子取值范围是[0, 1]。它表示“未来奖励相对于当前奖励的打折程度”。γ越接近1智能体越有远见γ越接近0智能体越短视只看眼前。具体怎么选我的经验是如果你的任务持续时间很长比如机器人走迷宫你可以用0.95甚至0.99如果任务本身很短比如一步到位的那种决策用0.8都行。但要注意γ太大有两个副作用一是累积回报的方差会变得很大训练不稳定二是优化难度上升因为目标函数变得“更平”。入门阶段多用0.9比较稳。3.2 价值函数策略的“长期眼光”有了折扣回报之后就能定义价值函数。状态价值函数V^π(s)表示从状态s出发按照策略π一直走下去得到的期望折扣累积回报V^π(s) E[ G_t | S_t s, π ]它的含义很直观如果我现在站在这个位置上未来大概能赚多少钱。另一个更常用的变体是动作价值函数Q^π(s, a)表示在状态s做了一个动作a之后再按策略π继续走的期望累积回报Q^π(s, a) E[ G_t | S_t s, A_t a, π ]区分这两种价值非常关键。V只看状态好不好Q看动作好不好。你需要“状态的好坏”可以用V你需要决定“在状态s下做哪个动作”必须用Q。这就是为什么Q是一个“状态—动作”为节点的表而V是以“状态”为节点的表。3.3 贝尔曼方程把未来价值拆成现在加将来价值函数要如何计算你不可能真的把无限步的回报全算出来贝尔曼方程给了我们一种“动态规划”的拆法。它的核心思想是当前状态的价值等于即时奖励加上下一个状态的折扣价值V^π(s) Σ_a π(a|s) Σ_{s’,r} P(s, r | s, a) [ r γ V^π(s) ]对应Q函数Q^π(s, a) Σ_{s’,r} P(s, r | s, a) [ r γ Σ_{a} π(a|s) Q^π(s, a) ]这个方程看起来有点吓人但它只是在说一件事每一个状态的价值都可以用“下一个状态的价值”来表示。这就像一个递推公式只要你不断重复这个递推过程最后价值函数就会收敛到真正的期望回报。这里的P(s,r|s,a)是环境的转移概率也就是状态转移模型。不懂数学也能理解这个思路。你面试一个岗位时除了看这个月工资即时奖励还要考虑发展前景未来价值。如果这个岗位下一个阶段的发展空间很小那么现在工资再高长期来看也不是最优选择。贝尔曼方程就是把这种想法用等式表达出来。3.4 最优策略让每个状态都拿到最大的价值我们最终要找的是最优策略π*使得每个状态的价值都达到最大。最优V函数定义为V^*(s) max_π V^π(s)最优Q函数定义为Q^*(s, a) max_π Q^π(s, a)一旦有了最优Q策略就很好办了在每个状态s选择使得Q^(s, a)最大的那个动作a就好。这就是贪心策略。强化学习算法的很多工作重点就在于“如何逼近Q^”而不是直接在不同策略之间做比较。这也是为什么各种SOTA算法里Q-learning类方法始终占有一席之地。4. 第一个练手环境用Q学习走通一个网格世界4.1 为什么选网格世界作为第一个项目数学概念讲再多不写代码还是会飘。所以我建议你动手做一个最简单的网格世界环境。不说别的这个环境足够小状态和动作都一目了然非常适合入门。它的设定是这样的有一个6×6的格子地图左上角是起点右下角是终点。智能体每一步可以选择上、下、左、右四个动作。地图里有几个格子是陷阱踩到之后会掉回起点同时获得负奖励。到达终点获得正奖励并且回合结束。所有其他行动获得-0.1的微小惩罚目的是鼓励智能体用最少步数到达终点。这个环境简单到连“环境类”都可以不写直接用numpy数组就能表示。但它包含了强化学习入门需要的所有要素状态、动作、奖励、终止条件、转移过程。走通这个例子之后你会对Q-learning有非常扎实的体感。4.2 环境定义与Q表初始化下面这段代码我实际跑过在我的机器上没有任何依赖问题。我用的是Python和Numpy。你直接复制到文件里保存成grid_q.py运行就能看到训练过程中的Q表变化。import numpy as np import random # 网格环境6x6 GRID_SIZE 6 # 终点和陷阱位置 GOAL (5, 5) TRAPS [(2, 2), (3, 4), (5, 1)] START (0, 0) # 动作上、下、左、右 ACTIONS {up: (-1, 0), down: (1, 0), left: (0, -1), right: (0, 1)} ACTION_LIST list(ACTIONS.keys()) def step(state, action): 返回新状态、奖励、是否结束 dx, dy ACTIONS[action] x, y state nx, ny x dx, y dy # 越界惩罚并留在原地 if nx 0 or nx GRID_SIZE or ny 0 or ny GRID_SIZE: return state, -1.0, False new_state (nx, ny) if new_state GOAL: return new_state, 10.0, True if new_state in TRAPS: return START, -5.0, False return new_state, -0.1, False # Q表初始化状态数 x 动作数 Q np.zeros((GRID_SIZE * GRID_SIZE, len(ACTION_LIST))) def state_index(state): x, y state return x * GRID_SIZE y def epsilon_greedy(state, epsilon): if random.random() epsilon: return random.randint(0, len(ACTION_LIST) - 1) s_idx state_index(state) return int(np.argmax(Q[s_idx]))这段代码有两个细节值得注意。一是状态用坐标元组表示而Q表下标用一维索引。这不是必须的但我建议你这样做因为后面做可视化时一维索引映射到网格坐标会很方便。二是越界动作给对方-1的惩罚这比我一开始什么都不给要好很多。加上惩罚之后智能体很快就学会了避开边界而不是在墙上反复试探。4.3 Q-learning更新逻辑核心一行代码接下来是训练的循环。Q-learning的更新公式是Q(s, a) ← Q(s, a) α [ r γ max_{a} Q(s, a) - Q(s, a) ]这里的α是学习率控制新信息覆盖旧信息的程度。我们把公式翻译成代码alpha 0.1 gamma 0.9 epsilon 0.4 episodes 1000 for eps in range(episodes): state START total_reward 0 steps 0 # 每个回合最多走50步防止无限循环 while steps 50: action_idx epsilon_greedy(state, epsilon) next_state, reward, done step(state, ACTION_LIST[action_idx]) s_idx state_index(state) ns_idx state_index(next_state) # 核心更新 best_next np.max(Q[ns_idx]) Q[s_idx, action_idx] alpha * (reward gamma * best_next - Q[s_idx, action_idx]) state next_state total_reward reward steps 1 if done: break # 探索率随时间衰减 epsilon max(0.01, epsilon * 0.995)你可能已经注意到这里的更新公式用的不是“实际看到的下一步奖励”去累计而是“即时奖励下一个状态的最大Q值”。这是因为机器人下一步本来就会选择Q值最大的动作所以我们要用最优动作的价值来做“下一步期望值”。这背后有一个假设我们的策略会逐步向最优策略收敛。这行代码就是Q-learning最核心的地方你只要弄懂它就弄懂了所有基于值函数的强化学习算法。4.4 怎么确认训练结果是对的只把代码跑完不算懂你得会检查结果。训练完之后打印一下终点附近的Q值。正常情况下终点上方那个状态5,4的最佳动作应该是“down”Q值大约是10。如果发现这个状态的Q值很低很可能是γ选得太小或者智能体还没走通到终点。另一个更直观的检查方式是对每个网格看智能体会朝哪个方向走。把整个网格染成箭头图就能看到一条从起点到终点的路径。我自己在实验时用了这样一个简单断言从起点出发每一步选最佳动作如果最终能到达终点说明Q学习目标基本完成。# 检查学习结果从起点出发每一步选argmax观察能否到达终点 state START path [state] while len(path) 50: s_idx state_index(state) action_idx int(np.argmax(Q[s_idx])) next_state, reward, done step(state, ACTION_LIST[action_idx]) state next_state path.append(state) if done: break print(走到的格子数:, len(path)) print(最终的格子:, state GOAL)这里有个很容易犯的错误你会想当然地认为“最优策略就是每一步都走最优动作”但如果在训练阶段不加入探索智能体可能永远学不会最优动作。所以上面测试用的策略是“纯贪心”它和训练时的ε-greedy策略是两码事。这个区别我最初没搞懂导致我怀疑结果是不是自己“作弊”了。现在想想训练时用随机策略增强探索测试时用纯贪心评估性能这是强化学习实验的基本功。5. 实操过程中的常见坑与排查思路5.1 奖励设计是最大的坑没有之一我把奖励设计排在第一个因为我在这个坑里摔过很多次。一个常见的错误是奖励设置得太稀疏智能体走几十步之后才能拿到一个非零奖励整个训练过程基本都是0Q表根本不收敛。解决方法是给中间过程设计一些“小梯度惩罚”。比如上面例子里每一步给-0.1就是为了让智能体在探索时感受到“走得越多越不划算”。但是惩罚也不能过大否则智能体会倾向于在角落里原地绕圈因为每走一步都在亏损它宁可不动。这种“懒惰策略”在真实项目中经常出现尤其是强化学习机器人任务里很多智能体学会了站在原地不干活就是惩罚设计不合理。另一个实际问题是非对称奖励。比如躲避障碍物碰一次墙扣3分到达目标得1分。这种设置会让智能体变得极其保守甚至会绕着目标走。一个相对稳的调整原则是让成功路径上的累积奖励明显优于失败路径上的累积奖励而不是只关注单步奖励大小。你可以画一条“最佳路线”和“最差路线”的累积奖励曲线看看它们的差距是否足够大。5.2 学习率太高Q值振荡学习率太低收敛慢学习率α的取值直接影响更新步长。α太大会导致Q值震荡甚至发散α太小会导致收敛速度太慢。初学者容易用一个固定α跑遍所有任务这是不对的。我的建议是在训练前期用大一点的α比如0.2让Q值快速逼近后期用小一点的α比如0.05做细调。更高级的做法是让α随时间衰减和ε衰减类似。还有一点需要特别注意Q-learning更新是增量式的同一个状态和动作会被反复访问多次。在环境不稳定的情况下如果α偏高会因为随机性抖动很大。建议在每一次实验中记录一下Q值的梯度或方差画出曲线看看是平稳下降还是上下乱跳。如果震荡先降α。5.3 探索率衰减太慢后期还在乱走衰减太快学不到新信息ε-greedy策略里的ε就是探索率它是智能体做随机动作的概率。开局设成0.5训练到后期衰减到0.05这样的设置很常见。但“怎么衰减”大有讲究。如果衰减太快智能体还没充分探索环境就开始贪心最后容易陷入局部最优。如果衰减太慢后期明明已经找到好路径智能体还乱走浪费训练回合。我自己的经验是把探索率衰减设计成“依赖已完成回合数”的函数而不是依赖总训练时间。比如固定1000回合你可以让ε从0.5线性衰减到0.05每回合衰减0.45/1000。如果你发现1000回合不够就把衰减速度调慢。别忘了设置ε的下限否则它可能会衰减到0导致完全停止探索。5.4 环境随机性太大跑三次结果完全不同网格世界是确定性环境跑几次结果一致。但是很多真实任务比如股市交易、机器人关节控制环境本身有随机噪声。这时候即使同一个种子、同一个算法两次训练也可能产生不同曲线。解决这个问题最基础的方法是固定随机种子。你可以用np.random.seed和一些官方库的seed方式来做。固定种子之后至少每次复现结果是一致的。但这还不够。就算固定种子随机噪声依然是存在的曲线看起来还是会上下波动。这时候你要做的是“多次试验取平均”。具体做法是用5个不同种子跑5遍记录每次的回合奖励然后画平均曲线和置信区间。如果你只跑一遍看到一个凸起就以为效果变了很容易被误导。5.5 代码调试的实用技巧分开看“学习”和“评估”最后分享一个我调试强化学习代码的习惯不要把训练和评估混在一起。训练过程中偶尔让策略贪心去跑一个“评估回合”记录它能不能找到目标而不是只看训练时的累积奖励。因为训练时探索率很高累积奖励天然会偏低这并不代表策略不好。我写了一个简单工具函数每50回合跑一次纯贪心评估评估步数和奖励记下来。这个曲线比训练曲线更能反映学习效果。def evaluate(Q, max_steps50): state START total 0 for _ in range(max_steps): action_idx int(np.argmax(Q[state_index(state)])) next_state, reward, done step(state, ACTION_LIST[action_idx]) total reward state next_state if done: return total, state GOAL return total, False这条评估曲线如果是在稳步上升说明Q函数在学习过程中是有效的如果不动可能是γ太小导致远期奖励没有传导开来如果上升后下降往往是α或ε设置有冲突需要回调。6. 写在系列开篇的话啃下第一块硬骨头的正确姿势这篇“强化学习入门”第一篇就写到这里。我不打算在结尾给你一堆“未来展望”式的东西我只想说说自己学强化学习的一点体会。我是在一个很小的网格世界项目里第一次感觉到“智能体真的在学东西”。那种看着Q表从全零变成有方向感、看着箭头图从一团乱麻变成一条清晰道路的感觉比跑通任何深度学习模型都要妙。但这个过程一开始并不顺利。我光是理解“折扣累积回报”就花了一个星期后来又倒在了探索率衰减的调节上。每次看到loss曲线上蹿下跳都会怀疑自己是不是把代码写错了。后来我发现强化学习有一个天然特征你无法一步到位看到正确答案你需要耐心地、一遍一遍地跑实验把“直觉”和“数学公式”慢慢对上。所以我想给还在入门阶段的你一个建议不要追求一口气吃成胖子不要上来就复现DQN、PPO这类大算法。先在像网格世界这样的小环境里亲手把Q-learning写出来跑通看看Q表长什么样然后故意改坏某个参数观察性能曲线怎么变化。这个过程的意义远大于收藏二十篇教程。接下来我会在这个系列里逐步深入讲完Q-learning我会用DQN处理图像输入再讲Policy Gradient、Actor-Critic以及如何在一个更接近真实业务的项目里设计奖励函数。但这个系列的第一篇我还是想让你彻底搞清楚那四个循环步骤、那一个更新公式、和那一个“探索与利用的平衡”。这些才是你后面所有工作的真正地基。如果你在照着这篇文章做的时候遇到什么奇怪的现象请在纸上把我前面画的那个“状态、动作、奖励、策略、价值”闭环逐项检查一遍。大部分问题都能在这个闭环上找到答案。