news 2026/9/28 20:38:22

斯坦福CS224R深度强化学习课程全解析:从算法原理到本地实验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
斯坦福CS224R深度强化学习课程全解析:从算法原理到本地实验

如果你平时主要接触的是监督学习和各类深度学习框架,第一次听到“深度强化学习”这个词,大概率会有这样一种感觉:知道它很厉害,也知道 AlphaGo、自动驾驶、大模型对齐这些场景都离不开它,但是真要自己上手,又不知道从哪里开始。

这两年大模型技术快速普及,强化学习的应用范围也被重新定义了。无论是 RLHF(基于人类反馈的强化学习)、Agent 的任务规划,还是具身智能里的决策控制,底层都离不开强化学习这套方法论。很多人发现,之前靠纯监督学习能解决的问题正在变少,需要模型自己试错、自己决策的场景越来越多,而这类问题恰恰是监督学习不擅长、强化学习专门处理的。

斯坦福大学的 CS224R 课程,全称是 Deep Reinforcement Learning,也就是深度强化学习。这门课是斯坦福 CS 系列中对强化学习覆盖比较系统的一门进阶课。和 CS224N 讲自然语言处理、CS231N 讲计算机视觉不同,CS224R 聚焦的是“智能体如何在环境中通过交互学会决策”。如果你正在寻找一条从算法原理到代码实验的完整学习路径,这门课是一个不错的选择。

这篇文章会围绕 CS224R 这门课展开,说清楚课程讲什么、适合谁、怎么学,并给出一套可以直接上手的本地实验方案。全文不是单纯复述课程大纲,而是从一名技术学习者的角度,帮你把“看视频”转化为“真学会”。

1. 这篇文章真正要解决的问题

先说一个很多自学强化学习的人都会踩的坑:资料太散。

网上的强化学习内容大致分两类。一类是偏科普的博客和视频,讲概念很生动,但一到公式推导就直接跳过,看完只觉得“懂了”,真去实现一个算法时无从下手。另一类是论文和开源代码,信息密度很高,但对初学者不友好,一个 PPO 实现动辄上千行,里面夹杂了大量工程优化技巧,新手根本分不清哪些是算法核心、哪些只是训练技巧。

CS224R 的价值在于,它把“概念—公式—代码—实验”串在了一条完整的教学路径里。它不是只讲某个具体算法,而是带着你把强化学习的主干过一遍:从马尔可夫决策过程出发,到深度 Q 网络,再到策略梯度和 Actor-Critic 体系,最后延伸到与大模型、机器人控制相关的方向。

这篇文章要解决三个问题:

  1. 帮你判断 CS224R 是否适合你当前的学习阶段,避免跟风刷课。
  2. 帮你梳理课程的技术主线,看懂算法之间的演进关系,而不是孤立地记名词。
  3. 给出一套可以在本地跑通的强化学习最小实验,配合课程内容做验证,让“看懂”变成“做出来”。

如果你正在做大模型应用开发、机器人控制、游戏 AI,或者单纯想把机器学习的基础补完整,这篇文章的内容应该对你有用。

2. CS224R 课程的核心内容与学习价值

2.1 课程定位:不是入门课,是进阶体系课

CS224R 的定位是研究生级别的深度强化学习课程。它假设你已经具备深度学习基础,对神经网络、反向传播、常见的优化方法不陌生,但不要求你之前系统学过强化学习。

这意味着课程节奏会比较快。前几周讲清楚基础概念之后,很快就会进入现代强化学习算法的核心部分。如果完全没有机器学习基础直接看这门课,会非常吃力。建议先把深度学习和 Python 的基础补起来,再进入这门课的学习。

从课程内容看,CS224R 覆盖的算法脉络大致如下:

  • 序列决策问题与马尔可夫决策过程(MDP)
  • 基于价值的深度强化学习,核心是 DQN 及其改进
  • 基于策略的强化学习,核心是策略梯度定理与 REINFORCE
  • Actor-Critic 体系,核心是 A2C / A3C 与 PPO
  • 最大熵强化学习,核心是 SAC
  • 与深度生成模型、大语言模型结合的前沿主题

这套内容从 2018 年课程成立开始就在不断更新,到 2025 年春季版本,课程中关于大语言模型智能体和 RLHF 相关的比重明显提升,和当前工业界关注的方向是接轨的。

2.2 这门课解决了自学者的什么痛点

第一个痛点是“不知道算法为什么这样设计”。

很多人在博客里见过 DQN 的代码,知道它有经验回放和 target network,但不知道为什么必须有这两个组件。课程会从 TD 误差和自举(bootstrapping)讲起,让你理解 DQN 训练不稳定的根源,然后再引入经验回放和 target network 作为解决方案。这样的学习路径不是背代码,而是在理解问题。

第二个痛点是“新旧算法之间的关系看不懂”。

PPO 为什么是 TRPO 的简化?DDPG 和 TD3 之间差了什么?SAC 的熵正则到底解决了什么问题?这些算法如果一个个孤立地看,很容易晕。CS224R 的课程编排会强调算法之间的递进关系:策略梯度解决连续动作问题,Actor-Critic 降低方差,PPO 让更新更稳,SAC 让探索更充分。理解这条主线之后,再看论文和开源代码会轻松很多。

第三个痛点是“理论与实验脱节”。

课程配套的作业和项目通常需要你实现算法并在仿真环境中调试。这种实战训练对理解强化学习尤其重要,因为强化学习算法对超参数和随机种子非常敏感,很多“理论正确”的实现就是训练不出来。只有亲手调过一遍,才能理解为什么强化学习工程的难度常常大于算法本身的难度。

2.3 值得关注的课程亮点

CS224R 在 2025 年春季版本中有一个很突出的特点:越来越多地把强化学习视为大模型时代的基础能力,而不是只在游戏和机器人里使用的冷门技术。

具体来说,课件中会用强化学习的框架来分析大语言模型的对齐问题:奖励模型如何打分、策略模型如何更新、如何防止模型在优化奖励时出现奖励攻击(reward hacking)。这种视角对做 LLM 应用的人来说很有价值,因为它把 RLHF 从“黑盒指令微调”还原成了标准的强化学习问题,理解起来会更透彻。

另外,课程涉及的连续控制、多智能体、离线强化学习等内容,也是机器人、自动驾驶、游戏 AI 等方向的核心基础。学完这门课,再去看这些领域的论文,会有一种“地图已经点亮”的感觉。

3. 深度强化学习的核心概念梳理

如果你是第一次接触深度强化学习,先把下面几个概念理解到位,后续看课程视频会顺畅很多。

3.1 强化学习的本质:智能体与环境交互

监督学习的特点是给定输入和标签,模型学习输入到输出的映射。强化学习则完全不同:智能体(agent)在环境(environment)中执行动作(action),环境返回新的状态(state)和奖励(reward),智能体根据奖励调整策略。整个过程没有“标准答案”,只有好与坏的结果反馈。

这在生活中很容易找到类比。学骑自行车就是典型的强化学习过程:你不会因为一次摔倒就知道“正确答案”,而是通过反复尝试,让身体逐渐学会保持平衡。摔倒就是负奖励,平稳骑行就是正奖励,最终学会的是一套动作策略,而不是某个固定动作。

技术定义上,强化学习问题通常建模为马尔可夫决策过程(MDP),可以表示为五元组:状态集合、动作集合、状态转移概率、奖励函数和折扣因子。CS224R 对 MDP 的讲解比较细致,会专门讨论“为什么马尔可夫假设在现实问题中并不总成立,以及如何通过状态表示来近似满足它”。

3.2 回报、折扣因子与价值函数

智能体在环境中会持续决策,所以累计奖励比单步奖励更重要。但未来奖励存在不确定性,且时间越远价值越低,因此引入折扣因子 gamma。gamma 越接近 1,智能体越看重长期收益;gamma 越接近 0,智能体越短视。

价值函数是强化学习的核心工具,常见的有两种:

  • 状态价值函数 V(s):从状态 s 出发,按照当前策略继续执行,能获得的期望回报。
  • 动作价值函数 Q(s, a):在状态 s 执行动作 a 之后,再按照当前策略继续执行,能获得的期望回报。

课程里会强调:强化学习算法本质上都在做同一件事——估计价值函数,并据此改进策略。区别只是哪种方式更高效、更稳定。

3.3 策略、探索与利用

策略就是智能体从状态到动作的映射规则。强化学习的目标就是找到最优策略,让期望回报最大。

这里有一个贯穿始终的矛盾:探索与利用。利用是指选择当前已知最好的动作,探索是指尝试不确定但有潜力的新动作。如果完全利用,智能体可能陷入局部最优;如果完全探索,算法无法收敛。DQN 的 epsilon-greedy、SAC 的熵正则,本质上都是在用不同方式平衡这个矛盾。

3.4 on-policy 与 off-policy 的直观区别

很多初学者在这里容易混淆。简单来说:

  • on-policy 算法:用来评估和改进的策略,与用来收集数据的策略是同一个。典型代表是 PPO、A2C。
  • off-policy 算法:数据由旧策略收集,但可以用新策略来评估和学习。典型代表是 DQN、DDPG、SAC。

一个方便理解的类比是:on-policy 是“在实战中学习,边打边总结”;off-policy 是“回头看录像,分析历史对战记录”。课程在介绍这些算法时会特别强调两类算法的差异,因为它直接决定了数据效率和训练稳定性。

4. 学习 CS224R 的前置条件与学习建议

网上很多人在看 CS224R 时坚持不下去,并不是因为课程质量不好,而是前置知识没有补齐。结合课程难度,下面几项是建议提前准备好的。

4.1 数学基础

概率论与数理统计是最重要的。强化学习的核心公式都涉及期望、条件概率、贝叶斯公式,概率论不扎实的话,策略梯度定理基本看不懂。线性代数要熟悉矩阵乘法、特征分解等基本操作,因为它们贯穿神经网络的前向传播和梯度计算。微积分主要涉及偏导数和链式法则,理解反向传播时必须有这个基础。

数学基础薄弱不等于不能学,而是需要刻意补课。推荐的做法是:看课程视频时,遇到公式推导卡住的地方,先停下来,把涉及的数学概念单独查清楚,不要指望跟着视频念一遍就会了。

4.2 机器学习与深度学习基础

建议先掌握以下内容再进入 CS224R:

  • 神经网络的基本结构,包括全连接层、卷积层、激活函数。
  • 反向传播和梯度下降的基本原理。
  • 过拟合、正则化、学习率等基本概念。
  • 使用 Python 和 PyTorch 训练过一个最简单的分类模型。

如果没有这些基础,建议先学 CS229 或 CS231N 的相关章节,再回来刷 CS224R。直接硬啃不是不行,但性价比很低。

4.3 编程基础与工具链

课程作业和项目通常基于 Python 和 PyTorch,部分实验会用到仿真环境。建议提前熟悉以下工具:

  • Python 3 的基本语法和面向对象编程。
  • NumPy 的基础操作。
  • PyTorch 的 Tensor、Module、Optimizer 基本用法。
  • 基本的命令行操作和虚拟环境管理。

如果你对大模型工具链比较熟悉但对 PyTorch 不熟,建议先花两三天时间写几个小模型练手,避免在课程实验阶段被工具链卡住。

5. 如何高效观看这套视频课程

“英文原声|中英字幕”是这套视频的显著特征,对英语不算流利的同学来说是很好的学习资源。但高效利用它并不仅仅是“打开视频播放”这么简单。

5.1 中英字幕的正确使用方式

建议分两遍观看。

第一遍使用中英字幕同时开启,重点理解课程内容的整体脉络。遇到听不懂的术语可以看英文字幕学习标准表达,遇到复杂的算法逻辑可以看中文字幕帮助理解。

第二遍只开英文字幕,甚至不开字幕。这一遍的目标不是“听清每一句话”,而是训练自己用英文理解技术概念的能力。强化学习领域的术语在论文里也用英文,能直接用英文理解算法,大幅提升后续读论文的效率。

真正容易踩坑的地方是:一直开着中英字幕,以为自己听懂了,实际上只是在阅读中文字幕。建议每个章节看完后,用自己的话复述一遍算法流程,能说清楚才算真的理解了。

5.2 与课件、论文、代码配套使用

只看视频远远不够。CS224R 的课程主页会提供幻灯片、阅读论文和作业说明。建议按照以下顺序学习一个章节:

  1. 先看视频,建立整体认知。
  2. 再读对应章节的幻灯片,补充视频中跳过的推导细节。
  3. 找到 slides 中引用的经典论文,重点读算法描述和实验设置部分。
  4. 查看课程作业,尝试独立完成代码实现。

这个方法看起来慢,但效果远好于把视频刷完然后大脑一片空白。深度强化学习是动手学科,只看不练基本等于白看。

5.3 笔记与代码仓库管理

建议为这门课创建一个专门的代码仓库,按章节或算法类型组织目录结构,例如:

cs224r-study/ ├── notes/ │ ├── 01-mdp-basics.md │ ├── 02-dqn.md │ └── 03-policy-gradient.md ├── labs/ │ ├── dqn_cartpole.py │ ├── reinforce_cartpole.py │ └── ppo_cartpole.py └── papers/ ├── dqn.pdf └── ppo.pdf

笔记不要直接抄公式,而是用自己的语言把算法流程写出来。例如 DQN 的笔记应该写清楚“为什么要固定 target network”“经验回放缓冲区的容量如何影响训练”,而不是抄一遍损失函数表达式。

6. 本地实验示例:用 CartPole 验证策略梯度算法

课程中的作业已经能提供很好的训练,但如果你想在看视频的过程中就动手验证,这里给出一个最简单的强化学习实验:使用 REINFORCE 算法训练 CartPole 环境。

6.1 环境准备与依赖安装

本实验使用 OpenAI Gym 的 CartPole-v1 环境和 PyTorch。建议在虚拟环境中安装依赖,避免污染系统环境:

python -m venv cs224r-lab source cs224r-lab/bin/activate pip install torch gym numpy

如果网络环境受限,可以改用国内镜像源安装:

pip install torch gym numpy -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,用下面的命令确认环境可用:

python -c "import gym; env = gym.make('CartPole-v1'); print('env ok', env.observation_space, env.action_space)"

预期输出中会显示观测空间是 Box(4,),动作空间是 Discrete(2),说明环境正常。

6.2 实现 REINFORCE 策略梯度算法

REINFORCE 是策略梯度方法中最基础的算法。它的核心思路是:用神经网络直接参数化策略,在环境里收集一段完整轨迹,计算每个时间步的累计折扣回报,然后用“回报乘以对数概率的梯度”来更新网络参数。

完整代码如下:

# 文件路径:cs224r-lab/reinforce_cartpole.py import gym import numpy as np import torch import torch.nn as nn import torch.optim as optim from torch.distributions import Categorical class PolicyNet(nn.Module): def __init__(self, n_state=4, n_action=2, hidden=64): super().__init__() self.fc1 = nn.Linear(n_state, hidden) self.fc2 = nn.Linear(hidden, hidden) self.fc3 = nn.Linear(hidden, n_action) def forward(self, x): x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) return self.fc3(x) def compute_returns(rewards, gamma=0.99): returns = [] G = 0 for r in reversed(rewards): G = r + gamma * G returns.insert(0, G) return torch.tensor(returns, dtype=torch.float32) def train(): env = gym.make("CartPole-v1") policy = PolicyNet() optimizer = optim.Adam(policy.parameters(), lr=1e-2) for episode in range(500): state, _ = env.reset() log_probs = [] rewards = [] done = False while not done: state_t = torch.tensor(state, dtype=torch.float32).unsqueeze(0) logits = policy(state_t) dist = Categorical(logits=logits) action = dist.sample() log_probs.append(dist.log_prob(action)) next_state, reward, terminated, truncated, _ = env.step(action.item()) done = terminated or truncated rewards.append(float(reward)) state = next_state returns = compute_returns(rewards) log_probs_tensor = torch.stack(log_probs) policy_loss = - (log_probs_tensor * returns).sum() optimizer.zero_grad() policy_loss.backward() optimizer.step() if (episode + 1) % 50 == 0: avg_reward = sum(rewards) print(f"Episode {episode + 1}, total reward: {avg_reward}") if __name__ == "__main__": train()

这个实现里需要注意几个关键点:

  • 策略网络输出的是 logits,不是概率。通过 Categorical 分布采样动作,并用log_prob(action)计算选中的动作的对数概率,这是策略梯度更新的基础。
  • compute_returns从轨迹末尾向前计算折扣回报,保证每个时间步的回报都包含未来奖励。
  • 损失函数取了负号,因为我们需要最大化期望回报,而优化器默认是最小化目标。

6.3 运行与验证

运行下面的命令开始训练:

python reinforce_cartpole.py

正常情况下,前几十个 episode 的总奖励会在 20 到 50 之间徘徊,之后逐步上升。到 200 到 400 个 episode 附近,总奖励可以达到 200 甚至更高,接近 CartPole-v1 的回合上限。

如果训练始终无法超过 100,可能需要调整学习率或 hidden 层大小。这个例子只用了最简单的策略梯度方法,收敛速度不算快,但它足够帮助你理解强化学习的完整训练循环:采样、计算回报、更新策略、再采样。

7. 常见问题与排查思路

结合学习这门课和做实验的常见情况,整理了一份问题排查表,供实际学习时参考。

问题现象可能原因排查方式解决方案
视频能看懂,但自己做作业无从下手只看视频,没有动手实践回顾课件中的伪代码,对照伪代码逐步实现先写一个最简版本跑通,再按课程要求扩展
训练 CartPole 不收敛学习率设置不当或回报计算错误打印每个 episode 的总奖励和损失值检查 returns 计算、降低学习率、调整网络结构
训练过程中 loss 变为 NaN数值溢出或 log_prob 计算出问题检查 logits 中是否有异常值,增加打印日志增加数值稳定性处理,降低学习率
DQN 训练不稳定没有正确使用 target network确认 target network 的参数是否定期拷贝每 N 步同步一次 target network 参数
公式推导看不懂数学前置知识不足逐个术语查资料,结合概率论教材先补概率论和线性代数,再回头读论文
算法细节懂了但不会工程化缺少大规模实验经验学习开源实现,对比自己的代码结构复现一个成熟算法,对比代码差异,做好实验日志

8. 最佳实践与工程建议

深度强化学习的学习曲线比传统深度学习更陡,工程调试也更复杂。下面这些建议来自实际学习与实验经验,希望帮你少走弯路。

8.1 训练实验必须做日志与可复现设计

强化学习实验的随机性很大。同样一份代码,不同随机种子可能产生完全不同的收敛曲线。因此,第一件事就是固定随机种子:

import random import numpy as np import torch def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed)

同时记录每次实验的超参数、代码版本、环境版本和随机种子,形成可复现的实验记录。很多看似算法优化带来的提升,实际上只是随机种子的运气。

建议使用类似下面的结构记录实验信息:

实验编号: exp_001 算法: REINFORCE 环境: CartPole-v1 学习率: 0.01 网络结构: [64, 64] 折扣因子: 0.99 随机种子: 42 结果: episode 300 时平均 reward 达到 200

8.2 从简单的环境开始验证算法

不要一开始就在复杂环境里调试新算法。一个策略梯度实现,在 CartPole 上都跑不稳,放到 MuJoCo 或 Atari 上只会更难受。建议先做最小验证,再逐步增加环境复杂度。

课程中涉及的算法,建议都先在以下环境中验证:

  • CartPole-v1:最简单,适合验证基础算法逻辑。
  • LunarLander-v2:稍复杂,适合验证算法稳定性。
  • 连续控制环境:适合验证 DDPG、SAC、PPO 等算法。

8.3 关注算法的显式与隐式假设

深度强化学习算法有很多隐含设计,初学者容易忽略。比如:

  • DQN 需要经验回放,因为样本之间存在强相关性,直接按顺序训练会导致梯度不稳定。
  • PPO 使用 clipped surrogate objective,是为了防止策略更新幅度过大。
  • SAC 的熵正则,是为了让策略保持探索能力,避免过早收敛到局部最优。

这些设计背后都有明确的动机。学习时建议多问一句:如果没有这个组件,算法会怎样?把这个问题想明白,算法之间的关系就清楚了。

8.4 与前沿方向结合学习

CS224R 在 2025 年的课程内容已经和生成式 AI、大语言模型产生了明显交叉。如果你本身是做 LLM 应用开发的,建议学完基础算法后,多看一些 RLHF、推理模型和 Agent 相关的论文。你会发现之前以为是“黑盒”的 RLHF,本质上就是课程中学过的策略梯度与奖励模型。

从工程角度看,强化学习正在成为大模型能力的底层支撑,这部分价值会在后续几年持续放大。现在把基础打牢,后续的边际收益会很高。

9. 总结与后续学习方向

CS224R 的价值不在于让你记住十几个算法名称,而在于帮你建立一条完整的技术主线:从 MDP 出发,理解强化学习问题的形式化方式;从 DQN 和策略梯度出发,理解两类基本方法各自的优劣;从 Actor-Critic 和 PPO、SAC 出发,理解现代强化学习算法的工程化设计。

如果你打算认真学习这门课,建议按以下节奏推进:

  1. 先完成机器学习基础补课,确保能看懂神经网络训练的基本流程。
  2. 按章节顺序观看视频,配合课件和论文阅读,不跳步。
  3. 每学完一个算法模块,就在本地跑一个最小实验,验证算法逻辑。
  4. 学完基础算法后,再读 RLHF 和 Agent 相关的论文,把课程知识迁移到大模型场景。

如果你目前主要做大模型应用,可以先从课程中与大模型相关的章节入手,再回头补强化学习基础。如果你主要做后台开发,强化学习不一定是你当前的核心技能,但理解它的基本思想,对系统设计和智能体开发都有帮助。

最后提醒一句:这门课的视频、课件和作业都是很好的学习资源,但资源本身不会自动转化为能力。真正的提升来自动手写代码、调试实验、阅读论文这个过程。跑通一个算法不难,难的是在反复失败中理解算法为什么这样设计、在什么条件下会失效。这恰恰是 CS224R 最值得花时间的地方。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 20:37:17

Kuikly Compose UI Inspector实战:可视化调试与性能分析的完整指南

Kuikly Compose UI Inspector实战:可视化调试与性能分析的完整指南 【免费下载链接】KuiklyUI 基于KMP技术的高性能、全平台开发框架,具备统一代码库、极致易用性和动态灵活性。 Provide a high-performance, full-platform development framework with …

作者头像 李华
网站建设 2026/9/28 20:36:52

园区访客管理系统有哪些品牌?四类厂商横向看清

一、先说结论:这个问题难答,不是因为你没找到资料 搜“园区访客管理系统有哪些品牌”,出来的是几十家官网,看着都差不多,报价却能差好几倍。 问题不在资料太少,在于**“访客管理系统”这个词底下装的不是一…

作者头像 李华
网站建设 2026/9/28 20:36:46

计算机网络:网络层(二)——IPv4编址、CIDR与路由选择

引言 计算机网络:网络层(一)——服务模型、路由算法与IPv4数据报 在上篇文章中我们介绍了网络层的功能是什么,以及数据报应该如何传输,在本期我们将了解IPv4地址怎样进行划分,路由器怎样根据地址选择路由 …

作者头像 李华
网站建设 2026/9/28 20:36:19

DC-DC辐射发射超标的EMC整改实战:从48MHz振铃到全频段通过

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 20:36:10

开源鸿蒙平台 KMP/CMP 三方库「Okio」适配全流程

本文记录 okio 接入 OpenHarmony 的完整过程,覆盖 KMP/CMP 工程盘点、ohosArm64 目标、Kotlin/Native 动态库、C ABI/N-API 桥接、ArkUI 真机页面、HAP 签名和真机验收。 本次适配复用 Okio 的 Kotlin Multiplatform 公共 API 和 Buffer 实现,再由 OpenH…

作者头像 李华
网站建设 2026/9/28 20:34:29

秋招电控岗简历没回音?10个开源项目做深才有工程信号

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华