<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>Hana&apos;s Blog</title><description>一个笨蛋学生</description><link>https://hana-blog.top</link><item><title>Paper Reading: CV 1</title><link>https://hana-blog.top/blog/paper-reading-cv1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-cv1</guid><description>浅度一些视觉相关的工作</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;import { ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;CV Paper Reading&apos;,
items: [
{
title: &apos;Batch 1&apos;,
href: &apos;/blog/paper-reading-cv1&apos;,
order: &apos;1&apos;
},
{
title: &apos;Batch 2&apos;,
href: &apos;/blog/paper-reading-cv2&apos;,
order: &apos;2&apos;
}
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;计算机视觉基础模型关注从图像、视频和点云中学习可迁移的表征与几何结构。相关工作覆盖经典视觉骨干、自监督预训练、开放词表检测、通用分割、点云表征与视觉几何，核心作用是为分类、检测、分割、三维理解、场景重建和机器人感知等任务提供稳定的基础感知能力。&lt;/p&gt;
&lt;h2&gt;AlexNet&lt;/h2&gt;
&lt;h2&gt;VGG&lt;/h2&gt;
&lt;h2&gt;ResNet&lt;/h2&gt;
&lt;h2&gt;ViT&lt;/h2&gt;
&lt;h2&gt;MAE&lt;/h2&gt;
&lt;h2&gt;DINO&lt;/h2&gt;
&lt;h2&gt;DINOv2&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/DINOv2.4g550rsa3m.webp&quot; alt=&quot;&quot;&gt;
DINOv2 可以看作是 DINO 的规模化与工程化升级版本，目标不再只是验证 self-distillation 在 ViT 上的有效性，而是训练出一组可以直接作为冻结视觉 Backbone 使用的通用特征模型。在数据层面，DINOv2 不再局限于 ImageNet 这类标准数据集，而是从大规模无标签图像中进行去重、过滤、检索和聚类，构建出包含 1.42 亿张图像的 LVD-142M 数据集，从而获得覆盖更广、质量更高的视觉概念分布。在方法层面，DINOv2 仍然沿用教师-学生自蒸馏框架，但将训练目标从 DINO 中偏图像级的表示学习进一步扩展到 patch-level 表示学习：除了使用 DINO loss 对齐全局视图的语义分布，还引入 iBOT 风格的 masked patch prediction，让学生网络在部分 patch 被 mask 的情况下预测教师网络给出的 patch-level 目标。这样模型不仅能学到 image-level 的全局语义，也能学到适合分割、深度估计和视觉对应等任务的 dense features。为了在更大数据和更大模型上稳定训练，DINOv2 还强化了防塌缩机制，包括 Sinkhorn-Knopp centering 和 KoLeo regularization，使特征分布更加均衡、输出空间利用更加充分。总体来看，DINOv2 的核心贡献在于把 DINO 的自监督 ViT 路线扩展为一套可规模化训练、可蒸馏到不同模型尺寸、且能在多种下游任务中直接迁移的通用视觉特征体系。&lt;/p&gt;
&lt;h2&gt;DINOv3&lt;/h2&gt;
&lt;p&gt;DINOv3 可以看作是 DINOv2 路线的进一步规模化与 dense feature 强化版本，目标是训练一个可以直接作为冻结视觉 Backbone 使用的通用视觉基础模型。数据层面，DINOv3 使用 LVD-1689M 数据集进行训练，共包含 16.89 亿张无标注图像。这些图像来自约 170 亿张 Instagram 公开帖子图像构成的大规模候选池，随后通过内容过滤、去重、DINOv2 特征聚类、均衡采样和检索式筛选得到最终训练集，从而在保证规模的同时尽量提升视觉概念覆盖和数据质量。模型层面，DINOv3 继续沿用教师-学生自监督蒸馏框架，并将 Teacher 扩展到约 7B 参数规模，同时通过蒸馏得到 ViT 和 ConvNeXt 的多种小模型，方便在不同计算预算下部署。DINOv3 最关键的新方法是 Gram Anchoring：论文发现，在继续放大 DINOv2 并延长训练后，图像级分类能力仍会提升，但 patch-level dense features 可能出现退化，影响分割、深度估计和视觉对应等任务。Gram Anchoring 用早期 dense feature 更干净的模型作为约束对象，通过对齐 patch 特征之间的 Gram 相似性结构，帮助模型在继续学习全局语义的同时保留局部几何关系和空间一致性。总体来看，DINOv3 的核心贡献不在于提出全新的自监督范式，而在于证明 DINOv2 风格的自蒸馏视觉模型可以在更大数据、更大模型和更长训练下继续扩展，并通过 Gram Anchoring 解决 dense feature 质量保持的问题。&lt;/p&gt;
&lt;h2&gt;Grounding DINO&lt;/h2&gt;
&lt;p&gt;从 workflow 看，图像先经过 backbone 得到 visual features，文本经过 text encoder 得到 text features，随后在 Feature Enhancer 中做 Fusion。Language-Guided Query Selection 会计算 image tokens 和 text tokens 的相似度，再对文本维度取最大值，得到每个 image token 与整段 prompt 的最高相关分数，最后选出 top-K 个 image tokens 作为 decoder 的初始候选 query。并同时取出 reference boxes，每个 image token 本身来自 feature map 的一个空间位置，检测器也会为每个位置生成或预测一个粗略候选框。Cross-Modal Decoder 再继续 refine 这些 query，若有 L 层 decoder，则有 L 层中间表征。Prediction Head 以每层的中间表征去预测 box refinement，一路计算 query feature 与 text feature 的相似度。&lt;/p&gt;
&lt;p&gt;训练时，所有数据都会被整理成 grounding 格式：[image, text, boxes, positive_map]，其中 positive_map 表示每个 box 对应文本中的哪些 token。普通检测数据可以把类别名拼成 prompt，phrase grounding 数据本来就有短语和 box 的对应关系，caption 数据则需要借助已有模型生成 pseudo boxes。由于预测的 query 和真实目标之间没有天然顺序，训练时先用 Hungarian Matching 做一对一匹配，再使用 query-text token matching 上的 focal loss、box L1 loss 和 GIoU loss。&lt;/p&gt;
&lt;h2&gt;Segment Anything&lt;/h2&gt;
&lt;p&gt;SAM 对歧义的处理也很关键。
如果模型只有一个输出，在单点提示这种天然模糊的场景里容易把多个合理 Mask 平均成一个不干净的结果。
因此 SAM 默认对单个提示预测 3 个 Mask，对应 whole、part、subpart 这类常见嵌套层级，并额外预测每个 Mask 的 IoU 置信度用于排序。
训练时对多个输出分别计算损失，但只反传最小损失的那一个，相当于允许模型把不同输出槽位分配给不同合理解释。
当输入包含多个提示时，歧义通常降低，模型则返回单个更确定的 Mask。
损失函数使用 focal loss 与 dice loss 的组合，IoU 预测头使用 MSE 监督。&lt;/p&gt;
&lt;p&gt;训练流程模拟交互式分割，而不是只做一次静态预测。
每个目标 Mask 先随机选择一个前景点或带噪声的框作为初始提示，模型预测后，再从预测错误区域采样新的前景点或背景点进行修正，并把上一轮未阈值化的 Mask logits 作为 Dense Prompt 输入下一轮。
论文默认使用 11 轮：1 个初始提示、8 个从错误区域采样的交互点，以及 2 轮不加入新点的自我细化。
这样训练出来的模型不仅能处理单点或框，也能自然接入「用户继续点击修正」的交互流程。&lt;/p&gt;
&lt;p&gt;数据层面的贡献同样重要。
由于互联网上没有现成的海量分割 Mask，作者构建了一个三阶段 Data Engine。
第一阶段是 Assisted-manual：标注员使用 SAM 辅助的浏览器工具点击前景和背景，并可用 brush/eraser 修正，最终在 12 万张图上收集 430 万个 Mask，平均每个 Mask 的标注时间从 34 秒降到 14 秒。
第二阶段是 Semi-automatic：先用检测器自动填入高置信 Mask，标注员再补充遗漏物体，累计达到 30 万张图、1020 万个 Mask。
第三阶段是 Fully automatic：对每张图使用 32×32 规则点网格提示 SAM，并通过 IoU 置信度、稳定性筛选、NMS 和重叠裁剪过滤结果，最终在 1100 万张授权且隐私处理过的图像上生成 11 亿个高质量 Mask，形成 SA-1B。&lt;/p&gt;
&lt;p&gt;实验强调的是 Zero-shot Transfer。
单前景点分割中，SAM 在 23 个多域数据集上有 16 个数据集的 mIoU 高于 RITM；如果用 oracle 从 3 个候选 Mask 中选择最接近 GT 的结果，则所有数据集都优于 RITM，说明单点评估会受到 GT 歧义影响。
在 BSDS500 边缘检测上，SAM 没有专门训练边缘任务，但 ODS 达到 0.768、R50 达到 0.928，召回很强但会预测更多未标注边缘。
在 LVIS 目标提案中，SAM 的 AR@1000 为 59.3，低于 ViTDet-H 的 63.0，但在 medium、common 和 rare objects 上反而超过 ViTDet-H。
在实例分割中，SAM 接收 ViTDet 的检测框作为 Prompt，在 COCO 上 AP 为 46.5、LVIS 上 AP 为 44.7，低于全监督 ViTDet，但人工评测认为 SAM 的边界质量更好。&lt;/p&gt;
&lt;p&gt;这篇论文最值得学习的是「任务定义、模型接口、数据飞轮」三者绑定在一起。
Promptable Segmentation 给模型提供统一接口，轻量 Mask Decoder 保证交互可用，Data Engine 又利用当前模型降低标注成本、反过来扩大数据规模。
因此 SAM 的创新并不只是一个 ViT 分割模型，而是把分割模型做成可组合的视觉基础设施。
它的局限也来自这个定位：SAM 追求通用性而不是某个数据集的最高 IoU，细小结构可能漏掉，偶尔会产生小的断裂或幻觉区域；重型 Image Encoder 也让完整端到端推理并非真正实时。
另外，论文中的 text-to-mask 只是探索性结果，还不能等价于后续开放词表分割模型。&lt;/p&gt;
&lt;h2&gt;SAM 2&lt;/h2&gt;
&lt;p&gt;Memory Bank 是 SAM 2 的核心数据结构。
它维护两类空间记忆：一类是最近若干未提示帧的 FIFO 记忆，用来表示短期运动和外观变化；另一类是用户提示过的关键帧记忆，用来长期锚定目标身份。
除了空间特征，SAM 2 还保存 object pointers，也就是从 Mask Decoder 输出 token 得到的轻量向量，用来提供高层语义级的目标身份信息。
每一帧预测完成后，Memory Encoder 会把当前预测 Mask 下采样，并与该帧未条件化的图像特征相加，再用轻量卷积融合，生成下一步可写入 Memory Bank 的记忆。
这形成了一个流式循环：读入一帧 → 查询记忆 → 预测 Mask → 编码新记忆 → 继续处理下一帧。&lt;/p&gt;
&lt;p&gt;Prompt Encoder 和 Mask Decoder 大体沿用 SAM。
点、框等 sparse prompts 通过位置编码和提示类型 Embedding 表示，Mask 通过卷积编码后加到 frame embedding 上。
Mask Decoder 继续使用 two-way Transformer，在 Prompt Token 和 frame embedding 之间双向交互，并输出 Mask、IoU 分数和目标可见性信息。
对于单点这类歧义提示，SAM 2 仍然预测多个候选 Mask；但在视频里，歧义不仅发生在空间上，也可能沿时间传播，因此如果用户没有额外提示消除歧义，模型会根据预测 IoU 选择一个候选继续传播。
当某帧目标被遮挡或不可见时，遮挡 / 可见性预测头也会参与判断，避免错误地强行生成目标 Mask。&lt;/p&gt;
&lt;p&gt;训练时，SAM 2 同时使用图像和视频数据，并模拟交互式视频标注过程。
论文默认采样 8 帧序列，随机选择最多 2 帧作为提示帧，并在训练过程中根据 GT masklet 和模型预测采样纠错点击。
初始提示的分布是：50% 使用 GT Mask，25% 使用 GT 内的正点，25% 使用 bounding box。
模型需要按时间顺序逐帧预测完整 masklet，并在收到纠错点击后利用新的提示和已有 memory 修正后续结果。
损失包括 Mask 的 focal loss 和 dice loss、IoU 预测的 L1/MAE loss，以及目标是否存在的 occlusion / object prediction cross-entropy，权重比例为 20:1:1:1。
为增强长视频能力，作者还加入 16 帧序列微调：选择编辑帧最多、最困难的一半 masklets，用较低学习率训练，并冻结图像编码器以适配 A100 80GB 显存。&lt;/p&gt;
&lt;p&gt;数据引擎是 SAM 2 的另一条主线。
Phase 1 使用原始 SAM 逐帧辅助标注视频，每帧都要重新分割，质量高但慢，平均 37.8 秒 / 帧，在 1.4K 视频上收集 16K masklets。
Phase 2 引入只能接收 Mask prompt 的 SAM 2 Mask：标注员先用 SAM 标第一帧，再让模型传播到后续帧，错误时仍需要在中间帧重新画 Mask，速度降到 7.4 秒 / 帧，收集 63.5K masklets。
Phase 3 使用完整 SAM 2，标注员可以直接用点或 Mask 在任意帧修正，模型利用 memory 继续传播，速度降到 4.5 秒 / 帧，相比 Phase 1 快 8.4 倍，同时保持可比质量，收集 197K masklets。
此外，作者还用规则点网格在首帧自动生成候选 masklets，并经过人工验证筛选，既增加覆盖度，也能把失败样本送回人工修正流程。
最终 SA-V 数据集包含 50.9K 视频、642.6K masklets 和 35.5M masks，是已有最大 VOS 数据集 Mask 数量的 53 倍；其中手工部分为 190.9K masklets 和 10.0M masks。&lt;/p&gt;
&lt;p&gt;实验上，SAM 2 的重点是 Zero-shot 视频和图像分割。
在 promptable video segmentation 中，SAM 2 在 9 个密集标注视频数据集上同时优于 SAM+XMem++ 和 SAM+Cutie，并且达到更好精度所需交互次数少于前者的三分之一。
在只给第一帧提示的 semi-supervised VOS 设定中，SAM 2 在 17 个视频数据集平均表现也最好：1-click 为 64.7，3-click 为 75.3，5-click 为 77.6，box 为 74.4，GT mask 为 79.3，均高于 SAM+XMem++ 和 SAM+Cutie。
在图像 Segment Anything 任务中，如果只用 SA-1B 训练，SAM 2 的 1-click mIoU 为 58.9，略高于 SAM 的 58.1，但速度从 21.7 FPS 提升到 130.1 FPS；使用作者的视频和图像混合数据后，SA-23 上升到 61.9，14 个新增视频图像化数据集上升到 69.6。
在传统 VOS SOTA 对比中，SAM 2 Hiera-L 在 MOSE val、DAVIS 2017 val、LVOS val、SA-V val/test 和 YTVOS 2019 val 上分别达到 77.9、90.7、78.0、77.9、78.4 和 89.3，并且 Hiera-B+ / Hiera-L 都能在 A100 上实时运行，分别约 43.8 / 30.2 FPS。&lt;/p&gt;
&lt;p&gt;这篇论文最值得学习的是它把 SAM 的「可提示接口」推广成了一个流式时序系统。
SAM 负责在单帧里根据 Prompt 找到目标，SAM 2 则进一步回答：当目标在视频里移动、遮挡、消失、重现时，模型如何记住它、如何接受用户在任意帧修正、如何把修正继续传播。
它的局限也主要来自视频长期建模：跨镜头切换、拥挤场景、长时间遮挡、快速移动的细小结构和多个外观相似目标仍然困难。
另外，SAM 2 虽然可以处理多目标，但基本是逐目标独立处理，只共享每帧图像特征，目标之间缺少显式交互。
因此 SAM 2 更像是一个通用交互式视频分割基础设施，而不是完全解决所有开放世界视频理解问题的最终模型。&lt;/p&gt;
&lt;h2&gt;SAM 3&lt;/h2&gt;
&lt;p&gt;PCS 和 PVS 的本质差异在于「一个提示对应一个实例」变成了「一个概念对应所有实例」。
这带来两个新难点。
第一是开放词表识别：模型要判断输入中是否存在某个短语对应的概念，尤其要处理大量 hard negatives，例如图中没有该概念、相似概念或细粒度概念。
第二是实例穷尽性：只分出一个目标是不够的，模型必须尽量找到所有匹配实例，还要允许用户通过正负 exemplar 交互式补充漏检或移除误检。
论文明确限制文本提示为简单名词短语，不直接解决长 referring expression 或需要复杂推理的语言查询；复杂查询可以用 MLLM 拆成多个 noun phrases 后再调用 SAM 3。
&lt;img src=&quot;https://pic.hana0721.top/sam3-arch.1vzaqf4jmb.webp&quot; alt=&quot;&quot;&gt;
模型结构由 detector 和 tracker 组成，两者共享一个对齐过的 Perception Encoder 视觉语言 backbone。
Detector 是 DETR 风格的开放词表检测/分割器：图像和文本由 PE 编码，image exemplars 如果存在，则由 exemplar encoder 编码；文本 token 和 exemplar token 合称 prompt tokens。
Fusion Encoder 让图像特征 cross-attend 到 prompt tokens，得到条件化图像特征；随后 DETR-like decoder 用 object queries 在条件化图像特征上定位匹配实例。
每层 decoder 都预测 query 是否匹配当前 prompt、box refinement；Mask head 借鉴 MaskFormer 产生实例 Mask，此外还有 semantic segmentation head 逐像素预测是否属于当前概念。&lt;/p&gt;
&lt;p&gt;Presence Token 是 SAM 3 中很关键的设计。
普通 DETR query 同时承担「识别是什么」和「定位在哪里」两个任务，但开放词表场景下，识别需要看全图上下文，定位却是局部问题，两者容易冲突。
SAM 3 引入一个全局 learned presence token，专门预测 noun phrase 是否存在于输入中，即全局 presence score。
各个 object query 只负责在「该概念存在」的条件下定位候选实例，最终实例分数由 query score 和 presence score 相乘得到。
这样 recognition 和 localization 被解耦，尤其有利于 hard negatives：当图中根本没有该概念时，presence head 可以整体压低预测，而不是让局部 query 硬找一个相似物体。&lt;/p&gt;
&lt;p&gt;Image exemplar 让 SAM 3 具备交互式概念适配能力。
一个 exemplar 由 bounding box 和二元标签组成，标签可以是 positive 或 negative。
Positive exemplar 表示「找和这个框中目标同类的所有实例」，negative exemplar 表示「不要找这类误检」。
Exemplar encoder 会融合 box 位置编码、正负标签编码和 ROI-pooled 视觉特征，再用小 Transformer 得到 exemplar prompt，最后和文本 prompt 拼接。
这和 SAM/SAM2 的视觉提示不同：SAM 的框通常只定位一个实例，而 SAM 3 的 exemplar 会泛化成概念提示，例如给一个狗的正框后，模型应当找出图中所有狗。&lt;/p&gt;
&lt;p&gt;视频部分可以理解为「detector 负责发现新实例，tracker 负责维持身份」。
在每一帧，detector 根据概念 prompt 检测当前帧所有匹配对象；tracker 则沿用 SAM 2 风格的 memory-based propagation，把上一帧已有 masklets 传播到当前帧。
随后，SAM 3 用 IoU-based matching 将传播结果和当前帧检测结果合并：已匹配的 masklet 更新位置，未匹配的新检测会 spawn 成新的 masklet。
为了降低拥挤场景中的身份混淆，模型还使用 temporal disambiguation：一方面用 masklet detection score 衡量某个轨迹在时间窗口内是否持续被检测支持，低于阈值则抑制；另一方面周期性用高置信 detector mask 重新 prompt tracker，替换 tracker 自己可能漂移的预测，让 memory bank 保持新鲜可靠。
用户也可以继续用正负 click 细化单个 mask 或 masklet，视频中修正后的 Mask 会继续传播到整段视频。&lt;/p&gt;
&lt;p&gt;训练分四个阶段：先预训练 Perception Encoder，再做 detector pre-training，然后 detector fine-tuning，最后冻结 backbone 训练 tracker。
数据引擎也对应地从图像走向视频。
Phase 1 使用 captioner/parser 提出 noun phrases，用开放词表检测器加 SAM 2 生成初始 Mask，人类执行 Mask Verification 和 Exhaustivity Verification，得到 4.3M image-NP pairs。
Phase 2 用 Phase 1 的人类验证数据微调 Llama 3.2，形成 AI verifiers 自动做 MV 和 EV，让人类专注困难样本；同时用 Llama-based pipeline 生成 hard negative NPs，并多轮更新 SAM 3，新增 122M image-NP pairs。
Phase 3 扩展到 15 个图像域，并从 alt-text 和基于 Wikidata 的 22.4M 节点 SA-Co ontology 中挖掘长尾细粒度概念，新增 19.5M image-NP pairs。
Phase 4 扩展到视频，通过运动/场景过滤和失败样本挖掘生成视频 masklets，最终得到 SA-Co/VIDEO。&lt;/p&gt;
&lt;p&gt;SA-Co 数据集的规模很大。
高质量图像数据 SA-Co/HQ 包含 5.2M images、4M unique NPs、146.1M image-NP pairs 和 52.3M masks。
合成数据 SA-Co/SYN 由成熟数据引擎和 AI verifiers 生成，包含 39.4M images、38M NPs、1.7B image-NP pairs 和 1.4B masks。
外部数据 SA-Co/EXT 包含 9.3M images、497.4K NPs 和 70.5M masks。
视频数据 SA-Co/VIDEO 包含 52.5K videos、24.8K NPs、134K video-NP pairs 和 467K masklets。
评测集覆盖 207K unique phrases、121K images/videos 和超过 3M media-phrase pairs，并显式包含大量 hard negatives。&lt;/p&gt;
&lt;p&gt;实验上，SAM 3 在图像 PCS with text 中显著优于已有开放词表系统。
在 SA-Co/Gold 实例分割上，SAM 3 的 cgF1 为 54.1，而最强基线 OWLv2* 为 24.6，Human oracle 为 72.8；也就是说 SAM 3 超过最强基线两倍，并达到约 74% 的人类表现。
在 LVIS instance segmentation 上，SAM 3 的 cgF1/AP 为 37.2/48.5；在 box detection 上，LVIS cgF1/AP 为 40.6/53.6，COCO AP 为 56.4。
语义分割上，SAM 3 在 ADE-847、PascalConcept-59 和 Cityscapes 上分别达到 13.8、60.8 和 65.2 mIoU，超过强 specialist baseline APE。&lt;/p&gt;
&lt;p&gt;Exemplar 和交互结果体现了 SAM 3 的新接口价值。
单个 exemplar 提示下，SAM 3 在 COCO、LVIS 和 ODinW13 上相对 T-Rex2 分别提升 +18.3、+10.3 和 +20.5 AP+。
从文本 prompt 开始逐步添加 exemplar 时，3 次点击后 cgF1 比 text-only 高 +21.6，也比理想化的 PVS 单实例修正高 +2.0。
原因是 PCS exemplar 不只是修正一个实例，而是会泛化到类似目标：正样本能补一类漏检，负样本能压一类误检。
计数任务也受益于「先分割再计数」：SAM 3 在 CountBench 上 MAE 0.12、Accuracy 93.8%，并且能给出实例 Mask，而多数 MLLM 只能输出数字。&lt;/p&gt;
&lt;p&gt;视频 PCS 中，SAM 3 也把文本概念扩展到开放词表视频分割。
在 SA-Co/VEval 的三个子集上，SAM 3 分别达到 SA-V 30.3 cgF1 / 58.0 pHOTA、YT-Temporal-1B 50.8 / 69.9、SmartGlasses 36.4 / 63.6。
在公开 benchmark 上，SAM 3 在 LVVIS test mAP 为 36.3，BURST test HOTA 为 44.5，OVIS val mAP 为 60.5，整体明显优于 GLEE、LLMDet + SAM 3 Tracker 以及 tracking-by-detection 替代方案。
在传统 PVS/VOS 能力上，SAM 3 也没有退化：VOS 中在 MOSEv1、DAVIS17、LVOSv2、SA-V val/test、YTVOS19、MOSEv2 上分别达到 78.4、92.2、88.5、83.5、84.4、89.7、60.3，其中 MOSEv2 比 prior work 高 6.5 点。
在 SA-37 交互式图像分割上，SAM 3 的 1/3/5-click mIoU 为 66.1、81.3、85.1，3-click 和 5-click 超过 SAM 2.1 L。&lt;/p&gt;
&lt;p&gt;消融说明这篇论文的关键收益确实来自「识别-定位解耦、hard negatives、数据引擎」三件事。
Presence head 让 cgF1 从 50.7 提升到 52.2，IL_MCC 从 0.77 到 0.82。
Hard negatives 数量从 0 增加到 30/img 时，cgF1 从 28.3 提升到 43.0，主要提升来自 image-level recognition。
训练数据方面，仅 EXT 为 23.7 cgF1，加入 SYN 后到 32.8，加入 HQ 后到 45.5，EXT+SYN+HQ 达到 47.4。
AI verifiers 也很关键：在伪标签上加入 EV verifier 可把 cgF1 从 54.0 提到 61.2，再加入 MV verifier 到 62.3，论文认为 AI verifiers 关闭了模型与人类表现差距的大约一半。&lt;/p&gt;
&lt;p&gt;我觉得 SAM 3 的核心贡献是把 SAM 系列从「视觉位置提示」推进到「开放词表概念提示」。
SAM 解决单图实例分割，SAM 2 解决视频中同一实例的记忆与传播，SAM 3 则进一步解决「给一个概念，找出所有实例，并在视频里保持身份」的问题。
局限也很清楚：它不擅长 out-of-domain 术语，需要额外 domain expansion 或 fine-tuning；文本输入被限制为简单 noun phrases，复杂推理查询要借助 MLLM agent；视频中推理成本随被跟踪对象数量线性增长；概念本身还存在天然歧义，例如类别边界、主观形容词和上下文依赖。
所以 SAM 3 更像是开放词表实例分割/视频分割的基础接口，而不是通用视觉推理模型。&lt;/p&gt;
&lt;h2&gt;SAM 3D&lt;/h2&gt;
&lt;p&gt;这篇工作的核心动机是 3D 的「数据壁垒」。
多视角几何或合成数据可以提供 3D 监督，但真实世界中大量物体只出现在单张自然图像里，而且常常有遮挡、场景杂乱、尺度变化和上下文依赖。
已有 image-to-3D 模型多在 isolated object renders 上训练，对居中、干净的物体效果不错，但在自然场景中距离远、遮挡重、只露出一部分的物体上容易失败。
SAM 3D 的直觉是：人类能从单图恢复 3D，很大程度依赖 recognition cue 和 context cue；模型也应当从目标局部外观、完整场景上下文和物体类别先验中联合推断 3D。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/sam3d-arch.2dpci7c1qe.webp&quot; alt=&quot;&quot;&gt;
模型输入编码分成 cropped object 和 full image 两路。
第一路把目标 Mask 裁剪出的物体图像和裁剪后的 binary mask 一起送入 DINOv2 encoder，提供高分辨率、聚焦目标的细节信息。
第二路把完整图像和 full-image binary mask 送入 encoder，提供全局场景上下文、尺度、遮挡关系和识别线索。
模型还可以选择性接收 point map &lt;code&gt;P&lt;/code&gt;，这个点图可以来自 LiDAR、iPhone 深度传感器或单目深度模型，用于辅助布局估计；但论文也强调形状和纹理质量并不强依赖 point map。&lt;/p&gt;
&lt;p&gt;整体架构是两阶段 latent flow matching。
第一阶段是 Geometry Model，建模 &lt;code&gt;p(O, R, t, s | I, M)&lt;/code&gt;：其中 &lt;code&gt;O&lt;/code&gt; 是 &lt;code&gt;64^3&lt;/code&gt; 的 coarse shape voxel，&lt;code&gt;R&lt;/code&gt; 使用 6D rotation representation，&lt;code&gt;t&lt;/code&gt; 是平移，&lt;code&gt;s&lt;/code&gt; 是尺度。
Geometry Model 是一个约 1.2B 参数的 flow transformer，采用 Mixture-of-Transformers 架构，在输入图像/Mask 条件下联合生成粗形状和布局。
第二阶段是 Texture &amp;#x26; Refinement Model，建模 &lt;code&gt;p(S, T | I, M, O)&lt;/code&gt;：它先从 coarse shape 中抽取 active voxels，再用约 600M 参数的 sparse latent flow transformer 细化几何并合成纹理。
最后，结构化 latent 可以通过两个 VAE decoder 输出 mesh 或 3D Gaussian splats；两个 decoder 共享同一个 VAE encoder，因此处在相同的 structured latent space 中。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/sam3d-train.8hh4knfj2u.webp&quot; alt=&quot;&quot;&gt;
训练流程非常像大模型训练范式：pretraining、mid-training、post-training，再加 alignment 和 distillation。
Pretraining 使用 isolated synthetic 3D assets，作者从 Objaverse-XL 和授权数据中收集 2.7M object meshes，每个物体渲染 24 个视角，构成 Iso-3DO，用 2.5T training tokens 学习基本形状和纹理生成能力。
Mid-training 构造 semi-synthetic 的 render-and-paste 数据 RP-3DO，把带纹理 mesh alpha-composite 到自然图像里，包含遮挡-被遮挡对，以及把真实物体替换成位置和尺度相似的合成物体。
RP-3DO 有 61M samples 和 2.8M unique meshes，用 2.7T training tokens 训练 mask-following、occlusion robustness、layout estimation 和 visual cue usage。
这一步之后，模型已经见过所有输入/输出模态，但仍然主要是合成或半合成数据，因此需要真实图像 post-training。&lt;/p&gt;
&lt;p&gt;Post-training 是 SAM 3D 最重要的数据飞轮。
3D 标注不像 2D Mask 标注，普通标注员很难直接建 mesh，但他们可以在多个候选 3D 模型里选择最像图中物体的那个，并给出质量评分。
因此，数据引擎会先用当前模型和一组已有 learned / retrieval-based models 生成候选形状与纹理，再让人类从 &lt;code&gt;N=8&lt;/code&gt; 个候选中选最好的一个。
如果质量评分超过阈值，最佳候选成为正样本 &lt;code&gt;D+ = (I, M, S, T, R, t, s)&lt;/code&gt;；其他较差候选变成偏好优化中的负样本 &lt;code&gt;D-&lt;/code&gt;。
早期由于模型还弱，候选主要来自 ensemble；随着迭代推进，SAM 3D 自己最终贡献了约 80% 的标注数据。
整个真实数据收集最终覆盖接近 1M images，约 3.14M untextured meshes 和约 100K textured meshes。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/sam3d-data.6wrdl6jh80.webp&quot; alt=&quot;&quot;&gt;
真实数据引擎被拆成三个标注子任务。
Stage 1 选择目标物体 &lt;code&gt;(I, M)&lt;/code&gt;：从多种真实图像数据源采样，并用 3D-oriented taxonomy 平衡物体分布，Mask 来自已有分割标注或人类选择。
Stage 2 选择形状和纹理 &lt;code&gt;(S, T)&lt;/code&gt;：人类在多个模型候选中选择最符合输入图像和 Mask 的 3D object，并拒绝低质量候选。
Stage 2.5 处理 hard examples：当没有模型能生成合理形状时，样本会被路由给专业 3D artists 直接标注，形成高质量 Art-3DO。
Stage 3 标注布局 &lt;code&gt;(R, t, s)&lt;/code&gt;：给定 Stage 2 的 object shape，标注员在 point cloud 中操作物体的平移、旋转和尺度，让 3D object 与 2.5D 场景对齐。&lt;/p&gt;
&lt;p&gt;模型更新同样分阶段。
SFT 先用噪声较大的非专家 MITL-3DO 数据缩小 synthetic-to-real gap，再用更小但质量更高的 Art-3DO 数据强化审美和几何质量，减少 floaters、bottomless meshes、缺失对称性等常见失败。
随后使用 DPO 做 preference alignment，把 Stage 2 中人类选择的 &lt;code&gt;D+&lt;/code&gt; 和较差候选 &lt;code&gt;D-&lt;/code&gt; 作为偏好对，优化人类更敏感但普通 flow matching 难以直接刻画的属性，例如闭合性、对称性和整体可用性。
最后，Geometry Model 做 distillation，将推理时的 function evaluations 从 25 降到 4，使 shape 和 layout 可以做到 sub-second 级生成。&lt;/p&gt;
&lt;p&gt;实验中，作者构建了新的 SA-3DAO benchmark：1000 个由专业 3D artists 从自然图像恢复并对齐的无纹理 3D objects。
这个 benchmark 比 isolated render 更接近真实使用场景，包含室内外场景、遮挡、小物体、稀有文化物体、衣物、滑雪缆车、自动扶梯等复杂目标。
在 SA-3DAO shape 指标上，SAM 3D 明显优于 Trellis、Hunyuan3D、Direct3D-S2、TripoSG、Hi3DGen 等方法：F1@0.01 为 0.2344，vIoU 为 0.2311，Chamfer 为 0.0400，EMD 为 0.1211。
相比最强基线，F1 和 vIoU 提升很大，Chamfer / EMD 也显著更低，说明它不仅能生成看起来像的形状，也更接近 artist ground truth 几何。
在 ISO3D isolated object 上，SAM 3D 的 Uni3D 为 0.3707，基本达到或略超 SOTA，说明它强化真实场景能力的同时没有牺牲干净物体能力。&lt;/p&gt;
&lt;p&gt;布局估计是 SAM 3D 区别于普通 image-to-3D 生成器的重要部分。
在 SA-3DAO 上，SAM 3D joint layout 的 3D IoU 为 0.4254、ICP-Rot 为 20.77、ADD-S 为 0.2661、ADD-S@0.1 为 0.7232，显著优于「先生成 mesh 再用 FoundationPose/Megapose 对齐」的 pipeline。
在 Aria Digital Twin 上，SAM 3D 的 3D IoU 为 0.4970、ADD-S@0.1 为 0.7673；论文强调这相当于把真实场景 joint shape+layout 能力从非常低的可用率提升到大幅可用。
如果再做 test-time layout optimization，ADT 上 3D IoU 可从 0.4837 提到 0.5258，2D IoU 从 0.5143 提到 0.6487。&lt;/p&gt;
&lt;p&gt;人类偏好实验也支持这个结论。
在真实物体和场景中，SAM 3D 的 object reconstruction 相比 prior SOTA 至少有 5:1 win rate，scene reconstruction 有约 6:1 win rate。
纹理评价中，在给定 SAM 3D shape 的条件下，标注员也明显偏好 SAM 3D 的 Texture &amp;#x26; Refinement：相对 Trellis，在 ISO3D、Preference Set、SA-3DAO、LVIS 上胜率分别为 81.1%、87.0%、86.2%、89.1%；相对 Hunyuan3D-2.1，在后三个真实/偏好集合上也都是 86% 以上。&lt;/p&gt;
&lt;p&gt;消融实验说明多阶段训练是必要的。
只做 Iso-3DO pretraining 时，SA-3DAO F1@0.01 只有 0.1349，vIoU 0.1202。
加入 RP-3DO mid-training 后提升到 0.1705 / 0.1683；加入 MITL-3DO SFT 到 0.2027 / 0.2025；MITL DPO 到 0.2156 / 0.2156；Art-3DO SFT 到 0.2331 / 0.2337；最终 Art-3DO DPO 达到 0.2344 / 0.2311，同时 Chamfer 降到 0.0400。
去掉 MITL-3DO、Art-3DO 或 DPO 都会退化，其中去掉 Art-3DO 影响最大，说明高质量 artist data 对真实世界形状质量非常关键。&lt;/p&gt;
&lt;p&gt;这篇论文对我的启发是：3D foundation model 的核心瓶颈不是网络结构本身，而是如何把真实图像和可信 3D supervision 连接起来。
SAM 3D 的数据引擎把「让人直接建模」改成了「让人从模型候选中选择并评分」，再把选择结果变成 SFT 数据和 DPO 偏好对，这和 RLHF / data flywheel 的思想非常接近。
因此它不是简单的 image-to-3D 模型，而是一套把 2D Mask、候选 3D、point map、人类偏好和专业 artist 数据组合起来的真实世界 3D 对齐系统。
局限也很明确：Geometry Model 的 coarse shape 是 &lt;code&gt;64^3&lt;/code&gt;，细结构、薄结构、手和脸等对分辨率敏感的部分可能失真；模型逐物体预测 layout，不显式推理多物体接触、稳定性、互穿或共平面；texture 预测不知道最终 pose，所以旋转对称物体有时会出现纹理朝向错误。
总体看，SAM 3D 是 SAM 系列从「分割物体」走向「把分割出的物体 3D 化」的重要一步，尤其适合机器人、AR/VR、游戏、影视和交互式媒体中的真实场景 3D 感知。&lt;/p&gt;
&lt;h2&gt;PointNet&lt;/h2&gt;
&lt;p&gt;分类任务中，PointNet 先得到全局 feature 再做 object classification；分割任务中，则把全局 feature 拼回每个点的局部 feature 上，再逐点预测 part / semantic label。
代表结果是 ModelNet40 overall accuracy 89.2%，ShapeNet part segmentation mean IoU 83.7%。
它的意义不在于复杂结构，而在于证明「共享 MLP + max pooling」就能直接吃 unordered point set，并成为后续点云网络的基本 building block。&lt;/p&gt;
&lt;h2&gt;PointNet++&lt;/h2&gt;
&lt;p&gt;PointNet++ 的动机很直接：PointNet 虽然解决了点集无序性，但它主要做全局聚合，没有显式建模 metric space 中的局部邻域结构。
点云里的局部几何非常重要，例如椅子腿、桌面边缘、墙角和家具边界；同时真实扫描点云往往采样密度不均匀，固定尺度的局部特征很容易失效。
PointNet++ 把 PointNet 变成层级局部算子。
&lt;img src=&quot;https://pic.hana0721.top/pnpp-arch.2yz04j1byk.webp&quot; alt=&quot;&quot;&gt;
每个 set abstraction level 包含三步：Sampling layer 用 farthest point sampling 选 centroids，保证覆盖整个点集；Grouping layer 用 ball query 按半径找局部邻域；PointNet layer 在每个局部邻域内运行 mini-PointNet，把可变数量的邻域点编码成固定长度 local feature。
堆叠多个 set abstraction level 后，点的数量逐层减少，receptive field 逐层增大，类似点云版本的层级 CNN。
&lt;img src=&quot;https://pic.hana0721.top/pnpp-da.icrpqonfx.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;为了解决非均匀密度，论文提出两种 density adaptive 设计。
MSG 在同一层用多个半径做 grouping，然后拼接多尺度特征；训练时使用 random input dropout，让模型学会在稀疏/稠密采样下选择合适尺度。
MRG 则把当前层局部 PointNet 特征和下一层更粗分辨率的特征拼接，计算更省，在低密度区域更依赖粗尺度信息，在高密度区域保留细节。
分割任务中，PointNet++ 还用 feature propagation，把高层稀疏点特征通过 kNN inverse-distance interpolation 传播回原始点，并和 skip features 拼接后逐点分类。&lt;/p&gt;
&lt;p&gt;代表结果上，PointNet++ 在 ModelNet40 上达到 90.7% accuracy，加入 normal 后达到 91.9%，超过 PointNet 的 89.2%。
在 ScanNet scene labeling 中，它比 PointNet 更能分出家具等局部物体，而不是只抓住房间整体布局。
总体来看，PointNet 是点云深度学习的「集合函数」基线，PointNet++ 则补上了局部结构、多尺度和密度鲁棒性，是后续点云 backbone 的重要起点。&lt;/p&gt;
&lt;h2&gt;Point-BERT&lt;/h2&gt;
&lt;p&gt;直观理解：Point-BERT 的贡献是定义了「点云词表 + masked token prediction」这条路线，让标准 Transformer 能通过自监督预训练获得点云表征。
缺点也比较明显：dVAE tokenizer 训练复杂、依赖额外 DGCNN/FoldingNet，整体预训练流程不够简洁。&lt;/p&gt;
&lt;h2&gt;Point-MAE&lt;/h2&gt;
&lt;p&gt;结果上，Point-MAE 在 ModelNet40 上达到 93.8%（8k 点为 94.04%），ScanObjectNN PB-T50-RS 达到 85.18%，比 Point-BERT 高 2.11%；Few-shot 四个设置也全面超过 Point-BERT；ShapeNetPart instance mIoU 为 86.1%。
它的经验结论也很重要：点云 MAE 更适合 random masking + 较高 mask ratio（约 60%–80%），让模型必须从稀疏可见局部推理整体结构。&lt;/p&gt;
&lt;p&gt;总体看，Point-BERT 证明了点云 masked pretraining 可行；Point-MAE 则说明不需要复杂离散词表，直接做几何重建也能学到更强、更简洁的 3D 表征。&lt;/p&gt;
&lt;h2&gt;PointGPT&lt;/h2&gt;
&lt;p&gt;PointGPT 关心的是：既然 BERT/MAE 式 masked modeling 已经能用于点云，那么 GPT 式 auto-regressive pretraining 能不能也迁移过来。
难点在于点云没有天然序列顺序，而且点云局部冗余较高，简单预测下一个 patch 可能不需要真正理解整体形状。&lt;/p&gt;
&lt;p&gt;方法上，它先用 FPS + kNN 把点云分成 point patches，再用 Morton-order curve 按空间邻近关系排序，把无序点云变成 patch 序列。
随后用 extractor-generator Transformer decoder 做自回归生成：extractor 学表征，generator 负责预测后续 patch；预训练后 generator 可以丢掉，主要保留 extractor 给下游任务用。
论文还提出 dual masking，在因果 mask 之外额外遮住一部分可 attending 的历史 tokens，降低点云冗余带来的捷径；同时用 relative direction prompts 给 generator 提示相邻 patch 的方向，但避免直接泄漏目标位置。
生成目标是后续 patch 的点坐标，loss 使用 &lt;code&gt;l1&lt;/code&gt; + &lt;code&gt;l2&lt;/code&gt; Chamfer Distance。&lt;/p&gt;
&lt;p&gt;结果上，PointGPT-L 在 ModelNet40 达到 94.9%，ScanObjectNN PB-T50-RS 达到 93.4%，ShapeNetPart instance mIoU 达到 86.6%；few-shot 四个设置也达到当时很强的结果。
需要注意，这些最佳结果来自更大模型、混合数据预训练和 post-pre-training；单纯的 PointGPT-S 更适合作为与 Point-MAE/Point-BERT 同量级比较。
总体看，PointGPT 的价值是把点云预训练从「补洞/重建」推进到「按空间序列自回归生成」，但其排序方式和扩展训练流程也让实现复杂度上升。&lt;/p&gt;
&lt;h2&gt;Point Transformer V3&lt;/h2&gt;
&lt;p&gt;Point Transformer V3 的出发点很有意思：它不是追求更复杂的 attention 机制，而是重新权衡 3D backbone 的准确率、速度、显存和可扩展性。
论文认为，在多数据集联合训练和大感受野成为趋势后，性能更可能来自 scale，而不是精细但昂贵的局部邻域设计。&lt;/p&gt;
&lt;p&gt;核心做法是 point cloud serialization。
PTv3 不再坚持完全 permutation invariant 的 KNN 邻域搜索，而是用 space-filling curves 把点云序列化，包括 Z-order、Hilbert 以及 Trans Z-order / Trans Hilbert。
点按 serialization code 排序后，在序列上划分 non-overlapping patches，并做高效 dot-product patch attention。
这样会牺牲一点精确 KNN 邻域，但换来更大的 patch size、更低延迟和更低显存；论文的关键判断是「效率提升后可以靠扩大感受野把精度补回来」。&lt;/p&gt;
&lt;p&gt;为了让不同 patch 之间交互，PTv3 在不同 attention block 中切换或打乱 serialization order，其中 Shuffle Order 效果最好。
位置编码方面，它放弃昂贵的相对位置编码 RPE，改用 xCPE：在 attention 前加一层 sparse convolution 和 skip connection，兼顾位置感知与效率。
整体网络仍是 U-Net 风格的四阶段 encoder-decoder。&lt;/p&gt;
&lt;p&gt;结果上，PTv3 把有效感受野从 PTv2 的 16 点扩到 1024 点，同时相对 PTv2 约有 3× 速度提升和 10× 显存效率提升。
语义分割中，scratch PTv3 / 预训练 PTv3 在 ScanNet test 上为 77.9% / 79.4%，S3DIS 6-fold 为 77.7% / 80.8%，nuScenes test 为 82.7% / 83.0%，SemanticKITTI test 为 74.2% / 75.5%。
所以 PTv3 的意义不是提出一个更花哨的 Transformer，而是把点云 Transformer 做成真正可规模化、可作为通用 3D backbone 使用的版本。&lt;/p&gt;
&lt;h2&gt;PointCLIP&lt;/h2&gt;
&lt;p&gt;PointCLIP 是把 2D CLIP 迁移到 3D 点云的早期代表。
它不训练 3D backbone，而是把点云从多个固定视角在线投影成 depth maps，再送入 CLIP image encoder；类别名则放进手写 prompt，比如 &lt;code&gt;point cloud depth map of a [CLASS].&lt;/code&gt;，由 CLIP text encoder 得到分类权重。
每个视角独立得到 image-text matching logits，最后用手工 view weights 加权融合。&lt;/p&gt;
&lt;p&gt;零样本版本完全不需要 3D 训练，但性能还比较初步：ModelNet10 为 30.23%，ModelNet40 为 20.18%，ScanObjectNN 为 15.38%。
论文真正有用的部分是 few-shot adapter：冻结 CLIP 的图像和文本编码器，只训练一个三层 inter-view adapter。
它先拼接多视角特征得到 global feature，再用残差方式把 global-guided adapted feature 加回每个 view feature，使少量 3D 样本学到的知识和 CLIP 的 2D 预训练知识融合。
在 ModelNet40 上，16-shot PointCLIP 能从 zero-shot 20.18% 提升到 87.20%。&lt;/p&gt;
&lt;p&gt;PointCLIP 还有一个有趣发现：它和传统 3D 全监督网络具有互补性。
例如 16-shot PointCLIP 与 PointNet++ ensemble，可把 PointNet++ 从 89.71% 提升到 92.10%；与 CurveNet ensemble，可从 93.84% 提升到 94.08%。
总体看，PointCLIP 的价值不是性能最强，而是证明了「多视角投影 + CLIP 对齐」可以作为 3D open-vocabulary / low-shot 学习的入口。&lt;/p&gt;
&lt;h2&gt;PointCLIP V2&lt;/h2&gt;
&lt;p&gt;PointCLIP V2 是对 PointCLIP 的系统升级：它认为原版主要卡在两个 domain gap 上。
第一是 visual gap，原版投影得到的是稀疏散点 depth map，和 CLIP 预训练时看到的真实图像差异太大；第二是 language gap，&lt;code&gt;a photo of a [CLASS]&lt;/code&gt; 或简单加 &lt;code&gt;depth map&lt;/code&gt; 的文本不足以描述 3D 形状。&lt;/p&gt;
&lt;p&gt;为了解决 visual gap，V2 提出 realistic projection，流程是 Quantize、Densify、Smooth、Squeeze。
它先把点云放入 3D voxel grid，再用 minimum pooling 做 densify，让投影更像有遮挡关系的实体表面；随后用 Gaussian filter 平滑噪声，最后沿深度维 squeeze 成更连续、更像真实图像的 depth map。
消融里，直接正交投影只有 57.35% ModelNet40 zero-shot；加入完整 realistic projection 后到 64.22%。&lt;/p&gt;
&lt;p&gt;为了解决 language gap，V2 用 GPT-3 根据 3D command 生成类别相关描述，而不是只用类别名模板。
命令包括 caption generation、question answering、paraphrase generation、words-to-sentence 四类；最终每个类别可得到大量 3D-specific descriptions，再送入 CLIP text encoder。
在 ModelNet40 zero-shot 上，不用 GPT prompt 是 39.11%，四类命令全用后达到 64.22%，说明文本侧对齐非常关键。&lt;/p&gt;
&lt;p&gt;结果上，V2 在 zero-shot 3D classification 上大幅超过 PointCLIP：ModelNet10 73.13%，ModelNet40 64.22%，ScanObjectNN PB_T50_RS 35.36%。
它还把同一框架扩展到更多 open-world 任务：ShapeNetPart zero-shot part segmentation instance mIoU 从 PointCLIP 的 31.0% 提升到 49.5%；ScanNet V2 zero-shot detection 达到 AP25 18.97%、AP50 11.53%。
Few-shot 中，V2 还可把 smoothing 变成可学习的 3D 卷积模块，并结合 inter-view adapter；ModelNet40 16-shot 达到 89.55%。&lt;/p&gt;
&lt;p&gt;总体看，PointCLIP V2 的核心贡献是同时 prompt CLIP 的视觉侧和语言侧，把原本只能做 3D 分类的 PointCLIP 扩展成统一的 3D open-world learner。
不过它仍依赖投影质量、GPT 生成文本和 CLIP 的 2D 先验，因此对细粒度几何和真实复杂场景的能力仍受 2D-3D 对齐上限约束。&lt;/p&gt;
&lt;h2&gt;ULIP&lt;/h2&gt;
&lt;p&gt;ULIP 的核心思想是：不再只在点云内部做自监督，而是把 3D encoder 对齐到 CLIP 已经学好的 image-text feature space。
它从 ShapeNet55 构造 image-text-point triplets：点云来自 CAD 模型采样；图像来自多视角渲染，每个物体有 30 张 RGB 和 30 张 depth 候选；文本来自 metadata，并用 64 个 prompt template 生成描述。
训练时冻结 CLIP image/text encoders，只更新 3D backbone，用 point-image 和 point-text contrastive loss 把点云特征拉进同一个语义空间。&lt;/p&gt;
&lt;p&gt;ULIP 是 backbone-agnostic 的，可以套在 PointNet++、PointBERT、PointMLP 等网络上。
标准分类中，它在 ScanObjectNN 上把 PointBERT 从 83.1% 提到 86.4%，把 PointMLP 从 85.7% 提到 88.8%；在 ModelNet40 上，PointMLP* + ULIP 达到 94.7%。
Zero-shot 3D classification 是更关键的结果：ModelNet40 上 PointBERT + ULIP 达到 60.4% top-1，而 PointCLIP 只有 20.2%；ScanObjectNN 上 PointNet++(msg) + ULIP 达到 49.9%，也远高于 PointCLIP 的 15.4%。
消融还显示，同时对齐 point-image-text 三种模态总是优于只对齐其中两种。&lt;/p&gt;
&lt;p&gt;总体看，ULIP 是 3D 多模态预训练的重要转折点：它证明了 3D backbone 可以通过少量三元组数据继承 CLIP 的开放词表语义，而不必把点云硬投影成 2D 图像再分类。&lt;/p&gt;
&lt;h2&gt;Point-Bind &amp;#x26; Point-LLM&lt;/h2&gt;
&lt;p&gt;Point-Bind 可以看成 ULIP 的多模态扩展：ULIP 主要对齐 image-text-point，Point-Bind 则借助 ImageBind，把 3D point cloud 对齐到图像、文本、音频等更多模态的联合空间。
训练数据由 3D-image-text-audio pairs 构成，其中 3D encoder 使用 I2P-MAE，后接两层 linear projection；ImageBind 的图像、文本、音频 encoders 冻结。
目标函数很直接：让 &lt;code&gt;F_3D&lt;/code&gt; 分别和 &lt;code&gt;F_2D&lt;/code&gt;、&lt;code&gt;F_T&lt;/code&gt;、&lt;code&gt;F_A&lt;/code&gt; 做 contrastive alignment；如果某些类别没有合理音频，就忽略 audio loss。&lt;/p&gt;
&lt;p&gt;对齐完成后，Point-Bind 展示了一系列 emergent 能力。
在 ModelNet40 zero-shot classification 上，Point-Bind + I2P-MAE 达到 78.0%，高于 ULIP2 的 66.4% 和 PointCLIP V2 的 64.2%。
在跨模态检索上，Point-Bind 的 mAP 为：3D→3D 63.23，2D→3D 34.59，3D→2D 42.83，Text→3D 64.50；其中 2D→3D 和 Text→3D 分别比 ULIP 高 14.29% 和 13.99%。
它还可以把 3D embedding 与 audio embedding 相加做组合检索，或者把多模态 encoder 接到 CLIP-Forge 这类 decoder 上做 any-to-3D generation。&lt;/p&gt;
&lt;p&gt;Point-LLM 是在 Point-Bind 之上的 3D LLM 尝试。
它参考 ImageBind-LLM，把 Point-Bind 的 3D embedding 通过 bind network、visual cache 和 zero-initialized gating 接入 LLaMA；训练阶段不需要 3D instruction data，只用公开 vision-language 数据做参数高效微调。
因此它可以用英文/中文回答关于 3D 点云的问题，也可以把点云和图像/音频一起作为条件进行跨模态推理。
总体看，这篇更像一个系统展示：从 3D 多模态对齐，到 3D 生成、检索、zero-shot 识别，再到 3D 指令跟随。&lt;/p&gt;
&lt;h2&gt;OpenScene&lt;/h2&gt;
&lt;p&gt;OpenScene 关注的是开放词表 3D scene understanding，而不是单物体点云分类。
它的目标是给每个 3D point 预测一个和 CLIP/text/image pixel 共嵌入的 dense feature，这样任意文本 query 都可以通过 cosine similarity 在 3D 场景里生成热力图或语义分割。&lt;/p&gt;
&lt;p&gt;方法分三步。
首先，用 OpenSeg 或 LSeg 这类开放词表 2D segmentation model 提取每张 RGB 图像的 dense pixel features；然后利用相机位姿和深度，把多视角 pixel features back-project 到 3D points，并做 multi-view average fusion，得到 &lt;code&gt;F_2D&lt;/code&gt;。
其次，训练一个 MinkowskiNet18A 只从 3D geometry 输入预测 &lt;code&gt;F_3D&lt;/code&gt;，用 cosine loss 蒸馏到 &lt;code&gt;F_2D&lt;/code&gt;，这样没有 RGB 时也能做开放词表查询。
最后，用文本 prompt 与 2D/3D features 的相似度决定每个点采用 2D fusion 还是 3D distill feature，形成 2D-3D ensemble。&lt;/p&gt;
&lt;p&gt;结果上，在 ScanNet 4 个 unseen 类别的 zero-shot segmentation 中，OpenScene-LSeg 达到 62.8% mIoU，而 3DGenZ 是 7.7%，MSeg voting 是 53.4%。
在标准场景语义分割中，OpenScene-OpenSeg zero-shot 在 nuScenes 上达到 42.1% mIoU / 61.8% mAcc，在 ScanNet 上为 47.5% / 70.7%，在 Matterport 上为 42.6% / 59.2%。
消融显示 2D-3D ensemble 通常优于只用 2D fusion 或 3D distillation；并且随着 label set 扩大，OpenScene 对长尾类别比全监督模型更稳。
总体看，OpenScene 把 CLIP 空间从 2D 图像和单物体 3D 扩展到真实 3D 场景，是开放词表 3D 场景理解的关键基线。&lt;/p&gt;
&lt;h2&gt;OpenMask3D&lt;/h2&gt;
&lt;p&gt;OpenMask3D 解决的是 OpenScene 的一个自然短板：OpenScene 学的是 per-point semantic feature，适合做语义热力图，但不能天然区分同类物体的多个 instance。
OpenMask3D 因此提出 open-vocabulary 3D instance segmentation：先拿到 class-agnostic 3D instance masks，再给每个 mask 计算一个开放词表 CLIP feature。&lt;/p&gt;
&lt;p&gt;具体流程是两阶段。
第一阶段使用预训练 Mask3D 的 mask proposal module，只保留 binary instance masks，丢掉 closed-set class labels 和 confidence scores。
第二阶段对每个 3D mask 计算 mask feature：先根据可见点数量和深度遮挡选择 top-k RGB-D views；再把 3D mask 投影到 2D，并用 SAM 根据投影点生成更干净的 2D object mask；随后围绕 2D mask 做多尺度 crop，用 CLIP visual encoder 提取 crop features，最后跨 crop 和 view 平均，得到 per-mask feature。
查询时只需要把文本或图像 query 编成 CLIP feature，与每个 mask feature 做相似度匹配。&lt;/p&gt;
&lt;p&gt;ScanNet200 validation 上，OpenMask3D 达到 AP 15.4、AP50 19.9、AP25 23.1，明显高于用 OpenScene features 加 mask 聚合的 open-vocabulary baselines；尤其 tail AP 为 14.9，而 OpenScene 2D Fusion + masks 是 9.9。
在 Replica 跨数据集测试中，OpenMask3D AP 为 13.1，也高于 OpenScene 变体。
如果 mask proposal 只用 ScanNet20 训练、再评估 ScanNet200，OpenMask3D 在 novel classes 上仍有 11.9 AP，说明它对未见类别主要依赖 CLIP mask feature，而不是 closed-set class head。
消融显示 SAM 2D mask refinement 和 multi-scale crops 都有帮助；oracle masks 实验中，OpenMask3D 的 tail AP 达到 32.9，甚至超过 fully supervised Mask3D 的 tail AP 17.9，说明瓶颈主要在 3D mask proposal 质量。&lt;/p&gt;
&lt;p&gt;总体看，OpenMask3D 是从开放词表 3D 语义理解走向开放词表 3D 实例级交互的重要一步：它把 CLIP 的开放语义绑定到 object mask，而不是散落在每个 point 上。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/62258773_p0_master1200.7zr0vfh0rf.webp"/><enclosure url="https://pic.hana0721.top/62258773_p0_master1200.7zr0vfh0rf.webp"/></item><item><title>Ubuntu OpenSSH 远程服务器配置教程</title><link>https://hana-blog.top/blog/openssh-server-setup</link><guid isPermaLink="true">https://hana-blog.top/blog/openssh-server-setup</guid><description>从安装 openssh-server 到公网访问、密钥登录、防火墙与安全加固，把一台 Ubuntu 配成可维护的 SSH 远程服务器。</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;有一台已经装好 Ubuntu 的机器后，最常见的下一步就是把它当作远端服务器使用：本地电脑通过 &lt;code&gt;ssh&lt;/code&gt; 登录，在服务器上跑代码、部署服务、传文件或远程开发。&lt;/p&gt;
&lt;p&gt;这篇文章整理一套从零开始的 OpenSSH 配置流程，目标是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;局域网内可以 SSH 登录&lt;/li&gt;
&lt;li&gt;公网或云服务器场景知道该放行哪些端口&lt;/li&gt;
&lt;li&gt;支持 SSH 密钥登录&lt;/li&gt;
&lt;li&gt;关闭高风险登录方式，减少被爆破的概率&lt;/li&gt;
&lt;li&gt;遇到连不上时能快速定位问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;默认环境：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;服务器：Ubuntu 22.04 / 24.04 或相近版本&lt;/li&gt;
&lt;li&gt;客户端：Linux / macOS / Windows PowerShell 均可&lt;/li&gt;
&lt;li&gt;SSH 服务端：OpenSSH Server&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. 安装 OpenSSH Server&lt;/h2&gt;
&lt;p&gt;在 Ubuntu 服务器上执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update
sudo apt install -y openssh-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后启动 SSH 服务，并设置开机自启：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl enable --now ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看服务状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl status ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果看到 &lt;code&gt;active (running)&lt;/code&gt;，说明 SSH 服务已经正常运行。&lt;/p&gt;
&lt;p&gt;也可以用下面的命令确认 22 端口是否在监听：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ss -tlnp | grep &apos;:22&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正常情况下会看到类似：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:((&quot;sshd&quot;,pid=1234,fd=3))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 创建一个普通登录用户&lt;/h2&gt;
&lt;p&gt;如果你现在只是在本机上用安装系统时创建的用户，也可以直接用这个用户登录。&lt;br&gt;
如果这台机器之后要长期当服务器用，推荐单独创建一个普通用户。&lt;/p&gt;
&lt;p&gt;例如创建 &lt;code&gt;hana&lt;/code&gt; 用户：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo adduser hana
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果这个用户需要执行 &lt;code&gt;sudo&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo usermod -aG sudo hana
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后就可以用这个用户远程登录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh hana@服务器IP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不推荐直接用 &lt;code&gt;root&lt;/code&gt; 远程登录。服务器日常运维最好使用普通用户登录，需要管理员权限时再通过 &lt;code&gt;sudo&lt;/code&gt; 提权。&lt;/p&gt;
&lt;h2&gt;3. 查看服务器 IP&lt;/h2&gt;
&lt;h3&gt;局域网 IP&lt;/h3&gt;
&lt;p&gt;如果你只需要在同一个 Wi-Fi / 路由器 / 实验室内网里连接，先查看服务器的内网 IP：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hostname -I
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出可能类似：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;192.168.1.20 172.17.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一般使用 &lt;code&gt;192.168.x.x&lt;/code&gt;、&lt;code&gt;10.x.x.x&lt;/code&gt; 或 &lt;code&gt;172.16.x.x&lt;/code&gt; 到 &lt;code&gt;172.31.x.x&lt;/code&gt; 这一类地址。&lt;/p&gt;
&lt;p&gt;然后在本地电脑上连接：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh hana@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一次连接时会看到主机指纹确认提示：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Are you sure you want to continue connecting (yes/no/[fingerprint])?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认 IP 没写错后输入：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后再输入服务器用户密码即可登录。&lt;/p&gt;
&lt;h3&gt;公网 IP&lt;/h3&gt;
&lt;p&gt;如果是云服务器，公网 IP 通常在云厂商控制台里可以直接看到。&lt;/p&gt;
&lt;p&gt;如果是放在家里或实验室的物理机器，则需要区分两件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;机器自己的内网 IP，例如 &lt;code&gt;192.168.1.20&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;路由器对外的公网 IP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以在服务器上查看对外出口 IP：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl ifconfig.me
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但能看到公网 IP 不代表外部一定能连进来。很多校园网、公司网络、家庭宽带可能处在 NAT 后面，没有可直接访问的公网入口。&lt;/p&gt;
&lt;h2&gt;4. 开放防火墙端口&lt;/h2&gt;
&lt;p&gt;Ubuntu 常用防火墙是 &lt;code&gt;ufw&lt;/code&gt;。如果你启用了它，需要允许 SSH 流量。&lt;/p&gt;
&lt;p&gt;查看防火墙状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;放行 OpenSSH：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw allow OpenSSH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者明确放行 22 端口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw allow 22/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;ufw&lt;/code&gt; 还没启用，可以启用它：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw enable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再次确认：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw status numbered
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果后面你把 SSH 改到了非默认端口，例如 &lt;code&gt;2222&lt;/code&gt;，记得提前放行新端口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw allow 2222/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;重点：先放行新端口，再改 SSH 配置。&lt;/strong&gt; 否则可能把自己锁在服务器外面。&lt;/p&gt;
&lt;h2&gt;5. 从本地电脑连接服务器&lt;/h2&gt;
&lt;p&gt;最基本的 SSH 命令格式是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh 用户名@服务器IP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh hana@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 SSH 服务不是默认的 22 端口，需要加 &lt;code&gt;-p&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -p 2222 hana@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows PowerShell、macOS Terminal、Linux Terminal 都可以直接使用这个命令。&lt;br&gt;
如果 Windows 提示找不到 &lt;code&gt;ssh&lt;/code&gt;，可以在系统设置的“可选功能”里安装 OpenSSH Client。&lt;/p&gt;
&lt;h2&gt;6. 配置 SSH 密钥登录&lt;/h2&gt;
&lt;p&gt;密码登录能用，但不适合长期暴露在公网。更推荐使用 SSH 密钥。&lt;/p&gt;
&lt;h3&gt;6.1 在本地电脑生成密钥&lt;/h3&gt;
&lt;p&gt;在本地电脑执行，不是在服务器上执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认会生成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私钥：&lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;公钥：&lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;看到 passphrase 提示时：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想安全一些：设置一个私钥密码&lt;/li&gt;
&lt;li&gt;想省事：直接回车留空&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更推荐设置 passphrase，然后配合 &lt;code&gt;ssh-agent&lt;/code&gt; 使用。&lt;/p&gt;
&lt;h3&gt;6.2 把公钥上传到服务器&lt;/h3&gt;
&lt;p&gt;Linux / macOS 通常可以直接用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-copy-id -i ~/.ssh/id_ed25519.pub hana@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果服务器端口不是 22：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 hana@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows PowerShell 没有 &lt;code&gt;ssh-copy-id&lt;/code&gt; 时，可以手动追加公钥：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh hana@192.168.1.20 &quot;mkdir -p ~/.ssh &amp;#x26;&amp;#x26; cat &gt;&gt; ~/.ssh/authorized_keys &amp;#x26;&amp;#x26; chmod 700 ~/.ssh &amp;#x26;&amp;#x26; chmod 600 ~/.ssh/authorized_keys&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上传完成后测试密钥登录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh hana@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不再要求输入服务器用户密码，说明密钥登录已经生效。&lt;br&gt;
如果你设置了私钥 passphrase，系统可能会要求输入私钥密码，这是正常的。&lt;/p&gt;
&lt;h2&gt;7. 配置本地 SSH 别名&lt;/h2&gt;
&lt;p&gt;每次输入 &lt;code&gt;ssh -p 2222 hana@1.2.3.4&lt;/code&gt; 很麻烦，可以在本地电脑配置 &lt;code&gt;~/.ssh/config&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Linux / macOS：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;notepad $env:USERPROFILE\.ssh\config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;Host my-server
  HostName 192.168.1.20
  User hana
  Port 22
  IdentityFile ~/.ssh/id_ed25519
  ServerAliveInterval 60
  ServerAliveCountMax 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后直接连接：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh my-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果是 Windows，也可以把私钥路径写成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;IdentityFile C:/Users/YourName/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这套配置对 &lt;code&gt;scp&lt;/code&gt;、&lt;code&gt;rsync&lt;/code&gt;、VSCode Remote SSH 也都有效。&lt;/p&gt;
&lt;h2&gt;8. 公网访问需要额外做什么&lt;/h2&gt;
&lt;p&gt;局域网能连，不代表公网能连。公网访问取决于你的机器放在哪里。&lt;/p&gt;
&lt;h3&gt;云服务器&lt;/h3&gt;
&lt;p&gt;云服务器通常需要检查三层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ubuntu 里的 SSH 服务是否正常运行&lt;/li&gt;
&lt;li&gt;Ubuntu 防火墙是否放行 SSH 端口&lt;/li&gt;
&lt;li&gt;云厂商安全组是否放行 SSH 端口&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;云厂商安全组里至少需要一条入站规则：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;协议：TCP
端口：22 或你的自定义端口
来源：你的本地公网 IP，或临时使用 0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更安全的做法是只允许自己的公网 IP，而不是长期开放给所有地址。&lt;/p&gt;
&lt;h3&gt;家里或实验室物理机&lt;/h3&gt;
&lt;p&gt;如果服务器在路由器后面，需要在路由器里设置端口转发：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;公网 TCP 2222 -&gt; 192.168.1.20:22
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;外部连接时写：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -p 2222 hana@你的公网IP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里推荐把公网端口设成 &lt;code&gt;2222&lt;/code&gt; 这类非默认端口，再转发到内网的 &lt;code&gt;22&lt;/code&gt; 端口。&lt;br&gt;
这不能替代密钥和安全配置，但可以减少一些默认端口扫描噪音。&lt;/p&gt;
&lt;p&gt;如果你的网络没有公网 IP，或者被校园网 / 公司网 NAT 隔离，可以考虑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tailscale / ZeroTier 这类内网穿透组网工具&lt;/li&gt;
&lt;li&gt;WireGuard 自建 VPN&lt;/li&gt;
&lt;li&gt;云服务器反向 SSH 隧道&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不要为了省事把路由器后台、服务器密码登录和弱密码一起暴露到公网。&lt;/p&gt;
&lt;h2&gt;9. 安全加固 SSH 服务端&lt;/h2&gt;
&lt;p&gt;确认密钥登录已经成功后，再进行安全加固。&lt;/p&gt;
&lt;p&gt;Ubuntu 的 OpenSSH 配置主文件是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;/etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更推荐把本地自定义配置放到：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;/etc/ssh/sshd_config.d/99-local.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样以后系统升级时更清晰。&lt;/p&gt;
&lt;p&gt;创建配置文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/ssh/sshd_config.d/99-local.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写入：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
X11Forwarding no
AllowUsers hana
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;含义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PermitRootLogin no&lt;/code&gt;：禁止 root 远程登录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PubkeyAuthentication yes&lt;/code&gt;：允许公钥登录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PasswordAuthentication no&lt;/code&gt;：禁止密码登录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KbdInteractiveAuthentication no&lt;/code&gt;：关闭键盘交互式密码认证&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X11Forwarding no&lt;/code&gt;：关闭不常用的图形转发&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AllowUsers hana&lt;/code&gt;：只允许指定用户登录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你有多个用户，可以写成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;AllowUsers hana deploy ubuntu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存后先检查配置语法：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo sshd -t
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有输出通常表示语法正确。然后重载 SSH：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl reload ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;不要立刻关闭当前 SSH 窗口。&lt;/strong&gt;&lt;br&gt;
请打开一个新的终端窗口，再测试一次：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh my-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认新连接可以登录后，再退出旧连接。&lt;/p&gt;
&lt;h2&gt;10. 是否需要修改默认端口&lt;/h2&gt;
&lt;p&gt;修改默认端口不是必须项。它不能真正替代密钥登录和防火墙策略，但能减少自动化扫描日志。&lt;/p&gt;
&lt;p&gt;如果要把 SSH 端口改成 &lt;code&gt;2222&lt;/code&gt;，先放行新端口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw allow 2222/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后编辑本地配置文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/ssh/sshd_config.d/99-local.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加或修改：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;Port 2222
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查配置并重载：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo sshd -t
sudo systemctl reload ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新开一个终端测试：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -p 2222 hana@服务器IP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认新端口可用后，再考虑删除旧的防火墙规则。&lt;/p&gt;
&lt;h2&gt;11. 安装 fail2ban（可选）&lt;/h2&gt;
&lt;p&gt;如果服务器必须暴露在公网，并且你还保留了密码登录，建议至少安装 &lt;code&gt;fail2ban&lt;/code&gt;。&lt;br&gt;
即使已经关闭密码登录，&lt;code&gt;fail2ban&lt;/code&gt; 也可以减少日志里的暴力扫描噪音。&lt;/p&gt;
&lt;p&gt;安装：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl status fail2ban
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看 SSH jail：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo fail2ban-client status sshd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果只是内网服务器，可以先不装，避免引入额外变量。&lt;/p&gt;
&lt;h2&gt;12. 常见问题排查&lt;/h2&gt;
&lt;h3&gt;12.1 Connection timed out&lt;/h3&gt;
&lt;p&gt;一般是网络路径不通，重点检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IP 是否写对&lt;/li&gt;
&lt;li&gt;端口是否写对&lt;/li&gt;
&lt;li&gt;服务器是否开机&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ufw&lt;/code&gt; 是否放行&lt;/li&gt;
&lt;li&gt;云服务器安全组是否放行&lt;/li&gt;
&lt;li&gt;路由器端口转发是否正确&lt;/li&gt;
&lt;li&gt;是否处在不可入站的 NAT 网络后面&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;服务器上看端口监听：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ss -tlnp | grep ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本地测试端口连通性：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nc -vz 服务器IP 22
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows PowerShell 可以用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Test-NetConnection 服务器IP -Port 22
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;12.2 Connection refused&lt;/h3&gt;
&lt;p&gt;通常说明网络到了服务器，但目标端口没有服务在监听。检查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl status ssh
sudo ss -tlnp | grep &apos;:22&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 SSH 没启动：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl restart ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;12.3 Permission denied&lt;/h3&gt;
&lt;p&gt;这类错误通常是认证失败。检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户名是否正确&lt;/li&gt;
&lt;li&gt;公钥是否写入服务器的 &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;服务器是否禁用了密码登录&lt;/li&gt;
&lt;li&gt;本地是否用了正确的私钥&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/.ssh&lt;/code&gt; 权限是否正确&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在服务器上修复权限：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在本地指定私钥测试：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -i ~/.ssh/id_ed25519 hana@服务器IP
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;12.4 改配置后彻底连不上&lt;/h3&gt;
&lt;p&gt;如果是云服务器，优先使用云厂商控制台的 VNC / Web Terminal 登录修复。&lt;br&gt;
如果是物理机，需要接显示器或通过管理口进入。&lt;/p&gt;
&lt;p&gt;常见恢复动作：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo sshd -t
sudo systemctl status ssh
sudo nano /etc/ssh/sshd_config.d/99-local.conf
sudo systemctl reload ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以修改 SSH 配置时，一定要遵守这个顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;保留当前 SSH 会话&lt;/li&gt;
&lt;li&gt;修改配置&lt;/li&gt;
&lt;li&gt;执行 &lt;code&gt;sudo sshd -t&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;执行 &lt;code&gt;sudo systemctl reload ssh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;新开终端测试新连接&lt;/li&gt;
&lt;li&gt;确认无误后再关闭旧会话&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;13. 一套推荐的最终配置&lt;/h2&gt;
&lt;p&gt;如果是个人长期使用的 Ubuntu 服务器，我通常会采用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通用户登录，不允许 root 登录&lt;/li&gt;
&lt;li&gt;SSH 密钥登录&lt;/li&gt;
&lt;li&gt;禁止密码登录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ufw&lt;/code&gt; 只放行必要端口&lt;/li&gt;
&lt;li&gt;云服务器安全组只允许自己的 IP 访问 SSH&lt;/li&gt;
&lt;li&gt;本地配置 &lt;code&gt;~/.ssh/config&lt;/code&gt; 别名&lt;/li&gt;
&lt;li&gt;长任务放在 &lt;code&gt;tmux&lt;/code&gt; 里跑&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最小可用服务端配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
X11Forwarding no
AllowUsers hana
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本地客户端配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;Host my-server
  HostName 服务器IP
  User hana
  Port 22
  IdentityFile ~/.ssh/id_ed25519
  ServerAliveInterval 60
  ServerAliveCountMax 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以后连接只需要：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh my-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到这里，这台 Ubuntu 机器就已经可以作为一台基础远端服务器使用了。后续可以继续接上 VSCode Remote SSH、&lt;code&gt;scp&lt;/code&gt;、&lt;code&gt;rsync&lt;/code&gt;、&lt;code&gt;tmux&lt;/code&gt; 等工具，形成完整的远程开发工作流。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/145491169_p0_master1200.2dpccm5v9t.webp"/><enclosure url="https://pic.hana0721.top/145491169_p0_master1200.2dpccm5v9t.webp"/></item><item><title>Paper Reading: CV 2</title><link>https://hana-blog.top/blog/paper-reading-cv2</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-cv2</guid><description>浅度一些视觉相关的工作</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;import { ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;CV Paper Reading&apos;,
items: [
{
title: &apos;Batch 1&apos;,
href: &apos;/blog/paper-reading-cv1&apos;,
order: &apos;1&apos;
},
{
title: &apos;Batch 2&apos;,
href: &apos;/blog/paper-reading-cv2&apos;,
order: &apos;2&apos;
}
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;计算机视觉基础模型关注从图像、视频和点云中学习可迁移的表征与几何结构。相关工作覆盖经典视觉骨干、自监督预训练、开放词表检测、通用分割、点云表征与视觉几何，核心作用是为分类、检测、分割、三维理解、场景重建和机器人感知等任务提供稳定的基础感知能力。&lt;/p&gt;
&lt;h2&gt;VGGT&lt;/h2&gt;
&lt;h2&gt;DUSt3R&lt;/h2&gt;
&lt;h2&gt;MASt3R&lt;/h2&gt;
&lt;h2&gt;Spann3R&lt;/h2&gt;
&lt;h2&gt;MonST3R&lt;/h2&gt;
&lt;h2&gt;CUT3R&lt;/h2&gt;
&lt;h2&gt;3D Gaussian Splatting&lt;/h2&gt;
&lt;h2&gt;Depth Anything&lt;/h2&gt;
&lt;h2&gt;Depth Anything V2&lt;/h2&gt;
&lt;h2&gt;Depth Anything 3&lt;/h2&gt;
&lt;h2&gt;Video Depth Anything&lt;/h2&gt;
&lt;h2&gt;Metric3D V2&lt;/h2&gt;
&lt;h2&gt;UniDepth&lt;/h2&gt;
&lt;h2&gt;MoGe&lt;/h2&gt;
&lt;h2&gt;FoundationStereo&lt;/h2&gt;
&lt;h2&gt;Florence-2&lt;/h2&gt;
&lt;h2&gt;YOLO-World&lt;/h2&gt;
&lt;h2&gt;Grounding DINO 1.5&lt;/h2&gt;
&lt;h2&gt;OWL-ViT&lt;/h2&gt;
&lt;h2&gt;GLIP&lt;/h2&gt;
&lt;h2&gt;SEEM&lt;/h2&gt;
&lt;h2&gt;X-Decoder&lt;/h2&gt;
&lt;h2&gt;Sapiens&lt;/h2&gt;
&lt;h2&gt;InternImage&lt;/h2&gt;
&lt;h2&gt;V-JEPA&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/139063757_p0_master1200.2yyzxgr9fg.webp"/><enclosure url="https://pic.hana0721.top/139063757_p0_master1200.2yyzxgr9fg.webp"/></item><item><title>Paper Reading: Embodied AI 5</title><link>https://hana-blog.top/blog/paper-reading-eba5</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-eba5</guid><description>从零开始的Embodied AI研究生活。</description><pubDate>Sun, 24 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;import { ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;Embodied AI Paper Reading&apos;,
items: [
{
title: &apos;Batch 1&apos;,
href: &apos;/blog/paper-reading-eba1&apos;,
order: &apos;1&apos;
},
{
title: &apos;Batch 2&apos;,
href: &apos;/blog/paper-reading-eba2&apos;,
order: &apos;2&apos;
},
{
title: &apos;Batch 3&apos;,
href: &apos;/blog/paper-reading-eba3&apos;,
order: &apos;3&apos;
},
{
title: &apos;Batch 4&apos;,
href: &apos;/blog/paper-reading-eba4&apos;,
order: &apos;4&apos;
},
{
title: &apos;Batch 5&apos;,
href: &apos;/blog/paper-reading-eba5&apos;,
order: &apos;5&apos;
}
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;RL菜鸡开始进军Embodied AI，慢慢积累，提升自己。&lt;/p&gt;
&lt;h2&gt;Scale Balanced Grasp&lt;/h2&gt;
&lt;h2&gt;EconomicGrasp&lt;/h2&gt;
&lt;h2&gt;RNGNet&lt;/h2&gt;
&lt;h2&gt;AnyGrasp&lt;/h2&gt;
&lt;h2&gt;GSNet&lt;/h2&gt;
&lt;h2&gt;HGGD&lt;/h2&gt;
&lt;h2&gt;FlexLoG&lt;/h2&gt;
&lt;h2&gt;GraNet&lt;/h2&gt;
&lt;h2&gt;TransGrasp&lt;/h2&gt;
&lt;h2&gt;S4G&lt;/h2&gt;
&lt;h2&gt;GtG 2.0&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/42175762_p0_master1200.6m4jay5vlg.webp"/><enclosure url="https://pic.hana0721.top/42175762_p0_master1200.6m4jay5vlg.webp"/></item><item><title>Paper Reading: Embodied AI 4</title><link>https://hana-blog.top/blog/paper-reading-eba4</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-eba4</guid><description>从零开始的Embodied AI研究生活。</description><pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;import { ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;Embodied AI Paper Reading&apos;,
items: [
{
title: &apos;Batch 1&apos;,
href: &apos;/blog/paper-reading-eba1&apos;,
order: &apos;1&apos;
},
{
title: &apos;Batch 2&apos;,
href: &apos;/blog/paper-reading-eba2&apos;,
order: &apos;2&apos;
},
{
title: &apos;Batch 3&apos;,
href: &apos;/blog/paper-reading-eba3&apos;,
order: &apos;3&apos;
},
{
title: &apos;Batch 4&apos;,
href: &apos;/blog/paper-reading-eba4&apos;,
order: &apos;4&apos;
},
{
title: &apos;Batch 5&apos;,
href: &apos;/blog/paper-reading-eba5&apos;,
order: &apos;5&apos;
}
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;RL菜鸡开始进军Embodied AI，慢慢积累，提升自己。&lt;/p&gt;
&lt;h2&gt;4D-VLA&lt;/h2&gt;
&lt;h2&gt;D(R,O) Grasp&lt;/h2&gt;
&lt;h2&gt;Fast-in-Slow&lt;/h2&gt;
&lt;h2&gt;NavDP&lt;/h2&gt;
&lt;h2&gt;Nav $A^3$&lt;/h2&gt;
&lt;h2&gt;StreamVLN&lt;/h2&gt;
&lt;h2&gt;V-JEPA 2&lt;/h2&gt;
&lt;h2&gt;MolmoAct&lt;/h2&gt;
&lt;h2&gt;MimicGen&lt;/h2&gt;
&lt;h2&gt;SpatialVLA&lt;/h2&gt;
&lt;h2&gt;TraceVLA&lt;/h2&gt;
&lt;h2&gt;Hume&lt;/h2&gt;
&lt;h2&gt;RICL&lt;/h2&gt;
&lt;h2&gt;OmniVTLA&lt;/h2&gt;
&lt;h2&gt;Spatial Traces&lt;/h2&gt;
&lt;h2&gt;Embodied-R1&lt;/h2&gt;
&lt;h2&gt;RynnEC&lt;/h2&gt;
&lt;h2&gt;RoboRefer&lt;/h2&gt;
&lt;h2&gt;Discrete Diffusion VLA&lt;/h2&gt;
&lt;h2&gt;G0&lt;/h2&gt;
&lt;h2&gt;InternScenes&lt;/h2&gt;
&lt;h2&gt;GraspNet&lt;/h2&gt;
&lt;p&gt;&amp;#x3C;ArxivRating
id=&apos;https://openaccess.thecvf.com/content_CVPR_2020/papers/Fang_GraspNet-1Billion_A_Large-Scale_Benchmark_for_General_Object_Grasping_CVPR_2020_paper.pdf&apos;
publication=&apos;CVPR2020&apos;
tldr=&apos;&apos;
rank={0}&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;h2&gt;GraspNet-1Billion&lt;/h2&gt;
&lt;p&gt;&amp;#x3C;ArxivRating
id=&apos;https://journals.sagepub.com/doi/10.1177/02783649231193710&apos;
publication=&apos;IJRR2023&apos;
tldr=&apos;&apos;
rank={0}&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;h2&gt;PointNetGPD&lt;/h2&gt;
&lt;h2&gt;High Precision Grasp Pose Detection&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/67997247_p0_master1200.6po3p2g2e0.webp"/><enclosure url="https://pic.hana0721.top/67997247_p0_master1200.6po3p2g2e0.webp"/></item><item><title>共享服务器下 Git 版本管理实战（Linux/macOS/Windows）</title><link>https://hana-blog.top/blog/shared-server-git-ssh-workflow</link><guid isPermaLink="true">https://hana-blog.top/blog/shared-server-git-ssh-workflow</guid><description>适用于多人共用 Linux 账号：安全配置 Git 身份、SSH 转发并完成远程推送，附 macOS/Windows 对照步骤。</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这个场景非常典型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码仓库：&lt;code&gt;https://github.com/your-username/your-repo.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;服务器：固定为 Linux&lt;/li&gt;
&lt;li&gt;使用方式：多人共用同一个 Linux 系统用户&lt;/li&gt;
&lt;li&gt;本地电脑：可能是 Linux / macOS / Windows&lt;/li&gt;
&lt;li&gt;目标：既要规范用 Git 管理版本，又不能把个人凭证留在共享机器上&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇教程把完整流程整理成可复用版本：所有仓库操作都在 Linux 服务器执行，本地电脑相关步骤按 Linux / macOS / Windows 分别给出命令。&lt;/p&gt;
&lt;h2&gt;一、先确认仓库状态（Linux 服务器）&lt;/h2&gt;
&lt;p&gt;先进入项目目录，确认当前分支、远程地址、工作区状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /path/to/your/project
git branch --show-current
git remote -v
git status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预期重点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当前分支是 &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;origin&lt;/code&gt; 指向你的仓库&lt;/li&gt;
&lt;li&gt;先识别是否有不该提交的大目录（如 &lt;code&gt;checkpoints/&lt;/code&gt;、&lt;code&gt;outputs/&lt;/code&gt;、&lt;code&gt;external/&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;二、先做提交边界清理：补 &lt;code&gt;.gitignore&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;这一步的目的不是“立刻提交”，而是先把实验产物和本地依赖从源码中隔离。&lt;/p&gt;
&lt;p&gt;示例（按你项目实际调整）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-gitignore&quot;&gt;# local configs
configs/paths.sh

# experiment artifacts
checkpoints/
outputs/
logs/

# caches
data/
.cache/

# local external deps
external/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;补完后再看状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果结果里主要剩下源码和文档改动，说明提交边界已经干净。&lt;/p&gt;
&lt;h2&gt;三、共享账号场景下的 Git 身份配置（只配当前仓库）&lt;/h2&gt;
&lt;p&gt;多人共用一个 Linux 用户时，不要用全局身份，避免污染他人项目。&lt;/p&gt;
&lt;p&gt;先做前置检查：&lt;code&gt;git config user.email&lt;/code&gt; 里要填的邮箱，必须先在 GitHub 账号里添加并完成验证。&lt;/p&gt;
&lt;p&gt;GitHub 邮箱设置路径：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Settings&lt;/code&gt; -&gt; &lt;code&gt;Emails&lt;/code&gt; -&gt; &lt;code&gt;Add email address&lt;/code&gt; -&gt; &lt;code&gt;Verify&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后再回到服务器配置仓库级身份：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config user.name &quot;your-github-username&quot;
git config user.email &quot;your-email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --get user.name
git config --get user.email
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;推荐原则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用仓库级配置（不加 &lt;code&gt;--global&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;不使用 &lt;code&gt;credential.helper store&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;不在共享机器长期保存个人 PAT/私钥&lt;/li&gt;
&lt;li&gt;如果开启了 GitHub 邮箱隐私策略，优先使用 GitHub 提供的 &lt;code&gt;noreply&lt;/code&gt; 邮箱做 &lt;code&gt;user.email&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;四、把远程切到 SSH&lt;/h2&gt;
&lt;p&gt;GitHub SSH URL 固定格式：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git@github.com:&amp;#x3C;用户名&gt;/&amp;#x3C;仓库名&gt;.git&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git@github.com:your-username/your-repo.git&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;设置并验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote set-url origin git@github.com:your-username/your-repo.git
git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;五、核心安全做法：用 SSH Agent Forwarding（不在服务器留私钥）&lt;/h2&gt;
&lt;h3&gt;5.1 在本地电脑准备 SSH key（按你的本机系统执行）&lt;/h3&gt;
&lt;p&gt;如果你本地还没有 key，先生成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux / macOS&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t ed25519 -C &quot;your-email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Windows（PowerShell）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;ssh-keygen -t ed25519 -C &quot;your-email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加载 key 到 agent：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux / macOS&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-add ~/.ssh/id_ed25519
ssh-add -l
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Windows（PowerShell）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
ssh-add -l
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 配置 SSH 别名并开启转发（本地电脑）&lt;/h3&gt;
&lt;p&gt;配置文件路径：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux / macOS：&lt;code&gt;~/.ssh/config&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Windows：&lt;code&gt;C:\Users\&amp;#x3C;你用户名&gt;\.ssh\config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;配置示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sshconfig&quot;&gt;Host &amp;#x3C;server-alias&gt;
  HostName &amp;#x3C;你的服务器IP或域名&gt;
  User &amp;#x3C;服务器用户名&gt;
  IdentityFile ~/.ssh/id_ed25519
  ForwardAgent yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后登录服务器（任意系统本机都一样）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -A &amp;#x3C;server-alias&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 在 GitHub 账号里添加 SSH 公钥（关键主步骤）&lt;/h3&gt;
&lt;p&gt;先在本地电脑读取公钥（按你的本机系统执行）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux / macOS&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Windows（PowerShell）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后到 GitHub 网页完成绑定：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;右上角头像 -&gt; &lt;code&gt;Settings&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;左侧进入 &lt;code&gt;SSH and GPG keys&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;New SSH key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Key type&lt;/code&gt; 选择 &lt;strong&gt;Authentication Key&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Title&lt;/code&gt; 填一个容易识别的名称（例如 &lt;code&gt;my-laptop-agent-forward&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Key&lt;/code&gt; 粘贴整行公钥&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;Add SSH key&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这一段必须完成，否则后面的 &lt;code&gt;ssh -T git@github.com&lt;/code&gt; 很可能出现 &lt;code&gt;Permission denied (publickey)&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;5.4 在服务器上验证 agent 转发&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-add -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果能看到你本地 key 的指纹，说明转发已生效。&lt;/p&gt;
&lt;h2&gt;六、第一次连 GitHub 的认证与排错&lt;/h2&gt;
&lt;p&gt;在服务器上测试：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你遇到过的报错是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;git@github.com: Permission denied (publickey).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这通常表示“这把公钥还没绑定到目标 GitHub 账号”，不是服务器命令错了。&lt;/p&gt;
&lt;p&gt;解决步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;确认你已经完成上面的 &lt;code&gt;Settings&lt;/code&gt; -&gt; &lt;code&gt;SSH and GPG keys&lt;/code&gt; -&gt; &lt;code&gt;New SSH key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;检查粘贴的是完整公钥（包含开头 &lt;code&gt;ssh-ed25519&lt;/code&gt; 和中间长串内容）&lt;/li&gt;
&lt;li&gt;保存后重新测试：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过标志通常是类似：&lt;code&gt;Hi your-username! You&apos;ve successfully authenticated...&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;七、开始版本管理并推送&lt;/h2&gt;
&lt;p&gt;建议每个明确阶段做一次小提交：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git add &amp;#x3C;files&gt;
git commit -m &quot;feat: your change&quot;
git push origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;针对你的这类实验项目，推荐把提交拆小：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chore: add gitignore for artifacts and local deps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs: add api compatibility notes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feat: add baseline scripts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样回滚和审阅都会轻松很多。&lt;/p&gt;
&lt;p&gt;如果 &lt;code&gt;git push&lt;/code&gt; 因邮箱策略被拒绝（常见于开启了 “Block command line pushes that expose my email”）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;到 GitHub &lt;code&gt;Settings&lt;/code&gt; -&gt; &lt;code&gt;Emails&lt;/code&gt; 确认邮箱已验证，或复制你的 &lt;code&gt;noreply&lt;/code&gt; 邮箱&lt;/li&gt;
&lt;li&gt;在仓库重新设置：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config user.email &quot;your-noreply-email@users.noreply.github.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;用新邮箱重做提交并推送：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git commit --amend --reset-author --no-edit
git push origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;八、共享机器最佳实践清单&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Git 身份用仓库级配置，不用全局。&lt;/li&gt;
&lt;li&gt;不把个人私钥长期放在共享 Linux 账号目录。&lt;/li&gt;
&lt;li&gt;不使用 &lt;code&gt;credential.helper store&lt;/code&gt; 明文落盘 PAT。&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;ssh -A&lt;/code&gt; 转发本地凭证，断开会话即失效。&lt;/li&gt;
&lt;li&gt;按功能阶段做小提交，避免把大文件和临时产物混入历史。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;这套流程本质上做了两件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把“代码版本管理”做规范（清晰提交边界 + 小步提交）&lt;/li&gt;
&lt;li&gt;把“共享账号安全”做正确（本地持有私钥 + 会话转发认证）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果后续你要继续在这个仓库迭代，直接复用这一套即可。&lt;/p&gt;</content:encoded><h:img src="https://Minakanmi-Yuki.github.io/picx-images-hosting/101003773_p0_master1200.3uvfruqg2c.webp"/><enclosure url="https://Minakanmi-Yuki.github.io/picx-images-hosting/101003773_p0_master1200.3uvfruqg2c.webp"/></item><item><title>Linux 无图形界面服务器代理配置（Clash Verge 方案）</title><link>https://hana-blog.top/blog/linux-server-clash-verge-proxy</link><guid isPermaLink="true">https://hana-blog.top/blog/linux-server-clash-verge-proxy</guid><description>面向纯 SSH 服务器：Clash Verge 无 GUI 不可用时，使用 mihomo 内核完成订阅、选节点、systemd 常驻与终端代理。</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;你这个场景是“Linux 服务器只有 SSH，没有图形桌面”。&lt;/p&gt;
&lt;p&gt;先说结论：&lt;strong&gt;无 GUI 环境不能直接运行 Clash Verge&lt;/strong&gt;（它是基于 WebView 的图形客户端）。
在服务器上应改为使用它的内核方案：&lt;code&gt;mihomo&lt;/code&gt; 命令行。&lt;/p&gt;
&lt;p&gt;这篇给你一套纯命令行流程，目标是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不依赖图形界面&lt;/li&gt;
&lt;li&gt;可开机自启&lt;/li&gt;
&lt;li&gt;默认不走全局代理，需要时再临时 &lt;code&gt;export&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;终端里的 &lt;code&gt;curl&lt;/code&gt; / &lt;code&gt;apt&lt;/code&gt; / &lt;code&gt;git&lt;/code&gt; 可按命令局部走代理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;0. 前置条件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;服务器系统：Linux（Ubuntu / Debian / CentOS）&lt;/li&gt;
&lt;li&gt;你有一个可用的 Clash 订阅链接&lt;/li&gt;
&lt;li&gt;你有 &lt;code&gt;sudo&lt;/code&gt; 权限&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. 安装 mihomo（替代 Clash Verge GUI）&lt;/h2&gt;
&lt;p&gt;不要先手写通配符解压。先把文件下载到当前目录，再解压。&lt;/p&gt;
&lt;p&gt;下面脚本会自动识别架构并从官方最新 release 获取下载链接：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://github.com/MetaCubeX/mihomo/releases/latest&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/downloads/mihomo
cd ~/downloads/mihomo

ARCH=&quot;$(uname -m)&quot;
if [ &quot;$ARCH&quot; = &quot;x86_64&quot; ]; then
  PATTERN=&apos;mihomo-linux-amd64[^&quot;]*\.gz&apos;
elif [ &quot;$ARCH&quot; = &quot;aarch64&quot; ] || [ &quot;$ARCH&quot; = &quot;arm64&quot; ]; then
  PATTERN=&apos;mihomo-linux-arm64[^&quot;]*\.gz&apos;
else
  echo &quot;Unsupported arch: $ARCH&quot;
  exit 1
fi

URL=&quot;$(curl -fsSL https://api.github.com/repos/MetaCubeX/mihomo/releases/latest \
  | grep -oE &quot;https://[^\&quot;]+${PATTERN}&quot; \
  | head -n 1)&quot;

if [ -z &quot;$URL&quot; ]; then
  echo &quot;Failed to resolve download url&quot;
  exit 1
fi

curl -L &quot;$URL&quot; -o mihomo.gz
gunzip -f mihomo.gz
chmod +x mihomo
sudo install -m 755 mihomo /usr/local/bin/mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证安装：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mihomo -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你遇到的这类错误：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;gzip: mihomo-linux-amd64-*.gz: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本质是当前目录里没有匹配文件，常见原因有两个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;还没下载 &lt;code&gt;mihomo&lt;/code&gt; 压缩包&lt;/li&gt;
&lt;li&gt;文件名和通配符不一致&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;先 &lt;code&gt;ls -lh&lt;/code&gt; 看目录里实际文件名，再执行解压。&lt;/p&gt;
&lt;h2&gt;2. 准备配置目录与 &lt;code&gt;config.yaml&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;创建工作目录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo mkdir -p /etc/mihomo/providers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新建 &lt;code&gt;/etc/mihomo/config.yaml&lt;/code&gt;（最小可用模板）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
secret: &quot;your-strong-secret&quot;

proxy-providers:
  mysub:
    type: http
    url: &quot;https://your-subscription-url&quot;
    path: ./providers/mysub.yaml
    interval: 3600
    health-check:
      enable: true
      interval: 300
      url: https://www.gstatic.com/generate_204

proxy-groups:
  - name: Proxy
    type: select
    use:
      - mysub

rules:
  - MATCH,Proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你“只有一个订阅链接”，只需要改这两处：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;secret&lt;/code&gt;：改成你自己的随机字符串（例如 16~32 位）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;url&lt;/code&gt;：改成你的订阅链接原文&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;示例（只演示要改的行）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;secret: &quot;8e1d3f4a5b6c7d8e&quot;
url: &quot;https://your-subscription-url&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;url&lt;/code&gt; 必须带引号，避免 &lt;code&gt;&amp;#x26;&lt;/code&gt;、&lt;code&gt;?&lt;/code&gt; 等字符导致 YAML 解析错误。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;写入文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo tee /etc/mihomo/config.yaml &gt;/dev/null &amp;#x3C;&amp;#x3C;&apos;YAML&apos;
mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
secret: &quot;your-strong-secret&quot;

proxy-providers:
  mysub:
    type: http
    url: &quot;https://your-subscription-url&quot;
    path: ./providers/mysub.yaml
    interval: 3600
    health-check:
      enable: true
      interval: 300
      url: https://www.gstatic.com/generate_204

proxy-groups:
  - name: Proxy
    type: select
    use:
      - mysub

rules:
  - MATCH,Proxy
YAML
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你的机场给的是“完整配置文件”而不是订阅 URL，可以直接把完整 YAML 覆盖到 &lt;code&gt;/etc/mihomo/config.yaml&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2.1 只有订阅链接时，怎么判断用哪种方式&lt;/h3&gt;
&lt;p&gt;先看订阅返回的前几行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -L &quot;你的订阅链接&quot; | head -n 20
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果能看到 &lt;code&gt;proxies:&lt;/code&gt;、&lt;code&gt;proxy-groups:&lt;/code&gt;、&lt;code&gt;rules:&lt;/code&gt;，说明这是完整配置。直接保存为 &lt;code&gt;/etc/mihomo/config.yaml&lt;/code&gt; 即可。&lt;/li&gt;
&lt;li&gt;如果主要是节点列表，没有完整规则组，就用本文上面的 &lt;code&gt;proxy-providers&lt;/code&gt; 模板，把链接填到 &lt;code&gt;url&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 先前台启动测试&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo mihomo -d /etc/mihomo -t
sudo mihomo -d /etc/mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关注是否有报错；确认正常后用 &lt;code&gt;Ctrl+C&lt;/code&gt; 停止，继续做 systemd 常驻。&lt;/p&gt;
&lt;h2&gt;4. 配置 systemd 开机自启&lt;/h2&gt;
&lt;p&gt;创建服务文件 &lt;code&gt;/etc/systemd/system/mihomo.service&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[Unit]
Description=mihomo Daemon, Another Clash Kernel.
After=network.target NetworkManager.service systemd-networkd.service iwd.service

[Service]
Type=simple
LimitNPROC=500
LimitNOFILE=1000000
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
Restart=always
ExecStartPre=/usr/bin/sleep 1s
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl daemon-reload
sudo systemctl enable mihomo
sudo systemctl start mihomo
sudo systemctl status mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.1 不开机自启，只手动启动&lt;/h3&gt;
&lt;p&gt;如果你不希望开机自动拉起，只想“需要时再开”：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl daemon-reload
sudo systemctl disable mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后按需手动控制：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl start mihomo
sudo systemctl stop mihomo
sudo systemctl restart mihomo
sudo systemctl status mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开机自启和手动启动并不冲突：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;enable&lt;/code&gt; / &lt;code&gt;disable&lt;/code&gt; 决定“开机是否自动启动”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start&lt;/code&gt; / &lt;code&gt;stop&lt;/code&gt; 决定“当前这一刻是否运行”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;查看日志：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;journalctl -u mihomo -o cat -f
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 无 GUI 下切换节点（API）&lt;/h2&gt;
&lt;p&gt;先在当前 shell 定义变量（推荐）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export MIHOMO_API=&quot;http://127.0.0.1:9090&quot;
export MIHOMO_SECRET=&quot;your-strong-secret&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;config.yaml&lt;/code&gt; 里的 &lt;code&gt;secret&lt;/code&gt; 仍然是固定字符串；这里用环境变量只是为了命令行调用更方便。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可选：写入 &lt;code&gt;~/.bashrc&lt;/code&gt;，每次登录自动可用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo &apos;export MIHOMO_API=&quot;http://127.0.0.1:9090&quot;&apos; &gt;&gt; ~/.bashrc
echo &apos;export MIHOMO_SECRET=&quot;your-strong-secret&quot;&apos; &gt;&gt; ~/.bashrc
source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果是多人共用账号，不建议把 &lt;code&gt;MIHOMO_SECRET&lt;/code&gt; 长期明文写在共享用户的 &lt;code&gt;~/.bashrc&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;先注意请求头写法，必须是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;-H &quot;Authorization: Bearer ${MIHOMO_SECRET}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;错误写法（会返回 &lt;code&gt;{&quot;message&quot;:&quot;Unauthorized&quot;}&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;-H &quot;your-strong-secret&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看策略组节点：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -s -H &quot;Authorization: Bearer ${MIHOMO_SECRET}&quot; \
  &quot;${MIHOMO_API}/proxies&quot; | head
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把 &lt;code&gt;Proxy&lt;/code&gt; 组切到指定节点（例如 &lt;code&gt;HK-01&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -s -X PUT \
  -H &quot;Authorization: Bearer ${MIHOMO_SECRET}&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d &apos;{&quot;name&quot;:&quot;HK-01&quot;}&apos; \
  &quot;${MIHOMO_API}/proxies/Proxy&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用完后清理变量（可选）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;unset MIHOMO_API MIHOMO_SECRET
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 让终端命令走代理&lt;/h2&gt;
&lt;p&gt;当前 shell 临时生效：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl https://ipinfo.io
curl -I https://www.google.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;取消：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;unset http_proxy https_proxy all_proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;服务器场景不建议写进 &lt;code&gt;~/.bashrc&lt;/code&gt; 全局生效，推荐做成手动开关函数：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;proxy_on() {
  export http_proxy=http://127.0.0.1:7890
  export https_proxy=http://127.0.0.1:7890
  export all_proxy=socks5://127.0.0.1:7890
}

proxy_off() {
  unset http_proxy https_proxy all_proxy
}

proxy_status() {
  env | grep -i proxy || echo &quot;proxy is off&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把上面函数放进 &lt;code&gt;~/.bashrc&lt;/code&gt; 或 &lt;code&gt;~/.zshrc&lt;/code&gt; 后，按需执行 &lt;code&gt;proxy_on&lt;/code&gt; / &lt;code&gt;proxy_off&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;7. 常见工具代理&lt;/h2&gt;
&lt;h3&gt;APT（单次命令局部代理）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo env \
  http_proxy=http://127.0.0.1:7890 \
  https_proxy=http://127.0.0.1:7890 \
  apt update
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装包也是同理：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo env \
  http_proxy=http://127.0.0.1:7890 \
  https_proxy=http://127.0.0.1:7890 \
  apt install -y &amp;#x3C;package-name&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Git（单次命令局部代理）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git -c http.proxy=http://127.0.0.1:7890 \
    -c https.proxy=http://127.0.0.1:7890 \
    clone &amp;#x3C;repo-url&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;pull&lt;/code&gt; / &lt;code&gt;push&lt;/code&gt; 也可以同样加 &lt;code&gt;-c&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git -c http.proxy=http://127.0.0.1:7890 \
    -c https.proxy=http://127.0.0.1:7890 \
    pull
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. 排障速查&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;curl&lt;/code&gt; 仍然直连&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;env | grep -i proxy
ss -lntp | grep 7890
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认代理变量已设置，且 &lt;code&gt;mihomo&lt;/code&gt; 确实监听在 &lt;code&gt;127.0.0.1:7890&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;2) 服务起不来&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl status mihomo
journalctl -u mihomo -o cat -e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;大多数情况是 &lt;code&gt;config.yaml&lt;/code&gt; 语法错误或订阅 URL 无效。&lt;/p&gt;
&lt;h3&gt;3) API 连接被拒绝&lt;/h3&gt;
&lt;p&gt;检查 &lt;code&gt;external-controller&lt;/code&gt; 是否是 &lt;code&gt;127.0.0.1:9090&lt;/code&gt;，以及 &lt;code&gt;secret&lt;/code&gt; 是否和请求头一致。&lt;/p&gt;
&lt;p&gt;如果返回：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;message&quot;:&quot;Unauthorized&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通常不是端口错了，而是认证头格式错误。应使用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -s -H &quot;Authorization: Bearer ${MIHOMO_SECRET}&quot; \
  &quot;${MIHOMO_API}/proxies&quot; | head
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;mihomo.service not found&lt;/code&gt; / &lt;code&gt;Unit file does not exist&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;这表示 systemd 服务文件还没创建成功，或文件名不对。&lt;/p&gt;
&lt;p&gt;先确认二进制路径存在：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;which mihomo
ls -l /usr/local/bin/mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再重新创建服务文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo tee /etc/systemd/system/mihomo.service &gt;/dev/null &amp;#x3C;&amp;#x3C;&apos;UNIT&apos;
[Unit]
Description=mihomo Daemon, Another Clash Kernel.
After=network.target NetworkManager.service systemd-networkd.service iwd.service

[Service]
Type=simple
Restart=always
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
UNIT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后加载并启动：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl daemon-reload
sudo systemctl enable mihomo
sudo systemctl start mihomo
sudo systemctl status mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你只想手动启动，不开机自启，把 &lt;code&gt;enable&lt;/code&gt; 换成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl disable mihomo
sudo systemctl start mihomo
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;9. 最小闭环（无 GUI）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 1) 启动服务
sudo systemctl start mihomo

# 2) 配置终端代理
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890

# 3) 验证
curl https://ipinfo.io
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到这里，你在纯 SSH 服务器上的代理就可用了。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/99749488_p0_master1200.83an1pr7zr.webp"/><enclosure url="https://pic.hana0721.top/99749488_p0_master1200.83an1pr7zr.webp"/></item><item><title>Vim 详细使用教程：从入门到高频操作</title><link>https://hana-blog.top/blog/vim-detailed-guide</link><guid isPermaLink="true">https://hana-blog.top/blog/vim-detailed-guide</guid><description>一篇系统化 Vim 教程，覆盖模式切换、移动、编辑、复制粘贴、查找替换、可视模式、宏、寄存器与常用配置。</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果你经常在 Linux 服务器、终端或 SSH 环境里工作，&lt;code&gt;Vim&lt;/code&gt; 仍然是最值得掌握的编辑器之一。&lt;/p&gt;
&lt;p&gt;它的学习曲线确实比普通文本编辑器陡，但一旦掌握核心操作，你会明显感受到两个变化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手不用频繁离开键盘&lt;/li&gt;
&lt;li&gt;编辑速度和文本控制力都会上一个台阶&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇教程按“先能独立编辑文件，再逐步提升效率”的顺序来讲，尽量把高频操作一次讲清楚。&lt;/p&gt;
&lt;h2&gt;1. Vim 是什么，适合什么场景&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Vim&lt;/code&gt; 是一个以键盘操作为核心的文本编辑器，尤其适合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;远程服务器上快速改配置&lt;/li&gt;
&lt;li&gt;在终端里直接编辑代码或脚本&lt;/li&gt;
&lt;li&gt;处理大量文本、日志、配置文件&lt;/li&gt;
&lt;li&gt;希望减少鼠标依赖的人&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你只是偶尔改一两行文本，图形编辑器当然更直观；但如果你经常 SSH 到远端机器，&lt;code&gt;Vim&lt;/code&gt; 基本属于必备技能。&lt;/p&gt;
&lt;h2&gt;2. 安装与启动&lt;/h2&gt;
&lt;p&gt;Ubuntu / Debian:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update
sudo apt install -y vim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CentOS / RHEL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo yum install -y vim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;macOS (Homebrew):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install vim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看版本：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vim --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打开文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vim filename.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果文件不存在，保存时会自动创建。&lt;/p&gt;
&lt;h2&gt;3. 先理解 Vim 的核心：模式&lt;/h2&gt;
&lt;p&gt;Vim 最重要的概念不是命令本身，而是&lt;strong&gt;模式&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;3.1 普通模式&lt;/h3&gt;
&lt;p&gt;打开 Vim 后默认进入普通模式。&lt;br&gt;
这个模式主要用于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;移动光标&lt;/li&gt;
&lt;li&gt;删除、复制、粘贴&lt;/li&gt;
&lt;li&gt;搜索&lt;/li&gt;
&lt;li&gt;跳转&lt;/li&gt;
&lt;li&gt;执行命令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你可以把普通模式理解成“控制模式”。&lt;/p&gt;
&lt;h3&gt;3.2 插入模式&lt;/h3&gt;
&lt;p&gt;插入模式就是正常输入文字的模式。&lt;/p&gt;
&lt;p&gt;常见进入方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i&lt;/code&gt;：在光标前插入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a&lt;/code&gt;：在光标后插入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;I&lt;/code&gt;：跳到行首并插入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A&lt;/code&gt;：跳到行尾并插入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;o&lt;/code&gt;：在下一行新开一行并插入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O&lt;/code&gt;：在上一行新开一行并插入&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;退出插入模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;按 &lt;code&gt;Esc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.3 可视模式&lt;/h3&gt;
&lt;p&gt;可视模式用于选中文本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;v&lt;/code&gt;：字符级选择&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V&lt;/code&gt;：整行选择&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+v&lt;/code&gt;：块选择（列编辑非常有用）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.4 命令行模式&lt;/h3&gt;
&lt;p&gt;在普通模式下输入 &lt;code&gt;:&lt;/code&gt; 进入命令行模式，用于保存、退出、替换、设置选项等。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:w
:q
:wq
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 第一次使用的最小闭环&lt;/h2&gt;
&lt;p&gt;你至少先记住这几步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;vim test.txt&lt;/code&gt; 打开文件&lt;/li&gt;
&lt;li&gt;按 &lt;code&gt;i&lt;/code&gt; 进入插入模式&lt;/li&gt;
&lt;li&gt;输入内容&lt;/li&gt;
&lt;li&gt;按 &lt;code&gt;Esc&lt;/code&gt; 回到普通模式&lt;/li&gt;
&lt;li&gt;输入 &lt;code&gt;:wq&lt;/code&gt; 保存并退出&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你不想保存：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:q!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果文件没改动，直接退出：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:q
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 光标移动：真正决定效率的部分&lt;/h2&gt;
&lt;p&gt;很多人学 Vim 只记编辑命令，但真正拉开差距的是移动。&lt;/p&gt;
&lt;h3&gt;5.1 基础移动&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;h&lt;/code&gt;：左&lt;/li&gt;
&lt;li&gt;&lt;code&gt;j&lt;/code&gt;：下&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k&lt;/code&gt;：上&lt;/li&gt;
&lt;li&gt;&lt;code&gt;l&lt;/code&gt;：右&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这四个键是最基础的移动方式。&lt;/p&gt;
&lt;h3&gt;5.2 按单词移动&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;w&lt;/code&gt;：跳到下一个单词开头&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b&lt;/code&gt;：跳到上一个单词开头&lt;/li&gt;
&lt;li&gt;&lt;code&gt;e&lt;/code&gt;：跳到当前或下一个单词末尾&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你在代码里频繁移动，&lt;code&gt;w b e&lt;/code&gt; 比方向键高效很多。&lt;/p&gt;
&lt;h3&gt;5.3 按行移动&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;：跳到行首&lt;/li&gt;
&lt;li&gt;&lt;code&gt;^&lt;/code&gt;：跳到本行第一个非空字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$&lt;/code&gt;：跳到行尾&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.4 按屏幕和文档移动&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gg&lt;/code&gt;：跳到文件开头&lt;/li&gt;
&lt;li&gt;&lt;code&gt;G&lt;/code&gt;：跳到文件末尾&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:10&lt;/code&gt;：跳到第 10 行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10G&lt;/code&gt;：跳到第 10 行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+d&lt;/code&gt;：向下翻半页&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+u&lt;/code&gt;：向上翻半页&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+f&lt;/code&gt;：向下翻一页&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+b&lt;/code&gt;：向上翻一页&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.5 行内快速跳转&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;f{字符}&lt;/code&gt;：向右找到本行中下一个指定字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F{字符}&lt;/code&gt;：向左找到本行中上一个指定字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;t{字符}&lt;/code&gt;：跳到目标字符前一位&lt;/li&gt;
&lt;li&gt;&lt;code&gt;T{字符}&lt;/code&gt;：跳到目标字符后一位&lt;/li&gt;
&lt;li&gt;&lt;code&gt;;&lt;/code&gt;：重复上一次 &lt;code&gt;f/F/t/T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;,&lt;/code&gt;：反向重复&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如你想跳到这一行下一个 &lt;code&gt;)&lt;/code&gt;，直接按：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;f)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 编辑文本的高频操作&lt;/h2&gt;
&lt;h3&gt;6.1 删除&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;x&lt;/code&gt;：删除当前字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dd&lt;/code&gt;：删除当前整行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dw&lt;/code&gt;：删除到下一个单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;d$&lt;/code&gt;：删除到行尾&lt;/li&gt;
&lt;li&gt;&lt;code&gt;d0&lt;/code&gt;：删除到行首&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.2 复制与粘贴&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;yy&lt;/code&gt;：复制当前行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yw&lt;/code&gt;：复制一个单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;p&lt;/code&gt;：在光标后粘贴&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P&lt;/code&gt;：在光标前粘贴&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.3 修改&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;change&lt;/code&gt; 系列本质上是“删除后立刻进入插入模式”。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cw&lt;/code&gt;：修改一个单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc&lt;/code&gt;：修改整行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c$&lt;/code&gt;：从当前位置改到行尾&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.4 撤销与重做&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;u&lt;/code&gt;：撤销&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+r&lt;/code&gt;：重做&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.5 重复上一次操作&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt;：重复上一次修改&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是 Vim 里非常强的一个键。&lt;/p&gt;
&lt;p&gt;例如你刚执行过一次 &lt;code&gt;cw&lt;/code&gt; 把某个变量名改掉，接着移动到下一个变量，按 &lt;code&gt;.&lt;/code&gt; 就能重复同样的修改动作。&lt;/p&gt;
&lt;h2&gt;7. 数字前缀：批量操作的关键&lt;/h2&gt;
&lt;p&gt;Vim 大量命令都支持“数字 + 动作”。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;3j&lt;/code&gt;：向下移动 3 行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;5x&lt;/code&gt;：删除 5 个字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3dd&lt;/code&gt;：删除 3 行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2yy&lt;/code&gt;：复制 2 行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;4w&lt;/code&gt;：向前跳 4 个单词&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个机制很重要，因为它能把重复操作压缩成一个动作。&lt;/p&gt;
&lt;h2&gt;8. 文本对象：Vim 真正强大的地方&lt;/h2&gt;
&lt;p&gt;文本对象的思路是：&lt;strong&gt;不是按字符编辑，而是按语义结构编辑&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;8.1 单词对象&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ciw&lt;/code&gt;：修改当前单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;diw&lt;/code&gt;：删除当前单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yiw&lt;/code&gt;：复制当前单词&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里的 &lt;code&gt;iw&lt;/code&gt; 是 &lt;code&gt;inner word&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;8.2 括号对象&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ci(&lt;/code&gt;：修改圆括号里的内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;di(&lt;/code&gt;：删除圆括号里的内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yi(&lt;/code&gt;：复制圆括号里的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8.3 引号对象&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ci&quot;&lt;/code&gt;：修改双引号内内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ci&apos;&lt;/code&gt;：修改单引号内内容&lt;/li&gt;
&lt;li&gt;输入 &lt;code&gt;ci&lt;/code&gt; 后再接反引号键：修改反引号内内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;name = &quot;hana_blog&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;光标放在引号内部任意位置，按：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;ci&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就会直接清空引号内内容并进入插入模式。&lt;/p&gt;
&lt;h3&gt;8.4 around 与 inner&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i&lt;/code&gt;：inside / inner，不包括边界&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a&lt;/code&gt;：around，包括边界&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ci&quot;&lt;/code&gt;：只改引号内部内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ca&quot;&lt;/code&gt;：连引号一起替换&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;9. 可视模式：选中、缩进、批量编辑&lt;/h2&gt;
&lt;h3&gt;9.1 基础选择&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;v&lt;/code&gt; 进入字符选择&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V&lt;/code&gt; 进入行选择&lt;/li&gt;
&lt;li&gt;选中后按 &lt;code&gt;d&lt;/code&gt; 删除、按 &lt;code&gt;y&lt;/code&gt; 复制、按 &lt;code&gt;&gt;&lt;/code&gt; 缩进、按 &lt;code&gt;&amp;#x3C;&lt;/code&gt; 反缩进&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;9.2 块选择&lt;/h3&gt;
&lt;p&gt;按 &lt;code&gt;Ctrl+v&lt;/code&gt; 进入块选择后，可以做列级编辑。&lt;/p&gt;
&lt;p&gt;常见场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同时给多行前面加注释&lt;/li&gt;
&lt;li&gt;同时删除多行开头的相同前缀&lt;/li&gt;
&lt;li&gt;做整列对齐&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如给多行前面加 &lt;code&gt;#&lt;/code&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Ctrl+v&lt;/code&gt; 选中多行的第一列&lt;/li&gt;
&lt;li&gt;按 &lt;code&gt;I&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输入 &lt;code&gt;# &lt;/code&gt;&lt;/li&gt;
&lt;li&gt;按 &lt;code&gt;Esc&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vim 会把这段内容批量插入到所有选中的行前面。&lt;/p&gt;
&lt;h2&gt;10. 搜索与替换&lt;/h2&gt;
&lt;h3&gt;10.1 搜索&lt;/h3&gt;
&lt;p&gt;向下搜索：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;/keyword
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;向上搜索：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;?keyword
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;搜索结果跳转：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n&lt;/code&gt;：下一个&lt;/li&gt;
&lt;li&gt;&lt;code&gt;N&lt;/code&gt;：上一个&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.2 高亮与取消高亮&lt;/h3&gt;
&lt;p&gt;开启搜索高亮：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:set hlsearch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;取消当前高亮：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:nohlsearch
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10.3 替换&lt;/h3&gt;
&lt;p&gt;替换当前行第一个匹配：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:s/old/new/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;替换当前行所有匹配：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:s/old/new/g
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;替换全文所有匹配：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:%s/old/new/g
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;替换全文并逐个确认：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:%s/old/new/gc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的 &lt;code&gt;c&lt;/code&gt; 表示 confirm。&lt;/p&gt;
&lt;h2&gt;11. 多文件与多窗口操作&lt;/h2&gt;
&lt;h3&gt;11.1 同时打开多个文件&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vim file1.txt file2.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Vim 内切换缓冲区：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:bn&lt;/code&gt;：下一个 buffer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:bp&lt;/code&gt;：上一个 buffer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:ls&lt;/code&gt;：查看 buffer 列表&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:b 2&lt;/code&gt;：跳到第 2 个 buffer&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;11.2 分屏&lt;/h3&gt;
&lt;p&gt;水平分屏：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:split
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;垂直分屏：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:vsplit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打开指定文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:split another.txt
:vsplit another.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;窗口切换：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl+w&lt;/code&gt; 然后 &lt;code&gt;h/j/k/l&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关闭当前窗口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:q
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11.3 标签页&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:tabnew&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:tabnext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:tabprev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:tabclose&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过对很多人来说，buffer + split 已经够用了。&lt;/p&gt;
&lt;h2&gt;12. 寄存器：理解复制粘贴背后的机制&lt;/h2&gt;
&lt;p&gt;Vim 的复制、删除内容其实会进入寄存器。&lt;/p&gt;
&lt;p&gt;查看寄存器：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:registers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;常见寄存器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&quot;&lt;/code&gt;：默认寄存器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;：最近一次复制内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%&lt;/code&gt;：当前文件名&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/&lt;/code&gt;：最近一次搜索内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;指定寄存器复制：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;ayy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表示把当前行复制到 &lt;code&gt;a&lt;/code&gt; 寄存器。&lt;/p&gt;
&lt;p&gt;从 &lt;code&gt;a&lt;/code&gt; 寄存器粘贴：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;ap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你经常复制一段内容，期间又做了删除操作，寄存器会非常有用。&lt;/p&gt;
&lt;h2&gt;13. 宏：把重复编辑自动化&lt;/h2&gt;
&lt;p&gt;宏适合处理“同一种编辑动作重复很多次”的场景。&lt;/p&gt;
&lt;h3&gt;13.1 录制宏&lt;/h3&gt;
&lt;p&gt;按：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;qa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表示开始录制到寄存器 &lt;code&gt;a&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;完成操作后按：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;q
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结束录制。&lt;/p&gt;
&lt;h3&gt;13.2 执行宏&lt;/h3&gt;
&lt;p&gt;执行一次：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;@a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行 10 次：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;10@a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;举例：如果你有很多格式相似的行，需要逐行插入固定前缀或做相同替换，宏会比手工操作快很多。&lt;/p&gt;
&lt;h2&gt;14. 实用命令汇总&lt;/h2&gt;
&lt;p&gt;保存：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:w
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另存为：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:w newfile.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退出：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:q
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存并退出：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:wq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;强制退出不保存：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:q!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显示行号：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:set number
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显示相对行号：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:set relativenumber
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开启语法高亮：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;:syntax on
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;15. 推荐的基础配置&lt;/h2&gt;
&lt;p&gt;你可以先从一个最小 &lt;code&gt;.vimrc&lt;/code&gt; 开始：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vim&quot;&gt;set number
set relativenumber
set tabstop=2
set shiftwidth=2
set expandtab
set autoindent
set hlsearch
set incsearch
set ignorecase
set smartcase
syntax on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配置文件位置通常是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux / macOS: &lt;code&gt;~/.vimrc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这套配置的作用分别是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显示绝对行号与相对行号&lt;/li&gt;
&lt;li&gt;Tab 宽度设为 2&lt;/li&gt;
&lt;li&gt;用空格代替 Tab&lt;/li&gt;
&lt;li&gt;自动缩进&lt;/li&gt;
&lt;li&gt;搜索时高亮、增量显示&lt;/li&gt;
&lt;li&gt;搜索默认忽略大小写，但如果输入大写则大小写敏感&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;16. 新手最容易踩的坑&lt;/h2&gt;
&lt;h3&gt;16.1 忘记自己还在插入模式&lt;/h3&gt;
&lt;p&gt;一旦发现快捷键都不生效，第一反应先按 &lt;code&gt;Esc&lt;/code&gt;，回到普通模式再说。&lt;/p&gt;
&lt;h3&gt;16.2 不会退出&lt;/h3&gt;
&lt;p&gt;这个几乎是每个 Vim 新手都踩过的坑。记住：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:q!&lt;/code&gt; 不保存退出&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:wq&lt;/code&gt; 保存退出&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;16.3 只会用方向键&lt;/h3&gt;
&lt;p&gt;如果一直依赖方向键，你会很难真正体会 Vim 的效率。&lt;br&gt;
至少先强迫自己多用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hjkl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;w b e&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 ^ $&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gg G&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;16.4 把 Vim 当普通编辑器硬用&lt;/h3&gt;
&lt;p&gt;Vim 的效率来自“动作组合”，不是单个命令。&lt;/p&gt;
&lt;p&gt;比如下面这些都应该当成组合去理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ciw&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;d$&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yi(&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3dd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:%s/old/new/gc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;17. 一条推荐学习路径&lt;/h2&gt;
&lt;p&gt;如果你不想一次记太多，可以按这个顺序学：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先学进入 / 退出插入模式：&lt;code&gt;i a o Esc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;再学保存退出：&lt;code&gt;:w :q :wq :q!&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;再学基础移动：&lt;code&gt;hjkl w b 0 $ gg G&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;再学删除复制粘贴：&lt;code&gt;x dd yy p u&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;再学高频组合：&lt;code&gt;ciw dw cw .&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;最后学搜索替换、可视块、寄存器、宏&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样会比一次背完整命令表更容易真正用起来。&lt;/p&gt;
&lt;h2&gt;18. 结语&lt;/h2&gt;
&lt;p&gt;Vim 最难的不是命令本身，而是接受它的编辑思维：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通模式负责控制&lt;/li&gt;
&lt;li&gt;插入模式只负责输入&lt;/li&gt;
&lt;li&gt;大部分高效操作都来自“动作 + 对象”的组合&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当你开始习惯 &lt;code&gt;ciw&lt;/code&gt;、&lt;code&gt;dd&lt;/code&gt;、&lt;code&gt;p&lt;/code&gt;、&lt;code&gt;.&lt;/code&gt;、&lt;code&gt;/&lt;/code&gt;、&lt;code&gt;:%s///gc&lt;/code&gt; 这类操作后，很多文本编辑任务会变得非常顺手。&lt;/p&gt;
&lt;p&gt;如果你正在服务器上工作，最值得先练熟的不是全部功能，而是下面这组最小集合：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;i  a  o  Esc
:wq  :q!
hjkl  w  b  0  $
dd  yy  p  u
/  n  N
ciw  cw  .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把这些练熟，再继续扩展到可视块、宏和寄存器，Vim 就会开始真正好用起来。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/99213551_p0_master1200.4jopcsx1ya.webp"/><enclosure url="https://pic.hana0721.top/99213551_p0_master1200.4jopcsx1ya.webp"/></item><item><title>在本地电脑上配置 GitHub 权限（SSH/HTTPS）</title><link>https://hana-blog.top/blog/github-auth-local-setup</link><guid isPermaLink="true">https://hana-blog.top/blog/github-auth-local-setup</guid><description>面向本地电脑的 GitHub 权限配置教程：Git 身份、SSH key、PAT、Credential Manager、多账号切换与常见报错排查。</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果你要在自己的电脑上把代码推到 GitHub，核心其实只需要配对三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Git 身份是谁&lt;/li&gt;
&lt;li&gt;你的电脑拿什么方式向 GitHub 认证&lt;/li&gt;
&lt;li&gt;凭证是临时输入、系统托管，还是长期缓存&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;很多人把这三件事混在一起，所以一遇到 &lt;code&gt;Permission denied&lt;/code&gt;、&lt;code&gt;Authentication failed&lt;/code&gt;、推错账号，就很容易卡住。&lt;/p&gt;
&lt;p&gt;这篇教程专门讲“&lt;strong&gt;本地电脑如何配置 GitHub 权限&lt;/strong&gt;”，按 SSH 和 HTTPS 两条路线都给一套可用流程。&lt;/p&gt;
&lt;h2&gt;0. 先区分两个概念&lt;/h2&gt;
&lt;p&gt;配置 GitHub 权限时，最容易混淆的是下面两件事：&lt;/p&gt;
&lt;h3&gt;0.1 Git 身份&lt;/h3&gt;
&lt;p&gt;这是提交记录里显示的作者信息：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global user.name &quot;your-name&quot;
git config --global user.email &quot;your-email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它决定的是 commit 里写谁，不等于“你有没有权限 push 到 GitHub”。&lt;/p&gt;
&lt;h3&gt;0.2 GitHub 认证&lt;/h3&gt;
&lt;p&gt;这是你电脑连接 GitHub 时真正用到的授权方式，常见有两种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSH key&lt;/li&gt;
&lt;li&gt;HTTPS + Personal Access Token (PAT)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user.name&lt;/code&gt; / &lt;code&gt;user.email&lt;/code&gt; 解决“你是谁”&lt;/li&gt;
&lt;li&gt;SSH / PAT 解决“你有没有权限访问这个仓库”&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. 先检查本地环境&lt;/h2&gt;
&lt;p&gt;先确认 Git 和 SSH 工具都可用。&lt;/p&gt;
&lt;p&gt;Linux / macOS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git --version
ssh -V
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows（PowerShell）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;git --version
ssh -V
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;git&lt;/code&gt; 不存在，先安装 Git。&lt;/p&gt;
&lt;p&gt;如果 &lt;code&gt;ssh&lt;/code&gt; 不存在：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows: 安装 Git for Windows 或启用 OpenSSH Client&lt;/li&gt;
&lt;li&gt;macOS / Linux: 通常系统自带&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 配置 Git 身份&lt;/h2&gt;
&lt;p&gt;如果这台电脑主要由你自己使用，通常直接配置全局身份就够了：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global user.name &quot;your-github-username&quot;
git config --global user.email &quot;your-email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查结果：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global --get user.name
git config --global --get user.email
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你希望提交记录绑定到 GitHub 账号，&lt;code&gt;user.email&lt;/code&gt; 最好使用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你在 GitHub 已验证过的邮箱&lt;/li&gt;
&lt;li&gt;或者 GitHub 提供的 &lt;code&gt;noreply&lt;/code&gt; 邮箱&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;查看某个仓库当前实际生效的身份：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --get user.name
git config --get user.email
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你只想对当前仓库单独配置，不要加 &lt;code&gt;--global&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;3. 推荐路线 A：使用 SSH key&lt;/h2&gt;
&lt;p&gt;对个人电脑来说，SSH 通常是最省心的一种方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不需要每次输入 Token&lt;/li&gt;
&lt;li&gt;更适合长期使用&lt;/li&gt;
&lt;li&gt;配合 &lt;code&gt;ssh-agent&lt;/code&gt; 后体验最好&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 生成 SSH key&lt;/h2&gt;
&lt;p&gt;推荐使用 &lt;code&gt;ed25519&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Linux / macOS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t ed25519 -C &quot;your-email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows（PowerShell）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;ssh-keygen -t ed25519 -C &quot;your-email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行过程中会看到几项提示：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Enter file in which to save the key (...)
Enter passphrase (...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路径直接用默认值&lt;/li&gt;
&lt;li&gt;&lt;code&gt;passphrase&lt;/code&gt; 最好设置，不要留空&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;默认路径通常是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux / macOS: &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;C:\Users\你的用户名\.ssh\id_ed25519&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对应公钥文件是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;id_ed25519.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 把公钥加到 GitHub&lt;/h2&gt;
&lt;p&gt;先查看公钥内容。&lt;/p&gt;
&lt;p&gt;Linux / macOS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows（PowerShell）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Get-Content $HOME\.ssh\id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;复制整行公钥后，到 GitHub 页面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;右上角头像 -&gt; &lt;code&gt;Settings&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;进入 &lt;code&gt;SSH and GPG keys&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;New SSH key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Title&lt;/code&gt; 随便填，例如 &lt;code&gt;My Laptop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;把刚才复制的公钥粘进去&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;Add SSH key&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这一步完成后，你的 GitHub 账号才真正认识这台电脑的密钥。&lt;/p&gt;
&lt;h2&gt;6. 启动 ssh-agent 并加载私钥&lt;/h2&gt;
&lt;h3&gt;6.1 Linux / macOS&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;eval &quot;$(ssh-agent -s)&quot;
ssh-add ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 Windows（PowerShell）&lt;/h3&gt;
&lt;p&gt;先启动 OpenSSH Agent 服务：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Get-Service ssh-agent
Start-Service ssh-agent
Set-Service -Name ssh-agent -StartupType Automatic
ssh-add $HOME\.ssh\id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你设置了 passphrase，这里会要求你输入一次。&lt;/p&gt;
&lt;h2&gt;7. 测试 SSH 认证是否成功&lt;/h2&gt;
&lt;p&gt;执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一次连接时，可能会看到主机指纹确认：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Are you sure you want to continue connecting (yes/no/[fingerprint])?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输入：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果认证成功，常见输出类似：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Hi your-username! You&apos;ve successfully authenticated...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. 把仓库远程地址切到 SSH&lt;/h2&gt;
&lt;p&gt;查看当前远程地址：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果是 HTTPS，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;https://github.com/your-username/your-repo.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以改成 SSH：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote set-url origin git@github.com:your-username/your-repo.git
git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后推送：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;9. 备选路线 B：使用 HTTPS + PAT&lt;/h2&gt;
&lt;p&gt;如果你暂时不想配 SSH，也可以直接走 HTTPS，但现在不能再用 GitHub 登录密码，必须用 &lt;code&gt;Personal Access Token&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;10. 创建 PAT&lt;/h2&gt;
&lt;p&gt;GitHub 页面路径：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;右上角头像 -&gt; &lt;code&gt;Settings&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Developer settings&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Personal access tokens&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;Tokens (classic)&lt;/code&gt; 或 Fine-grained Token&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你是新手，本地个人电脑使用 &lt;code&gt;Tokens (classic)&lt;/code&gt; 会更直接。&lt;/p&gt;
&lt;p&gt;创建时至少注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设定过期时间&lt;/li&gt;
&lt;li&gt;给出需要的仓库权限&lt;/li&gt;
&lt;li&gt;私人仓库常见需要 &lt;code&gt;repo&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;生成后要立刻保存，因为 GitHub 通常只显示一次。&lt;/p&gt;
&lt;h2&gt;11. HTTPS 推送时怎么填用户名和密码&lt;/h2&gt;
&lt;p&gt;当你执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果提示输入：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Username for &apos;https://github.com&apos;:
Password for &apos;https://github.com&apos;:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应该这样填：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Username&lt;/code&gt;：你的 GitHub 用户名&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Password&lt;/code&gt;：你的 PAT，不是 GitHub 登录密码&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;12. 让系统记住凭证&lt;/h2&gt;
&lt;p&gt;如果你每次都输 PAT，会比较烦。更合理的做法是让系统凭证管理器接管。&lt;/p&gt;
&lt;h3&gt;12.1 Windows&lt;/h3&gt;
&lt;p&gt;Git for Windows 通常可配 &lt;code&gt;manager-core&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global credential.helper manager-core
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后凭证会进入 Windows Credential Manager。&lt;/p&gt;
&lt;h3&gt;12.2 macOS&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global credential.helper osxkeychain
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;12.3 Linux&lt;/h3&gt;
&lt;p&gt;如果你没有桌面凭证管理器，最简单是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global credential.helper cache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这只做内存缓存，安全性通常比明文存盘好。&lt;/p&gt;
&lt;p&gt;不推荐：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global credential.helper store
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为它可能把 Token 明文写到本地文件里。&lt;/p&gt;
&lt;h2&gt;13. 多 GitHub 账号怎么处理&lt;/h2&gt;
&lt;p&gt;这是本地电脑最常见的进阶需求之一：工作号一个，私人号一个。&lt;/p&gt;
&lt;h3&gt;13.1 SSH 多账号方案&lt;/h3&gt;
&lt;p&gt;思路是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个账号各自一把 SSH key&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;~/.ssh/config&lt;/code&gt; 里用不同 Host 别名&lt;/li&gt;
&lt;li&gt;不同仓库绑定不同远程地址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;Host github-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal

Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后你的远程地址可以写成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;git@github-personal:yourname/repo.git
git@github-work:company/repo.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样不同仓库就会走不同密钥。&lt;/p&gt;
&lt;h3&gt;13.2 Git 身份也分仓库设置&lt;/h3&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config user.name &quot;work-account&quot;
git config user.email &quot;work@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样只影响当前仓库。&lt;/p&gt;
&lt;h2&gt;14. 最常见的报错与处理&lt;/h2&gt;
&lt;h3&gt;14.1 &lt;code&gt;Permission denied (publickey)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;通常说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;公钥没加到 GitHub&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssh-agent&lt;/code&gt; 没加载私钥&lt;/li&gt;
&lt;li&gt;仓库远程地址和你预期账号不一致&lt;/li&gt;
&lt;li&gt;你实际用了另一把 key&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优先检查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-add -l
git remote -v
ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;14.2 &lt;code&gt;Authentication failed&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;这通常发生在 HTTPS 路线下，常见原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你输入的是 GitHub 登录密码，不是 PAT&lt;/li&gt;
&lt;li&gt;PAT 权限不够&lt;/li&gt;
&lt;li&gt;PAT 已过期&lt;/li&gt;
&lt;li&gt;系统里缓存了旧凭证&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;14.3 提交记录不显示在 GitHub 账号下&lt;/h3&gt;
&lt;p&gt;通常不是权限问题，而是邮箱不匹配。&lt;/p&gt;
&lt;p&gt;检查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --get user.email
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认这个邮箱已经在 GitHub 账号中添加并验证。&lt;/p&gt;
&lt;h3&gt;14.4 推到了错误账号&lt;/h3&gt;
&lt;p&gt;通常是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统缓存了另一个账号的 PAT&lt;/li&gt;
&lt;li&gt;SSH 走了另一把 key&lt;/li&gt;
&lt;li&gt;仓库配置的 &lt;code&gt;user.name&lt;/code&gt; / &lt;code&gt;user.email&lt;/code&gt; 与预期不一致&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;15. 一条推荐工作流&lt;/h2&gt;
&lt;p&gt;如果你是“自己的电脑 + 长期使用 GitHub”，我更推荐：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;配 Git 全局身份&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;ed25519&lt;/code&gt; SSH key&lt;/li&gt;
&lt;li&gt;把公钥加到 GitHub&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;ssh-agent&lt;/code&gt; 管理私钥&lt;/li&gt;
&lt;li&gt;把远程地址改成 SSH&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;也就是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global user.name &quot;your-name&quot;
git config --global user.email &quot;your-email@example.com&quot;
ssh-keygen -t ed25519 -C &quot;your-email@example.com&quot;
ssh-add ~/.ssh/id_ed25519
ssh -T git@github.com
git remote set-url origin git@github.com:your-username/your-repo.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这套组合通常是最稳的。&lt;/p&gt;
&lt;h2&gt;16. 结语&lt;/h2&gt;
&lt;p&gt;在本地电脑上配置 GitHub 权限，本质上不是“装一个工具就完事”，而是把下面三层关系理顺：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Git 提交身份&lt;/li&gt;
&lt;li&gt;GitHub 认证方式&lt;/li&gt;
&lt;li&gt;本地凭证管理方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你希望长期稳定使用，优先选 SSH。&lt;br&gt;
如果你只是临时使用或公司流程要求 HTTPS，再用 PAT + 系统凭证管理器。&lt;/p&gt;
&lt;p&gt;只要把这套关系理顺，以后遇到 GitHub 权限问题，基本都能很快定位。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/98000873_p0_master1200.491vjnsp6s.webp"/><enclosure url="https://pic.hana0721.top/98000873_p0_master1200.491vjnsp6s.webp"/></item><item><title>用 scp 向远程服务器传送文件</title><link>https://hana-blog.top/blog/scp-upload-to-server</link><guid isPermaLink="true">https://hana-blog.top/blog/scp-upload-to-server</guid><description>一篇讲清 scp 文件上传的实用教程：基础命令、目录传输、自定义端口、SSH 密钥登录与常见报错排查。</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;scp&lt;/code&gt; 是最常见的远程传文件命令之一。&lt;br&gt;
如果你已经能通过 &lt;code&gt;ssh&lt;/code&gt; 登录服务器，那么通常也可以直接用 &lt;code&gt;scp&lt;/code&gt; 把本地文件传到远程机器上。&lt;/p&gt;
&lt;p&gt;这篇文章只解决一个实际问题：&lt;strong&gt;如何把本地文件或目录上传到远程 Linux 服务器&lt;/strong&gt;。默认场景如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地电脑：Linux / macOS / Windows（PowerShell）&lt;/li&gt;
&lt;li&gt;远程机器：Linux 服务器&lt;/li&gt;
&lt;li&gt;远程登录方式：用户名 + IP（或域名）+ SSH 端口&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. &lt;code&gt;scp&lt;/code&gt; 最基本的命令格式&lt;/h2&gt;
&lt;p&gt;上传文件的基本写法是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp 本地文件 用户名@服务器IP:远程路径
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如，把当前目录下的 &lt;code&gt;notes.txt&lt;/code&gt; 上传到服务器家目录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp notes.txt username@192.168.1.20:~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这条命令的含义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;notes.txt&lt;/code&gt;：本地文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;username&lt;/code&gt;：远程服务器用户名&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.20&lt;/code&gt;：远程服务器 IP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~&lt;/code&gt;：远程用户家目录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果服务器要求密码，执行后会提示你输入远程登录密码。&lt;/p&gt;
&lt;p&gt;如果你已经在本地 &lt;code&gt;~/.ssh/config&lt;/code&gt; 里给服务器配了 SSH 别名，那么这里也可以直接用别名替代 &lt;code&gt;username@服务器IP&lt;/code&gt;。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp notes.txt my-server:~
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 上传到指定目录&lt;/h2&gt;
&lt;p&gt;如果你想把文件上传到远程机器上的某个明确目录，可以直接写完整路径：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp notes.txt username@192.168.1.20:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再比如上传一个压缩包：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp project.tar.gz username@192.168.1.20:/home/username/projects/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建议先确认远程目录确实存在，否则可能报错：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh username@192.168.1.20 &quot;mkdir -p /home/username/projects&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 上传目录而不是单个文件&lt;/h2&gt;
&lt;p&gt;如果要上传整个文件夹，需要加 &lt;code&gt;-r&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -r my_project username@192.168.1.20:/home/username/projects/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-r&lt;/code&gt; 表示递归复制&lt;/li&gt;
&lt;li&gt;&lt;code&gt;my_project&lt;/code&gt; 可以是目录&lt;/li&gt;
&lt;li&gt;远程目标目录建议提前创建好&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果目录里文件较多，这个命令可能会执行一段时间。&lt;/p&gt;
&lt;h2&gt;4. 服务器不是 22 端口时怎么写&lt;/h2&gt;
&lt;p&gt;很多云服务器或实验室机器不会使用默认的 &lt;code&gt;22&lt;/code&gt; 端口。&lt;br&gt;
这时要用大写 &lt;code&gt;-P&lt;/code&gt; 指定端口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -P 2222 notes.txt username@192.168.1.20:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这里是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scp&lt;/code&gt; 用 &lt;code&gt;-P&lt;/code&gt; 指定端口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssh&lt;/code&gt; 通常用小写 &lt;code&gt;-p&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两个地方很容易写混。&lt;/p&gt;
&lt;h2&gt;5. 使用 SSH 密钥上传&lt;/h2&gt;
&lt;p&gt;如果你的服务器用私钥登录，可以用 &lt;code&gt;-i&lt;/code&gt; 指定私钥文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -i ~/.ssh/id_ed25519 notes.txt username@192.168.1.20:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果服务器端口也不是默认值，可以一起写：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -P 2222 -i ~/.ssh/id_ed25519 notes.txt username@192.168.1.20:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你已经在 &lt;code&gt;~/.ssh/config&lt;/code&gt; 里配置了 SSH Host 别名，那么命令可以进一步简化。&lt;/p&gt;
&lt;p&gt;例如配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;Host my-server
  HostName 192.168.1.20
  User username
  Port 2222
  IdentityFile ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后就可以直接写：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp notes.txt my-server:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个写法在日常使用里通常最省事。&lt;/p&gt;
&lt;h2&gt;6. Windows / macOS / Linux 上是否都能用&lt;/h2&gt;
&lt;h3&gt;Linux / macOS&lt;/h3&gt;
&lt;p&gt;终端里通常直接可用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -V
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Windows&lt;/h3&gt;
&lt;p&gt;Windows 10 / 11 一般自带 OpenSSH Client，可以在 PowerShell 里直接执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;scp .\notes.txt username@192.168.1.20:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果提示 &lt;code&gt;scp&lt;/code&gt; 不是内部或外部命令，先检查 OpenSSH Client 是否已安装。&lt;/p&gt;
&lt;h2&gt;7. 文件名带空格时怎么处理&lt;/h2&gt;
&lt;p&gt;如果本地文件名里有空格，建议加引号：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp &quot;my notes.txt&quot; username@192.168.1.20:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果远程目录路径里有空格，也建议整体加引号：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp notes.txt &quot;username@192.168.1.20:/home/username/my uploads/&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最稳妥的做法仍然是：&lt;strong&gt;文件名和目录名尽量避免空格&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;8. 如何确认上传成功&lt;/h2&gt;
&lt;p&gt;上传完成后，可以登录服务器检查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh username@192.168.1.20
ls -lh /home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以直接一条命令验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh username@192.168.1.20 &quot;ls -lh /home/username/uploads/&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你上传的是目录，也可以用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh username@192.168.1.20 &quot;du -sh /home/username/projects/my_project&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;9. 常见报错与处理&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;ssh: connect to host ... port ...: Connection refused&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;通常说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IP 或域名写错&lt;/li&gt;
&lt;li&gt;端口写错&lt;/li&gt;
&lt;li&gt;远程服务器 SSH 服务没开&lt;/li&gt;
&lt;li&gt;防火墙没有放行对应端口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优先检查你是否能先正常执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh username@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -p 2222 username@192.168.1.20
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;Permission denied&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;通常有两种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户名、密码或私钥不对&lt;/li&gt;
&lt;li&gt;你没有目标目录的写权限&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先确认登录是否正常，再确认上传目标是否是你有权限写入的位置，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/home/username/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/home/username/uploads/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/projects/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不要一开始就传到 &lt;code&gt;/root/&lt;/code&gt; 或系统目录。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;No such file or directory&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;通常表示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地文件路径写错&lt;/li&gt;
&lt;li&gt;远程目标目录不存在&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;处理方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地先 &lt;code&gt;ls&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;远程先 &lt;code&gt;ssh&lt;/code&gt; 登录确认目录&lt;/li&gt;
&lt;li&gt;需要时先执行 &lt;code&gt;mkdir -p&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;10. 实用命令速查&lt;/h2&gt;
&lt;p&gt;上传单个文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp file.txt username@server:~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上传到指定目录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp file.txt username@server:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上传整个目录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -r mydir username@server:/home/username/projects/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定端口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -P 2222 file.txt username@server:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定私钥：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp -i ~/.ssh/id_ed25519 file.txt username@server:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 SSH Host 别名：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp file.txt my-server:/home/username/uploads/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;11. 什么时候不建议继续用 &lt;code&gt;scp&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;scp&lt;/code&gt; 很适合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;临时上传文件&lt;/li&gt;
&lt;li&gt;上传脚本、压缩包、配置文件&lt;/li&gt;
&lt;li&gt;快速把本地项目丢到服务器上&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但如果你要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;长时间断点续传&lt;/li&gt;
&lt;li&gt;大量文件同步&lt;/li&gt;
&lt;li&gt;频繁增量更新目录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么通常更推荐 &lt;code&gt;rsync&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;12. 一句话总结&lt;/h2&gt;
&lt;p&gt;记住这一条就够你先用起来：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;scp 本地文件 用户名@服务器IP:远程路径
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果再多记两个参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-r&lt;/code&gt;：传目录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-P&lt;/code&gt;：指定端口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大多数 &lt;code&gt;scp&lt;/code&gt; 上传场景就已经能覆盖了。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/93990522_p0_master1200.et40soxif.webp"/><enclosure url="https://pic.hana0721.top/93990522_p0_master1200.et40soxif.webp"/></item><item><title>纯 SSH 服务器配置 RustDesk（命令行方案）</title><link>https://hana-blog.top/blog/rustdesk-cli-server-setup</link><guid isPermaLink="true">https://hana-blog.top/blog/rustdesk-cli-server-setup</guid><description>面向只有 SSH 的远程 Linux 服务器：从补桌面、安装 RustDesk，到设置固定密码、自建服务端配置与常见坑排查。</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果你现在只有一台远程 Linux 服务器的 SSH 权限，想把它接入 RustDesk，先记住一个关键前提：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;只有命令行可以操作&lt;/strong&gt;，不等于&lt;strong&gt;这台机器没有图形桌面&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;如果机器真的只有纯终端、没有图形会话，RustDesk 也不能凭空远控出一个“桌面”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;RustDesk 官方 Linux 文档明确提到两件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux 端优先使用发行版原生包安装&lt;/li&gt;
&lt;li&gt;登录界面远控仍然要求 &lt;code&gt;X11&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外，RustDesk 官方仓库的 Headless Linux 说明页提到：Linux headless 支持默认关闭，只在 Ubuntu 上做过测试，并要求 GNOME、&lt;code&gt;xserver-xorg-video-dummy&lt;/code&gt; 和 &lt;code&gt;lightdm&lt;/code&gt; 这类前置条件。&lt;/p&gt;
&lt;p&gt;所以这篇文章分两种情况写：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;服务器本来就有桌面，只是你现在只能 SSH 上去&lt;/li&gt;
&lt;li&gt;服务器真的没有桌面，需要先补一个最小可远控的图形环境&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文主线以 Ubuntu / Debian 为例。&lt;/p&gt;
&lt;h2&gt;0. 先判断你是哪一种机器&lt;/h2&gt;
&lt;p&gt;如果下面命令里能看到 GNOME、Xorg、display manager 之类的包，通常说明机器已经有图形环境：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;dpkg -l | rg &apos;ubuntu-desktop|gnome-shell|xorg|gdm3|lightdm&apos;
systemctl get-default
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果输出里已经有桌面组件，可以直接跳到“安装 RustDesk”&lt;/li&gt;
&lt;li&gt;如果基本什么都没有，而且默认 target 是 &lt;code&gt;multi-user.target&lt;/code&gt;，按“先补桌面”处理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. 机器完全没有桌面时：先补图形环境&lt;/h2&gt;
&lt;p&gt;如果你的远程机本质上是一台“纯 CLI 服务器”，先装一套 RustDesk 更稳的官方建议组合。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update
sudo apt install -y ubuntu-desktop xserver-xorg-video-dummy lightdm
sudo systemctl set-default graphical.target
sudo systemctl enable lightdm
sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一步的作用分别是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ubuntu-desktop&lt;/code&gt;：提供 GNOME 桌面&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xserver-xorg-video-dummy&lt;/code&gt;：没有物理显示器时，提供 dummy display&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lightdm&lt;/code&gt;：给无头场景一个更稳定的图形登录管理器&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这里我遵循 RustDesk 官方 headless 说明页的口径，优先写 GNOME + dummy driver + &lt;code&gt;lightdm&lt;/code&gt;。&lt;br&gt;
如果你打算用更轻的 &lt;code&gt;xfce4&lt;/code&gt;，社区里有人跑通，但官方页只明确写了 GNOME，稳定性就别混着赌。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;2. 安装 RustDesk&lt;/h2&gt;
&lt;p&gt;RustDesk 官方文档在 Linux 上推荐优先用发行版原生包：Ubuntu / Debian 用 &lt;code&gt;.deb&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;下面这段命令会自动识别架构，并从 RustDesk GitHub 最新 release 拉对应的 Debian 包：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/downloads/rustdesk
cd ~/downloads/rustdesk

ARCH=&quot;$(dpkg --print-architecture)&quot;
if [ &quot;$ARCH&quot; = &quot;amd64&quot; ]; then
  PATTERN=&apos;rustdesk-[^&quot;]*-x86_64\.deb&apos;
elif [ &quot;$ARCH&quot; = &quot;arm64&quot; ]; then
  PATTERN=&apos;rustdesk-[^&quot;]*-aarch64\.deb&apos;
else
  echo &quot;Unsupported arch: $ARCH&quot;
  exit 1
fi

URL=&quot;$(curl -fsSL https://api.github.com/repos/rustdesk/rustdesk/releases/latest \
  | grep -oE &quot;https://[^\&quot;]+${PATTERN}&quot; \
  | head -n 1)&quot;

if [ -z &quot;$URL&quot; ]; then
  echo &quot;Failed to resolve RustDesk release url&quot;
  exit 1
fi

curl -L &quot;$URL&quot; -o rustdesk.deb
sudo apt install -fy ./rustdesk.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rustdesk --version
systemctl status rustdesk --no-pager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;systemctl status rustdesk&lt;/code&gt; 提示服务不存在，也不用慌，先试：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl enable --now rustdesk
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 开启 headless 选项、设置固定密码、拿到 ID&lt;/h2&gt;
&lt;p&gt;如果这台机子没有物理显示器，或者你就是想让它长期作为“无人值守远控节点”，把下面三步做完：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo rustdesk --option allow-linux-headless Y
sudo rustdesk --password &apos;YourStrongPassword123&apos;
sudo rustdesk --get-id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你真正要记住的是两样东西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RustDesk ID&lt;/li&gt;
&lt;li&gt;你刚设置的固定密码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后在你本地电脑上的 RustDesk 客户端里，输入这个 ID 和密码即可连接。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;allow-linux-headless&lt;/code&gt; 是 RustDesk 官方 headless Linux 说明页明确要求开启的选项。&lt;br&gt;
如果你的机器本来就有显示器和完整桌面，这一步通常不是必须，但开了也没坏处。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;4. 如果你想重启后还能连登录界面：切到 X11&lt;/h2&gt;
&lt;p&gt;RustDesk 官方 Linux 文档写得很明确：&lt;strong&gt;登录界面远控仍然需要 X11&lt;/strong&gt;。&lt;br&gt;
也就是说，哪怕新版本已经有实验性的 Wayland 支持，登录界面这块仍然不要指望 Wayland。&lt;/p&gt;
&lt;p&gt;如果你装的是 &lt;code&gt;gdm3&lt;/code&gt;，直接改：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/gdm3/custom.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把这一行改成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;WaylandEnable=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存后重启：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你前面按本文装的是 &lt;code&gt;lightdm&lt;/code&gt;，通常会比 &lt;code&gt;gdm3 + Wayland&lt;/code&gt; 少一层坑。&lt;/p&gt;
&lt;h2&gt;5. 如果你用的是自建 RustDesk Server&lt;/h2&gt;
&lt;p&gt;如果你不是走 RustDesk 官方公共服务器，而是用自己的 &lt;code&gt;hbbs&lt;/code&gt; / &lt;code&gt;hbbr&lt;/code&gt;，CLI 场景下最省事的方式不是手改 GUI，而是直接注入配置字符串。&lt;/p&gt;
&lt;p&gt;RustDesk 官方部署文档里的 Linux 脚本就是这么做的：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;RUSTDESK_CFG=&apos;你的 config string&apos;
sudo rustdesk --config &quot;$RUSTDESK_CFG&quot;
sudo systemctl restart rustdesk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后再重新确认：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rustdesk --get-id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里有两个实用建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;config string&lt;/code&gt; 最好从你已经配好的 RustDesk 客户端或管理端导出，避免手敲 &lt;code&gt;ID Server&lt;/code&gt; / &lt;code&gt;Relay Server&lt;/code&gt; / &lt;code&gt;Key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;先执行 &lt;code&gt;--config&lt;/code&gt;，再执行 &lt;code&gt;systemctl restart rustdesk&lt;/code&gt;，不要反过来&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你暂时还没有现成的 config string，可以先用 RustDesk 官方公共服务器把链路打通，再切到自建服务。&lt;/p&gt;
&lt;h2&gt;6. 一套可直接复制的最小闭环&lt;/h2&gt;
&lt;p&gt;如果你的目标只是“把一台只有 SSH 的 Ubuntu 服务器接进 RustDesk”，最短路径就是下面这套：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update
sudo apt install -y ubuntu-desktop xserver-xorg-video-dummy lightdm
sudo systemctl set-default graphical.target
sudo systemctl enable lightdm
sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重连 SSH 后继续：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/downloads/rustdesk
cd ~/downloads/rustdesk

ARCH=&quot;$(dpkg --print-architecture)&quot;
if [ &quot;$ARCH&quot; = &quot;amd64&quot; ]; then
  PATTERN=&apos;rustdesk-[^&quot;]*-x86_64\.deb&apos;
elif [ &quot;$ARCH&quot; = &quot;arm64&quot; ]; then
  PATTERN=&apos;rustdesk-[^&quot;]*-aarch64\.deb&apos;
else
  echo &quot;Unsupported arch: $ARCH&quot;
  exit 1
fi

URL=&quot;$(curl -fsSL https://api.github.com/repos/rustdesk/rustdesk/releases/latest \
  | grep -oE &quot;https://[^\&quot;]+${PATTERN}&quot; \
  | head -n 1)&quot;

curl -L &quot;$URL&quot; -o rustdesk.deb
sudo apt install -fy ./rustdesk.deb

sudo systemctl enable --now rustdesk
sudo rustdesk --option allow-linux-headless Y
sudo rustdesk --password &apos;YourStrongPassword123&apos;
sudo rustdesk --get-id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到这里，这台服务器就已经具备“被 RustDesk 连接”的最小条件了。&lt;/p&gt;
&lt;h2&gt;7. 常见问题&lt;/h2&gt;
&lt;h3&gt;7.1 &lt;code&gt;rustdesk --get-id&lt;/code&gt; 没输出&lt;/h3&gt;
&lt;p&gt;优先检查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl status rustdesk --no-pager
journalctl -u rustdesk -n 100 --no-pager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;常见原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RustDesk 服务没起来&lt;/li&gt;
&lt;li&gt;机器外网不通，无法连到官方服务器或你的自建 &lt;code&gt;hbbs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;你切了自建服务，但没执行 &lt;code&gt;rustdesk --config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.2 能连上，但黑屏&lt;/h3&gt;
&lt;p&gt;这类问题大概率不是 RustDesk “安装失败”，而是&lt;strong&gt;没有可用图形会话&lt;/strong&gt;。按这个顺序检查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;有没有安装桌面环境&lt;/li&gt;
&lt;li&gt;有没有安装 &lt;code&gt;xserver-xorg-video-dummy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当前是不是 &lt;code&gt;graphical.target&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;登录界面是不是还在 Wayland&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7.3 重启后能 SSH，但 RustDesk 进不去登录界面&lt;/h3&gt;
&lt;p&gt;优先怀疑两件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没切到 &lt;code&gt;graphical.target&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;登录管理器还在 Wayland&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，下面两条至少要成立：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl get-default
grep -n &apos;WaylandEnable&apos; /etc/gdm3/custom.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.4 这台机器根本不需要桌面，我还该不该装 RustDesk？&lt;/h3&gt;
&lt;p&gt;如果你只是想：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;跑训练&lt;/li&gt;
&lt;li&gt;部署服务&lt;/li&gt;
&lt;li&gt;改代码&lt;/li&gt;
&lt;li&gt;看日志&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那其实 &lt;code&gt;SSH + tmux + VSCode Remote SSH&lt;/code&gt; 往往更合适。&lt;br&gt;
RustDesk 真正擅长的是“图形桌面远控”，不是替代终端运维。&lt;/p&gt;
&lt;h2&gt;8. 参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;RustDesk Linux 文档：&lt;a href=&quot;https://rustdesk.com/docs/en/client/linux/&quot;&gt;rustdesk.com/docs/en/client/linux/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RustDesk Client Deployment（Linux &lt;code&gt;--config&lt;/code&gt; 脚本）：
&lt;a href=&quot;https://rustdesk.com/docs/en/self-host/client-deployment/&quot;&gt;rustdesk.com/docs/en/self-host/client-deployment/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RustDesk Headless Linux Support（官方仓库 Wiki）：
&lt;a href=&quot;https://github.com/rustdesk/rustdesk/wiki/Headless-Linux-Support&quot;&gt;github.com/rustdesk/rustdesk/wiki/Headless-Linux-Support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://pic.hana0721.top/91341540_p0_master1200.7pw5duffp.webp"/><enclosure url="https://pic.hana0721.top/91341540_p0_master1200.7pw5duffp.webp"/></item><item><title>Vibe Coding For AI 2: Claude CLI 安装与使用指南</title><link>https://hana-blog.top/blog/vibe-coding-2</link><guid isPermaLink="true">https://hana-blog.top/blog/vibe-coding-2</guid><description>面向 macOS 和 Linux 用户的 Claude CLI 完整教程，从安装到熟练使用，覆盖核心功能与常用指令。</description><pubDate>Sat, 18 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文内容由 AI 生成，主要供博主自用参考，不保证完全准确，请以官方文档为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;什么是 Claude CLI？&lt;/h2&gt;
&lt;p&gt;Claude CLI（也叫 Claude Code）是 Anthropic 官方推出的命令行 AI 编程助手。
和 IDE 插件不同，它直接运行在终端里，能够读取、修改你的代码文件，执行命令，甚至帮你管理 Git 工作流。
对于习惯在终端工作的开发者来说，这是目前体验最好的 Coding Agent 之一。&lt;/p&gt;
&lt;p&gt;本文面向 macOS 和 Linux 用户，带你从零开始安装并熟练使用 Claude CLI。&lt;/p&gt;
&lt;h2&gt;安装前准备&lt;/h2&gt;
&lt;h3&gt;安装 Node.js&lt;/h3&gt;
&lt;p&gt;Claude CLI 通过 &lt;code&gt;npm&lt;/code&gt; 分发，所以首先需要确保系统上有 Node.js（版本 &gt;= 18）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS（推荐用 Homebrew）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Linux（Ubuntu/Debian）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Linux（Arch）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo pacman -S nodejs npm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node --version   # 应该 &gt;= 18
npm --version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;检查网络环境&lt;/h3&gt;
&lt;p&gt;Claude CLI 需要访问 Anthropic 的 API，国内用户需要确保终端走代理。
可以用以下命令检查当前 IP：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl ipin.io
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果返回的是国内 IP，需要先配置好终端代理再继续。&lt;/p&gt;
&lt;h2&gt;安装 Claude CLI&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;claude --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;出现版本号即为安装成功。&lt;/p&gt;
&lt;h2&gt;配置 API Key&lt;/h2&gt;
&lt;h3&gt;获取 API Key&lt;/h3&gt;
&lt;p&gt;前往 &lt;a href=&quot;https://console.anthropic.com/&quot;&gt;Anthropic Console&lt;/a&gt; 注册账号，进入 &lt;code&gt;API Keys&lt;/code&gt; 页面创建一个新的密钥。&lt;/p&gt;
&lt;p&gt;如果不想直接使用官方 API（价格较贵），也可以使用 &lt;a href=&quot;https://api.ikuncode.cc/&quot;&gt;Ikuncode&lt;/a&gt; 等中转服务，配置方式相同。&lt;/p&gt;
&lt;h3&gt;设置环境变量&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;临时设置（当前终端会话有效）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export ANTHROPIC_API_KEY=&quot;sk-ant-xxxxxxxx&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;永久设置（推荐）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;macOS/Linux 用户将以下内容添加到 &lt;code&gt;~/.zshrc&lt;/code&gt; 或 &lt;code&gt;~/.bashrc&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export ANTHROPIC_API_KEY=&quot;sk-ant-xxxxxxxx&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后重新加载配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;source ~/.zshrc   # 或 source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;使用中转 API&lt;/h3&gt;
&lt;p&gt;如果使用第三方中转服务，还需要额外设置 API 地址：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export ANTHROPIC_BASE_URL=&quot;https://你的中转地址/v1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;启动 Claude CLI&lt;/h2&gt;
&lt;p&gt;进入你的项目目录，直接运行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /path/to/your/project
claude
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首次启动会进入一个交互式 TUI 界面，类似下图的对话框。
你可以直接用自然语言描述你想做的事，Claude 会自动读取项目文件、执行命令、修改代码。&lt;/p&gt;
&lt;h2&gt;核心使用模式&lt;/h2&gt;
&lt;h3&gt;交互模式（默认）&lt;/h3&gt;
&lt;p&gt;直接运行 &lt;code&gt;claude&lt;/code&gt; 进入对话界面，这是最常用的模式。
在这里你可以持续对话，Claude 会记住整个会话的上下文。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;claude
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;单次执行模式&lt;/h3&gt;
&lt;p&gt;如果只想执行一个任务，不需要进入交互界面：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;claude -p &quot;帮我写一个读取 CSV 文件的 Python 函数&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-p&lt;/code&gt; 是 &lt;code&gt;--print&lt;/code&gt; 的缩写，执行完毕后直接退出，适合脚本集成。&lt;/p&gt;
&lt;h3&gt;管道模式&lt;/h3&gt;
&lt;p&gt;Claude CLI 支持从标准输入读取内容，非常适合配合其他命令使用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat error.log | claude -p &quot;分析这个错误日志，告诉我问题在哪里&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git diff | claude -p &quot;帮我写一个 commit message&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常用斜杠命令&lt;/h2&gt;
&lt;p&gt;在交互模式下，输入 &lt;code&gt;/&lt;/code&gt; 可以触发内置命令。&lt;/p&gt;
&lt;h3&gt;会话管理&lt;/h3&gt;
&lt;p&gt;| 命令 | 功能 |
|------|------|
| &lt;code&gt;/help&lt;/code&gt; | 查看所有可用命令及说明 |
| &lt;code&gt;/clear&lt;/code&gt; | 清空当前对话上下文，重新开始 |
| &lt;code&gt;/compact [说明]&lt;/code&gt; | 压缩对话历史节省 token，可附加保留说明 |
| &lt;code&gt;/cost&lt;/code&gt; | 查看当前会话的 token 消耗和费用估算 |
| &lt;code&gt;/quit&lt;/code&gt; 或 &lt;code&gt;/exit&lt;/code&gt; | 退出 Claude CLI |&lt;/p&gt;
&lt;h3&gt;模型与配置&lt;/h3&gt;
&lt;p&gt;| 命令 | 功能 |
|------|------|
| &lt;code&gt;/model&lt;/code&gt; | 交互式切换当前使用的模型 |
| &lt;code&gt;/doctor&lt;/code&gt; | 检查环境配置、API 连通性是否正常 |
| &lt;code&gt;/init&lt;/code&gt; | 在当前项目生成 &lt;code&gt;CLAUDE.md&lt;/code&gt; 配置文件 |
| &lt;code&gt;/config&lt;/code&gt; | 查看或修改 Claude CLI 的配置项 |
| &lt;code&gt;/vim&lt;/code&gt; | 切换到 Vim 键位模式（再次输入关闭） |&lt;/p&gt;
&lt;h3&gt;记忆与上下文&lt;/h3&gt;
&lt;p&gt;| 命令 | 功能 |
|------|------|
| &lt;code&gt;/memory&lt;/code&gt; | 查看和管理跨会话的持久记忆内容 |
| &lt;code&gt;/context&lt;/code&gt; | 查看当前上下文窗口的使用情况 |&lt;/p&gt;
&lt;h3&gt;代码与项目&lt;/h3&gt;
&lt;p&gt;| 命令 | 功能 |
|------|------|
| &lt;code&gt;/review&lt;/code&gt; | 对当前 git 改动进行代码审查 |
| &lt;code&gt;/pr_comments&lt;/code&gt; | 拉取并展示当前 PR 的评论 |
| &lt;code&gt;/bug&lt;/code&gt; | 快速进入 bug 报告模式，引导描述问题 |
| &lt;code&gt;/terminal-setup&lt;/code&gt; | 配置终端 shell 集成（如 iTerm2 / zsh） |&lt;/p&gt;
&lt;h3&gt;权限管理&lt;/h3&gt;
&lt;p&gt;| 命令 | 功能 |
|------|------|
| &lt;code&gt;/permissions&lt;/code&gt; | 查看当前会话的工具权限设置 |
| &lt;code&gt;/allowed-tools&lt;/code&gt; | 列出当前允许自动执行的工具列表 |&lt;/p&gt;
&lt;h3&gt;自定义 Slash 命令&lt;/h3&gt;
&lt;p&gt;除了内置命令，你还可以在 &lt;code&gt;.claude/commands/&lt;/code&gt; 目录下创建自定义命令。
每个 &lt;code&gt;.md&lt;/code&gt; 文件对应一个命令，文件名即命令名：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.claude/
└── commands/
    ├── deploy.md      # /deploy
    └── test-all.md    # /test-all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;文件内容就是发给 Claude 的 prompt 模板，支持 &lt;code&gt;$ARGUMENTS&lt;/code&gt; 占位符接收参数：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# deploy.md
运行 `bun build` 构建项目，然后执行 `$ARGUMENTS` 部署到对应环境。
部署前先确认没有未提交的改动。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用时：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/deploy production
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CLAUDE.md：项目级配置&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/init&lt;/code&gt; 命令会在项目根目录生成一个 &lt;code&gt;CLAUDE.md&lt;/code&gt; 文件，这是 Claude 的&quot;项目说明书&quot;。
你可以在里面写：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;项目架构说明&lt;/li&gt;
&lt;li&gt;常用命令（如何启动、测试、构建）&lt;/li&gt;
&lt;li&gt;代码规范和约定&lt;/li&gt;
&lt;li&gt;不希望 Claude 修改的文件或目录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每次启动 Claude CLI，它都会自动读取这个文件，从而更好地理解你的项目。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# CLAUDE.md 示例

## 项目说明
这是一个 FastAPI 后端项目，使用 PostgreSQL 数据库。

## 常用命令
- `uvicorn main:app --reload` - 启动开发服务器
- `pytest` - 运行测试
- `alembic upgrade head` - 执行数据库迁移

## 注意事项
- 不要修改 `migrations/` 目录下的文件
- 所有 API 接口需要添加类型注解
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;权限控制&lt;/h2&gt;
&lt;p&gt;Claude CLI 在执行文件操作或运行命令时，默认会弹出确认提示。
你可以通过以下方式调整权限策略：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;自动允许所有操作（谨慎使用）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;claude --dangerously-skip-permissions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;只允许读取，不允许写入：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在对话中告诉 Claude &quot;只读模式，不要修改任何文件&quot; 即可，它会遵守这个约束。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;针对特定工具设置权限：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;CLAUDE.md&lt;/code&gt; 或项目的 &lt;code&gt;.claude/settings.json&lt;/code&gt; 中可以精细控制哪些操作自动允许、哪些需要确认。&lt;/p&gt;
&lt;h2&gt;实用技巧&lt;/h2&gt;
&lt;h3&gt;多文件操作&lt;/h3&gt;
&lt;p&gt;Claude CLI 可以同时处理多个文件，直接描述需求即可：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;帮我把项目里所有的 console.log 替换成 logger.info，
并且确保 logger 已经在每个文件里正确导入
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;结合 Git 工作流&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;帮我查看最近 5 个 commit 的改动，
然后写一个总结性的 release note
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;当前分支有哪些改动还没有提交？帮我整理成一个 commit
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;代码审查&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;帮我审查 src/auth.py 这个文件，
重点关注安全漏洞和性能问题
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;调试辅助&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python main.py 2&gt;&amp;#x26;1 | claude -p &quot;程序报错了，帮我分析原因并给出修复方案&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q: 提示 &lt;code&gt;command not found: claude&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;检查 npm 全局安装路径是否在 PATH 里：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm config get prefix
# 将输出的路径加上 /bin 添加到 PATH
export PATH=&quot;$(npm config get prefix)/bin:$PATH&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Q: API 请求失败，提示 401&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;检查 &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; 是否正确设置，可以用以下命令确认：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo $ANTHROPIC_API_KEY
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Q: 响应很慢或超时&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;检查网络代理是否正常，确保终端流量走代理：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export https_proxy=http://127.0.0.1:7890
export http_proxy=http://127.0.0.1:7890
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Q: 如何更新到最新版本&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm update -g @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;Claude CLI 的核心优势在于它能直接操作你的代码库，而不只是给你看代码片段。
配置好之后，日常的重构、调试、写测试、整理 commit 这些事情都可以交给它来做，
你只需要用自然语言描述目标就好。&lt;/p&gt;
&lt;p&gt;如果你之前用过 Cursor 或者 GitHub Copilot，Claude CLI 更像是一个能主动干活的同事，
而不只是一个代码补全工具。上手之后，工作流会有质的变化。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/104786218_p0_master1200.8vnievbkwk.webp"/><enclosure url="https://pic.hana0721.top/104786218_p0_master1200.8vnievbkwk.webp"/></item><item><title>Vibe Coding For AI 3: Codex CLI 安装与使用指南</title><link>https://hana-blog.top/blog/vibe-coding-3</link><guid isPermaLink="true">https://hana-blog.top/blog/vibe-coding-3</guid><description>面向 macOS 和 Linux 用户的 Codex CLI 上手教程，覆盖安装、登录与常用命令说明。</description><pubDate>Sat, 18 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文内容由 AI 生成，主要供博主自用参考，不保证完全准确，请以官方文档为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;什么是 Codex CLI？&lt;/h2&gt;
&lt;p&gt;Codex CLI 是 OpenAI 推出的命令行 Coding Agent。
它可以直接在终端中读取和修改项目文件、执行命令、完成代码任务，也支持非交互模式用于脚本化流程。&lt;/p&gt;
&lt;p&gt;本文基于 &lt;code&gt;codex-cli 0.121.0&lt;/code&gt; 编写，面向 macOS 和 Linux 用户。&lt;/p&gt;
&lt;h2&gt;安装前准备&lt;/h2&gt;
&lt;h3&gt;安装 Node.js&lt;/h3&gt;
&lt;p&gt;Codex CLI 通过 &lt;code&gt;npm&lt;/code&gt; 分发，先确保系统有 Node.js。
当前 &lt;code&gt;@openai/codex&lt;/code&gt; 的 &lt;code&gt;engines&lt;/code&gt; 要求是 &lt;code&gt;node &gt;= 16&lt;/code&gt;，建议使用更高版本（如 Node 20 LTS）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS（Homebrew）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ubuntu/Debian：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Arch：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo pacman -S nodejs npm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证安装：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node --version
npm --version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;检查网络环境&lt;/h3&gt;
&lt;p&gt;如果终端无法正常访问外网，安装和登录都可能失败。
可以先测试当前出口 IP：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl ipin.io
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装 Codex CLI&lt;/h2&gt;
&lt;h3&gt;方式 1：npm 安装（推荐）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g @openai/codex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证是否安装成功：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;出现版本号即为成功。&lt;/p&gt;
&lt;h3&gt;方式 2：Homebrew 安装（可选）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install --cask codex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这条路径适合习惯用 Homebrew 管理工具的用户，但版本更新节奏可能和 npm 不一致。&lt;/p&gt;
&lt;h2&gt;登录与鉴权&lt;/h2&gt;
&lt;p&gt;Codex CLI 需要先登录才能使用。&lt;/p&gt;
&lt;h3&gt;交互登录&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看登录状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex login status
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;使用 API Key 登录&lt;/h3&gt;
&lt;p&gt;如果你已有 API Key，可以通过 stdin 方式登录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export OPENAI_API_KEY=&quot;sk-xxxx&quot;
printenv OPENAI_API_KEY | codex login --with-api-key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退出登录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex logout
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;启动方式&lt;/h2&gt;
&lt;h3&gt;交互模式（最常用）&lt;/h3&gt;
&lt;p&gt;进入项目目录后直接运行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /path/to/your/project
codex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以在启动时附带首条需求：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex &quot;帮我先梳理这个仓库的目录结构，然后给出重构计划&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;非交互模式（自动化）&lt;/h3&gt;
&lt;p&gt;适合 CI、脚本、批处理任务：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex exec &quot;检查 src 下的 TypeScript 类型错误并给出修复建议&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;管道模式（stdin）&lt;/h3&gt;
&lt;p&gt;把其他命令输出直接喂给 Codex：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git diff | codex exec &quot;根据这份改动写一个规范的 commit message&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常用子命令速查&lt;/h2&gt;
&lt;p&gt;| 命令 | 作用 |
|------|------|
| &lt;code&gt;codex&lt;/code&gt; | 启动交互式 TUI |
| &lt;code&gt;codex exec&lt;/code&gt; | 非交互执行一次任务 |
| &lt;code&gt;codex review&lt;/code&gt; | 非交互代码审查 |
| &lt;code&gt;codex login&lt;/code&gt; | 登录管理 |
| &lt;code&gt;codex logout&lt;/code&gt; | 清除本地登录凭据 |
| &lt;code&gt;codex resume&lt;/code&gt; | 恢复历史会话 |
| &lt;code&gt;codex fork&lt;/code&gt; | 从历史会话分叉新会话 |
| &lt;code&gt;codex apply &amp;#x3C;TASK_ID&gt;&lt;/code&gt; | 应用某个任务产出的 diff |
| &lt;code&gt;codex mcp&lt;/code&gt; | 管理 MCP 服务 |
| &lt;code&gt;codex completion&lt;/code&gt; | 生成 shell 补全脚本 |
| &lt;code&gt;codex sandbox&lt;/code&gt; | 在 Codex 提供的沙箱中运行命令 |&lt;/p&gt;
&lt;h2&gt;高频命令示例&lt;/h2&gt;
&lt;h3&gt;1) 审查当前未提交改动&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex review --uncommitted
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) 和指定基线分支做审查&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex review --base main
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) 继续最近一次会话&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex resume --last
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) 在指定目录启动&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex -C /path/to/repo
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) 指定模型启动&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex -m gpt-5.4
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) 开启 web search 能力&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex --search
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常见运行参数说明&lt;/h2&gt;
&lt;h3&gt;沙箱与安全&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-s, --sandbox read-only|workspace-write|danger-full-access&lt;/code&gt;
控制命令执行权限范围。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--full-auto&lt;/code&gt;
低摩擦自动执行（默认沙箱是 &lt;code&gt;workspace-write&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--dangerously-bypass-approvals-and-sandbox&lt;/code&gt;
跳过审批与沙箱，风险极高，只建议在外部已隔离环境使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;上下文与目录&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-C, --cd &amp;#x3C;DIR&gt;&lt;/code&gt;
指定工作目录。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--add-dir &amp;#x3C;DIR&gt;&lt;/code&gt;
给当前会话增加额外可写目录。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;模型与配置&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-m, --model &amp;#x3C;MODEL&gt;&lt;/code&gt;
指定模型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p, --profile &amp;#x3C;CONFIG_PROFILE&gt;&lt;/code&gt;
使用配置文件中的 profile。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c, --config key=value&lt;/code&gt;
临时覆写 &lt;code&gt;~/.codex/config.toml&lt;/code&gt; 的配置项。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Shell 补全（可选）&lt;/h2&gt;
&lt;p&gt;以 &lt;code&gt;zsh&lt;/code&gt; 为例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex completion zsh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以把输出重定向到你的补全目录并在 shell 配置中加载，之后 Tab 补全会更顺手。&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q: 提示 &lt;code&gt;command not found: codex&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;检查 npm 全局 bin 目录是否在 PATH 中：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm config get prefix
export PATH=&quot;$(npm config get prefix)/bin:$PATH&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Q: 如何升级 Codex CLI？&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm update -g @openai/codex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Q: 如何确认当前有哪些命令可用？&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;codex --help
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;Codex CLI 的核心价值是把 AI 代理能力直接带到终端工作流里。
如果你习惯命令行开发，先掌握这三件事就够用了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;codex&lt;/code&gt;（交互）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;codex exec&lt;/code&gt;（自动化）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;codex review&lt;/code&gt;（审查改动）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;剩下的能力，再根据项目需求逐步加上就行。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/120188807_p0_master1200.1lcf3tjzx5.webp"/><enclosure url="https://pic.hana0721.top/120188807_p0_master1200.1lcf3tjzx5.webp"/></item><item><title>Vibe Coding For AI 4: Gemini CLI 安装与使用指南</title><link>https://hana-blog.top/blog/vibe-coding-4</link><guid isPermaLink="true">https://hana-blog.top/blog/vibe-coding-4</guid><description>谷歌官方出品的 Gemini CLI 完整教程，探索其强大的上下文感知、自动任务执行与多模态交互能力。</description><pubDate>Sat, 18 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文内容由 AI 生成，主要供博主自用参考，不保证完全准确，请以官方文档为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;什么是 Gemini CLI？&lt;/h2&gt;
&lt;p&gt;Gemini CLI 是 Google 官方推出的命令行 AI 助手。
它不仅是一个简单的聊天界面，更是一个深度集成到开发流程中的 Coding Agent。
它能够感知项目上下文，读取和修改文件，执行 Shell 命令，并支持通过 Agent Skills 和 MCP（Model Context Protocol）进行扩展。&lt;/p&gt;
&lt;p&gt;对于追求极致性能和谷歌生态集成的开发者来说，Gemini CLI 是 Vibe Coding 的又一利器。&lt;/p&gt;
&lt;h2&gt;安装前准备&lt;/h2&gt;
&lt;h3&gt;安装 Node.js&lt;/h3&gt;
&lt;p&gt;Gemini CLI 要求 &lt;strong&gt;Node.js 20.0.0+&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS（Homebrew）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Linux（Ubuntu/Debian）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://deb.nodesource.com/setup_23.x | sudo -E bash -
sudo apt-get install -y nodejs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;验证安装：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node --version   # 确保版本 &gt;= 20
npm --version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;检查网络环境&lt;/h3&gt;
&lt;p&gt;确保你的终端可以顺畅访问 Google 的 API 服务。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl ipin.io
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装 Gemini CLI&lt;/h2&gt;
&lt;h3&gt;推荐方式：npm 全局安装&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g @google/gemini-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;其他方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Homebrew:&lt;/strong&gt; &lt;code&gt;brew install gemini-cli&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;npx (无需安装直接运行):&lt;/strong&gt; &lt;code&gt;npx @google/gemini-cli&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安装完成后验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gemini --version
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;配置与鉴权&lt;/h2&gt;
&lt;h3&gt;获取 API Key&lt;/h3&gt;
&lt;p&gt;前往 &lt;a href=&quot;https://aistudio.google.com/&quot;&gt;Google AI Studio&lt;/a&gt; 获取你的 Gemini API Key。&lt;/p&gt;
&lt;h3&gt;设置环境变量&lt;/h3&gt;
&lt;p&gt;将密钥添加到你的 shell 配置文件（如 &lt;code&gt;~/.zshrc&lt;/code&gt; 或 &lt;code&gt;~/.bashrc&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export GEMINI_API_KEY=&quot;你的_API_KEY&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果使用中转服务，可以设置基础 URL：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export GEMINI_BASE_URL=&quot;https://你的中转地址/v1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;启动与基本用法&lt;/h2&gt;
&lt;h3&gt;交互模式（REPL）&lt;/h3&gt;
&lt;p&gt;在项目根目录运行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gemini
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入交互界面后，你可以像在网页端一样与 Gemini 对话，但它现在拥有操作你本地文件的权限。&lt;/p&gt;
&lt;h3&gt;非交互模式（单次查询）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gemini -p &quot;分析当前目录结构并总结项目功能&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;管道模式&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat error.log | gemini &quot;解释这个错误并给出修复方案&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;恢复会话&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gemini -r latest
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;核心功能特性&lt;/h2&gt;
&lt;h3&gt;1. 上下文感知&lt;/h3&gt;
&lt;p&gt;Gemini CLI 会自动读取项目中的文件结构，并尊重 &lt;code&gt;.geminiignore&lt;/code&gt; 文件（类似于 &lt;code&gt;.gitignore&lt;/code&gt;）。
你可以通过 &lt;code&gt;GEMINI.md&lt;/code&gt; 文件为 AI 提供项目特定的指令和规范。&lt;/p&gt;
&lt;h3&gt;2. 快捷文件引用&lt;/h3&gt;
&lt;p&gt;在对话中，你可以使用 &lt;code&gt;@&lt;/code&gt; 符号快速引用文件内容：
&lt;code&gt;帮我重构一下 @src/utils/date.ts 里的格式化函数&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;3. Shell 集成&lt;/h3&gt;
&lt;p&gt;在交互界面中，可以使用 &lt;code&gt;!&lt;/code&gt; 前缀直接执行 Shell 命令：
&lt;code&gt;!npm test&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;4. 自动任务执行&lt;/h3&gt;
&lt;p&gt;你可以给 Gemini 下达复杂的指令，它会自动拆解步骤并执行，例如：
&lt;code&gt;帮我把项目中所有的 React 组件从 Class 组件重构成 Function 组件，并添加 Vitest 测试&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;常用斜杠命令&lt;/h2&gt;
&lt;p&gt;| 命令 | 功能 |
|------|------|
| &lt;code&gt;/help&lt;/code&gt; | 显示所有可用命令 |
| &lt;code&gt;/model set &amp;#x3C;model&gt;&lt;/code&gt; | 切换使用的 Gemini 模型版本 |
| &lt;code&gt;/resume&lt;/code&gt; | 搜索并恢复历史会话 |
| &lt;code&gt;/rewind&lt;/code&gt; | 在历史记录中回溯，甚至可以撤销代码更改 |
| &lt;code&gt;/settings&lt;/code&gt; | 配置 CLI 的外观和行为 |
| &lt;code&gt;/quit&lt;/code&gt; | 退出当前会话 |&lt;/p&gt;
&lt;h2&gt;进阶技巧：Agent Skills&lt;/h2&gt;
&lt;p&gt;Gemini CLI 支持通过 &lt;strong&gt;Agent Skills&lt;/strong&gt; 扩展功能。你可以通过编写简单的脚本或配置，让 Gemini 具备特定领域的专家知识（如特定框架的优化技巧、复杂的部署流程等）。&lt;/p&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;Gemini CLI 凭借其强大的多模态处理能力和 Google 的模型优势，在处理大规模代码重构和复杂逻辑理解上表现出色。
它是目前命令行 Coding Agent 中最全能的选手之一。&lt;/p&gt;
&lt;p&gt;如果你已经习惯了在前几篇教程中提到的 Claude CLI 或 OpenCode，Gemini CLI 的强大工具链和恢复机制（Rewind）绝对值得你一试。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/120269913_p0_master1200.3k8lu5pi8s.webp"/><enclosure url="https://pic.hana0721.top/120269913_p0_master1200.3k8lu5pi8s.webp"/></item><item><title>SSH 断连不掉任务：tmux 入门教程</title><link>https://hana-blog.top/blog/tmux-ssh-session-management</link><guid isPermaLink="true">https://hana-blog.top/blog/tmux-ssh-session-management</guid><description>针对 SSH 网络不稳定场景的 tmux 快速教程：会话创建、分离重连、窗口分屏与常用快捷键。</description><pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;远程开发最常见的痛点之一：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地网络波动&lt;/li&gt;
&lt;li&gt;SSH 会话断开&lt;/li&gt;
&lt;li&gt;正在跑的脚本一起中断&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你经常在服务器上跑训练、部署或长时间任务，&lt;code&gt;tmux&lt;/code&gt; 基本是必备工具。&lt;/p&gt;
&lt;h2&gt;1. tmux 能解决什么&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tmux&lt;/code&gt; 是终端复用器。你可以把它理解成“可恢复的终端容器”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;任务跑在 tmux 会话里，不依赖当前 SSH 连接&lt;/li&gt;
&lt;li&gt;你断网、关电脑、重连后都能继续接上原来的终端状态&lt;/li&gt;
&lt;li&gt;一个 SSH 连接里还能开多个窗口和分屏&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话：&lt;strong&gt;把任务和网络连接解耦&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;2. 安装 tmux&lt;/h2&gt;
&lt;p&gt;Ubuntu / Debian:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update
sudo apt install -y tmux
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CentOS / RHEL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo yum install -y tmux
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;macOS (Homebrew):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install tmux
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查版本：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux -V
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 第一次使用（最小闭环）&lt;/h2&gt;
&lt;p&gt;下面这套流程，够你马上上手：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;登录服务器后创建会话&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux new -s work
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;在会话里启动任务（例如训练脚本）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python train.py
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;需要离开时，分离会话（任务继续跑）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;按 &lt;code&gt;Ctrl+b&lt;/code&gt;，松开后再按 &lt;code&gt;d&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;以后重连服务器，恢复会话&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux attach -t work
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 你会用到的核心命令&lt;/h2&gt;
&lt;p&gt;列出所有会话：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新建会话：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux new -s session_name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;连接会话：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux attach -t session_name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分离当前会话：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux detach
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关闭会话：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux kill-session -t session_name
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 常用快捷键（先记这几个）&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tmux&lt;/code&gt; 默认前缀键是 &lt;code&gt;Ctrl+b&lt;/code&gt;，后面所有快捷键都以它开头。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl+b&lt;/code&gt; 然后 &lt;code&gt;d&lt;/code&gt;：分离会话&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+b&lt;/code&gt; 然后 &lt;code&gt;c&lt;/code&gt;：新建窗口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+b&lt;/code&gt; 然后 &lt;code&gt;n&lt;/code&gt; / &lt;code&gt;p&lt;/code&gt;：切换下一个 / 上一个窗口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+b&lt;/code&gt; 然后 &lt;code&gt;%&lt;/code&gt;：左右分屏&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+b&lt;/code&gt; 然后 &lt;code&gt;&quot;&lt;/code&gt;：上下分屏&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+b&lt;/code&gt; 然后 &lt;code&gt;x&lt;/code&gt;：关闭当前 pane&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. SSH 场景下的推荐习惯&lt;/h2&gt;
&lt;h3&gt;习惯 A：任务一律在 tmux 里启动&lt;/h3&gt;
&lt;p&gt;不要直接在普通 SSH shell 里跑长任务。先 &lt;code&gt;tmux new -s xxx&lt;/code&gt;，再启动脚本。&lt;/p&gt;
&lt;h3&gt;习惯 B：会话命名有语义&lt;/h3&gt;
&lt;p&gt;例如：&lt;code&gt;train&lt;/code&gt;, &lt;code&gt;deploy&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;，方便你 &lt;code&gt;tmux ls&lt;/code&gt; 后快速找到目标。&lt;/p&gt;
&lt;h3&gt;习惯 C：用日志文件做双保险&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python train.py 2&gt;&amp;#x26;1 | tee train.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;即使你暂时没 attach，也能通过日志确认任务状态。&lt;/p&gt;
&lt;h2&gt;7. 常见问题&lt;/h2&gt;
&lt;h3&gt;Q1: &lt;code&gt;tmux attach&lt;/code&gt; 提示找不到会话&lt;/h3&gt;
&lt;p&gt;先看会话列表：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tmux ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果确实不存在，说明之前的会话已经退出，需要重新创建。&lt;/p&gt;
&lt;h3&gt;Q2: 连接时提示 &lt;code&gt;sessions should be nested with care&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;你可能在一个 tmux 里又 attach 了另一个 tmux。通常先分离当前层，回到普通 shell 再 attach 目标会话。&lt;/p&gt;
&lt;h3&gt;Q3: 如何退出 tmux 并结束任务&lt;/h3&gt;
&lt;p&gt;在 tmux 的 shell 中执行 &lt;code&gt;exit&lt;/code&gt;（或终止前台任务），该 pane 关闭；会话所有 pane 都退出后，会话会自动结束。&lt;/p&gt;
&lt;h2&gt;8. 一条推荐工作流&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh your_server
tmux new -s train
python train.py 2&gt;&amp;#x26;1 | tee train.log
# 按 Ctrl+b, d 分离
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后任意时间：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh your_server
tmux attach -t train
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样即使 SSH 中断，你的任务也不会跟着掉。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/126903857_p0_master1200.2dpaix2l3u.webp"/><enclosure url="https://pic.hana0721.top/126903857_p0_master1200.2dpaix2l3u.webp"/></item><item><title>Ubuntu 24.04 安装 MATLAB R2024a</title><link>https://hana-blog.top/blog/matlab-ubuntu-24-04-install</link><guid isPermaLink="true">https://hana-blog.top/blog/matlab-ubuntu-24-04-install</guid><description>基于 Ubuntu 24.04 的 MATLAB R2024a 安装实操：挂载 ISO、图形安装、命令行启动配置、卸载流程。</description><pubDate>Thu, 16 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这篇记录整理了我在 Ubuntu 24.04 上安装 MATLAB R2024a 的流程，重点是&lt;strong&gt;可复现&lt;/strong&gt;和&lt;strong&gt;后续维护方便&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;0. 准备条件&lt;/h2&gt;
&lt;p&gt;安装前请先确认：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你有有效的 MathWorks 账号与许可证（个人/学校/单位均可）&lt;/li&gt;
&lt;li&gt;系统是 Ubuntu 24.04 x86_64&lt;/li&gt;
&lt;li&gt;磁盘空间建议至少 25 GB&lt;/li&gt;
&lt;li&gt;有图形界面（或你能通过远程桌面运行图形安装器）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. 下载 MATLAB R2024a 安装包（官方）&lt;/h2&gt;
&lt;p&gt;建议使用 MathWorks 官方渠道下载，避免镜像被篡改或版本不一致：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;官网下载入口：&lt;code&gt;https://www.mathworks.com/downloads/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;产品页（Linux）：&lt;code&gt;https://www.mathworks.com/products/matlab.html&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;想省事，可以直接在终端使用 &lt;code&gt;aria2c&lt;/code&gt; 下载（非官方）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;aria2c &quot;magnet:?xt=urn:btih:5C78E38581C13CE9D63EA0222BEE4A6BF10328D3&amp;#x26;tr=http%3A%2F%2Fbt4.t-ru.org%2Fann%3Fmagnet&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，博主也是用这个链接下载的，下载完成后会得到一个压缩包，解压后里面有 &lt;code&gt;R2024a_Linux.iso&lt;/code&gt; 安装镜像和 &lt;code&gt;Crack&lt;/code&gt; 文件夹。&lt;/p&gt;
&lt;h2&gt;2. 挂载安装镜像&lt;/h2&gt;
&lt;p&gt;如果你已经拿到官方安装镜像（例如 &lt;code&gt;R2024a_Linux.iso&lt;/code&gt;），可以先挂载：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo mkdir -p /mnt/matlab
sudo mount -o loop R2024a_Linux.iso /mnt/matlab
ls /mnt/matlab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;能看到 &lt;code&gt;install&lt;/code&gt; 等文件说明挂载成功。&lt;/p&gt;
&lt;h2&gt;3. 启动安装器&lt;/h2&gt;
&lt;p&gt;在挂载目录中运行官方安装程序：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /mnt/matlab
sudo ./install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果没有问题，就会进入向导。首先，会要求你进行登陆。如果有账号直接登就好，如果没有账号就注册一个，博主是使用 gmail 注册的。
&lt;img src=&quot;https://pic.hana0721.top/mathwork-signin.3k8lr0yus2.webp&quot; alt=&quot;&quot;&gt;
登陆后，就会进入这个页面，让你输入密钥。这个时候先点击左上角的切换为 &lt;code&gt;I have a File Installation Key&lt;/code&gt;。
然后，打开 &lt;code&gt;Crack&lt;/code&gt; 文件夹，找到 &lt;code&gt;readme.txt&lt;/code&gt;，里面有若干密钥，选择 &lt;code&gt;Full desktop&lt;/code&gt; 版本的密钥输入即可。
&lt;img src=&quot;https://pic.hana0721.top/mathwork-key.32ik2g2sgi.webp&quot; alt=&quot;&quot;&gt;
接着，下一个页面会让你选择许可证，许可证的在 &lt;code&gt;Crack&lt;/code&gt; 文件夹里，选择 &lt;code&gt;license.lic&lt;/code&gt; 文件即可。
&lt;img src=&quot;https://pic.hana0721.top/mathwork-lic.5trmaiuxz1.webp&quot; alt=&quot;&quot;&gt;
选择安装路径，这里一般使用 &lt;code&gt;home/your_username/MATLAB/R2024a&lt;/code&gt;，也可以自定义路径。
&lt;img src=&quot;https://pic.hana0721.top/mathwork-path.1ow0yf33xd.webp&quot; alt=&quot;&quot;&gt;
选择需要的 &lt;code&gt;Product&lt;/code&gt;，如果不清楚可以默认全选。
&lt;img src=&quot;https://pic.hana0721.top/mathwork-product.6m4hs9kuj5.webp&quot; alt=&quot;&quot;&gt;
把共享信息和错误报告选项都取消勾选，保护隐私。
&lt;img src=&quot;https://pic.hana0721.top/mathwork-option.3ns7oreph1.webp&quot; alt=&quot;&quot;&gt;
等待安装完成即可，安装过程可能需要一些时间，耐心等待。
&lt;img src=&quot;https://pic.hana0721.top/mathwork-install.64eg3omvhb.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;4. 配置命令行启动&lt;/h2&gt;
&lt;p&gt;安装完成后，为了在任意路径直接启动 MATLAB，可以创建软链接，确保你在 &lt;code&gt;Crack&lt;/code&gt; 路径下，执行以下命令。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo cp libmwlmgrimpl.so ~/MATLAB/R2024a/bin/glnxa64/matlab_startup_plugins/lmgrimpl/
sudo ln -s ~/MATLAB/R2024a/bin/matlab /usr/local/bin/matlab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后测试：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;matlab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果能正常打开，说明安装与路径配置都已完成。&lt;/p&gt;
&lt;h2&gt;5. 卸载镜像&lt;/h2&gt;
&lt;p&gt;安装结束后可以卸载挂载点：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo umount /mnt/matlab
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 删除（卸载）MATLAB R2024a&lt;/h2&gt;
&lt;p&gt;如果你需要完整卸载当前用户目录下的 MATLAB R2024a，可以按下面顺序执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo pkill -f MATLAB
sudo rm /usr/local/bin/matlab
sudo rm -rf ~/MATLAB/R2024a
sudo rm -rf ~/.matlab
ps aux | grep matlab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;ps&lt;/code&gt; 仍有 MATLAB 相关进程，使用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kill -9 &amp;#x3C;PID&gt;
&lt;/code&gt;&lt;/pre&gt;</content:encoded><h:img src="https://pic.hana0721.top/70937229_p0_master1200.6bhnz4ob71.webp"/><enclosure url="https://pic.hana0721.top/70937229_p0_master1200.6bhnz4ob71.webp"/></item><item><title>如何把本地项目上传到 GitHub 仓库（完整教程）</title><link>https://hana-blog.top/blog/upload-project-to-github</link><guid isPermaLink="true">https://hana-blog.top/blog/upload-project-to-github</guid><description>从创建仓库到首次 push，一步步教你把本地项目上传到 GitHub，并附 SSH/HTTPS 两种方式和常见报错排查。</description><pubDate>Thu, 16 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;很多同学第一次上传项目时，最容易卡在三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没初始化 Git&lt;/li&gt;
&lt;li&gt;远程仓库地址配错&lt;/li&gt;
&lt;li&gt;首次认证失败（SSH key / Token）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇教程按「从 0 到可用」整理，跟着做一遍就可以把自己的项目稳定上传到 GitHub。&lt;/p&gt;
&lt;h2&gt;0. 准备工作&lt;/h2&gt;
&lt;p&gt;先确认你已经安装好 Git：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果能看到版本号（例如 &lt;code&gt;git version 2.x.x&lt;/code&gt;），就可以继续。&lt;/p&gt;
&lt;h2&gt;1. 在 GitHub 上创建空仓库&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;打开 GitHub，点击右上角 &lt;code&gt;+&lt;/code&gt; -&gt; &lt;code&gt;New repository&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;填写仓库名（例如 &lt;code&gt;my-first-project&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;Public&lt;/code&gt; 或 &lt;code&gt;Private&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;先不要勾选 &lt;code&gt;Add a README file&lt;/code&gt;（避免和本地历史冲突）&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;Create repository&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;创建后页面会给你一个仓库地址，通常有两种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTPS：&lt;code&gt;https://github.com/你的用户名/仓库名.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;SSH：&lt;code&gt;git@github.com:你的用户名/仓库名.git&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 本地项目初始化 Git&lt;/h2&gt;
&lt;p&gt;进入你的项目目录，然后执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git init
git add .
git commit -m &quot;init: first commit&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你还没有配置 Git 身份，先设置（只需一次）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global user.name &quot;你的名字&quot;
git config --global user.email &quot;你的邮箱&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 添加 &lt;code&gt;.gitignore&lt;/code&gt;（强烈建议）&lt;/h2&gt;
&lt;p&gt;上传前，建议把不该入库的文件排除掉，比如依赖、构建产物、环境变量。&lt;/p&gt;
&lt;p&gt;常见示例（按你的项目类型调整）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-gitignore&quot;&gt;node_modules/
dist/
.env
.DS_Store
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你在 &lt;code&gt;git add .&lt;/code&gt; 之前忘了加 &lt;code&gt;.gitignore&lt;/code&gt;，先补上，然后再执行一次：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git add .
git commit -m &quot;chore: add gitignore&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 绑定远程仓库并推送&lt;/h2&gt;
&lt;p&gt;把 &lt;code&gt;origin&lt;/code&gt; 指向你刚创建的 GitHub 仓库：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote add origin &amp;#x3C;你的仓库地址&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看是否添加成功：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后推送：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git branch -M main
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首次成功后，后续只需要：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git add .
git commit -m &quot;feat: your message&quot;
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 认证方式选择：HTTPS 还是 SSH&lt;/h2&gt;
&lt;h3&gt;方式 A：HTTPS（简单）&lt;/h3&gt;
&lt;p&gt;优点：配置快。&lt;/p&gt;
&lt;p&gt;注意：GitHub 不再支持账号密码直登，通常要用 &lt;code&gt;Personal Access Token&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;&lt;code&gt;git push&lt;/code&gt; 后提示输入 password，应该填什么？&lt;/h4&gt;
&lt;p&gt;如果你的远程地址是 HTTPS（&lt;code&gt;https://github.com/...&lt;/code&gt;），这里的 &lt;code&gt;password&lt;/code&gt; 不是 GitHub 登录密码，而是 &lt;code&gt;Personal Access Token (PAT)&lt;/code&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GitHub 右上角头像 -&gt; &lt;code&gt;Settings&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Developer settings&lt;/code&gt; -&gt; &lt;code&gt;Personal access tokens&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;Tokens (classic)&lt;/code&gt;（新手更容易上手）&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;Generate new token&lt;/code&gt;，勾选 &lt;code&gt;repo&lt;/code&gt; 权限&lt;/li&gt;
&lt;li&gt;生成后复制 token（只显示一次）&lt;/li&gt;
&lt;li&gt;下次 &lt;code&gt;git push&lt;/code&gt; 时输入：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Username&lt;/code&gt;：你的 GitHub 用户名&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Password&lt;/code&gt;：刚才生成的 PAT&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可以先确认远程地址类型：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果显示的是 &lt;code&gt;https://github.com/...&lt;/code&gt;，就一定要用 PAT。&lt;/p&gt;
&lt;h3&gt;方式 B：SSH（推荐长期使用）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;生成密钥：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;拷贝公钥内容（&lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;到 GitHub -&gt; &lt;code&gt;Settings&lt;/code&gt; -&gt; &lt;code&gt;SSH and GPG keys&lt;/code&gt; -&gt; &lt;code&gt;New SSH key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;添加后测试：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看到 &lt;code&gt;Hi &amp;#x3C;username&gt;!&lt;/code&gt; 类似提示说明配置成功。&lt;/p&gt;
&lt;h2&gt;6. 常见报错与解决&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;remote origin already exists&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;说明你之前加过远程地址：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote remove origin
git remote add origin &amp;#x3C;你的仓库地址&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;src refspec main does not match any&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;通常是你还没提交过代码。先提交一次：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git add .
git commit -m &quot;init&quot;
git branch -M main
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;Permission denied (publickey)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;说明 SSH 密钥未配置好。按上面的 SSH 步骤重新检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地是否有密钥文件&lt;/li&gt;
&lt;li&gt;GitHub 是否已添加公钥&lt;/li&gt;
&lt;li&gt;远程地址是否为 SSH 格式&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;rejected&lt;/code&gt; / &lt;code&gt;non-fast-forward&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;远程有新提交，本地没同步：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git pull --rebase origin main
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;7. 最小工作流（记住这 3 行）&lt;/h2&gt;
&lt;p&gt;日常开发最常用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git add .
git commit -m &quot;feat: 描述你这次改了什么&quot;
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到这里，你的项目已经可以稳定托管在 GitHub 上了。后续再学习分支协作（&lt;code&gt;git checkout -b&lt;/code&gt;）、Pull Request、CI/CD 就会顺很多。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/126839080_p0_master1200.232gprncye.webp"/><enclosure url="https://pic.hana0721.top/126839080_p0_master1200.232gprncye.webp"/></item><item><title>A800 服务器深度学习环境标准配置教程</title><link>https://hana-blog.top/blog/linux-dl-env-setup</link><guid isPermaLink="true">https://hana-blog.top/blog/linux-dl-env-setup</guid><description>按统一标准配置 A800 研究环境：Driver 570+、CUDA Toolkit 12.8、cuDNN 9、PyTorch cu128，并附版本不一致排查。</description><pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这篇教程按一套固定标准来配 A800 服务器，目标是“可复现、可维护、少踩坑”。&lt;/p&gt;
&lt;h2&gt;标准配置（本文统一口径）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;GPU: NVIDIA A800&lt;/li&gt;
&lt;li&gt;Driver: &lt;code&gt;570+&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;CUDA Toolkit: &lt;code&gt;12.8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;cuDNN: &lt;code&gt;9.x (for CUDA 12)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;PyTorch: &lt;code&gt;cu128&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. 基础系统初始化&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update &amp;#x26;&amp;#x26; sudo apt upgrade -y
sudo apt install -y git curl wget vim tmux htop btop unzip zip build-essential dkms linux-headers-$(uname -r)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 驱动检查（A800）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nvidia-smi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重点看两项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Driver Version&lt;/li&gt;
&lt;li&gt;GPU 是否识别到 A800&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只要驱动正常，后续 PyTorch GPU 通常就能跑起来。&lt;/p&gt;
&lt;h2&gt;2.1 已安装过旧驱动时：卸载并重装（推荐流程）&lt;/h2&gt;
&lt;p&gt;如果你之前装过其他版本驱动，建议按下面流程“先卸载，再安装目标版本”。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;下面命令会卸载现有 NVIDIA 驱动并重启，建议在有控制台（云厂商 VNC/管理终端）保障的前提下执行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;第一步：卸载旧驱动&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl stop nvidia-persistenced || true
sudo apt remove --purge -y &apos;^nvidia-.*&apos; &apos;^libnvidia-.*&apos;
sudo apt autoremove -y
sudo apt autoclean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可选：重启一次，确保旧模块完全退出。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;第二步：安装目标驱动（A800 推荐 570）&lt;/h3&gt;
&lt;p&gt;先看系统推荐：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ubuntu-drivers devices
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后二选一：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自动安装推荐版本（最稳）：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ubuntu-drivers autoinstall
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;手动指定 570（你当前目标）：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install -y nvidia-driver-570-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;nvidia-driver-570-server&lt;/code&gt; 不存在，可改用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install -y nvidia-driver-570
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装后重启：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;第三步：验收&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nvidia-smi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;能识别 A800&lt;/li&gt;
&lt;li&gt;Driver Version 为目标版本（如 570.x）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过后再继续下面 CUDA Toolkit 与 cuDNN 的安装步骤。&lt;/p&gt;
&lt;h2&gt;3. 清理旧 CUDA Toolkit&lt;/h2&gt;
&lt;p&gt;先检查当前 &lt;code&gt;nvcc&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nvcc --version
which nvcc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果是旧版本，先清理旧工具链：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt remove --purge -y &quot;cuda*&quot; &quot;nvidia-cuda-toolkit&quot; &quot;libcudnn*&quot;
sudo apt autoremove -y
sudo apt autoclean
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 安装 CUDA Toolkit 12.8&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update
sudo apt install -y cuda-toolkit-12-8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写入环境变量（非常关键）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo &apos;export PATH=/usr/local/cuda-12.8/bin:$PATH&apos; &gt;&gt; ~/.bashrc
echo &apos;export LD_LIBRARY_PATH=/usr/local/cuda-12.8/lib64:$LD_LIBRARY_PATH&apos; &gt;&gt; ~/.bashrc
source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nvcc --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应该看到 &lt;code&gt;release 12.8&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;5. 安装 cuDNN 9（CUDA 12 系）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install -y libcudnn9-cuda-12 libcudnn9-dev-cuda-12
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1 验证 cuDNN 是否安装成功&lt;/h3&gt;
&lt;p&gt;先看系统层是否有 cuDNN 动态库：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ldconfig
ldconfig -p | grep cudnn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预期输出应该至少包含类似下面的行（版本号可能不同）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;libcudnn.so.9 (libc6,x86-64) =&gt; /usr/lib/x86_64-linux-gnu/libcudnn.so.9
libcudnn_ops.so.9 (libc6,x86-64) =&gt; /usr/lib/x86_64-linux-gnu/libcudnn_ops.so.9
libcudnn_cnn.so.9 (libc6,x86-64) =&gt; /usr/lib/x86_64-linux-gnu/libcudnn_cnn.so.9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果没有任何输出，通常说明 cuDNN 没装好，或者库路径没被系统识别。&lt;/p&gt;
&lt;p&gt;再看包管理器是否安装到位：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;dpkg -l | grep -E &quot;libcudnn9|libcudnn9-dev&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 安装 Miniconda 与终端初始化&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
rm -f Miniconda3-latest-Linux-x86_64.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后，做终端初始化并使其生效：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;~/miniconda3/bin/conda init bash
source ~/.bashrc
conda --version
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你用的是 &lt;code&gt;zsh&lt;/code&gt;，把 &lt;code&gt;bash&lt;/code&gt; 换成 &lt;code&gt;zsh&lt;/code&gt;：&lt;code&gt;conda init zsh&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;后续环境创建与包安装放在下一节进行。&lt;/p&gt;
&lt;h2&gt;7. 安装 PyTorch（cu128）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip install --upgrade pip
pip install torch torchvision torchaudio \
  --index-url https://download.pytorch.org/whl/cu128 \
  --extra-index-url https://pypi.tuna.tsinghua.edu.cn/simple
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你网络到官方源很慢，可以先临时设置国内镜像，再执行安装：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip config set global.extra-index-url https://download.pytorch.org/whl/cu128
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;常用国内镜像（任选其一）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;清华：&lt;code&gt;https://pypi.tuna.tsinghua.edu.cn/simple&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;中科大：&lt;code&gt;https://pypi.mirrors.ustc.edu.cn/simple&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;阿里云：&lt;code&gt;https://mirrors.aliyun.com/pypi/simple&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;腾讯云：&lt;code&gt;https://mirrors.cloud.tencent.com/pypi/simple&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;华为云：&lt;code&gt;https://repo.huaweicloud.com/repository/pypi/simple&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如改成中科大：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip config set global.index-url https://pypi.mirrors.ustc.edu.cn/simple
pip config set global.extra-index-url https://download.pytorch.org/whl/cu128
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后，如需恢复默认：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip config unset global.index-url
pip config unset global.extra-index-url
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证 GPU：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python - &amp;#x3C;&amp;#x3C;&apos;PY&apos;
import torch
print(&apos;torch:&apos;, torch.__version__)
print(&apos;cuda available:&apos;, torch.cuda.is_available())
if torch.cuda.is_available():
    print(&apos;device:&apos;, torch.cuda.get_device_name(0))
PY
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. 常用科研包（按需）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip install numpy scipy pandas matplotlib seaborn scikit-learn
pip install jupyterlab ipykernel tqdm pyyaml rich einops opencv-python
pip install tensorboard wandb
&lt;/code&gt;&lt;/pre&gt;</content:encoded><h:img src="https://pic.hana0721.top/143484250_p0_master1200.9ddjzdu0z1.webp"/><enclosure url="https://pic.hana0721.top/143484250_p0_master1200.9ddjzdu0z1.webp"/></item><item><title>在 VSCode 上配置 SSH 远程开发</title><link>https://hana-blog.top/blog/vscode-ssh-setup</link><guid isPermaLink="true">https://hana-blog.top/blog/vscode-ssh-setup</guid><description>覆盖 Windows / macOS / Linux 的 VSCode Remote SSH 配置：从生成密钥到连接远程服务器，再到常见问题排查。</description><pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在本地写代码、在远程 Linux 机器上跑环境，是现在很常见的开发方式。&lt;br&gt;
这篇教程按“能快速用起来”的思路整理，&lt;strong&gt;覆盖 Windows / macOS / Linux 三个平台&lt;/strong&gt;，默认你已经有一台可登录的 Linux 服务器（云主机 / 学校实验室机器均可）。&lt;/p&gt;
&lt;h2&gt;1. 安装 VSCode Remote SSH 插件&lt;/h2&gt;
&lt;p&gt;在 VSCode 扩展市场安装：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Remote - SSH&lt;/code&gt;（作者 Microsoft）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安装完成后，左侧活动栏会出现远程图标。&lt;/p&gt;
&lt;h2&gt;2. 生成 SSH 密钥（本地，按系统）&lt;/h2&gt;
&lt;h3&gt;Windows（PowerShell）&lt;/h3&gt;
&lt;p&gt;先确认 OpenSSH 客户端可用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;ssh -V
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果提示找不到命令，请在「设置 -&gt; 可选功能」安装 OpenSSH Client。然后生成密钥：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认路径通常是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私钥：&lt;code&gt;C:\Users\你的用户名\.ssh\id_ed25519&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;公钥：&lt;code&gt;C:\Users\你的用户名\.ssh\id_ed25519.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;macOS / Linux（Terminal）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认路径：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私钥：&lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;公钥：&lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;关于 passphrase 提示（你遇到的这个）&lt;/h3&gt;
&lt;p&gt;当你看到：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Enter passphrase for &quot;/Users/xxx/.ssh/id_ed25519&quot; (empty for no passphrase):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是在询问你的&lt;strong&gt;私钥密码&lt;/strong&gt;（不是服务器密码）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想要更安全：输入一个 passphrase（推荐）&lt;/li&gt;
&lt;li&gt;想省事：直接回车留空（不推荐）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你已经设置了 passphrase，又不想每次都输入，可以把密钥加入 &lt;code&gt;ssh-agent&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;3. 把公钥上传到服务器（按系统）&lt;/h2&gt;
&lt;p&gt;这一步会让你先输入一次&lt;strong&gt;服务器登录密码&lt;/strong&gt;。输入后如果没有报错，就是成功。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-copy-id -i ~/.ssh/id_ed25519.pub -p your_port username@your_server_ip
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 配置 SSH Host（本地）&lt;/h2&gt;
&lt;p&gt;编辑 SSH 配置文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows：&lt;code&gt;C:\Users\你的用户名\.ssh\config&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS / Linux：&lt;code&gt;~/.ssh/config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;添加：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;Host my-server
  HostName your_server_ip
  User username
  Port your_port
  IdentityFile ~/.ssh/id_ed25519
  ServerAliveInterval 60
  ServerAliveCountMax 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果是 Windows，&lt;code&gt;IdentityFile&lt;/code&gt; 也可以写完整路径，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ssh-config&quot;&gt;IdentityFile C:/Users/YourName/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样你后面直接 &lt;code&gt;ssh my-server&lt;/code&gt; 就能连，不用每次敲完整 IP。&lt;/p&gt;
&lt;h2&gt;5. 在 VSCode 连接远程机器&lt;/h2&gt;
&lt;p&gt;在 VSCode 中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Cmd/Ctrl + Shift + P&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输入 &lt;code&gt;Remote-SSH: Connect to Host...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;my-server&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;首次连接时确认 fingerprint&lt;/li&gt;
&lt;li&gt;如果弹出密码框，输入你的&lt;strong&gt;服务器密码&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;连接成功后，左下角会显示 &lt;code&gt;SSH: my-server&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;说明：即使你配置了密钥，首次仍可能要求你输入一次密码或 passphrase，属于正常现象。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="https://pic.hana0721.top/133376054_p0_master1200.1ziuqmv790.webp"/><enclosure url="https://pic.hana0721.top/133376054_p0_master1200.1ziuqmv790.webp"/></item><item><title>Vibe Coding For AI 1: Windows 上配置 Coding Agent 指南</title><link>https://hana-blog.top/blog/vibe-coding-1</link><guid isPermaLink="true">https://hana-blog.top/blog/vibe-coding-1</guid><description>学会在 Windows 上配置 Vibe Coding，提升开发效率和代码质量。</description><pubDate>Sat, 04 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;什么是 Vibe Coding？&lt;/h2&gt;
&lt;p&gt;Vibe Coding 是一种新兴的编程范式，旨在通过 AI 助手（Coding Agent）来提升开发效率和代码质量。
在 Vibe Coding 中，开发者与 AI 助手进行实时互动，AI 可以根据上下文提供代码建议、自动补全、错误检查等功能。
这种方式不仅加快了开发速度，还能帮助开发者更好地理解和优化代码。
在这篇文章中，教会你如何在 Windows 上配置 Vibe Coding。
对于 AI 供应商，考虑到经济实惠和易用性，推荐使用 &lt;a href=&quot;https://api.ikuncode.cc/&quot;&gt;Ikuncode&lt;/a&gt; 的中转服务。
本教程中使用的 CLI 工具是 &lt;a href=&quot;https://github.com/opencode-ai/opencode&quot;&gt;OpenCode&lt;/a&gt;，支持多种 AI 供应商，提供了丰富的功能和良好的用户体验。&lt;/p&gt;
&lt;h2&gt;OpenCode CLI 配置&lt;/h2&gt;
&lt;p&gt;首先，需要在 Windows 系统上打开终端，推荐使用 &lt;code&gt;PowerShell&lt;/code&gt;。
在启动栏中搜索 &lt;code&gt;PowerShell&lt;/code&gt; 即可，记得使用管理员权限运行。
咱们使用 &lt;code&gt;npm&lt;/code&gt; 来安装 &lt;code&gt;OpenCode&lt;/code&gt;，所以先要确保 Windows 上安装好了 &lt;code&gt;npm&lt;/code&gt;。
在安装之前，建议确认终端的代理情况，可以使用以下命令检查当前的代理设置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl ipin.io
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果代理设置正确，会返回你的 IP 地址信息。如果没有魔法，下载 &lt;code&gt;OpenCode&lt;/code&gt; 可能会遇到网络问题。
接下来，使用以下命令安装 &lt;code&gt;Node.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -o node-installer.msi https://nodejs.org/dist/v20.11.0/node-v20.11.0-x64.msi
node-installer.msi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后，可以使用以下命令验证安装，出现版本号即为成功。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node --version
npm --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，就可以安装 &lt;code&gt;OpenCode&lt;/code&gt; 啦，使用以下命令全局安装。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g opencode-ai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后，可以使用以下命令验证安装，出现以下页面即为成功。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;opencode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top//opencode.58hy7doj9k.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;其实，到这里就可以尝试进行 Vibe Coding 了，因为 &lt;code&gt;OpenCode&lt;/code&gt; 内置了一些免费的 AI 模型，可以直接使用。
不过，免费模型的性能和功能可能有限，如果想要更好的体验，建议配置一个更强大的 AI 供应商。&lt;/p&gt;
&lt;h2&gt;IkunCode 中转站 配置&lt;/h2&gt;
&lt;p&gt;IkunCode 是一个提供 AI 模型中转服务的平台，支持多种 AI 供应商，价格合理，使用方便。
首先，需要在 &lt;a href=&quot;https://api.ikuncode.cc/&quot;&gt;Ikuncode&lt;/a&gt; 官网注册一个账号，完成注册后登录。
&lt;a href=&quot;https://api.ikuncode.cc/&quot;&gt;Ikuncode&lt;/a&gt; 涵盖了多个 AI 供应商的模型，例如 &lt;code&gt;Anthropic&lt;/code&gt;、&lt;code&gt;OpenAI&lt;/code&gt;、&lt;code&gt;Google&lt;/code&gt; 等。
这里以 &lt;code&gt;OpenAI&lt;/code&gt; 的 &lt;code&gt;Codex-5.1&lt;/code&gt; 模型为例，演示如何配置 &lt;code&gt;OpenCode&lt;/code&gt; 使用 &lt;code&gt;Ikuncode&lt;/code&gt; 的中转服务。
当然，天下没有免费的午餐，想要使用这些模型，是需要烧钱的，这里选择 &lt;code&gt;Codex-5.1&lt;/code&gt; 也是因为比较实惠。
接下来，点击上方的 &lt;code&gt;控制台&lt;/code&gt;，可以看到自己的账户余额，点击 &lt;code&gt;钱包管理&lt;/code&gt;，即可进行充值。
充值完毕后，点击左侧的 &lt;code&gt;令牌管理&lt;/code&gt;，再点击 &lt;code&gt;添加令牌&lt;/code&gt;，即可创建一个新的 API 令牌。按照下图选择，即可配置出 &lt;code&gt;OpenAI&lt;/code&gt; 的密钥。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top//ikuncode-key.99txlschjq.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;CC-Switch 密钥管理&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;CC-Switch&lt;/code&gt; 是一个开源的密钥管理工具，可以帮助开发者安全地存储和管理 API 密钥。
首先啦，就是要安装 &lt;code&gt;CC-Switch&lt;/code&gt;，进入 &lt;a href=&quot;https://github.com/farion1231/cc-switch/releases&quot;&gt;CC-Switch 发布页面&lt;/a&gt;，选择安装 &lt;code&gt;msi&lt;/code&gt; 版本，下载并安装。
接下来打开 &lt;code&gt;CC-Switch&lt;/code&gt;，点击上方的 &lt;code&gt;OpenCode&lt;/code&gt;，再点击 &lt;code&gt;添加密钥&lt;/code&gt;，按照咱的配置方案，输入以下信息，然后保存就好啦。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top//CC-Switch.pfx4ff7hb.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;OpenCode 配置验证&lt;/h2&gt;
&lt;p&gt;配置完成后，就可以开始使用 &lt;code&gt;OpenCode&lt;/code&gt; 进行 Vibe Coding 了。
进入你的项目文件夹，运行 &lt;code&gt;opencode&lt;/code&gt; 命令启动 &lt;code&gt;OpenCode&lt;/code&gt; 啦。
首先，可以使用 &lt;code&gt;/models&lt;/code&gt; 命令查看当前可用的 AI 模型，确认 &lt;code&gt;OpenAI&lt;/code&gt; 的 &lt;code&gt;Codex-5.1&lt;/code&gt; 模型已经成功配置，然后选择切换。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top//opencode-models.2h8vzc36jt.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OpenCode&lt;/code&gt; 提供了两种模型，分别是 &lt;code&gt;Build&lt;/code&gt; 和 &lt;code&gt;Plan&lt;/code&gt;，其中 &lt;code&gt;Build&lt;/code&gt; 模型适合生成代码，而 &lt;code&gt;Plan&lt;/code&gt; 模型适合生成开发计划和任务分解，使用 &lt;code&gt;Tab&lt;/code&gt; 键进行切换。
接下来，向 Agent 打个招呼，如果有回应，那么就配置成功啦！恭喜你进入 Vibe Coding 的世界了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top//opencode-hello.8adu8mx2iz.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/129563571_p0_master1200.1ziuqmv78w.webp"/><enclosure url="https://pic.hana0721.top/129563571_p0_master1200.1ziuqmv78w.webp"/></item><item><title>Paper Reading: Embodied AI 3</title><link>https://hana-blog.top/blog/paper-reading-eba3</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-eba3</guid><description>从零开始的Embodied AI研究生活。</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;import { ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;Embodied AI Paper Reading&apos;,
items: [
{
title: &apos;Batch 1&apos;,
href: &apos;/blog/paper-reading-eba1&apos;,
order: &apos;1&apos;
},
{
title: &apos;Batch 2&apos;,
href: &apos;/blog/paper-reading-eba2&apos;,
order: &apos;2&apos;
},
{
title: &apos;Batch 3&apos;,
href: &apos;/blog/paper-reading-eba3&apos;,
order: &apos;3&apos;
},
{
title: &apos;Batch 4&apos;,
href: &apos;/blog/paper-reading-eba4&apos;,
order: &apos;4&apos;
},
{
title: &apos;Batch 5&apos;,
href: &apos;/blog/paper-reading-eba5&apos;,
order: &apos;5&apos;
}
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;RL菜鸡开始进军Embodied AI，慢慢积累，提升自己。&lt;/p&gt;
&lt;h2&gt;RoboVerse&lt;/h2&gt;
&lt;h2&gt;AgiBot World Colosseo&lt;/h2&gt;
&lt;h2&gt;UniVLA&lt;/h2&gt;
&lt;h2&gt;GraspVLA&lt;/h2&gt;
&lt;h2&gt;GRAPE&lt;/h2&gt;
&lt;h2&gt;A0&lt;/h2&gt;
&lt;h2&gt;OneTwoVLA&lt;/h2&gt;
&lt;h2&gt;RDT-1B&lt;/h2&gt;
&lt;h2&gt;Octo&lt;/h2&gt;
&lt;h2&gt;FAST&lt;/h2&gt;
&lt;h2&gt;Magma&lt;/h2&gt;
&lt;h2&gt;ChatVLA&lt;/h2&gt;
&lt;h2&gt;ChatVLA-2&lt;/h2&gt;
&lt;h2&gt;RoboBrain&lt;/h2&gt;
&lt;h2&gt;CoT-VLA&lt;/h2&gt;
&lt;h2&gt;Interleave-VLA&lt;/h2&gt;
&lt;h2&gt;Knowledge Insulating Vision-Language-Action Models&lt;/h2&gt;
&lt;h2&gt;Hi Robot&lt;/h2&gt;
&lt;h2&gt;Scenethesis&lt;/h2&gt;
&lt;h2&gt;LBM&lt;/h2&gt;
&lt;h2&gt;UniVLA&lt;/h2&gt;
&lt;h2&gt;SmolVLA&lt;/h2&gt;
&lt;h2&gt;ThinkAct&lt;/h2&gt;
&lt;h2&gt;VIDAR&lt;/h2&gt;
&lt;h2&gt;DreamVLA&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/66497987_p0_master1200.58hynbbxn8.webp"/><enclosure url="https://pic.hana0721.top/66497987_p0_master1200.58hynbbxn8.webp"/></item><item><title>RL笔记（29）：推理模型的崛起 (GRPO &amp; PRM)</title><link>https://hana-blog.top/blog/rl-note-29</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-29</guid><description>大模型训练的新范式：详解 DeepSeek 提出的 GRPO 如何彻底省去 Critic 网络，以及 PRM 如何通过过程监督让模型学会正确推理。</description><pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在上一篇笔记中，我们介绍了经典的 &lt;strong&gt;PPO + RLHF&lt;/strong&gt; 流程。虽然 PPO 非常有效，但在训练超大规模语言模型时，它面临两个挑战：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;资源消耗巨大&lt;/strong&gt;：PPO 需要维护 Actor、Ref、Reward、Critic 四个模型，显存开销极高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结果导向的局限&lt;/strong&gt;：传统的奖励模型只对最终答案评分（ORM），而不关心推理过程。如果模型凑巧猜对了答案，也会得到高分，这会导致“走捷径”和幻觉。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本章将介绍 &lt;strong&gt;GRPO&lt;/strong&gt; ——一种更轻量、更高效的策略优化算法，以及 &lt;strong&gt;PRM&lt;/strong&gt; ——一种对思维过程进行细粒度监督的奖励机制。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;GRPO (Group Relative Policy Optimization)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/2402.03300&quot;&gt;DeepSeekMath: Pushing the Limits of Mathematical Reasoning in LLMs&lt;/a&gt;
&lt;strong&gt;应用&lt;/strong&gt;：DeepSeek-V3 / DeepSeek-R1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GRPO 的核心创新在于：&lt;strong&gt;彻底丢弃了 Critic 模型，利用组内相对排名来估计优势函数（Advantage）。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;核心思想：组内相对评价&lt;/h3&gt;
&lt;p&gt;在 PPO 中，我们需要 Critic 网络来预测状态价值 $V(s)$，从而计算优势 $A = Q - V$。
而在 GRPO 中，对于每一个提示词（Prompt）$q$：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;让模型生成&lt;strong&gt;一组&lt;/strong&gt;不同的回复 ${o_1, o_2, o_3, ..., o_G}$（组大小为 $G$）。&lt;/li&gt;
&lt;li&gt;利用奖励模型（或规则奖励）计算出这一组回复的分数 ${r_1, r_2, r_3, ..., r_G}$。&lt;/li&gt;
&lt;li&gt;通过这组分数的&lt;strong&gt;相对强弱&lt;/strong&gt;来直接得出每个回复的优势。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;优势函数计算&lt;/h3&gt;
&lt;p&gt;第 $i$ 个回复的优势函数 $A_i$ 计算公式为：&lt;/p&gt;
&lt;p&gt;$$
A_i = \frac{r_i - \text{mean}(r_1, r_2, ..., r_G)}{\text{std}(r_1, r_2, ..., r_G)}
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉理解&lt;/strong&gt;：
这就像是在班级里考试。我们不需要一个绝对的“满分标准”（Critic），只需要看你在班级里的排名。如果你比班级平均分高，我们就增加你这种行为的概率；反之则降低。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;GRPO 损失函数&lt;/h3&gt;
&lt;p&gt;$$
L_{GRPO}(\theta) = \frac{1}{G} \sum_{i=1}^G \left[ \min\left( \frac{\pi_\theta(o_i|q)}{\pi_{\text{old}}(o_i|q)} A_i, \text{clip}\left(\frac{\pi_\theta(o_i|q)}{\pi_{\text{old}}(o_i|q)}, 1-\epsilon, 1+\epsilon\right) A_i \right) - \beta D_{KL}(\pi_\theta || \pi_{\text{ref}}) \right]
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：由于省去了 Critic 网络，在大模型训练中可以节省约 &lt;strong&gt;50%&lt;/strong&gt; 的梯度计算相关显存，从而允许更长的上下文或更大的 Batch Size。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;PRM (Process Reward Models)&lt;/h2&gt;
&lt;p&gt;传统的奖励模型被称为 &lt;strong&gt;ORM (Outcome Reward Models)&lt;/strong&gt;：只看结果。
&lt;strong&gt;PRM (Process Reward Models)&lt;/strong&gt; 则是对推理链条中的&lt;strong&gt;每一个步骤&lt;/strong&gt;进行打分。&lt;/p&gt;
&lt;h3&gt;核心动机&lt;/h3&gt;
&lt;p&gt;在复杂的数学推导或编程任务中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;中间错一步，步步错&lt;/strong&gt;：即使最终答案对，中间逻辑也可能有毒。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;奖励稀疏&lt;/strong&gt;：只有到最后才给分，模型很难学会长链条的逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;训练与运作机制&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;步骤拆解&lt;/strong&gt;：将模型生成的思维链（CoT）利用换行符或特殊 token 拆分为 $S_1, S_2, ..., S_n$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;细粒度标注&lt;/strong&gt;：通过人类专家或更强模型（如 GPT-4）对每一个 $S_i$ 标注“正确”、“错误”或“中性”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型预测&lt;/strong&gt;：PRM 模型学习预测每一步的正确概率。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;强化学习中的应用&lt;/h3&gt;
&lt;p&gt;在 RL 过程中，奖励函数不再是标量，而是一个序列：
$$ \mathcal{R} = {r(S_1), r(S_2), ..., r(S_n)} $$
这允许 PPO 或 GRPO 进行&lt;strong&gt;更密集的奖励信号反馈&lt;/strong&gt;，显著提升模型处理复杂推理问题的逻辑严密性。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;案例分析：DeepSeek-R1 的强化学习范式&lt;/h2&gt;
&lt;p&gt;DeepSeek-R1 展示了 GRPO 结合“规则奖励”的惊人效果：&lt;/p&gt;
&lt;h3&gt;规则导向的 PRM&lt;/h3&gt;
&lt;p&gt;在推理任务中，我们有时不需要神经网络做奖励模型，而是使用&lt;strong&gt;硬规则&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;准确性奖励 (Accuracy)&lt;/strong&gt;：答案必须对（例如数学题，提取最后的结果比对）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;格式奖励 (Format)&lt;/strong&gt;：思维链必须放在 &lt;code&gt;&amp;#x3C;think&gt;&lt;/code&gt; 和 &lt;code&gt;&amp;#x3C;/think&gt;&lt;/code&gt; 标签之间。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;涌现能力&lt;/h3&gt;
&lt;p&gt;DeepSeek 发现，通过这种简单的 GRPO + 规则奖励，模型在训练过程中会出现&lt;strong&gt;自我反思（Self-reflection）&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当模型发现之前的推导有问题时，它会自发地写下“Wait, that&apos;s not right...”（等一下，这不对……），然后重新推导。&lt;/li&gt;
&lt;li&gt;这种能力并不是通过 SFT（监督微调）刻意教出来的，而是通过 RL 最大化奖励的过程中&lt;strong&gt;自发进化&lt;/strong&gt;出来的。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;总结与对比：PPO vs. GRPO&lt;/h2&gt;
&lt;p&gt;| 维度           | PPO (标准版)               | GRPO (组相对版)             |
| :------------- | :------------------------- | :-------------------------- |
| &lt;strong&gt;显存消耗&lt;/strong&gt;   | 高 (需要 Critic 网络)      | &lt;strong&gt;低 (丢弃 Critic 网络)&lt;/strong&gt;   |
| &lt;strong&gt;优势估计&lt;/strong&gt;   | 依赖学出来的 $V(s)$        | &lt;strong&gt;依赖组内样本的统计分布&lt;/strong&gt;  |
| &lt;strong&gt;训练稳定性&lt;/strong&gt; | 容易受 Critic 网络波动影响 | 更稳定，因为对比是相对的    |
| &lt;strong&gt;主要应用&lt;/strong&gt;   | 早期 ChatGPT, Llama 3 对齐 | &lt;strong&gt;DeepSeek-R1/V3 推理训练&lt;/strong&gt; |&lt;/p&gt;
&lt;h3&gt;启示&lt;/h3&gt;
&lt;p&gt;强化学习在大模型时代的进化方向非常明确：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;算力优化&lt;/strong&gt;：通过像 GRPO 这样的算法减少训练开销。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;逻辑监督&lt;/strong&gt;：通过 PRM 或 规则奖励 强化模型的推理过程，而不仅仅是最终答案。&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://pic.hana0721.top/142350190_p0_master1200.2yyy3wl757.webp"/><enclosure url="https://pic.hana0721.top/142350190_p0_master1200.2yyy3wl757.webp"/></item><item><title>RL笔记（28）：大语言模型与强化学习 (LLM + RLHF)</title><link>https://hana-blog.top/blog/rl-note-28</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-28</guid><description>大模型的最后一块拼图：详解基于人类反馈的强化学习 (RLHF)。涵盖从 SFT 到奖励模型，以及利用 PPO 算法进行策略对齐的完整流程。</description><pubDate>Tue, 06 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的笔记中，我们一直在研究如何让智能体在物理环境或博弈环境（如 Atari, MuJoCo, SMAC）中拿高分。而现在，我们要处理的对象是 &lt;strong&gt;大语言模型 (LLM)&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;大模型的训练通常分为三个阶段：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;预训练 (Pre-training)&lt;/strong&gt;：在海量文本上通过自监督学习“知识”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指令微调 (SFT, Supervised Fine-Tuning)&lt;/strong&gt;：在高质量问答对上学习“对话格式”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;人类对齐 (Alignment)&lt;/strong&gt;：通过强化学习，让模型生成符合人类价值观（有用、诚实、无害）的内容。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;为什么要用 RL 而不是 SFT？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;难以写出标准答案&lt;/strong&gt;：对于“写一首诗”这种开放性问题，不存在唯一的正解（Label）。人类可以很容易判断谁写得更好，但很难写出完美的示范。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分布偏移问题&lt;/strong&gt;：SFT 属于行为克隆（BC），如果模型在生成时产生了一个没见过的词，误差会迅速累积。RL 则让模型在“试错”中学会从各种回复中找到最优路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;核心映射：将 LLM 建模为 RL 问题&lt;/h2&gt;
&lt;p&gt;要用强化学习训练 LLM，我们首先需要将文本生成过程对应到 MDP 五元组中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;状态 (State, $s$)&lt;/strong&gt;：当前输入的提示词（Prompt）以及模型已经生成的 Token 序列。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作 (Action, $a$)&lt;/strong&gt;：模型预测的下一个 Token。动作空间就是词表（Vocab Size，通常为 3w ~ 10w）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;策略 (Policy, $\pi$)&lt;/strong&gt;：大语言模型本身，输入提示词，输出下一个词的概率分布。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;奖励 (Reward, $R$)&lt;/strong&gt;：反映生成的一整段话好不好。这个奖励不是环境给的，而是由 &lt;strong&gt;奖励模型 (Reward Model)&lt;/strong&gt; 评分。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;RLHF 的三大步骤&lt;/h2&gt;
&lt;p&gt;RLHF (Reinforcement Learning from Human Feedback) 通常包含以下经典流程：&lt;/p&gt;
&lt;h3&gt;阶段一：监督微调 (SFT)&lt;/h3&gt;
&lt;p&gt;在预训练模型的基础上，使用人类编写的高质量 (Prompt, Answer) 数据集进行微调。此时模型学会了基本的指令遵循能力。&lt;/p&gt;
&lt;h3&gt;阶段二：奖励模型训练 (Reward Modeling)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;给同一个 Prompt，让 SFT 后的模型生成多个不同的回复 ${y^1, y^2, y^3, y^4}$。&lt;/li&gt;
&lt;li&gt;让人类对这些回复进行排序（例如 $y^2 &gt; y^1 &gt; y^4 &gt; y^3$）。&lt;/li&gt;
&lt;li&gt;训练一个标量奖励模型 $r_\theta(x, y)$，使其输出的分数符合人类的排序规律。损失函数通常采用 &lt;strong&gt;Pairwise Ranking Loss&lt;/strong&gt;：
$$ L(\theta) = - \mathbb{E}&lt;em&gt;{(x, y_w, y_l) \sim \mathcal{D}} \left[ \log \sigma \left( r&lt;/em&gt;\theta(x, y&lt;em&gt;w) - r&lt;/em&gt;\theta(x, y_l) \right) \right] $$
其中 $y_w$ 是胜出的回复，$y_l$ 是失败的回复。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;阶段三：强化学习对齐 (PPO)&lt;/h3&gt;
&lt;p&gt;利用奖励模型给出的分数，通过 PPO 算法调整 LLM 的参数。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;RLHF 中的 PPO 目标函数&lt;/h2&gt;
&lt;p&gt;在对齐阶段，我们的目标是最大化奖励模型的分数。但如果只考虑奖励，模型可能会学会“钻空子”（Reward Hacking），生成一些人类看不懂但奖励模型给高分的乱码。&lt;/p&gt;
&lt;p&gt;因此，我们需要约束模型，使其不要偏离原始模型太远。目标函数定义为：&lt;/p&gt;
&lt;p&gt;$$
J(\phi) = \mathbb{E}&lt;em&gt;{x \sim \mathcal{D}, y \sim \pi&lt;/em&gt;\phi(y|x)} \left[ r_\theta(x, y) - \beta \log \left( \frac{\pi_\phi(y|x)}{\pi_{\text{ref}}(y|x)} \right) \right]
$$&lt;/p&gt;
&lt;h3&gt;公式拆解：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;$r_\theta(x, y)$&lt;/strong&gt;：奖励模型对生成的完整句子 $y$ 给出的预测分数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\log \left( \frac{\pi_\phi(y|x)}{\pi_{\text{ref}}(y|x)} \right)$&lt;/strong&gt;：这是新策略 $\pi_\phi$ 与参考模型（通常是 SFT 后的模型）$\pi_{\text{ref}}$ 之间的 &lt;strong&gt;KL 散度&lt;/strong&gt;（准确说是 KL 惩罚项）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\beta$&lt;/strong&gt;：KL 惩罚系数。$\beta$ 越大，模型越保守，越像原始模型。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉理解&lt;/strong&gt;：
这一项的作用是：&lt;strong&gt;“你可以尽量讨好人类，但不能忘了怎么说话。”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;LLM-PPO 的训练架构&lt;/h2&gt;
&lt;p&gt;在这一阶段，显存中通常需要同时加载四个模型（通常采用参数共享或层冻结来优化）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Actor (Policy Network)&lt;/strong&gt;：当前正在训练的 LLM，参数为 $\phi$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reference Model&lt;/strong&gt;：冻结的 SFT 模型，用于计算 KL 惩罚。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Critic (Value Network)&lt;/strong&gt;：一个额外的头部，输入 Token 序列，预测从当前位置开始能获得的未来总奖励（即 $V(s)$）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reward Model&lt;/strong&gt;：冻结的评分模型。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;训练循环：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;采样 (Rollout)&lt;/strong&gt;：输入 Prompt $x$，Actor 生成回复 $y$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;评分 (Evaluation)&lt;/strong&gt;：Reward Model 对 $(x, y)$ 打分。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;计算优势 (Advantage)&lt;/strong&gt;：
利用序列中的每一个 Token 的 TD Error 来计算优势 $\hat{A}$。
注意：在 LLM 中，奖励通常是在&lt;strong&gt;最后一个 Token&lt;/strong&gt; 给出的，而前面的 Token 奖励为 0（除了 KL 惩罚）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新 (Update)&lt;/strong&gt;：
利用 PPO 的 Clip 损失函数更新 Actor，利用 MSE 损失更新 Critic。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;进阶：DPO (Direct Preference Optimization)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/2305.18290&quot;&gt;Direct Preference Optimization: Your Language Model is Secretly a Reward Model&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PPO 虽然经典，但非常复杂（需要 4 个模型，显存开销巨大，训练极不稳定）。
2023 年提出的 &lt;strong&gt;DPO&lt;/strong&gt; 彻底简化了这个过程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DPO 的核心发现&lt;/strong&gt;：
我们可以通过数学变换，直接将奖励函数 $r$ 表达为最优策略 $\pi^*$ 的函数。这意味着我们&lt;strong&gt;不需要训练奖励模型&lt;/strong&gt;，也不需要强化学习，直接在偏好数据 $(y_w, y_l)$ 上进行&lt;strong&gt;监督式学习&lt;/strong&gt;即可。&lt;/p&gt;
&lt;p&gt;DPO 损失函数：&lt;/p&gt;
&lt;p&gt;$$
L_{DPO}(\pi_\phi; \pi_{\text{ref}}) = - \mathbb{E}&lt;em&gt;{(x, y_w, y_l) \sim \mathcal{D}} \left[ \log \sigma \left( \beta \log \frac{\pi&lt;/em&gt;\phi(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \beta \log \frac{\pi_\phi(y_l|x)}{\pi_{\text{ref}}(y_l|x)} \right) \right]
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直觉&lt;/strong&gt;：如果模型在好回复 $y_w$ 上的概率提升比在坏回复 $y_l$ 上快，Loss 就变小。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;LLM + RL 的结合标志着强化学习从“解决玩具问题”走向了“赋能通用人工智能”。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;RLHF&lt;/strong&gt; 建立了模型行为与人类偏好的桥梁。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PPO&lt;/strong&gt; 提供了稳定的优化框架，通过 KL 惩罚保证了语言的自然性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DPO&lt;/strong&gt; 等后续算法进一步降低了对齐的门槛，使得中小型团队也能进行模型对齐。&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://pic.hana0721.top/139369087_p0_master1200.9gx5x7w6ei.webp"/><enclosure url="https://pic.hana0721.top/139369087_p0_master1200.9gx5x7w6ei.webp"/></item><item><title>Paper Reading: Embodied AI 2</title><link>https://hana-blog.top/blog/paper-reading-eba2</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-eba2</guid><description>从零开始的Embodied AI研究生活。</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;import { ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;Embodied AI Paper Reading&apos;,
items: [
{
title: &apos;Batch 1&apos;,
href: &apos;/blog/paper-reading-eba1&apos;,
order: &apos;1&apos;
},
{
title: &apos;Batch 2&apos;,
href: &apos;/blog/paper-reading-eba2&apos;,
order: &apos;2&apos;
},
{
title: &apos;Batch 3&apos;,
href: &apos;/blog/paper-reading-eba3&apos;,
order: &apos;3&apos;
},
{
title: &apos;Batch 4&apos;,
href: &apos;/blog/paper-reading-eba4&apos;,
order: &apos;4&apos;
},
{
title: &apos;Batch 5&apos;,
href: &apos;/blog/paper-reading-eba5&apos;,
order: &apos;5&apos;
}
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;RL菜鸡开始进军Embodied AI，慢慢积累，提升自己。&lt;/p&gt;
&lt;h2&gt;LLARVA&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/LLARVA.8z70f5s0j6.webp&quot; alt=&quot;&quot;&gt;
LLARVA 是一种 OpenVLA-like 的 VLA，使用 Instruction Tuning 进行微调，并通过输出 2D Visual Trace 去引导 Action 生成。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/LLARVA-prompt.7axnhz1uu9.webp&quot; alt=&quot;&quot;&gt;
LLARVA 的输入指令被规范化为 Prompt，详细见图。
LLM 的主干为 LLaMA2 7B，并使用 LoRA 进行微调。
先预测 2D Visual Trace 再生成 Action Chunk，本质上是基于 CoT 的思想。&lt;/p&gt;
&lt;h2&gt;ATM&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/ATM.83aizqrop9.webp&quot; alt=&quot;&quot;&gt;
ATM 的亮点在于仅可以使用 Action-free 的数据去训练机器人操作策略。
ATM 是一种 Pipeline 方法，分为任意点轨迹建模与轨迹引导策略学习两个部分。
在任意点轨迹建模部分，ATM 利用了大量 Label-free 的视频训练了一个 Points-Tracker，输入是当前观察 Obs、任务 Instruction 以及初始 Points，输出是未来的 Points-Trajectory。
在轨迹引导策略学习部分，策略网络以上个步骤预测的 Points-Trajectory 以及当前 Obs 为输入，输出机器人的 Action。&lt;/p&gt;
&lt;p&gt;任意点轨迹建模：
在数据处理部分，ATM 使用了离线的 Points-Tracker 模型去得出的 Label。
同时，过滤掉了大部分静止的 Points，只保留了 32 个高频活动的 Points。
对于当前 Obs，使用 ViT 将图像切分为 Patches，并随机 Mask 掉 50% 的 Patches，得出 Image Token。
对于任务 Instruction，使用 BERT 进行信息提取 Language Token。
对于初始 Points，在文中是有32个，编码为 Track Token。
以 Obs 以及 Instruction 为 Base 的 Points-Tracker 被定义为一个 Transformer，输入为 Image Token、Language Token 以及 Track Token，输出为未来的 Points-Trajectory。
按照题主的理解，输出的 Points-Trajectory 其实就是任务导向的 Vision-Language Fusion。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/ATM-policy.9kgo1hvysf.webp&quot; alt=&quot;&quot;&gt;
轨迹引导策略学习：
首先，使用了 BERT-like 的 Transformer 对 Image Token 以及 Track Token 进行 Fusion，得出特征 [CLS]。
然后，策略网络设定为 MLP，以 [CLS] 以及 Track Token 为输入，输出机器人的 Action。
在策略学习的过程中，并没有引入 Instruction 的信息，因为在轨迹建模阶段已经引入了。
同时，对 Track Token 进行了两次 Fusion，这样会有更好的效果，详细见原文。&lt;/p&gt;
&lt;h2&gt;Track2Act&lt;/h2&gt;
&lt;p&gt;Track2Act 本质上和 ATM 是一样的，都是以 Points-Trajectory 去引导动作的生成。
Track2Act 主要分为三个阶段：Points-Trajectory 预测、Open-loop 动作求解以及 Residual Policy 闭环修正。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/Track2Act-pt.13m9i5xvjf.webp&quot; alt=&quot;&quot;&gt;
在 Points-Trajectory 预测阶段，Track2Act 使用了 DiT 生成模型。
DiT 的 Condition 被设置为初始图像 $I_0$ 以及目标图像 $\mathcal{G}$ 的表征，给定随机采样的查询点集 $P_0$，生成 Points-Trajectory。&lt;/p&gt;
&lt;p&gt;在 Open-loop 动作求解阶段，Track2Act 使用了一个确定性的算法进行刚性动作的求解，不涉及神经网络训练。
这个算法的输入是 Points-Trajectory，输出一系列的刚性变换 $[\textbf{T}&lt;em&gt;t]&lt;/em&gt;{t=1}^H$。
具体求解算法见原文，这里不详细展开。
最后，基于刚性变换，可以求解出机器人的 Open-loop 动作序列 $[\bar{a}&lt;em&gt;t]&lt;/em&gt;{t=1}^H$。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/Track2Act-rp.4n877z0ttz.webp&quot; alt=&quot;&quot;&gt;
在 Residual Policy 闭环修正阶段，Track2Act 使用一个 Residual Policy 去修正 Open-loop 动作。其实就是使用一个 Transformer-Encoder 去预测误差，最后加在一起。&lt;/p&gt;
&lt;p&gt;最后简单瑞萍一下，其实博主觉得后面两个阶段完成可以使用神经网络进行表征，有点多余，虽然残差修正动作这个思想是好的。&lt;/p&gt;
&lt;h2&gt;ECoT&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/ECoT.73ufntxfff.webp&quot; alt=&quot;&quot;&gt;
ECoT 这篇工作指出 LLM 那边的 CoT 是无法直接迁移到 Embodied 任务环境中的。
若只是简单的语义推理，那么就会出现大量不符合实际物理环境的幻觉。
因此，ECoT 在 CoT 的基础上添加了智能体对环境的感知，从而使得 CoT 具有 Embodied 性质。
ECoT 的具体设置见上图，和 CoT 类似，只是加入了 Embodied 的感知。
ECoT 使用的模型基座是 OpenVLA，使用 ECoT 后效果得到了很好的改善。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/ECoT-data.1vz50u4k0o.webp&quot; alt=&quot;&quot;&gt;
ECoT 还提出了一套使用机器人数据产生 CoT 训练数据的 Pipeline。
具体就不多说了，图里面说的很清楚。&lt;/p&gt;
&lt;p&gt;这种推理过程是显式的，具有一定的解释性，缺点就是这样会降低推理的速度，文中也给了一些解决方案。&lt;/p&gt;
&lt;h2&gt;VoxPoser&lt;/h2&gt;
&lt;p&gt;VoxPoser 是一种 Code for Robotic 的方法。
简单的来说，VoxPoser 使用 LLM 去生成 Python 代码，这里的代码会调用 VLM 的 API 生成 3D Value Map，然后进行 MPC。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/VoxPoser.1vz50wovrd.webp&quot; alt=&quot;&quot;&gt;
首先，VoxPoser 会告诉 LLM 任务的文本描述以及一些 VLM 具有的 API函数，让它去产生能够基于 RGB-D 图像计算 3D Value Map 的 Python 代码。
然后，使用 Python 执行器去执行代码，过程中会调用 VLM 的 API，其实就是 OWL-ViT 获取边框还有 SAM 获取 Mask，然后重建 3D Points Cloud，计算得出 3D Value Map。
最后，有了 3D Value Map 作为价值引导，就可以使用 MPC 的方法合成轨迹，进行规划。
在文中，提供了在线训练 MPC 以及 启发式 MPC 两种方式，前者需要环境交互，后者则无需训练。&lt;/p&gt;
&lt;h2&gt;PIVOT&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/PIVOT-example.2rvmgdu2g2.webp&quot; alt=&quot;&quot;&gt;
PIVOT 的思想很简单，就是把机器人的低级 Action 投影到图像中，并采用 VQA 的形式让 VLM 去选择 Action 集合。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/PIVOT.szfq1osvp.webp&quot; alt=&quot;&quot;&gt;
PIVOT 维护一个 Action 的分布，初始化为均匀分布，有点像 MPC 中的交叉熵方法。
PIVOT 使用的是 Zero-Shot 的迭代算法，不断去优化 Action 的分布，直到收敛。
首先，PIVOT 从分布中采样一系列的 Action，然后将 Action 投影到 2D 图像上，详细见图，每个 Action 都对应了序号。
然后，给定任务描述文本以及 2D 图像，编写合适的 Prompt 让 VLM 去选择最优 Action 集合。
接着，根据选取的 Action 集合去维护 Action 分布，类似于 MPC 中的交叉熵方法。
经过不断迭代，可以得出较好的 Action 候选集合。&lt;/p&gt;
&lt;p&gt;PIVOT 的思路很有启发性，但缺陷也是明显的。比如，3D 歧义理解、细粒度控制以及 VLM 的幻觉问题。&lt;/p&gt;
&lt;h2&gt;Code As Policies&lt;/h2&gt;
&lt;p&gt;CaP 核心思想不是训练策略网络，而是通过 Prompt 让 LLM 去调用机器人 API，从而完成任务。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/CaP.77e1n0y43g.webp&quot; alt=&quot;&quot;&gt;
首先，CaP 进行了 LMP 形式化，其实就是让策略利用 Python 语言的一些特性。
然后，为了处理复杂任务代码过长或者逻辑复杂的问题，CaP 使用了层次化代码生成的方法，其实就是自下而上分而治之的思想。
最后，CaP 进行了感知与动作的对齐。
LLM 会基于 Prompt 得出一些预定义的感知 API 以及 控制 API。
LLM 可以通过感知 API 去获取视觉信息的变量。
LLM 会计算控制 API 的参数，并执行。&lt;/p&gt;
&lt;p&gt;CaP 的工作虽然简单，但它将高层规划和底层执行联系在了一起。&lt;/p&gt;
&lt;h2&gt;RoboPoint&lt;/h2&gt;
&lt;p&gt;RoboPoint 通过构建一套程序化合成数据生成流水线，将通用视觉语言模型（VLM）微调为能根据自然语言指令预测精确 2D 空间操作点（Spatial Affordance）的模型，从而实现了无需真实世界数据训练的机器人零样本（Zero-shot）操控与导航。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/RoboPoint.lw7w2yur8.webp&quot; alt=&quot;&quot;&gt;
在数据生成阶段，RoboPoint 利用程序化生成技术构建多样化的 3D 仿真场景，通过利用模拟器中的精确几何信息自动计算物体间的空间关系，并创造性地采用“移除目标物体再采样”的策略来自动标注自由空间（Free Space），从而低成本地生成了海量包含“图像-指令-像素坐标点”的高质量合成训练数据。&lt;/p&gt;
&lt;p&gt;在指令微调阶段，RoboPoint 套用 LLaVA 的架构，冻住视觉层只练语言模型，把“找坐标”直接转化成“文本生成”任务，让模型直接输出坐标数字
同时，为了防止了 VLM 的遗忘，加入了 VQA 数据进行 Co-Training。&lt;/p&gt;
&lt;p&gt;在真实执行阶段，先使用 RoboPoint 计算出 RGB 图像上的 2D 坐标，然后结合深度图转换为 3D 坐标，最后直接套用一个预设好的抓取姿态，把目标传给传统的运动规划算法去解算路径，机械臂照做就行了。&lt;/p&gt;
&lt;p&gt;RoboPoint 其实也是一种高层决策的规划，也是一种 System 2 的方法。&lt;/p&gt;
&lt;h2&gt;GR-1&lt;/h2&gt;
&lt;p&gt;GR-1 证明了视频生成是策略学习的高效代理任务，它通过大规模视频生成式预训练迫使 GPT 模型内化环境动态与物理先验，从而在下游机器人任务中实现了卓越的小样本学习与零样本泛化能力。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/GR-1-all.1sfj4q0vsm.webp&quot; alt=&quot;&quot;&gt;
GR-1 采用 GPT-style Causal Transformer 架构处理多模态交织序列，先通过大规模视频生成预训练学习环境动态，再经由动作与未来帧联合预测任务进行微调，实现端到端的自回归策略学习。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/GR-1-input.2vf8flx9ka.webp&quot; alt=&quot;&quot;&gt;
在预训练阶段，GR-1 利用大规模人类第一视角视频数据集（Ego4D）执行语言条件下的视频预测任务，旨在无需动作标注即可习得通用物理动态。
模型架构上冻结了 CLIP 文本编码器与 MAE-ViT 视觉编码器（辅以 Perceiver Resampler 压缩特征），将语言指令、历史图像帧与可学习的 [OBS] Token 交织输入至 GPT 主干网络。
训练过程中，模型屏蔽 [ACT] Token，仅通过因果掩码注意力机制利用 [OBS] Token 驱动视觉解码器（Vision Decoder）重建未来帧像素，从而迫使模型内化视觉演变规律与视语对齐关系，为下游机器人操作任务提供具备强泛化能力的 World Model 初始化权重。&lt;/p&gt;
&lt;p&gt;在微调阶段，GR-1 加载预训练权重并扩展输入序列，将机器人本体状态（Proprioceptive State）与语言、图像交织输入，引入 [ACT] Token 用于回归 7-DoF 机械臂动作与夹爪状态。
训练采用多任务联合优化策略，在执行行为克隆（Behavior Cloning）学习策略分布的同时，保留未来帧预测（Video Prediction）作为辅助任务，通过联合最小化动作回归损失与图像重建损失，迫使策略在决策时显式利用预训练阶段习得的物理世界动态（World Model），从而实现感知到动作的高效迁移与对齐。&lt;/p&gt;
&lt;p&gt;GR-1 的 Limitation 也是明显的。
因为要处理高维图像，所以导致推理延迟较高。
同时预训练的人类视频与机械臂控制之间存在形态鸿沟（Embodiment Gap），目前仅依靠微调隐式对齐而缺乏显式的动作重定向机制。&lt;/p&gt;
&lt;h2&gt;HPT&lt;/h2&gt;
&lt;p&gt;HPT 使用一种模块化 Token 对齐机制将异构机器人的 Proprioception 与 Vision 映射到共享潜空间到通用策略架构，并在大规模 Cross-Embodied 数据上验证了机器人策略学习的 Scaling Laws 以及迁移能力。&lt;/p&gt;
&lt;p&gt;HPT 采用了一种高度模块化的 Stem-Trunk-Head 设计范式来解决 Cross-Embodied 难题。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/HPT-Stem.8z70jpnbae.webp&quot; alt=&quot;&quot;&gt;
在 Stem 模块中，利用 Cross-Attention 将异构的 Proprioception 与 Vision 对齐到统一的 Embodied 潜在表征。首先，机器人的 Vision 信息被编码成 Cross-Attention 中的 K 与 V，并与一组可学习的 Q 进行聚合得出 Vision Token。同样，机器人的 Proprioception 也经过相同的操作，得出 Proprio. Token。不同的 Embodied 对应了不同 Stem，是分别进行学习的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/HPT.2yyuezdjdc.webp&quot; alt=&quot;&quot;&gt;
在 Trunk 模块中，所有 Embodied 任务共享一个 Transformer，负责将 Stem 对齐后的多模态 Token 进行深度融合和推理。最终，经过池化后得出最终 Latent。&lt;/p&gt;
&lt;p&gt;在 Head 模块中，负责将 Trunk 得出 Latent 进行解码，这里的解码器可以是 MLP、DP 以及 ACT，得出机器人的 Action。请注意，异构的机器人分别对应了不同的 Head。&lt;/p&gt;
&lt;p&gt;在训练数据上，HPT 使用 OXE、Simulation 以及 Human Videos 等。
HPT 使用了最暴力的 BC 方法，证明了其 Scaling Law 的存在。&lt;/p&gt;
&lt;h2&gt;RoboDual&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/RoboDual-Overview.86u6gy6bzq.webp&quot; alt=&quot;&quot;&gt;
RoboDual 的整体架构由负责 High-level 决策的 Generalist 与负责 Low-level 决策的 Speicalist 组成。
其中，Generalist 是类似于 OpenVLA 的 VLA 模型，虽然具有一定的泛化能力，但推理效率比较低。
而 Specialist 是一种类似于 ACT 或是 DiT 的决策模型，在特定领域上有比较好的效果，且推理效率高，但难以适应新的任务环境。
RoboDual 做的事情就是将两者结合在一起，Generalist 负责 High-level 的任务规划，而 Specialist 进行 Low-level 的实时决策。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/RoboDual.et1hb05w6.webp&quot; alt=&quot;&quot;&gt;
RoboDual 的具体实现很简单，就是将 Generalist 归纳出的 Task Latent 与 Action Latnet 作为 Specialist 的 Condition，然后让 Specialist 输出 Action Chunk。
在文章中，Generalist 是 OpenVLA 架构，Specialist 是 DiT 架构。
由于 Generalist 和 Specialist 的推理效率不同，RoboDual 在推理过程中使用双线程的方式，即 Generalist 和 Specialist 分别使用独立的线程。
在实际的使用中，Specialist 拿到的 Generalist 的提示总是有延迟的。因此，RoboDual 在实际训练中采用了延迟感知训练的 Trick。&lt;/p&gt;
&lt;p&gt;实际上，RoboDual 在对 OpenVLA 进行了一定修改，让其输出 Action Chunk。因此，Specialist 是有一段比较粗略的动作轨迹作为指导的，它只是进一步地细化这段动作轨迹，相当于 Specialist 对 Generalist 的输出结果进行插值。&lt;/p&gt;
&lt;h2&gt;GR-2&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/GR-2.7i0wy0nlxr.webp&quot; alt=&quot;&quot;&gt;
GR-2 的思路和 GR-1 的一样，只是扩展了更多的优质数据集。
具体做法分为两个阶段。
首先，在大规模的 Web 级别的 Video 数据集预训练一个 World Model。
然后，在机器人数据上进行微调，得出一个 End-to-end 的 VLA 模型。&lt;/p&gt;
&lt;h2&gt;Humanoid Manipulation&lt;/h2&gt;
&lt;p&gt;{/* 这篇工作对 DP3 进行改进，并将其应用到通用机器人操作任务。 &lt;em&gt;/}
{/&lt;/em&gt; 该研究使用 Fourier GR1 作为实验平台，并固定住双腿，以确保稳定性。 &lt;em&gt;/}
{/&lt;/em&gt; 在数据采集方面，使用了遥操作收集。 &lt;em&gt;/}
{/&lt;/em&gt; 观测主要包含了 Camera 的 3D 点云以及机器人本体感知。 &lt;em&gt;/}
{/&lt;/em&gt; 在策略学习中，因为机器人的 Camera 并不是固定的，所以该研究提出了 iDP3，也就是以 Camera 作为参考系去调整 3D 点云的坐标，以适应任务环境。 &lt;em&gt;/}
{/&lt;/em&gt; 同时，使用卷积去代替 MLP 处理视觉信息会更加的平滑。 &lt;em&gt;/}
{/&lt;/em&gt; 最后，延长预测时域使得策略更加的平稳一致。 */}&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/iDP3.45i746rn30.webp&quot; alt=&quot;&quot;&gt;
这项工作对 DP3 算法进行了针对性改进，目的是让其能更好地服务于通用机器人的操作任务。
研究使用了 Fourier GR1 作为实验载体，为了保证操作过程中的绝对稳定，机器人的双腿被固定住，数据则通过遥操作的方式进行收集。
系统的输入端主要包含相机的 3D 点云以及机器人的本体感知数据。
在策略学习阶段，作者注意到机器人的相机并非固定不动，因此提出了名为 iDP3 的新方法，即以相机自身作为参考系去标准化 3D 点云的坐标，以此适应动态的任务环境。
此外，为了让视觉信息的处理更加平滑，算法使用卷积层代替了传统的 MLP。
最后配合延长的预测时域，成功使得机器人生成的策略动作更加平稳和连贯。&lt;/p&gt;
&lt;h2&gt;Surfer&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/Surfer.175x1vj6ob.webp&quot; alt=&quot;&quot;&gt;
Surfer 提出了一个基于世界模型（World Model）的架构，旨在通过接收历史图像帧（History Frames）、语言指令（Instruction）以及机器人的本体感知信息（Proprioception）来预测下一步的动作（Action）及执行后的未来帧。在多模态特征编码方面，Surfer 采用了高效的处理策略：对于视觉输入，利用冻结的 CLIP 编码器将图像帧转化为 Tokens，并引入 TokenLearner 进行压缩以提升推理效率；对于文本输入，使用冻结的 Text Encoder 提取 Text Latent；对于本体感知信息，则通过 MLP 编码得到 Proprio Latent。&lt;/p&gt;
&lt;p&gt;Surfer 的世界模型主体由两个基于多层 Transformer 的模块构成，分别负责动作预测与场景预测。在动作预测模块中，模型以历史帧的 Visual Tokens 作为 Query，将拼接后的 Text Latent 和 Proprio Latent 作为 Key 和 Value，通过 Cross-Attention 机制生成当前时间步的 Action。随后，帧预测模块复用历史帧 Tokens 作为 Query，并将刚刚预测出的 Action 作为 Key 和 Value，同样利用 Cross-Attention 推导出执行动作后的未来帧 Tokens，并采用 L2 Loss 进行监督训练。&lt;/p&gt;
&lt;p&gt;总结来看，Surfer 的核心亮点在于引入了未来帧预测作为动作决策的辅助监督信号。这种设计符合直觉：如果模型生成的动作能够准确预测出环境的未来状态，说明该动作决策本身是符合物理逻辑的。然而，这种方法的局限性也显而易见：一旦帧预测模块不够准确，错误的预测反而会引入噪声，形成负面监督。此外，该工作尚未验证模型在 Scaling up（扩大规模）后的表现，其在大规模数据下的潜力仍有待探索。&lt;/p&gt;
&lt;p&gt;{/* Surfer 构建了一个 World Model，以历史 Frame、语言 Instruction 以及机器人感知 Proprio 为输入，输出下一步的 Action 以及 Frame。 &lt;em&gt;/}
{/&lt;/em&gt; 在 Vision 处理方面，Surfer 使用用冻结的 CLIP 将 Frame 处理成 Tokens，并使用 TokenLearner 对这些 Tokens，以提高推理效率。 &lt;em&gt;/}
{/&lt;/em&gt; 在 Text 处理方面，Surfer 使用冻结的 Text Encoder 处理成 Text Latent。 &lt;em&gt;/}
{/&lt;/em&gt; 在 Proprio 处理方面，Surfer 使用 MLP 去学习得出 Proprio Latent。 &lt;em&gt;/}
{/&lt;/em&gt;  &lt;em&gt;/}
{/&lt;/em&gt; Surfer 的 World Model 分为两个部分，分别负责预测当前时间步的 Action 以及执行后的 Frame。 &lt;em&gt;/}
{/&lt;/em&gt; 这两个部分都是由多层的 Transformer 模块组成。 &lt;em&gt;/}
{/&lt;/em&gt; 在 Action 预测模块，使用经过 CLIP 处理的历史 Frame Tokens 作为 Query，[Text Latent, Proprio Latent] 作为 Key 和 Value，经过 Cross-Attention 得出 Action。 &lt;em&gt;/}
{/&lt;/em&gt; 在 Frame 预测模块，使用经过 CLIP 处理的历史 Frame Tokens 作为 Query，预测的 Action 作为 Key 和 Value，经过 Cross-Attention 得出执行 Action 后的 Frame Tokens，并使用 L2 Loss 来进行训练。 &lt;em&gt;/}
{/&lt;/em&gt;  &lt;em&gt;/}
{/&lt;/em&gt; 小结一下，Surfer 的亮点在于使用未来的 Frame 作为训练 Action 决策的监督信号，这相当于就是说如果执行这个 Action 能够预测出未来的 Frame，那么我的预测就是合理的，相当的符合直觉。缺点也是明显的，如果 Frame 预测得不准，那么这种监督反而还是一种负面的，且 Surfer 并没有验证 Scaling up 后的效果。 */}&lt;/p&gt;
&lt;h2&gt;SceneVerse&lt;/h2&gt;
&lt;p&gt;SceneVerse 是一个大型的 3D-VL 数据集，总共包含 2.5M 个场景-语言对，由 ScanNet、ARKitScenes、HM3D、3RScan、MultiScan、Structured3D、ProcTHOR 等多个 3D 数据集集合而成。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/SceneVerse-data.41ylez0j3l.webp&quot; alt=&quot;&quot;&gt;
为了确保不同数据源之间的一致性，每个 Scan 处理成了 $\mathbb{R}^{N \times 8}$ 的维度，分别为 3D 坐标、RGB 颜色、实例 ID 和语义标签定义。
首先，SceneVerse 将 3D 场景建模为 Graph，Object 为 Node，Object 之间的空间关系被建模为 Edge。
接着，SceneVerse 使用一种基于 Prompt 的 Pipeline 方法去生成不同纬度的 Caption，详细见图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/SceneVerse-GPS.70avihp8so.webp&quot; alt=&quot;&quot;&gt;
最后，SceneVerse 训练了一个叫 GPS 的 Transformer，用于对齐 3D 场景和文本信息，类似于 3D 领域中的 CLIP。
SceneVerse 的工作量还是很足的，值得肯定。&lt;/p&gt;
&lt;h2&gt;Robot See Robot Do&lt;/h2&gt;
&lt;p&gt;Robot See Robot Do 是一种 Pipeline 方法，用于完成 Articulated Object Manipulation 任务，只需单目人类示例即可完成学习。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/RSRD-overview.5xb68ueasx.webp&quot; alt=&quot;&quot;&gt;
RSRD 分为 See 和 Do 两个部分。在 See 的过程中，首先对静态 Object 进行特征学习。具体流程如下，基于环绕待操作 Object 的视频，先使用 3D Gaussian Splatting 转换为 3D 点云，再使用 GARField 对点进行聚类，最后使用 DINOv2 学习点的特征表示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/RSRD-4D.4qrv08p81o.webp&quot; alt=&quot;&quot;&gt;
最后，从人类演示视频中，反解出这每个部件在每一帧的 3D 位姿。具体流程如下，首先对每时间步可优化的部件姿态参数渲染出 DINO 特征和深度，并与输入帧中提取的 DINO 特征和单目深度进行比较。然后，使用 ARAP 损失惩罚高斯分布偏离其初始配置过远的情况，相对于邻近点。这些损失共同反向传播至部件姿态，并通过梯度下降进行优化，以恢复 3D 部件运动。在 Do 的过程中，则将物体的运动轨迹映射到机器人的操作中去。&lt;/p&gt;
&lt;p&gt;Robot See Robot Do 的方法虽然无需训练，但无法进行泛化，只能做一些简单的任务。&lt;/p&gt;
&lt;h2&gt;ReKep&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/Rekep.4clfp7etmb.webp&quot; alt=&quot;&quot;&gt;
来自斯坦福大学李飞飞团队（一作 Wenlong Huang）的这项工作提出了 ReKep 框架，通过利用 视觉语言模型（VLM） 将自然语言指令转化为基于 语义关键点的 Python 时空约束函数，并结合实时优化求解器，实现了无需特定任务训练即可完成 多阶段、双臂协同及动态响应的复杂机器人操作任务（如倒茶、叠衣服）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/Rekep-detail.4clfp7ezbi.webp&quot; alt=&quot;&quot;&gt;
Rekep 是一种 Pipeline 方法，主要分为「关键点提取」、「约束生成」以及「优化求解」。
在「关键点提取」阶段，Rekep 采用了 DINOv2 和 SAM 提取 Frame 中的关键点。
在「约束生成」阶段，Rekep 将带有关键点覆盖的 Frame 以及 文本指令输入到 GPT-4o 中，让其输出 Python 代码，以计算约束。
在「优化求解」阶段，Rekep 采用分层优化求解的形式，使用「寻找子目标」和「规划路径」两种优化结构。&lt;/p&gt;
&lt;p&gt;Rekep 比较的工程，像是各种模型的结合体，在实现上也比较合理，是一篇合格的工作。&lt;/p&gt;
&lt;h2&gt;OmniManip&lt;/h2&gt;
&lt;p&gt;OmniManip 的设计逻辑围绕「如何将自然语言转化为稳健的物理约束」展开。该系统并非试图让模型学会复杂的控制律，而是通过构建一个“对象中心”的规范坐标系，让模型在最适合描述物体功能的空间内进行推理。这张图展示了 OmniManip 系统的整体架构图，可以将其分为三个主要阶段：
&lt;img src=&quot;https://pic.hana0721.top/OmniManip.32iin0p6t4.webp&quot; alt=&quot;&quot;&gt;
任务感知与分解阶段：数据流的起点是用户的自然语言指令（如 &quot;Pour tea into the cup&quot;）和摄像头捕获的 RGB-D 原始图像 。
首先，系统使用 VFM 进行物体锚定（Object Grounding），具体做法是先用 Grounding DINO 计算得出 Object 的 Bounding Boxes，再使用 SAM 对 Object 生成像素级的 Mask。
随后，系统使用 VLM 进行任务划分（Stage Partitioning），根据 VLM 的知识将任务分解为多个阶段（Stages），每个阶段都明确了动作类型以及主动物体（Active）和被动物体（Passive）的角色分配 。
例如，在“倒茶”任务的第一阶段，主动物体是机器人夹持器，被动物体是茶壶，动作是“抓取”；而在第二阶段，主动物体变为茶壶，被动物体变为茶杯，动作变为“倾倒” 。这种基于物体的角色分配为后续提取交互原语奠定了逻辑基础。&lt;/p&gt;
&lt;p&gt;对象中心交互原语提取阶段：这是系统的核心，旨在将语义指令转化为精确的几何约束。
首先，系统利用 3D AIGC 技术从单图生成物体的 3D 网格模型，再通过 6D 姿态估计器（6D Pose Estimator）确定物体当前的位姿，并将其映射到功能性的「规范空间」中，使后续的推理具有视角无关性 。
&lt;img src=&quot;https://pic.hana0721.top/OmniManip-detail.70aw3p0ml1.webp&quot; alt=&quot;&quot;&gt;
随后，系统使用 VLM 提取交互原语，也就是在规范空间内识别出关键的交互点（如壶嘴、壶柄、杯口）和交互方向（如壶嘴喷出的轴线），上图展示了其中的技术细节。
左图展示了系统如何从 2D 图像中确定 3D 空间中的关键交互位置。
系统将交互点分为「可见且可触及」（Visible and Tangible，如壶柄）和「不可见或不可触及」（Invisible or Intangible，如杯底中心或被遮挡的部分）两类 。
为了帮助 VLM 克服 3D 感知不足，系统借助了 SCAFFOLD 提示机制，也就是在图像上覆盖了一层笛卡尔坐标网格（点阵），这样 VLM 就可以通过网格坐标来精确定位目标点位。
对于不可见的点，系统引导模型通过主视角定位，并结合正交视角（Orthogonal View）的几何关系来推断其在 3D 空间中的确切深度和位置 。
右图详细解释了系统如何从无数种可能的动作角度中筛选出最合适的一个。
系统不再进行全空间搜索，而是首先提取物体在规范空间中的主几何轴线作为候选方向（图中红箭头所示）。
VLM 会为每一个候选轴生成具体的自然语言描述（Captioning），例如「该轴穿过茶壶中心并垂直于注水口」。
大语言模型（LLM）接收这些语义描述，并根据当前任务（如「倒茶」）的需求对各个轴进行相关性评分。
最终输出一个排序列表，分值最高的轴（靠近 1）将被选定为执行任务的空间约束方向。
最后，系统将实现闭环规划。系统基于 VLM 去生成具体的空间约束（CONSTRAINTS），例如「主动轴与被动轴对齐」或「点间距小于 5cm」。
并基于 RRC 机制，根据生成的约束去预先渲染一张模拟交互后的 Frame，并使用 VLM 去 Checking 这张 Frame，如果不合理就重新采样，直到成功。&lt;/p&gt;
&lt;p&gt;在线轨迹规划与执行阶段：系统将逻辑约束转化为物理运动的执行过程。
系统根据上一阶段确定的几何约束，实时求解一个优化方程。该方程同时考虑了约束损失（确保动作对齐）、避障损失（防止碰撞）和平滑损失（确保运动不突兀）。
在执行过程中，系统输出一系列末端执行器的位姿序列，驱动机器人动作。
同时，6D 位姿追踪器（6D Pose Tracker）持续监测物体的实时位置。如果物体发生了意外移动，追踪器会将最新的位姿反馈给优化算法，动态更新动作，从而实现极高的鲁棒性。&lt;/p&gt;
&lt;p&gt;OmniManip 结合了之前 ViLA、VoxPoser、ReKep、CoPa 等多项技术的优势，提出了一个从语义理解到物理执行的完整解决方案。
通过对比可见，OmniManip 的先进性主要体现在将「高级常识验证」与「底层姿态优化」通过「对象中心表示」深度耦合，从而在保证通用性的同时，显著提升了执行的确定性和精度。&lt;/p&gt;
&lt;h2&gt;SOFAR&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/SOFAR.5fl56dgtmd.webp&quot; alt=&quot;&quot;&gt;
SOFAR 通过引入「语义取向」概念，将自然语言指令直接映射为 3D 空间中的几何取向向量，揭示了通过「语义-几何」深度对齐来实现具身智能开放世界 6-DoF 精细操控的核心路径。
&lt;img src=&quot;https://pic.hana0721.top/SOFAR-data.8hh17lhsa9.webp&quot; alt=&quot;&quot;&gt;
为了训练模型理解「语义取向」，SOFAR 构建了一个大规模的「语义-几何」对齐数据集，名为 OrienText300K。该数据集基于 VLM 与 Objaverse 生成，包含了 350K 个物体配对的语义取向标签。OrienText300K 的数据由三元组 [3D 网络模型, 语言描述, 语义取向] 构成，其中语义取向表示为语言描述对应的单位方向向量。
&lt;img src=&quot;https://pic.hana0721.top/SOFAR-net.7lkjs588b8.webp&quot; alt=&quot;&quot;&gt;
随后，SOFAR 基于 OrienText300K 训练了一个名为 PointSO 的 Transformer 模型，专门用于从自然语言指令和 3D 点云中预测对应的语义取向向量。由于原始点云数据量巨大，PointSO 使用最远点采样（FPS）方法将点云压缩为若干关键点，并使用 K-最近邻（KNN）算法为每个关键点寻找相邻的点，形成局部点云块（Patches）。在语言嵌入方面，PointSO 使用冻结的 CLIP 文本编码器将输入的自然语言指令转化为全局文本 Token。在 Fusion 方面，PointSO 没有采用复杂的交叉注意力，而是直接将文本 Token 投影到与几何 Token 相同的维度后，进行逐 Token 相加（Token-wise Addition）。这种策略能更有效地将语义偏置注入到每一个几何局部表征中。最后，PointSO 通过一个 MLP 层将融合后的特征映射到一个单位向量空间，输出最终的语义取向向量。
&lt;img src=&quot;https://pic.hana0721.top/SOFAR-pipeline.4qrvmct634.webp&quot; alt=&quot;&quot;&gt;
SOFAR 的工作流水线展示了从非结构化视觉输入到结构化控制输出的完整过程。首先，系统接受 RGB-D 图像以及用户自然语言指令作为输入。然后，系统使用 VLM 解析得出物品列表 (Object List) 以及取向信息提示 (Orientation Info)，这样可以将抽象的人类意图分解为具体的处理对象和语义约束，为后续的感知模块提供指引。接着，系统使用 VFM 进行物品锚定，具体做法是先用 Florence-2 得出物品的 Bounding Boxes，再使用 SAM 生成物品 Mask，并使用相机内参将 2D 坐标转换为 3D 点云。随后，系统将自然语言指令与点云输入到预训练的 PointSO 模型中，预测得出语义取向向量。最终，系统将这些信息整理成 JSON 格式的场景图以及标记图像 (Annotated Image)，以便 VLM 可以高效阅读和处理，后面就是常规的基于 VLM 的 Task Planning 或是 Manipulation 了。&lt;/p&gt;
&lt;p&gt;SOFAR 通过语义取向实现 6-DoF 对齐，其自动标注方案极具启发性。但流水线解耦易导致误差累积（占比约 74%），且 5° 精度与 8.5s 延迟制约了高精度实时任务的应用，仍需闭环优化 。&lt;/p&gt;
&lt;h2&gt;PIVOT-R&lt;/h2&gt;
&lt;p&gt;PIVOT-R 是 Surfer 的改进版本，通过引入原语建模航点（Primitive-modeled Waypoints）强化了物理逻辑，并使用异步多模型架构完成任务拆解、场景预测和动作预测，从而提升了复杂机器人操作任务的执行效率和鲁棒性。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/PIVOT-R.8dxfckhy52.webp&quot; alt=&quot;&quot;&gt;
PIVOT-R 系统基于异步分层执行器（AHE），并通过三条并行的模块实现高效推理。首先，原语解析模块接受用户自然语言指令和当前视觉观察，并使用预训练的 VLM 结合 Prompt 对场景进行语义理解，并将抽象指令转化为具体的动作原语（如「接近目标」或「抓取」）。接着，航点感知模块接受动作原语以及历史视觉观察，分别使用 Text Encoder 和 Image Encoder 进行嵌入，经过特殊设计的 Cross-Attention 模块进行融合，最终预测完成动作原语后的 Frame。最后，动作预测模块接受当前视觉观察特征、经过 MLP 投影的历史机器人状态以及源自航点感知模块的未来帧特征，经过 Cross-Attention 机制生成当前时间步的动作输出。这三个模块之间通过异步执行器进行协调，允许它们以不同的频率独立更新，从而提高系统的响应速度和效率。&lt;/p&gt;
&lt;p&gt;不同于 Surfer 逐帧对环境建模导致计算冗余，而 PIVOT-R 仅聚焦关键「航点（Waypoints）」，有效过滤了噪声并捕捉了本质物理逻辑。同时，凭借异步分层执行器（AHE），PIVOT-R 实现了 28 倍的推理效率提升，解决了 Surfer 难以实时部署的痛点。总得来说，是篇不错的改进工作，但仍然存在一些局限性，如对航点的定义和选择依赖较大，且在复杂场景下可能仍会面临误差累积的问题。&lt;/p&gt;
&lt;h2&gt;ManipGen&lt;/h2&gt;
&lt;p&gt;ManipGen 是一种基于局部策略空间不变性的框架，旨在实现长程操纵任务 sim2real 的零样本迁移。
&lt;img src=&quot;https://pic.hana0721.top/ManipGen.4xv3lsycy0.webp&quot; alt=&quot;&quot;&gt;
这张流程图展示了 ManipGen 框架的三个核心阶段：从大规模仿真专家获取，到通用视觉策略蒸馏，最后到零样本长程任务部署。在第一阶段，ManipGen 引入大规模 3D 模型数据库，并使用最远点采样（FPS）提取关键点，同时结合 Affordance 信息确定交互区域，最后在仿真环境中利用「特权信息」使用 RL 训练大量的「专家策略」，并得出大量的「专家经验」。在第二阶段，由于特权信息在现实中不可得，此阶段利用多任务 DAgger 算法将专家经验「蒸馏」给仅依赖视觉的通用模型。该策略接受来自腕部相机的实时 RGB-D、初始 RGB-D、物品分割 Mask 以及机器人本体感知作为输入，经过 ResNet18 以及 MLP 处理后输出动作。在第三阶段，ManipGen 使用 VLM 接受自然语言指令和全局视图，并解析为一系列的 [object, skill] 元组。对于每个元组，使用 Neural MP 计算从当前位置到目标物体交互区域的避障轨迹。到达目标区域后，则激活阶段二中「局部策略」，完成具体的交互动作。不断重复这一过程，直到完成整个长程任务。&lt;/p&gt;
&lt;p&gt;ManipGen 展现了很典型的 sim2real 思路，通过局部策略的空间不变性实现了长程操纵任务的零样本迁移。其创新点在于引入了「特权信息」来训练专家策略，并通过多任务 DAgger 将其蒸馏给视觉模型，从而克服了 sim2real 中的感知鸿沟。然而，该方法仍然依赖于高质量的仿真数据和专家策略的有效性，在复杂环境下可能面临泛化能力不足的问题。&lt;/p&gt;
&lt;h2&gt;DemoGen&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/DemoGen.8vnhfde3lh.png&quot; alt=&quot;&quot;&gt;
DemoGen 是一种基于 One-shot 人类演示生成大规模合成演示数据的框架，其核心思想是将机器人轨迹分解为多段 Skill Segments 和 Motion Segments，并利用点云在空间中的等变性以及连接性质来实现从单一演示到大规模数据的生成。DemoGen 的方法分为以下几个阶段。&lt;/p&gt;
&lt;p&gt;源演示的感知与语义解析。首先，DemoGen 方法接收一段使用单目 RGBD 相机记录的源轨迹作为输入，并使用一系列的点云预处理方法降低噪声与冗余。接着，DemoGen 使用 Grounded SAM 模型对点云进行分割，将点云划分为 Object Clusters 和 Effector Clusters。随后，DemoGen 根据 Effector 与 Object 几何中心距离将轨迹分解为 Skill Segments（机器人与物体接触的部分）和 Motion Segments（机器人在空中运动的部分）。
&lt;img src=&quot;https://pic.hana0721.top/DemoGen-example.9rjyutnyer.webp&quot; alt=&quot;&quot;&gt;
基于 TAMP 的动作轨迹迁移。针对一个新的场景，DemoGen 通过计算变换矩阵将源轨迹中的 Object Cloud 映射到新场景中的 Object 位置。同时，DemoGen 对 Skill Segments 进行迁移，保持机器人与物体的相对位置关系不变。针对 Effector 操作 Object 的过程，其 Object Cloud 会与 Effector Cloud 进行绑定，确保在迁移过程中保持一致的交互关系。对于 Motion Segments，DemoGen 则通过调用运动规划器（如 RRT-Connect）在 SE(3) 空间中重新规划轨迹，生成无碰撞的运动路径。&lt;/p&gt;
&lt;p&gt;根据上述流程，DemoGen 能够从单一的人类演示中生成大规模的合成 3D 演示数据，从而为机器人学习提供丰富的训练样本。最终，DemoGen 训练了 DP3 模型，并在多个长程操纵任务中验证了其生成数据的有效性和泛化能力。局限也很明显，DemoGen 提出的几何平移假设难以建模复杂的非刚性交互，比如布料操作或液体处理等任务。&lt;/p&gt;
&lt;h2&gt;ArticuBot&lt;/h2&gt;
&lt;p&gt;ArticuBot 是一种 Sim2Real 方法，主要贡献了一个对 Articulation Object 操作的仿真数据生成方案，以及一个基础的分层策略模型。
&lt;img src=&quot;https://pic.hana0721.top/ArticuBot.5c1js44i07.webp&quot; alt=&quot;&quot;&gt;
仿真演示生成。ArticuBot 在仿真环境中引入了 PartNet-Mobility 数据集，使用 Franka 机械臂生成示范数据。
进一步，ArticuBot 将轨迹分为三个阶段。
首先是「Grasp Sampling」，给定 Articulation Object，利用标注获取连接件 Point Cloud 的，并使用采样方法生成多个抓取候选点。
接着是「Motion Planning」，有了抓取候选点，那么就可以使用运动规划器（如 RRT-Connect）在 SE(3) 空间中生成无碰撞的运动轨迹。
最后是「Opening Action」，在前两个阶段的基础上，可以计算末端执行器的位姿序列，完成对 Articulation Object 的操作。
这一阶段最终输出了 4.23 万条高质量的演示轨迹（Observation-Action Pairs）作为后续学习的基石。&lt;/p&gt;
&lt;p&gt;分层策略学习。ArticuBot 的策略模型采用了分层设计，分为 High-level Policy 和 Low-level Policy 两个层次。
High-level Policy 负责预测「机器人应该移动到何处」，ArticuBot 使用了一种 Weighted Displacement Model， 接受分割后的 Object Point Cloud 为输入，输出末端子目标位姿 (Predicted Sub-goal eef)。
Low-level Policy 则负责预测「如何使用末端执行器去解决任务」，ArticuBot 使用了 DP3 模型，接受多源输入，最终输出一系列的 Action。&lt;/p&gt;
&lt;h2&gt;LAPA&lt;/h2&gt;
&lt;p&gt;LAPA 是一种基于 Latent Action 的 VLA 预训练方法，旨在通过引入一个中间的 Latent Action 表征来指导 VLA 模型的学习，从而提升其在复杂任务中的表现和泛化能力。
&lt;img src=&quot;https://pic.hana0721.top/LAPA.b9h3eovoi.webp&quot; alt=&quot;&quot;&gt;
首先，LAPA 采用大量无动作标签的视频数据进行预训练，使用一个专门设计的 VQVAE 模型来学习从视觉输入到 Latent Action 的映射关系。
Latent Action 被设计为多个离散的变量序列，和 Dreamer 一样类似。
接着，很容易将 data 处理为 [Frame, Instruction, Latent Action] 的形式，用于去训练 VLLA 模型，其实就是在 VLM 的基础接上一层 MLP 去预测 Latent Action。
具体训练细节为，冻结视觉编码器，只微调主干和后续的 MLP 层。
最终，LAPA 使用机器人数据进行微调，将 Latent Action 的 MLP 层替换为直接预测机器人 Action 的 MLP 层。
同样，也是冻结视觉编码器，只微调主干和后续的 MLP 层。
后续的 GR00T 工作就是在 LAPA 的基础上，扩展了更多的优质数据集。&lt;/p&gt;
&lt;h2&gt;GR00T&lt;/h2&gt;
&lt;p&gt;GROOT N1 是继承了 LAPA 思想的工作。
该研究将异构训练语料库构建为一个金字塔结构，特点是从底层到顶层，数据量逐渐减少，但针对性逐渐增强。
在数据金字塔最底层，这些数据包含了基础常识与视觉先验。
例如，包括了 Reddit、Wikipedia、Common Crawl 等的互联网规模 Web 数据，以及包括了 Ego4D、EPIC-KITCHENS、HOI4D 等的人类视角视频。
在数据金字塔中层，主要是一些多样性增强与合成轨迹的数据。
例如，GROOT 使用 DexMimicGen 系统，在 RoboCasa 等物理引擎中生成的合成轨迹，以及使用视频生成模型（如 WAN2.1）扩充的 Counterfactual 视频数据。
在数据金字塔顶层，则是一些真实的机器人数据。例如，GROOT 使用了 OXE 以及 AgiBot-Alpha 数据，这些都是源自真实机器人操作的高质量数据集。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/GROOT.45i8p9rxr9.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;GROOT N1 采用 PI-like 架构，虽然文中说是 Double System，但实际上只是个快慢 System，和 RoboDual 的双线程有点类似。
在预训练阶段，GROOT N1 实现了异构数据对齐。
针对无标签数据，GROOT N1 采用了 LAPA 的思想，使用 VQ-VAE 去编码 Latent Action，并作为监督信号。
同时，对于视频生成模型产生的神经轨迹，GROOT N1 采用逆动力学模型去还原 Action。
而对于那些带有动作标签的数据，则直接使用动作标签作为监督信号。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/64131498_p0_master1200.45i9cfg3rk.webp"/><enclosure url="https://pic.hana0721.top/64131498_p0_master1200.45i9cfg3rk.webp"/></item><item><title>RL笔记（27）：MARL 最后的波纹 (MAT &amp; HASAC)</title><link>https://hana-blog.top/blog/rl-note-27</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-27</guid><description>多智能体领域的 SOTA 之作：详解 Multi-Agent Transformer (MAT) 如何将博弈转化为序列建模，以及 HASAC 如何结合异构理论与 SAC 的样本效率。</description><pubDate>Mon, 05 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的笔记中，我们看到了 MARL 的发展脉络：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MAPPO&lt;/strong&gt;：On-Policy，稳定但样本效率低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HAPPO&lt;/strong&gt;：引入序列更新，解决了异构和单调性问题，但仍是 On-Policy。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decision Transformer&lt;/strong&gt;：将单智能体 RL 变成了序列预测。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本章将介绍两个集大成者：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;MAT (Multi-Agent Transformer)&lt;/strong&gt;：将 &quot;Transformer&quot; 和 &quot;序列决策&quot; 引入 MARL，把多智能体博弈变成了一个自回归（Auto-Regressive）的序列生成问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HASAC (Heterogeneous-Agent SAC)&lt;/strong&gt;：将 HAPPO 的 &quot;序列更新理论&quot; 应用于 SAC，打造出兼具理论保证和极高样本效率的 Off-Policy 算法。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Multi-Agent Transformer (MAT)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/2205.14953&quot;&gt;Multi-Agent Reinforcement Learning is a Sequence Modeling Problem&lt;/a&gt; (NeurIPS 2022)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;核心思想：把“并发”变成“串行”&lt;/h3&gt;
&lt;p&gt;传统 MARL（如 QMIX, MAPPO）假设所有智能体在同一时刻&lt;strong&gt;同时&lt;/strong&gt;采取行动，联合动作分布为 $\pi(\mathbf{a}|s) = \prod \pi(a^i|s)$（假设独立）。&lt;/p&gt;
&lt;p&gt;MAT 提出了一种颠覆性的视角：&lt;strong&gt;联合策略可以分解为序列预测&lt;/strong&gt;。
利用概率链式法则，联合动作的概率可以写成：&lt;/p&gt;
&lt;p&gt;$$
\pi(\mathbf{a}|s) = \prod_{i=1}^n \pi(a^i | s, a^1, a^2, ..., a^{i-1})
$$&lt;/p&gt;
&lt;p&gt;这意味着：Agent 1 先动；Agent 2 看到 Agent 1 的动作后再动；Agent 3 看到 1 和 2 的动作后再动……
这与 &lt;strong&gt;Transformer&lt;/strong&gt; 的自回归生成（预测下一个单词）完全一致！&lt;/p&gt;
&lt;h3&gt;架构设计：Encoder-Decoder&lt;/h3&gt;
&lt;p&gt;MAT 使用了标准的 Transformer 架构：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Encoder（处理观测）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入：所有智能体的局部观测序列 $(o^1, o^2, ..., o^n)$。&lt;/li&gt;
&lt;li&gt;作用：利用 Self-Attention 提取智能体之间的交互特征，生成联合状态表征。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Decoder（生成动作）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入：Encoder 的输出 + 之前的智能体动作序列 $(a^1, ..., a^{i-1})$。&lt;/li&gt;
&lt;li&gt;输出：当前智能体 $i$ 的动作概率分布 $\pi(a^i | \dots)$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;机制&lt;/strong&gt;：类似于 GPT，通过 Masked Attention 确保智能体 $i$ 只能看到它之前的队友动作，看不到未来的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;训练目标&lt;/h3&gt;
&lt;p&gt;MAT 使用 PPO 的目标函数进行端到端训练。&lt;/p&gt;
&lt;p&gt;$$
L(\theta) = \min(r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_t)
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：这种序列化建模天然地解决了&lt;strong&gt;非平稳性&lt;/strong&gt;问题。对于 Agent $i$ 来说，它做决策时，队友 $1 \sim i-1$ 的动作已经是&lt;strong&gt;已知&lt;/strong&gt;的（Fixed），环境不再是“薛定谔”的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能&lt;/strong&gt;：MAT 在 SMAC 等基准测试中展现了惊人的性能，尤其是在需要复杂协调的任务中，且具有极强的 Zero-Shot 泛化能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Heterogeneous-Agent SAC (HASAC)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/2109.11251&quot;&gt;Trust Region Policy Optimization in Multi-Agent Reinforcement Learning&lt;/a&gt; (ICLR 2022)
&lt;em&gt;注：HASAC 是该论文提出的 HARL (Heterogeneous-Agent RL) 框架下的 Off-Policy 变体。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;核心动机：效率至上&lt;/h3&gt;
&lt;p&gt;HAPPO 虽然理论完美（保证单调提升），但它是 On-Policy 的，数据利用率低，训练慢。
SAC 是单智能体中样本效率最高的 Off-Policy 算法。
&lt;strong&gt;HASAC = HAPPO 的序列更新理论 + SAC 的最大熵 Off-Policy 机制。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;算法原理&lt;/h3&gt;
&lt;p&gt;HASAC 继承了 HAPPO 的 &lt;strong&gt;多智能体优势分解引理&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$
A_{\boldsymbol{\pi}}^{\text{joint}}(s, \mathbf{a}) = \sum_{i=1}^n A_{\pi}^{i}(s, a^i, a^1, ..., a^{i-1})
$$&lt;/p&gt;
&lt;h3&gt;训练流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;随机排列&lt;/strong&gt;：每一轮训练，随机打乱智能体更新顺序（例如 $1 \to 2 \to \dots \to n$）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;序列更新&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;对于智能体 $i$，它的 Critic $Q_i$ 需要评估的是：在队友 $1 \sim i-1$ 已经更新了新策略，而队友 $i+1 \sim n$ 还在用旧策略的情况下的价值。&lt;/li&gt;
&lt;li&gt;目标函数结合了 SAC 的熵正则化：
$$
J(\pi^i) = \mathbb{E}&lt;em&gt;{\mathcal{D}} \left[ Q^{\pi&lt;/em&gt;{\text{old}}}(s, a^1, ..., a^i, ..., a^n) - \alpha \log \pi^i(a^i|s) \right]
$$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键点&lt;/strong&gt;：在计算 $Q$ 值时，输入的动作向量 $\mathbf{a}$ 是混合的：
&lt;ul&gt;
&lt;li&gt;$a^{1:i-1}$ 来自&lt;strong&gt;当前最新&lt;/strong&gt;的策略。&lt;/li&gt;
&lt;li&gt;$a^{i}$ 是当前正在优化的。&lt;/li&gt;
&lt;li&gt;$a^{i+1:n}$ 来自&lt;strong&gt;旧&lt;/strong&gt;策略（Replay Buffer 中的动作或旧策略采样）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;优缺点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;极高的样本效率&lt;/strong&gt;：Off-Policy 机制让它可以利用历史数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异构友好&lt;/strong&gt;：不需要参数共享，适合不同类型的智能体协作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;收敛保证&lt;/strong&gt;：继承了 HARL 的单调性证明。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：计算复杂度较高，因为需要串行更新每个智能体，且 Critic 需要处理混合动作输入。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;深度对比：四大天王&lt;/h2&gt;
&lt;p&gt;至此，我们已经集齐了 MARL 领域的四大顶级算法。&lt;/p&gt;
&lt;p&gt;| 维度          | MAPPO                 | HAPPO          | MAT                      | HASAC              |
| :------------ | :-------------------- | :------------- | :----------------------- | :----------------- |
| &lt;strong&gt;核心机制&lt;/strong&gt;  | PPO + CTDE            | 序列更新 + PPO | &lt;strong&gt;Transformer + 自回归&lt;/strong&gt; | &lt;strong&gt;序列更新 + SAC&lt;/strong&gt; |
| &lt;strong&gt;策略类型&lt;/strong&gt;  | On-Policy             | On-Policy      | On-Policy                | &lt;strong&gt;Off-Policy&lt;/strong&gt;     |
| &lt;strong&gt;决策方式&lt;/strong&gt;  | 独立 (同步)           | 独立 (执行时)  | &lt;strong&gt;序列 (串行执行)&lt;/strong&gt;      | 独立 (执行时)      |
| &lt;strong&gt;同构/异构&lt;/strong&gt; | 强依赖同构 (参数共享) | &lt;strong&gt;异构友好&lt;/strong&gt;   | 同构/异构皆可            | &lt;strong&gt;异构友好&lt;/strong&gt;       |
| &lt;strong&gt;样本效率&lt;/strong&gt;  | 低                    | 低             | 中                       | &lt;strong&gt;高&lt;/strong&gt;             |
| &lt;strong&gt;适用场景&lt;/strong&gt;  | 大规模同质集群        | 复杂异构协作   | 极复杂序列决策           | 需要快速收敛的任务 |&lt;/p&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果你追求&lt;strong&gt;极致的性能&lt;/strong&gt;和对复杂策略的建模能力，&lt;strong&gt;MAT&lt;/strong&gt; 是首选，它代表了 RL 与 LLM 结合的趋势。&lt;/li&gt;
&lt;li&gt;如果你追求&lt;strong&gt;训练速度&lt;/strong&gt;和&lt;strong&gt;样本利用率&lt;/strong&gt;，或者你的智能体是&lt;strong&gt;异构&lt;/strong&gt;的（比如一个无人机配合一个机械臂），&lt;strong&gt;HASAC&lt;/strong&gt; 是目前的最强选择。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://pic.hana0721.top/134041716_p0_master1200.5q80bz7b6v.webp"/><enclosure url="https://pic.hana0721.top/134041716_p0_master1200.5q80bz7b6v.webp"/></item><item><title>RL笔记（26）：异构智能体信任区域优化 (HAPPO &amp; HATRPO)</title><link>https://hana-blog.top/blog/rl-note-26</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-26</guid><description>从经验主义回归理论严谨性：详解 HAPPO 如何解决 MARL 中的单调提升难题。涵盖多智能体优势分解引理、序列更新机制以及与 MAPPO 的本质区别。</description><pubDate>Sun, 04 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的笔记中，我们介绍了 &lt;strong&gt;MAPPO&lt;/strong&gt;，它简单地让每个智能体运行 PPO，并共用一个中心化 Critic。虽然在工程上很成功，但它在理论上存在一个巨大的漏洞：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;多智能体更新冲突&lt;/strong&gt;。
在单智能体中，PPO/TRPO 保证了每一步更新，策略性能 $J(\pi)$ 都是&lt;strong&gt;单调不减&lt;/strong&gt;的。
但在多智能体中，如果所有智能体&lt;strong&gt;同时&lt;/strong&gt;更新策略，这就好比两个人抬桌子，A 想往左，B 想往右，虽然两人各自觉得自己的方向是对的，但合起来的结果可能导致桌子翻倒（联合策略性能下降）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HAPPO (Heterogeneous-Agent PPO)&lt;/strong&gt; 和 &lt;strong&gt;HATRPO&lt;/strong&gt; 的核心贡献在于：提出了&lt;strong&gt;多智能体优势分解引理&lt;/strong&gt;，并据此设计了&lt;strong&gt;序列更新&lt;/strong&gt;方案，在理论上证明了联合策略的单调提升，特别适用于&lt;strong&gt;异构（Heterogeneous）&lt;/strong&gt; 智能体场景。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;理论基石：多智能体优势分解引理&lt;/h2&gt;
&lt;p&gt;我们希望优化联合策略 $\boldsymbol{\pi} = (\pi^1, \pi^2, ..., \pi^n)$ 以最大化联合回报 $J(\boldsymbol{\pi})$。
类似于 TRPO，我们关注新旧策略的回报差 $J(\boldsymbol{\pi}&lt;em&gt;{\text{new}}) - J(\boldsymbol{\pi}&lt;/em&gt;{\text{old}})$。&lt;/p&gt;
&lt;h3&gt;优势分解引理 (Multi-Agent Advantage Decomposition Lemma)&lt;/h3&gt;
&lt;p&gt;HAPPO 证明了，联合策略的优势函数（Joint Advantage）可以精确分解为每个智能体局部优势函数的&lt;strong&gt;累加&lt;/strong&gt;，前提是这些优势是&lt;strong&gt;按顺序&lt;/strong&gt;计算的。&lt;/p&gt;
&lt;p&gt;$$
A_{\boldsymbol{\pi}}^{\text{joint}}(s, \mathbf{a}) = \sum_{i=1}^n A_{\pi}^{i}(s, a^i, a^1, a^2, ..., a^{i-1})
$$&lt;/p&gt;
&lt;p&gt;这里的 $A_{\pi}^{i}$ 是第 $i$ 个智能体的优势函数，但有一个关键细节：
它不仅依赖于当前状态 $s$ 和动作 $a^i$，还依赖于&lt;strong&gt;排在他前面的智能体 $(a^1, a^2, ..., a^{i-1})$ 的动作&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这意味着：如果我们按照某种顺序 $1, 2, ..., n$ 依次更新智能体，那么整体性能的提升等于每个人贡献的提升之和。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;核心机制：序列更新 (Sequential Update)&lt;/h2&gt;
&lt;p&gt;为了利用上述引理，HAPPO 抛弃了 MAPPO 的“同步更新”模式，采用了 &lt;strong&gt;序列更新&lt;/strong&gt; 模式。&lt;/p&gt;
&lt;h3&gt;流程&lt;/h3&gt;
&lt;p&gt;在每一次训练迭代中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;随机排列&lt;/strong&gt;：随机生成一个智能体的更新顺序（例如：Agent 3 -&gt; Agent 1 -&gt; Agent 2）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;依次更新&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Agent 3&lt;/strong&gt;：基于旧策略 $\boldsymbol{\pi}&lt;em&gt;{\text{old}}$ 计算自己的优势，更新策略，变为 $\pi^3&lt;/em&gt;{\text{new}}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent 1&lt;/strong&gt;：看到 Agent 3 已经更新了（环境变了），基于 $(\pi^3_{\text{new}}, \pi^1_{\text{old}}, \pi^2_{\text{old}})$ 计算自己的优势，更新为 $\pi^1_{\text{new}}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent 2&lt;/strong&gt;：看到 3 和 1 都变了，基于 $(\pi^3_{\text{new}}, \pi^1_{\text{new}}, \pi^2_{\text{old}})$ 更新自己。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;循环结束&lt;/strong&gt;：所有智能体更新完毕，进入下一轮采样。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;为什么这样就稳了？&lt;/h3&gt;
&lt;p&gt;通过这种方式，对于每一个智能体 $i$ 来说，它在更新时，排在它前面的队友已经是“新策略”了，排在后面的还是“旧策略”。它只需要在当前这个&lt;strong&gt;固定&lt;/strong&gt;的上下文中找到最优提升方向，就能保证整个团队的总分是增加的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉&lt;/strong&gt;：
就像多人过独木桥。大家一起跑容易挤掉下去（同步更新）。HAPPO 的做法是：第一个人先走稳，第二个人看准第一个人的位置再走，依次类推。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;HATRPO 与 HAPPO&lt;/h2&gt;
&lt;p&gt;基于这个理论框架，衍生出了两个算法。它们都共享同一个&lt;strong&gt;中心化 Critic&lt;/strong&gt; $V(s)$。&lt;/p&gt;
&lt;h3&gt;HATRPO (Trust Region)&lt;/h3&gt;
&lt;p&gt;这是 TRPO 的多智能体版本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：最大化局部优势期望。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;约束&lt;/strong&gt;：严格限制每个智能体更新前后的 KL 散度：
$$ \mathbb{E}[D_{KL}(\pi^i_{\text{old}} || \pi^i_{\text{new}})] \le \delta $$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;求解&lt;/strong&gt;：同样需要使用共轭梯度法（CG），计算量较大，但理论性质最强。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;HAPPO (Proximal Policy)&lt;/h3&gt;
&lt;p&gt;这是 PPO 的多智能体版本，更实用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：使用 Clip 技巧来替代 KL 约束。
定义比率 $r_t^i(\theta) = \frac{\pi^i(a^i|s)}{\pi^i_{\text{old}}(a^i|s)}$，损失函数为：
$$
L^{HAPPO}(\theta^i) = \mathbb{E} \left[ \min \left( r_t^i(\theta) \hat{M}^i, \text{clip}\left(r_t^i(\theta), 1-\epsilon, 1+\epsilon\right) \hat{M}^i \right) \right]
$$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键点 $\hat{M}^i$&lt;/strong&gt;：这是&lt;strong&gt;修正后的优势函数&lt;/strong&gt;。
在计算 Agent $i$ 的优势时，必须扣除排在它前面的队友 $1, ..., i-1$ 已经拿走的优势，确保不重复计算功劳。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;深度对比：HAPPO vs. MAPPO&lt;/h2&gt;
&lt;p&gt;这两个算法非常相似，都是 PPO + CTDE，很容易混淆。&lt;/p&gt;
&lt;p&gt;| 维度         | MAPPO (Multi-Agent PPO)       | HAPPO (Heterogeneous-Agent PPO)                 |
| :----------- | :---------------------------- | :---------------------------------------------- |
| &lt;strong&gt;更新方式&lt;/strong&gt; | &lt;strong&gt;同步更新 (Simultaneous)&lt;/strong&gt;   | &lt;strong&gt;序列更新 (Sequential)&lt;/strong&gt;                       |
| &lt;strong&gt;理论保证&lt;/strong&gt; | 无单调提升保证 (可能震荡)     | &lt;strong&gt;有单调提升保证&lt;/strong&gt;                              |
| &lt;strong&gt;参数共享&lt;/strong&gt; | 强烈依赖 (Parameter Sharing)  | &lt;strong&gt;不需要&lt;/strong&gt; (天生支持异构)                       |
| &lt;strong&gt;优势计算&lt;/strong&gt; | $A(s, a^i)$ (假设队友不变)    | $A(s, a^i, a^1, ..., a^{i-1})$ (考虑队友更新)   |
| &lt;strong&gt;适用场景&lt;/strong&gt; | 同质智能体 (如星际争霸小狗海) | &lt;strong&gt;异构智能体&lt;/strong&gt; (如牧羊犬与羊、不同类型的机器人) |
| &lt;strong&gt;计算量&lt;/strong&gt;   | 较小                          | 稍大 (因为要串行计算)                           |&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;HAPPO 系列算法在 MARL 领域具有重要的理论地位：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;解决了 Heterogeneous (异构) 问题&lt;/strong&gt;：以前的算法（如 QMIX, MAPPO）通常假设智能体共享参数以加速收敛。HAPPO 证明了即使每个智能体结构完全不同（异构），只要按顺序更新，也能高效协作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解决了 Trust Region 问题&lt;/strong&gt;：它将单智能体的“信任区域”概念成功推广到了多智能体，解决了多智能体同时更新导致的&lt;strong&gt;环境非平稳&lt;/strong&gt;和&lt;strong&gt;信度分配&lt;/strong&gt;难题。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你面对的任务中，智能体各不相同（例如一个无人机配合一个地面车），且协作极其复杂，HAPPO 往往比 MAPPO 表现更稳健。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/116834498_p0_master1200.1e974fnzok.webp"/><enclosure url="https://pic.hana0721.top/116834498_p0_master1200.1e974fnzok.webp"/></item><item><title>Paper Reading: Embodied AI 1</title><link>https://hana-blog.top/blog/paper-reading-eba1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-eba1</guid><description>从零开始的Embodied AI研究生活。</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;import { ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;Embodied AI Paper Reading&apos;,
items: [
{
title: &apos;Batch 1&apos;,
href: &apos;/blog/paper-reading-eba1&apos;,
order: &apos;1&apos;
},
{
title: &apos;Batch 2&apos;,
href: &apos;/blog/paper-reading-eba2&apos;,
order: &apos;2&apos;
},
{
title: &apos;Batch 3&apos;,
href: &apos;/blog/paper-reading-eba3&apos;,
order: &apos;3&apos;
},
{
title: &apos;Batch 4&apos;,
href: &apos;/blog/paper-reading-eba4&apos;,
order: &apos;4&apos;
},
{
title: &apos;Batch 5&apos;,
href: &apos;/blog/paper-reading-eba5&apos;,
order: &apos;5&apos;
}
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;RL菜鸡开始进军Embodied AI，慢慢积累，提升自己。&lt;/p&gt;
&lt;h2&gt;ACT&lt;/h2&gt;
&lt;h2&gt;DP&lt;/h2&gt;
&lt;h2&gt;RT-1&lt;/h2&gt;
&lt;h2&gt;RT-2&lt;/h2&gt;
&lt;h2&gt;OpenVLA&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/openvla.iclclg6tx.webp&quot; alt=&quot;&quot;&gt;
OpenVLA基本继承了RT-2的思想，形成了OpenVLA-like的VLA范式。
简单来说，就是使用LLM对Image观测以及Language指令进行Fusion，输出Action Token，最后再Detokenizer成机器人可执行的Action。
OpenVLA使用Prismatic-7B VLM作为主干，文中详细解释了为什么采用Prismatic-7B VLM。
和RT-2一样，使用256个频率最低的词元作为Action Token。
训练数据主要使用了OXE Dataset，并对数据进行了处理，以便于训练。
最终，OpenVLA展示了在不同消费级和服务级的GPU上的运行效果。
然而，OpenVLA也有着诸多Limitation。
首先，它只接受单图像预测，无法处理异构的机器人设置。
其次，它的吞吐量不够，无法实现高频率的控制。
最后，同时也是最大的硬伤，和RT-2一样，在面对Co-Training时，选择频率最低的256个Token作为Action Token，会出现双峰学习问题。
但不可否认的是，OpenVLA还是一份伟大的工作，开创了OpenVLA-like的VLA学习范式。&lt;/p&gt;
&lt;h2&gt;OXE | RT-X&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/OXE.5moa1wm2lz.webp&quot; alt=&quot;&quot;&gt;
Open X-Embodiment数据集包含超过100万个真实机器人轨迹，涵盖22种机器人实体，从单机械臂机器人到双臂机器人和四足机器人。
该数据集是通过汇集来自全球 34 个机器人研究实验室的 60 个现有机器人数据集，并将它们转换为一致的数据格式以便于下载和使用而构建的。
&lt;img src=&quot;https://pic.hana0721.top/RT-X.3yex4q5zbj.webp&quot; alt=&quot;&quot;&gt;
该团队还使用OXE训练了RT-1和RT-2，展现出泛化性能。
工作量如此之大，伟大无需多言。&lt;/p&gt;
&lt;h2&gt;Unified Video Action Model&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/UVA-easy.465lu585n.webp&quot; alt=&quot;&quot;&gt;
从数据流上看，UVA首先学习了Image与Action Chunk的Joint Latent，然后使用Diffusion模型解码出未来的Action Chunk以及观测Image。
为了兼容多种类型的输入，并解耦出对应的Action Chunk以及Image，UVA采用了掩码学习。
&lt;img src=&quot;https://pic.hana0721.top/UVA-all.2h8s31j1cb.webp&quot; alt=&quot;&quot;&gt;
下面讲解学习Joint Latent的流程。
对于每张历史Image使用预训练的VAE编码器进行编码，得出$N$个$d$维的Image Token。
对于每个历史Action Chunk，为了对齐Image Token的维度，采用重复$M$次拼接的方式对齐到$N$维度，并经过一个MLP投影到$N\times d$。
对于要预测的Image，处理方式与历史Image一样，但加上了定义好的掩码。
最终，将三类Token拼接在一起，输入到Transformer中进行编码，得出Joint Latent。
接下来讲解预测流程。
对于预测Image，将每个Token作为Image Diffusion的Condition，经过去噪还原出局部的VAE的Latent，最后拼接经过VAE的解码器还原出Image。
对于预测Action Chunk，则将所有的Token作为Action Diffusion的Condition，经过去噪得出Future Action Chunk。
UVA使用了灵活目标的掩码训练方法，有助于防止模型对特定任务产生过拟合，从而提升模型的整体通用性和鲁棒性。&lt;/p&gt;
&lt;h2&gt;Genie Envisioner&lt;/h2&gt;
&lt;h2&gt;TinyVLA&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/TinyVLA.64ebqsl3yh.webp&quot; alt=&quot;&quot;&gt;
TinyVLA首次提出了VLM+DP的VLA范式，这个范式后续被应用到了 $\pi$ 系列。
在这个范式下，Action不再由LLM的Token Detokenize出来，而是直接由一个Action Expert（DP或是ACT）输出。
这样做，可以避免RT-2以及OpenVLA中的双峰学习问题，能够避免灾难性遗忘，使得VLM和Action Expert各司其职。
在TinyVLA中，选用了Pythia作为VLM主干，选用DP作为Action Expert。
Action Expert以VLM主干输出的信息为Condition，输出Action Chunk。
在训练过程中，使用LoRA微调VLM，防止了VLM的灾难性遗忘，且可以进行高效的微调。
在实验部分，TinyVLA超越了OpenVLA，且可以在OOD的场景下完成一些任务。
TinyVLA应该是最早提出PI-like架构的工作，算是祖师爷。&lt;/p&gt;
&lt;h2&gt;CogACT&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/CogACT.67xxokoxle.webp&quot; alt=&quot;&quot;&gt;
CogACT也是一种PI-like的VLA，分为VLM Fusion和Action Expert模块。
在VLM Fusion模块中，CogACT采用了一种BERT-like的方式进行Fusion，输入Image观测以及Language指令，输出类似于[CLS]的Cognition Feature。
在Action Expert模块中，以Cognition Feature为Condition，经过一个DiT得出Action Chunk。
&lt;img src=&quot;https://pic.hana0721.top/CogACT-ensembling.8s3s17oxad.webp&quot; alt=&quot;&quot;&gt;
CogACT和ACT一样，使用了类似的Action Ensembling，但CogACT中的权重是自适应的，避免了不同模式中不合理地聚合Action。&lt;/p&gt;
&lt;h2&gt;PI-0&lt;/h2&gt;
&lt;h2&gt;PI-0.5&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/pi-0.5.77e4ti1nhe.webp&quot; alt=&quot;&quot;&gt;
PI-0.5 是 PI-0 的续作，主要在模型训练上做了一些改进。
PI-0.5 对 VLM 进行了大量的 Co-Training,包括使用了 VQA、Discretized Actions、Sub Tasks 以及 Boxes Bounding 等多种数据。
这样做的话，可以让 VLM 在 Inference 过程中先拆分 Sub Tasks，然后再使用 Action Expert 去预测 Action Chunk。
同时，Co-Training 可以有效预防 VLM 的灾难性遗忘。
另外一点，PI-0.5 使用了 FAST 编码器，可以将 Token 转换为离散的动作，这样可以在预训练过程中输出离散动作。
这样做可以使得 Pre-Training 与 Post-Training 的学习更加的耦合，以提高模型的泛化能力。&lt;/p&gt;
&lt;h2&gt;GR-3&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/GR-3.4qrwfuzlkf.webp&quot; alt=&quot;&quot;&gt;
GR-3 是字节继 GR-2 后的工作，整体采用了 PI-like 的架构，也就是使用 VLM 进行 Fusion，并使用 Flow Matching 的 Action Expert 去输出 Action Chunk。
GR-3 的 VLM 使用的是 Qwen2.5-VL-3B-Instruct。
在训练上，GR-3 采用了 Co-Training 的方式。
对于无 Action 标签的 Web 数据，采用 VQA 的方式进行训练。
对于有 Action 标签的机器人数据，采用 Flow Matching 损失进行训练。
这样使用 Co-Training 的方式，可以让 VLM 在训练过程中同时学习 Web 数据和机器人数据的知识，同时能避免破坏 VLM 的知识。&lt;/p&gt;
&lt;h2&gt;InstructVLA&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/InstructVLA.8dxg3hq98d.webp&quot; alt=&quot;&quot;&gt;
InstructVLA 采用了 PI-like 架构，也就是 VLM + Action Expert 的结构。
如图，InstructVLA 采用了 Eagle2-2B 作为 VLM 主干，使用 SigLIP 将图像信息映射到语言空间。
为了使得 VLM 能同时进行多模态推理和语言引导的 Latent Action 规划，InstructVLA 引入了一组可学习的 Action Query，这样就可以提取 Latent Action。
同时，为了使得 VLM 能够在推理与操作之间切换，InstructVLA 引入了 MoE 设计的 LoRA 模块，来适应不同的输入模式。
接着，Latent Action 被用于下游的 Flow Matching 进行 Action Chunk 的预测。
具体做法就是，将 Latent Action 以 FiLM 的方式注入到 DinoV2 中，同时作为 Flow Matching 的 Condition，以预测 Action Chunk。&lt;/p&gt;
&lt;p&gt;InstructVLA 的训练分为两个阶段，第一个阶段是动作预训练，第二个阶段是 VLA-IT。
在动作预训练阶段，InstructVLA 使用异构机器人数据集，例如 OXE、Bridge等。
训练时联合交叉熵与流匹配损失，冻结 VLM 主干的参数，只更新 MoE 设计的 LoRA 模型参数。
在 VLA-IT 阶段，InstructVLA 冻结了 Flow Matching 模块的参数，新增了语言 LoRA 适配器和 MoE 适应缩放头。
其中，MoE 模块是唯一可训练参数，并使用了 VLA-IT 数据集进行训练，详细可以见原文。&lt;/p&gt;
&lt;h2&gt;DinoV3&lt;/h2&gt;
&lt;h2&gt;FiS-VLA&lt;/h2&gt;
&lt;h2&gt;VIMA&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/VIMA.70atg93q8d.webp&quot; alt=&quot;&quot;&gt;
VIMA 的具体做法就是将 Robotic Manipulation 任务规范为 Multi-Modal Prompt。
Multi-Modal Prompt 具体实现为 Vision 与 Text 的排列组合，文中考虑了6种任务类别。
由于时代的局限，当时没有 Multi-Modal 的 Robotic Task 的 Benchmark。
于是，该团队提出了VIMA-Bench，可以通过脚本化的 Oracle 智能体生成大量的模仿学习数据。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/VIMA-Contral.60uq3314ou.webp&quot; alt=&quot;&quot;&gt;
VIMA 这个工作比较早期，所以只使用了 T5 进行 Vision 与 Text 的 Fusion。
对于 Text，采用预训练的 T5 词嵌入获取 Text Token。
对于 Scene Vision，首先使用领域微调的 Mask R-CNN 来提取物体，然后经过 ViT 编码成 Scene Token。
对于 Object Vision，和 Scene Vision一样的方式，编码为 Object Token。
经过 T5 模型，可以得出 Prompt Tokens。&lt;/p&gt;
&lt;p&gt;VIMA 控制器采用了 Decoder-Only 的架构，于 Decision Transformer 类似，采用序列建模的方式。
VIMA 控制器以 History 为输入，输出 Action。
这部分与 Fusion 相似，被处理成了若干的 Vision Token，然后与机器人的 Joint Position 以及 Action 拼接，形成 History Token。
在推理过程中，经过了多层的 Self-Attention 与 Cross-Attention 结构，自回归产生下一步的 Action。
在 Cross-Attention 模块中，Prompt Tokens 被投影至 $K_{\mathcal{P}}$ 和 $V_{\mathcal{P}}$，并使用 History 投影的 $Q_{\mathcal{H}}$查询。&lt;/p&gt;
&lt;h2&gt;SayCan&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/SayCan.86u4ow4enj.webp&quot; alt=&quot;&quot;&gt;
SayCan 是任务规划的早期工作。任务规划就是给一个高级任务指令，分解为一系列机器人可执行的下游任务。
很显然，具有强泛化能力的 LLM 非常适合做这个任务，但也有一些问题。
最主要的问题就是 LLM 无法识别到物理世界，从而产生一些机器人无法执行的任务，也就是幻觉问题。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/SayCan-method.4ubeuinub1.webp&quot; alt=&quot;&quot;&gt;
为了解决这个问题，SayCan 引入了 RL 中的 Value Function，表示为在当前状态下执行成功某个 skill 的成功率。
在 SayCan设置的场景中，机器人有固定的 skill 集合，每次从里面选择一个 skill 执行。
直接让 LLM 输出需要执行的 skill 是不保险的，因为可能输出 skill 集合中没有的 skill。
所以，SayCan 不再让 LLM 去输出下一步执行的 skill，而是让 LLM 给所有的 skill 进行评分。
同时，SayCan 会给 LLM 一些 Example Prompt，让 LLM 能够更加有效地提取知识。
最后将两者结合，去选择机器人应该执行的 skill。&lt;/p&gt;
&lt;h2&gt;PaLM-E&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/PaLM-E.45i5blou92.webp&quot; alt=&quot;&quot;&gt;
PaLM-E 做的事情很简单，就是在 PaLM 的基础上添加若干的视觉编码组件，如 ViT、OSRT等。
然后，使用 VQA 与机器人场景数据进行 Co-Training，这样就得到了一个通用的 VLM，可以处理多种任务。
在机器人场景下，PaLM-E 可以发挥 Planning 的作用。&lt;/p&gt;
&lt;p&gt;PaLM-E 其实没有多少的 insight，主要是在输入上做处理，具体可以去看原文。
但 PaLM-E 的效果很好，在 Planning 任务中薄纱了当时的 SayCan，且有跨任务的能力。&lt;/p&gt;
&lt;h2&gt;ViLA&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/ViLA.2oc09ver89.webp&quot; alt=&quot;&quot;&gt;
ViLA 的思路非常简单，就是告诉 VLM 当前要执行的 Task 以及 Obs，然后使用 Prompt 输出 CoT 形成 Task Planning，
然后维护 TODO List，每次执行 TODO List 中的第一条 Low-level Task，不断循环直到任务结束。&lt;/p&gt;
&lt;p&gt;ViLA 的思路虽然简单，但是有用且本质，非常具有启发性。&lt;/p&gt;
&lt;h2&gt;CoPa&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/CoPa-overview.6m4dqmphlv.webp&quot; alt=&quot;&quot;&gt;
CoPa 结合了许多种方法，让机器人直接输出 Action 去完成 high-level 任务。
CoPa 将大多数操作任务分为任务导向抓取和运动规划两个阶段。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/CoPa-grounding.7axnand0m8.webp&quot; alt=&quot;&quot;&gt;
在&quot;任务导向抓取&quot;中，分为粗粒度 Object 锚定和细粒度 Grasp 锚定。
在粗粒度 Object 锚定中，先使用 SoM 去分割出 Obs 中的可抓取 Object，然后再使用 GPT-4V 去筛选最终抓取 Object。
在细粒度 Grasp 锚定中，使用同样的方式去筛选最终 Grasp 的位置。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/CoPa-grasp.8dxclj8uhq.webp&quot; alt=&quot;&quot;&gt;
进一步，CoPa 使用 GraspNet 去生成机器人的候选抓取姿态，并使用 Grasp 锚定进行筛选。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/CoPa-planning.3govrov2ot.webp&quot; alt=&quot;&quot;&gt;
在&quot;任务运动规划&quot;中，首先采用同样的锚定方式识别多个任务相关的部件。
然后，使用 GPT-4V 生成操作约束限制。
最后，可以得出一系列的 SE(3) 的矩阵，求可以得出抓取姿态。&lt;/p&gt;
&lt;h2&gt;PointLLM&lt;/h2&gt;
&lt;p&gt;PointLLM 主要有两个贡献，第一个是提出了基于 Prompt 的 3D LLM 问答数据标注方法，第二点是训练一个 3D 点云版本的 VLM。
&lt;img src=&quot;https://pic.hana0721.top/PointLLM-data.1vz4sgdvpn.webp&quot; alt=&quot;&quot;&gt;
由于时代的局限性，当时没有用于 3D 点云用于 LLM 问答的数据集。
于是，这篇工作基于 Cap3D 数据集，基于 Prompt 使用 VLM 对数据进行标注。
&lt;img src=&quot;https://pic.hana0721.top/PointLLM-model.6pnzokytyj.webp&quot; alt=&quot;&quot;&gt;
随后，PointLLM 使用这份数据训练了一个 3D 点云版本的 VLM，如框架图所示。&lt;/p&gt;
&lt;h2&gt;EmbodiedGPT&lt;/h2&gt;
&lt;p&gt;EmbodiedGPT 是一个端到端的决策模型，并且具有跨任务的能力，如 TAMP、VQA 等。
&lt;img src=&quot;https://pic.hana0721.top/EmbodiedGPT.eszs0eugq.webp&quot; alt=&quot;&quot;&gt;
下面简单描述一下 EmbodiedGPT 的工作流，可以分为 Planning 和 low-level 决策执行阶段。&lt;/p&gt;
&lt;p&gt;对于 Planning 阶段，EmbodiedGPT 首先使用 ViT 对图像信息进行编码得出 Vision Token。
然后，Vision Token 与可学习的 Embodied Token 经过 Q-Former 进行 Fusion。
最后，Fusion 后的信息与 Text Prompt 一同输入到 LLaMA3 中得出 Planning Text。&lt;/p&gt;
&lt;p&gt;对于 low-level 决策执行阶段，Planning Token、Embodied Token 以及 Vision Token 再次在 Q-Former 进行 Fusion，得出 Instance Information。
同时，为了捕获全局信息，图像信息经过 ImageNet 预训练的 ResNet50 进行全局平均池化特征提取，得出 Global Information。
最后，这两个 Information 拼接在一起，并输入到 low-level 的决策网络中，以输出 low-level action。&lt;/p&gt;
&lt;p&gt;EmbodiedGPT 的亮点是借助 Q-Former 将 Planning 的信息融入了 low-level 的决策中。&lt;/p&gt;
&lt;h2&gt;RT-Trajectory&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/RT-Trajectory.491razmvk0.webp&quot; alt=&quot;&quot;&gt;
RT-Trajectory 做的事情很简单，就是使用运动 Trajectory 去代替了 RT-1 中的 Text Instruction。
在具体实现中，Trajectory 被表示为一张 轨迹图像。
在训练过程中，使用从数据集中标注的 Trajectory 图像，文中给出了详细的标注方法。
而在推理过程中，提供了三种方式去得出 Trajectory 图像，详细见图。&lt;/p&gt;
&lt;p&gt;RT-Trajectory 这篇工作证明了，在 low-level 控制中使用 Text 进行引导可能不是最优的选择，这种 Trajectory 引导能够处理更加细粒度的控制。
同时，RT-Trajectory 缺点也很明显，在推理过程中，无法保证 Trajectory 是合理的，特别是将 3D Trajectory 映射为 2D 图像，会存在失真的现象。&lt;/p&gt;
&lt;h2&gt;Im2Flow2Act&lt;/h2&gt;
&lt;p&gt;Im2Flow2Act 是一个 End-to-End 的模型，其核心思想是基于 Object Flow 去生成 low-level policy。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/Im2Flow2Act-flow.1hsp38n80q.webp&quot; alt=&quot;&quot;&gt;
Im2Flow2Act 首先基于 Task Description 以及
初始 Frame 去生成 Object Flow。对于初始 Frame，使用 DINO 去检测出 Object 的框图，然后在框图中均匀采样 Points ，作为 Object Flow 的初始 Points。之后，以初始 Frame、初始 Points 以及 Task Description 为 Condition，使用视频生成模型 AnimateDiff 去生成 Object Flow。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/im2flow2act.491rba042s.webp&quot; alt=&quot;&quot;&gt;
为了确保实时感知，Im2Flow2Act 首先根据初始 Frame 以及当前 Frame 使用点追踪算法 TAPIR 去捕获 Object 的当前的 Keypoints。随后，使用 BERT-like 的状态编码器将 Keypoints 压缩为 [CLS]。同时，为了进行时间对齐，也就是让机器人自己做到哪里了，Im2Flow2Act 使用了一个 Transformer 将 Object Flow、[CLS]以及机器人 Joint Position 编码成 Latent。最后，以 Latent、[CLS]以及 Joint Position 为 Condition，使用 DP 去生成 Action Chunk。&lt;/p&gt;
&lt;p&gt;这篇工作比 RT-Trajectory 更加有细粒度，但还是存在 3D 歧义的问题。
&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/133535742_p0_master1200.icpovr2id.webp"/><enclosure url="https://pic.hana0721.top/133535742_p0_master1200.icpovr2id.webp"/></item><item><title>RL笔记（25）：多智能体策略梯度 (MADDPG &amp; MAPPO)</title><link>https://hana-blog.top/blog/rl-note-25</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-25</guid><description>从连续控制到离散博弈：详解 CTDE 架构在 Actor-Critic 中的应用。涵盖 MADDPG 的多面手 Critic 设计与 MAPPO 的工程化胜利。</description><pubDate>Sat, 03 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言：Actor-Critic 的群体进化&lt;/h2&gt;
&lt;p&gt;我们在之前的笔记中学习了 &lt;strong&gt;CTDE (中心化训练，分布式执行)&lt;/strong&gt; 的思想。在 Value-Based 方法（如 QMIX）中，CTDE 体现在将 $Q_{tot}$ 分解为 $Q_i$。&lt;/p&gt;
&lt;p&gt;而在 &lt;strong&gt;Actor-Critic&lt;/strong&gt; 架构中，CTDE 的实现更加直观且灵活：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Actor (策略)&lt;/strong&gt;：必须是&lt;strong&gt;局部&lt;/strong&gt;的 ($\pi(a_i|o_i)$)，因为执行时只能靠自己。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Critic (价值)&lt;/strong&gt;：必须是&lt;strong&gt;全局&lt;/strong&gt;的 ($Q(s, \mathbf{a})$ 或 $V(s)$)，因为训练时我们可以利用上帝视角来更准地评估局势，从而指导 Actor。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本章将介绍这一范式下的两个里程碑算法：针对连续动作的 &lt;strong&gt;MADDPG&lt;/strong&gt; 和目前最强的 Baseline &lt;strong&gt;MAPPO&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;MADDPG (Multi-Agent DDPG)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/1706.02275&quot;&gt;Multi-Agent Actor-Critic for Mixed Cooperative-Competitive Environments&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;MADDPG 是 DDPG 算法在多智能体环境下的自然延伸，由 OpenAI 在 2017 年提出。它主要解决了&lt;strong&gt;非平稳性&lt;/strong&gt;问题。&lt;/p&gt;
&lt;h3&gt;核心思想：Critic 知道一切&lt;/h3&gt;
&lt;p&gt;在独立学习（Independent DDPG）中，Critic 只输入 $(o_i, a_i)$。当队友 $j$ 的策略 $\pi_j$ 改变时，环境对 $i$ 来说就变了，导致 Critic 震荡。&lt;/p&gt;
&lt;p&gt;MADDPG 提出：&lt;strong&gt;Critic 应该输入所有人的动作&lt;/strong&gt;。
$$ Q_i^{\boldsymbol{\pi}}(s, a_1, \dots, a_N) $$
只要输入了联合动作 $\mathbf{a} = (a_1, \dots, a_N)$，环境的状态转移 $P(s&apos;|s, \mathbf{a})$ 就是由物理规律决定的，是&lt;strong&gt;平稳 (Stationary)&lt;/strong&gt; 的。&lt;/p&gt;
&lt;h3&gt;架构设计&lt;/h3&gt;
&lt;p&gt;对于 $N$ 个智能体，每个智能体 $i$ 维护两个网络：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Actor $\mu_{\theta_i}(o_i)$&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入&lt;/strong&gt;：仅局部观测 $o_i$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输出&lt;/strong&gt;：确定性动作 $a_i$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：执行时完全独立。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Critic $Q_{\phi_i}(s, a_1, \dots, a_N)$&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入&lt;/strong&gt;：全局状态 $s$（或所有人的观测） + &lt;strong&gt;所有人的动作&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输出&lt;/strong&gt;：标量 Q 值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：仅在训练时使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;训练流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Critic 更新&lt;/strong&gt;：最小化贝尔曼误差。
$$ y = r&lt;em&gt;i + \gamma Q_i&apos;(s&apos;, a&apos;_1, \dots, a&apos;_N)|&lt;/em&gt;{a&apos;_j = \mu&apos;_j(o&apos;_j)} $$
注意：计算目标值时，需要用到&lt;strong&gt;每个智能体的 Target Actor&lt;/strong&gt; 来预测下一步动作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actor 更新&lt;/strong&gt;：确定性策略梯度。
$$ \nabla*{\theta_i} J = \mathbb{E} [\nabla*{a&lt;em&gt;i} Q_i(s, a_1, \dots, a_N)|&lt;/em&gt;{a&lt;em&gt;i=\mu_i(o_i)} \cdot \nabla&lt;/em&gt;{\theta_i} \mu_i(o_i)] $$
注意：Critic 对 $a_i$ 求导，告诉 Actor $i$ 如何调整动作能提高集体（或个人）收益。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;优缺点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：可以处理连续动作；适用于合作、竞争或混合任务（每个 Critic 可以最大化不同的奖励 $r_i$）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：Critic 的输入维度随人数线性增长，难以扩展到大规模集群。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;MAPPO (Multi-Agent PPO)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/2103.01955&quot;&gt;The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;长期以来，人们认为 Off-Policy（如 MADDPG/QMIX）在 MARL 中更高效。但 MAPPO (2021) 证明：&lt;strong&gt;只要调参得当，简单的 On-Policy PPO 也能吊打复杂的 Off-Policy 算法。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;核心思想：Centralized Value Function&lt;/h3&gt;
&lt;p&gt;MAPPO 的结构极其简单，就是 PPO + CTDE。
它与 IPPO（独立 PPO）唯一的区别在于 &lt;strong&gt;Critic&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IPPO Critic&lt;/strong&gt;: $V(o_i)$ —— 只看自己，不仅视野窄，而且受队友策略变化干扰严重。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MAPPO Critic&lt;/strong&gt;: $V(s)$ —— &lt;strong&gt;看全局&lt;/strong&gt;。Critic 学习的是全局状态价值函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;为什么 $V(s)$ 比 $Q(s, \mathbf{a})$ 好？&lt;/h3&gt;
&lt;p&gt;MADDPG 使用 $Q(s, \mathbf{a})$，这需要输入巨大的联合动作空间。
MAPPO 使用 $V(s)$ 来计算优势函数：
$$ \hat{A}&lt;em&gt;i(t) = \sum (\gamma \lambda)^l (r&lt;/em&gt;{i, t+l} + \gamma V(s*{t+1+l}) - V(s*{t+l})) $$。
$V(s)$ 不需要输入动作，维度低，训练更容易收敛。&lt;/p&gt;
&lt;h3&gt;成功的关键：工程技巧 (Implementation Matters)&lt;/h3&gt;
&lt;p&gt;MAPPO 的成功不仅仅在于算法，更在于 5 个关键的工程实践：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;输入特征处理&lt;/strong&gt;：将 Agent ID 作为 One-hot 向量拼接到状态中（在参数共享时区分不同个体）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数共享 (Parameter Sharing)&lt;/strong&gt;：所有智能体共用一个 Actor 和一个 Critic 网络（适用于同质智能体），极大加速收敛。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PopArt&lt;/strong&gt;：对 Critic 的目标值（Value Target）进行&lt;strong&gt;归一化&lt;/strong&gt;，处理奖励尺度差异大的问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据并行&lt;/strong&gt;：使用多个并行环境收集数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;裁剪 (Clipping)&lt;/strong&gt;：PPO 本身的 Clip 机制有效防止了非平稳环境下的策略崩塌。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;深度对比：MADDPG vs. MAPPO&lt;/h2&gt;
&lt;p&gt;| 维度            | MADDPG                  | MAPPO                        |
| :-------------- | :---------------------- | :--------------------------- | ---- |
| &lt;strong&gt;基础算法&lt;/strong&gt;    | DDPG (Off-Policy)       | PPO (On-Policy)              |
| &lt;strong&gt;策略类型&lt;/strong&gt;    | 确定性 ($\mu(o)$)       | 随机性 ($\pi(a               | o)$) |
| &lt;strong&gt;动作空间&lt;/strong&gt;    | &lt;strong&gt;连续&lt;/strong&gt; (擅长)         | 连续 &amp;#x26; 离散 (都擅长)         |
| &lt;strong&gt;Critic 形式&lt;/strong&gt; | $Q(s, a_1, \dots, a_N)$ | $V(s)$                       |
| &lt;strong&gt;通信需求&lt;/strong&gt;    | 训练时需知晓他人动作    | 训练时需知晓全局状态         |
| &lt;strong&gt;样本效率&lt;/strong&gt;    | 较高 (Replay Buffer)    | 较低 (需大量采样)            |
| &lt;strong&gt;稳定性&lt;/strong&gt;      | 较差 (超参数敏感)       | &lt;strong&gt;极高&lt;/strong&gt; (鲁棒性强)          |
| &lt;strong&gt;SOTA 表现&lt;/strong&gt;   | 早期基准                | 目前 SMAC 等环境的主流强基准 |&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;多智能体策略方法的发展经历了从“各自为战”到“全局协同”的过程。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MADDPG&lt;/strong&gt; 解决了连续动作下的多智能体博弈问题，通过将“队友的动作”显式输入 Critic，在数学上恢复了平稳性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MAPPO&lt;/strong&gt; 则展示了“大道至简”的力量，证明了通过引入全局价值函数 $V(s)$ 并配合优秀的工程实现，On-Policy 算法也能在复杂的协作任务中达到 SOTA 水平。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;至此，我们已经涵盖了 MARL 的两大主流流派：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Value-Based&lt;/strong&gt;: QMIX, QPLEX (适合离散动作，强显式协作)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Policy-Based&lt;/strong&gt;: MADDPG, MAPPO (适合连续动作，通用性强)。&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://pic.hana0721.top/112236596_p0_master1200.232gogbiox.webp"/><enclosure url="https://pic.hana0721.top/112236596_p0_master1200.232gogbiox.webp"/></item><item><title>RL笔记（24）：超越单调性 (QTRAN, WQMIX, QPLEX)</title><link>https://hana-blog.top/blog/rl-note-24</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-24</guid><description>打破 QMIX 的枷锁：详解 QTRAN、Weighted QMIX 和 QPLEX 如何突破单调性约束。涵盖软约束松弛、非对称加权投影及对偶对决架构的完全表达能力证明。</description><pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在上一章中，我们介绍了 &lt;strong&gt;QMIX&lt;/strong&gt;，它通过强制混合网络的权重非负，实现了对 IGM 原则的单调性近似。
然而，单调性是一个&lt;strong&gt;充分非必要条件&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;局限性&lt;/strong&gt;：QMIX 无法表示非单调的协作任务（例如：智能体 A 和 B 必须同时做动作 X 才能得分，单独做反而扣分。这种“异或”逻辑违反了单调性）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;挑战&lt;/strong&gt;：我们需要一种方法，既能满足 IGM 原则（保证分布式执行），又能覆盖&lt;strong&gt;所有&lt;/strong&gt;可能的联合价值函数空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本章将介绍三种试图突破 QMIX 天花板的进阶算法：&lt;strong&gt;QTRAN&lt;/strong&gt;（基于变换）、&lt;strong&gt;WQMIX&lt;/strong&gt;（基于加权）和 &lt;strong&gt;QPLEX&lt;/strong&gt;（基于优势函数）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;QTRAN: Learning to Factorize with Transformation&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/1905.05408&quot;&gt;QTRAN: Learning to Factorize with Transformation for Cooperative MARL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;核心思想：变换与松弛&lt;/h3&gt;
&lt;p&gt;QTRAN 认为，直接学习一个满足 IGM 的 $Q_{tot}$ 太难了。
不如我们将 $Q_{tot}$ 拆解为两部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;$Q&apos;_{tot}$&lt;/strong&gt;：一个易于分解的部分（如 VDN 的求和形式），用于指导动作选择。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$V_{tot}$&lt;/strong&gt;：一个状态价值修正项，用于补足残差，确保逼近真实的 $Q^*$。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;数学构造&lt;/h3&gt;
&lt;p&gt;我们定义变换后的目标函数：&lt;/p&gt;
&lt;p&gt;$$
Q_{tot}(s, \mathbf{u}) \approx Q&apos;&lt;em&gt;{tot}(s, \mathbf{u}) + V&lt;/em&gt;{tot}(s, \mathbf{u})
$$&lt;/p&gt;
&lt;p&gt;其中 $Q&apos;&lt;em&gt;{tot}(s, \mathbf{u}) = \sum&lt;/em&gt;{i=1}^n Q_i(u_i)$ 是我们实际用来选动作的函数。&lt;/p&gt;
&lt;p&gt;为了保证 $\arg\max Q&apos;&lt;em&gt;{tot} = \arg\max Q&lt;/em&gt;{tot}$（IGM 原则），QTRAN 推导出了一组充分条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;最优动作一致性&lt;/strong&gt;：在最优动作 $\bar{\mathbf{u}}$ 处，两者相等。
$$ Q&apos;&lt;em&gt;{tot}(\bar{\mathbf{u}}) - Q&lt;/em&gt;{tot}(\bar{\mathbf{u}}) + V_{tot}(\bar{\mathbf{u}}) = 0 $$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非最优动作界限&lt;/strong&gt;：在非最优动作 $\mathbf{u}$ 处，$Q&apos;&lt;em&gt;{tot}$ 不会“篡位”。
$$ Q&apos;&lt;/em&gt;{tot}(\mathbf{u}) - Q_{tot}(\mathbf{u}) + V_{tot}(\mathbf{u}) \ge 0 $$&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;损失函数设计&lt;/h3&gt;
&lt;p&gt;QTRAN 将上述硬约束转化为软损耗（Soft Constraints）加入训练：&lt;/p&gt;
&lt;p&gt;$$
L_{opt} = (Q&apos;&lt;em&gt;{tot}(\bar{\mathbf{u}}) - y&lt;/em&gt;{target})^2 + \lambda \sum_{\mathbf{u} \in \mathcal{U}, \mathbf{u} \ne \bar{\mathbf{u}}} (Q&apos;&lt;em&gt;{tot}(\mathbf{u}) - Q&lt;/em&gt;{tot}(\mathbf{u}) + V_{tot}(\mathbf{u}))^2
$$&lt;/p&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：理论上具有完全的表达能力 (Full Expressiveness)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：实际训练中，软约束很难被完美满足，且计算量巨大（涉及所有动作空间的求和）。在复杂任务上表现往往不如 QMIX。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Weighted QMIX (WQMIX)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/2006.10800&quot;&gt;Weighted QMIX: Expanding Monotonic Value Function Factorisation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;核心思想：非对称加权&lt;/h3&gt;
&lt;p&gt;WQMIX 指出 QMIX 的核心问题是 &lt;strong&gt;相对过泛化 (Relative Overgeneralization)&lt;/strong&gt;：为了拟合某些非最优的低分动作，模型被迫拉低了最优动作的 Q 值。&lt;/p&gt;
&lt;p&gt;WQMIX 提出：&lt;strong&gt;我们其实不在乎非最优动作的 Q 值准不准，我们只在乎最优动作的 Q 值准不准。&lt;/strong&gt;
因此，我们可以给最优样本赋予极高的权重。&lt;/p&gt;
&lt;h3&gt;算法架构&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;无限制网络 $\hat{Q}^*$&lt;/strong&gt;：使用一个普通的前馈网络（不加绝对值约束）来估计真实的联合 Q 值。这保证了表达能力，但不满足 IGM。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单调网络 $Q_{tot}$&lt;/strong&gt;：使用标准的 QMIX 结构（满足 IGM），用来做策略执行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加权投影&lt;/strong&gt;：通过加权 Loss 强行让 $Q_{tot}$ 去逼近 $\hat{Q}^*$。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;损失函数&lt;/h3&gt;
&lt;p&gt;$$
\mathcal{L} = \sum_{i=1}^b w(s, \mathbf{u}) \left( \hat{Q}^*(s, \mathbf{u}) - Q_{tot}(s, \mathbf{u}) \right)^2
$$&lt;/p&gt;
&lt;p&gt;权重函数 $w$ 的设计体现了&lt;strong&gt;乐观主义&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 $\hat{Q}^*$ 认为当前动作很好的（可能是潜在的最优解），给大权重 $w=1$。&lt;/li&gt;
&lt;li&gt;如果 $\hat{Q}^*$ 认为当前动作很差，给小权重 $w=\alpha \ll 1$。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这使得 $Q_{tot}$ 即使受限于单调性，也会优先保证在最优动作附近的形状是正确的，从而突破了结构瓶颈。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;QPLEX: Duplex Dueling Multi-Agent Q-Learning&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/2008.01062&quot;&gt;QPLEX: Duplex Dueling Multi-Agent Q-Learning&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;核心思想：基于优势的 IGM&lt;/h3&gt;
&lt;p&gt;QPLEX 是目前的 SOTA 方法之一。它借鉴了 Dueling DQN 的思想，指出 IGM 原则其实只关乎 &lt;strong&gt;优势函数 (Advantage)&lt;/strong&gt;，与状态价值 $V(s)$ 无关。&lt;/p&gt;
&lt;p&gt;$$ Q*{tot}(s, \mathbf{u}) = V*{tot}(s) + A_{tot}(s, \mathbf{u}) $$&lt;/p&gt;
&lt;p&gt;只要保证 $A_{tot}$ 和局部 $A_i$ 在“正负号”上的一致性，就能满足 IGM，而不需要限制权重的正负。&lt;/p&gt;
&lt;h3&gt;数学构造&lt;/h3&gt;
&lt;p&gt;QPLEX 构造了如下形式的联合优势函数：&lt;/p&gt;
&lt;p&gt;$$
A_{tot}(s, \mathbf{u}) = \sum_{i=1}^n \lambda_i(s, \mathbf{u}) A_i(s, u_i)
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;关键点：只要系数 $\lambda_i(s, \mathbf{u}) &gt; 0$，那么 $A_{tot}$ 的符号就由 $A_i$ 决定。
&lt;ul&gt;
&lt;li&gt;如果所有 $u_i$ 都是局部最优（$A_i=0$），那么 $A_{tot}=0$（全局最优）。&lt;/li&gt;
&lt;li&gt;如果有任何一个 $u_i$ 不是最优（$A_i &amp;#x3C; 0$），那么 $A_{tot} &amp;#x3C; 0$（全局非最优）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;网络架构&lt;/h3&gt;
&lt;p&gt;QPLEX 使用 &lt;strong&gt;多头注意力机制 (Multi-Head Attention)&lt;/strong&gt; 来动态生成权重 $\lambda_i$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这不仅保证了 $\lambda_i &gt; 0$，还赋予了模型根据当前状态动态调整每个智能体权重的能力。&lt;/li&gt;
&lt;li&gt;通过这种严格的数学构造，QPLEX 在理论上实现了&lt;strong&gt;完全表达能力&lt;/strong&gt;，同时保留了 VDN 般高效的计算效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;总结与对比&lt;/h2&gt;
&lt;p&gt;| 算法      | 核心机制               | 表达能力      | IGM 保证            | 计算复杂度 |
| :-------- | :--------------------- | :------------ | :------------------ | :--------- |
| &lt;strong&gt;QMIX&lt;/strong&gt;  | 单调性约束 (权重 $&gt;0$) | 受限 (单调类) | 严格                | 低         |
| &lt;strong&gt;QTRAN&lt;/strong&gt; | 软约束松弛 + 罚项      | 完全          | 近似 (软约束)       | 极高       |
| &lt;strong&gt;WQMIX&lt;/strong&gt; | 双网络 + 非对称加权    | 近似完全      | 严格 (投影后)       | 中         |
| &lt;strong&gt;QPLEX&lt;/strong&gt; | 优势分解 + 注意力权重  | &lt;strong&gt;完全&lt;/strong&gt;      | &lt;strong&gt;严格 (数学构造)&lt;/strong&gt; | 中         |&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;演进脉络&lt;/strong&gt;：
从 QMIX 的“削足适履”（为了 IGM 牺牲表达能力），到 QPLEX 的“量体裁衣”（通过精巧的数学构造同时实现 IGM 和完全表达能力），值分解算法在 MARL 领域已经发展得相当成熟。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/105886628_p0_master1200.5fl6its319.webp"/><enclosure url="https://pic.hana0721.top/105886628_p0_master1200.5fl6its319.webp"/></item><item><title>RL笔记（23）：多智能体值分解 (VDN &amp; QMIX)</title><link>https://hana-blog.top/blog/rl-note-23</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-23</guid><description>如何在不牺牲独立决策能力的前提下，实现复杂的协作？详解多智能体强化学习中的值分解流派。涵盖 IGM 原则、VDN 的线性分解与 QMIX 的单调性约束设计。</description><pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在上一篇笔记中，我们讨论了 MARL 的两种极端：&lt;strong&gt;JAL（完全中心化）&lt;/strong&gt; 理论完美但不可计算，&lt;strong&gt;IPPO（完全独立）&lt;/strong&gt; 计算简单但理论有毒。&lt;/p&gt;
&lt;p&gt;我们需要一种折中方案：&lt;strong&gt;CTDE（中心化训练，分布式执行）&lt;/strong&gt;。
对于 Value-Based 方法来说，CTDE 的核心挑战在于：&lt;strong&gt;如何利用全局 $Q_{tot}$ 来指导局部 $Q_i$ 的更新，同时确保局部贪婪决策能导致全局最优？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就是 &lt;strong&gt;值分解 (Value Decomposition)&lt;/strong&gt; 算法的核心使命。本章将介绍这一流派的开山之作 &lt;strong&gt;VDN&lt;/strong&gt; 和经典之作 &lt;strong&gt;QMIX&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;理论基石：Dec-POMDP 与 IGM&lt;/h2&gt;
&lt;h3&gt;问题形式化：Dec-POMDP&lt;/h3&gt;
&lt;p&gt;我们将多智能体协作任务建模为 &lt;strong&gt;去中心化部分可观测马尔可夫决策过程 (Dec-POMDP)&lt;/strong&gt;。
元组定义为 $G = \langle \mathcal{S}, N, U, P, R, Z, O, \gamma \rangle$：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$s \in \mathcal{S}$：全局状态。&lt;/li&gt;
&lt;li&gt;$u_i \in U$：智能体 $i$ 的动作，联合动作 $\mathbf{u} \in U^n$。&lt;/li&gt;
&lt;li&gt;$z_i \in Z$：智能体 $i$ 的局部观测。&lt;/li&gt;
&lt;li&gt;$\tau_i$：动作-观测历史（因为是部分可观测，智能体需要记住历史）。&lt;/li&gt;
&lt;li&gt;$Q_{tot}(s, \mathbf{u})$：全局联合价值函数，代表集体的利益。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;核心原则：IGM (Individual-Global-Max)&lt;/h3&gt;
&lt;p&gt;为了保证“分布式执行”的有效性，我们必须确保：&lt;strong&gt;当每个智能体都最大化自己的局部利益 $Q_i$ 时，集体的利益 $Q_{tot}$ 也同时被最大化。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这被称为 &lt;strong&gt;IGM 原则&lt;/strong&gt;，数学表达为：&lt;/p&gt;
&lt;p&gt;$$
\arg\max_{\mathbf{u}} Q_{tot}(\boldsymbol{\tau}, \mathbf{u}) =
\begin{pmatrix}
\arg\max_{u_1} Q_1(\tau_1, u_1) \
\vdots \
\arg\max_{u_n} Q_n(\tau_n, u_n)
\end{pmatrix}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直觉&lt;/strong&gt;：就像一支理想的足球队，如果前锋拼命进球（局部最优），后卫拼命防守（局部最优），那么整支球队的胜率（全局最优）也应该是最高的。如果满足 IGM，我们就不需要复杂的协调通信，各自为战即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;VDN (Value-Decomposition Networks)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/1706.05296&quot;&gt;Value-Decomposition Networks For Cooperative Multi-Agent Learning&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;VDN&lt;/strong&gt; 是值分解领域的奠基之作。它的思路非常简单粗暴：假设全局价值就是局部价值的&lt;strong&gt;直接加和&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;核心假设&lt;/h3&gt;
&lt;p&gt;$$
Q_{tot}(\boldsymbol{\tau}, \mathbf{u}) = \sum_{i=1}^n Q_i(\tau_i, u_i; \theta_i)
$$&lt;/p&gt;
&lt;h3&gt;训练与执行&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;训练时&lt;/strong&gt;：我们最小化 $Q_{tot}$ 与真实回报的 TD 误差：
$$ \mathcal{L}(\theta) = \left( r + \gamma \max*{\mathbf{u}&apos;} Q*{tot}(\boldsymbol{\tau}&apos;, \mathbf{u}&apos;; \theta^-) - Q*{tot}(\boldsymbol{\tau}, \mathbf{u}; \theta) \right)^2 $$
注意：这里的 $\max*{\mathbf{u}&apos;} Q_{tot}$ 可以很容易计算，因为 $\max \sum Q_i = \sum \max Q_i$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行时&lt;/strong&gt;：每个智能体只需选择 $u_i^* = \arg\max_{u_i} Q_i$，即可保证选择了全局最优动作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;局限性&lt;/h3&gt;
&lt;p&gt;VDN 满足 IGM 原则，但&lt;strong&gt;求和&lt;/strong&gt;的假设太强了。它限制了 $Q_{tot}$ 只能表示局部价值的线性组合，无法处理复杂的非线性协作（例如：只有当 A 和 B 同时做某事时，奖励才会暴增）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;QMIX&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;论文&lt;/strong&gt;：&lt;a href=&quot;https://arxiv.org/abs/1803.11485&quot;&gt;QMIX: Monotonic Value Function Factorisation for Deep Multi-Agent Reinforcement Learning&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;QMIX&lt;/strong&gt; 放宽了 VDN 的限制。它指出，要满足 IGM 原则，并不需要严格的线性求和，只需要满足 &lt;strong&gt;单调性约束 (Monotonicity Constraint)&lt;/strong&gt; 即可。&lt;/p&gt;
&lt;h3&gt;核心假设&lt;/h3&gt;
&lt;p&gt;$$
\frac{\partial Q_{tot}}{\partial Q_i} \ge 0, \quad \forall i \in {1, \dots, n}
$$&lt;/p&gt;
&lt;p&gt;即：&lt;strong&gt;局部价值 $Q_i$ 越高，全局价值 $Q_{tot}$ 也就越高。&lt;/strong&gt;
只要满足这个条件，$\arg\max Q_{tot}$ 就一定等价于 $\arg\max Q_i$ 的组合。&lt;/p&gt;
&lt;h3&gt;网络架构&lt;/h3&gt;
&lt;p&gt;QMIX 使用一个 &lt;strong&gt;混合网络 (Mixing Network)&lt;/strong&gt; 来拟合 $Q_{tot}$，它以所有 $Q_i$ 为输入，以 $Q_{tot}$ 为输出。
为了保证单调性（权重非负），QMIX 引入了一个 &lt;strong&gt;超网络 (Hypernetwork)&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Agent Network&lt;/strong&gt;: 输入局部观测 $\tau_i$，输出 $Q_i$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mixing Network&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;这是一个前馈神经网络，输入是 ${Q_1, \dots, Q_n}$，输出是 $Q_{tot}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键点&lt;/strong&gt;：这个网络的&lt;strong&gt;权重 (Weights)&lt;/strong&gt; 是由 Hypernetwork 生成的，且被强制取绝对值（保证非负）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hypernetwork&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;输入是全局状态 $s$。&lt;/li&gt;
&lt;li&gt;输出是 Mixing Network 的权重 $W$ 和偏置 $b$。&lt;/li&gt;
&lt;li&gt;公式：$W_{mix} = | \text{Hyper}(s) |$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过这种设计，QMIX 实现了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;非线性能力&lt;/strong&gt;：Mixing Network 可以是复杂的非线性函数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态依赖&lt;/strong&gt;：全局状态 $s$ 决定了混合的方式（例如在某些状态下，$Q_1$ 更重要；在另一些状态下，$Q_2$ 更重要）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单调性保证&lt;/strong&gt;：由于权重恒为正，混合网络对输入 $Q_i$ 保持单调递增。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;损失函数&lt;/h3&gt;
&lt;p&gt;QMIX 的训练也是标准的 DQN 风格：&lt;/p&gt;
&lt;p&gt;$$
\mathcal{L}(\theta) = \left( r + \gamma Q_{tot}(\boldsymbol{\tau}&apos;, s&apos;, \mathbf{u}&lt;em&gt;{max}&apos;; \theta^-) - Q&lt;/em&gt;{tot}(\boldsymbol{\tau}, s, \mathbf{u}; \theta) \right)^2
$$&lt;/p&gt;
&lt;p&gt;其中 $\mathbf{u}_{max}&apos;$ 是通过各个 $Q_i$ 贪婪选出的动作组合。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结与对比&lt;/h2&gt;
&lt;p&gt;| 维度             | VDN                         | QMIX                           |
| :--------------- | :-------------------------- | :----------------------------- |
| &lt;strong&gt;分解方式&lt;/strong&gt;     | 线性求和                    | 非线性混合                     |
| &lt;strong&gt;IGM 保证&lt;/strong&gt;     | 是 (Sum)                    | 是 (Monotonicity)              |
| &lt;strong&gt;全局信息利用&lt;/strong&gt; | 无 (仅通过反向传播隐式利用) | &lt;strong&gt;有 (Hypernetwork 输入 $s$)&lt;/strong&gt; |
| &lt;strong&gt;表达能力&lt;/strong&gt;     | 弱 (仅限线性关系)           | &lt;strong&gt;强 (单调非线性关系)&lt;/strong&gt;        |
| &lt;strong&gt;适用场景&lt;/strong&gt;     | 简单协作                    | 复杂非线性协作 (如集火攻击)    |&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;QMIX 的地位&lt;/strong&gt;：
它是 MARL 领域最经典的算法之一。虽然它只能处理满足单调性假设的任务（有些任务可能局部最优不等于全局最优），但在《星际争霸》(SMAC) 等主流测试平台上，QMIX 及其变体长期占据统治地位。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/104724685_p0_master1200.7p472bcti3.webp"/><enclosure url="https://pic.hana0721.top/104724685_p0_master1200.7p472bcti3.webp"/></item><item><title>RL笔记（22）：初入多智能体强化学习 (MARL)</title><link>https://hana-blog.top/blog/rl-note-22</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-22</guid><description>MARL 的两个极端：详解联合动作学习 (JAL) 与独立学习 (Independent RL)。深度分析“维度灾难”与“环境非平稳性”这对核心矛盾。</description><pubDate>Wed, 31 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的 19 篇笔记中，我们研究的都是 &lt;strong&gt;单智能体强化学习 (Single-Agent RL)&lt;/strong&gt;。
但在现实世界中，任务往往涉及多个个体。&lt;strong&gt;多智能体强化学习 (MARL)&lt;/strong&gt; 将问题扩展到了随机博弈（Stochastic Games）的领域。&lt;/p&gt;
&lt;p&gt;在进入复杂的 SOTA 算法之前，我们必须先理解解决 MARL 问题的两种&lt;strong&gt;最朴素、最极端&lt;/strong&gt;的思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;完全中心化 (JAL)&lt;/strong&gt;：把所有人看作一个人。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完全去中心化 (IPPO)&lt;/strong&gt;：把队友看作空气（或环境噪声）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这两种思路分别对应了 MARL 的两大核心难题：&lt;strong&gt;维度灾难&lt;/strong&gt; 与 &lt;strong&gt;非平稳性&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;理论模型：随机博弈&lt;/h2&gt;
&lt;p&gt;定义为一个元组 $(N, \mathcal{S}, \mathcal{A}, \mathcal{P}, \mathcal{R}, \gamma)$：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$N$：智能体数量。&lt;/li&gt;
&lt;li&gt;$\mathcal{S}$：全局状态空间。&lt;/li&gt;
&lt;li&gt;$\mathcal{A} = \mathcal{A}_1 \times \dots \times \mathcal{A}_N$：&lt;strong&gt;联合动作空间 (Joint Action Space)&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;状态转移取决于所有人的动作组合 $\mathbf{a} = (a_1, \dots, a_N)$。&lt;/li&gt;
&lt;li&gt;即 $s&apos; \sim \mathcal{P}(\cdot | s, \mathbf{a})$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;极端一：联合动作学习 (JAL)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Joint Action Learning (JAL)&lt;/strong&gt; 代表了 &lt;strong&gt;完全中心化 (Fully Centralized)&lt;/strong&gt; 的思路。&lt;/p&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;既然环境受所有人的动作 $\mathbf{a}$ 影响，那我们就构建一个拥有上帝视角的 &lt;strong&gt;“超级智能体” (Super Agent)&lt;/strong&gt;，它接收全局状态 $s$，直接输出联合动作 $\mathbf{a}$ 来控制所有单位。&lt;/p&gt;
&lt;h3&gt;方法&lt;/h3&gt;
&lt;p&gt;直接套用标准的 PPO 算法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入：$s$&lt;/li&gt;
&lt;li&gt;输出：$\mathbf{a} = (a_1, a_2, \dots, a_N)$&lt;/li&gt;
&lt;li&gt;策略：$\Pi_\theta(\mathbf{a} | s)$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;理论分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势：环境平稳&lt;/strong&gt;。
对于这个超级智能体来说，外界环境是静止的（Stationary），因为没有“其他人”在干扰它。因此，马尔可夫性质成立，RL 的收敛性理论依然有效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;致命缺陷：维度灾难 (Curse of Dimensionality)&lt;/strong&gt;。
假设每个智能体有 $|A|$ 个动作，共有 $N$ 个智能体。联合动作空间的大小为 $|A|^N$。
&lt;ul&gt;
&lt;li&gt;$|A|=5, N=10 \Rightarrow 5^{10} \approx 9,765,625$。&lt;/li&gt;
&lt;li&gt;输出层需要预测近一千万个概率值，这在计算上是不可行的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;极端二：独立学习 (Independent RL / IPPO)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Independent PPO (IPPO)&lt;/strong&gt; 代表了 &lt;strong&gt;完全去中心化 (Fully Decentralized)&lt;/strong&gt; 的思路。&lt;/p&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;“把队友当空气”&lt;/strong&gt;。
每个智能体 $i$ 都是一个独立的个体，它只关心自己的观测 $o_i$ 和奖励 $r_i$。它把所有“其他智能体”都视为&lt;strong&gt;环境的一部分&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;方法&lt;/h3&gt;
&lt;p&gt;同时运行 $N$ 个独立的 PPO 算法（参数可以共享，也可以不共享）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于智能体 $i$：
&lt;ul&gt;
&lt;li&gt;输入：局部观测 $o_i$&lt;/li&gt;
&lt;li&gt;输出：独立动作 $a_i$&lt;/li&gt;
&lt;li&gt;策略：$\pi_{\theta_i}(a_i | o_i)$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;理论分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势：线性扩展&lt;/strong&gt;。
计算复杂度随人数 $N$ 线性增长。无论有多少人，每个网络只输出 $|A|$ 个概率。这完美解决了维度灾难。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;致命缺陷：非平稳性 (Non-Stationarity)&lt;/strong&gt;。
从智能体 $i$ 的视角看，状态转移概率变成了：
$$ P(s&apos;|s, a&lt;em&gt;i) = \sum&lt;/em&gt;{\mathbf{a}&lt;em&gt;{-i}} P(s&apos;|s, a_i, \mathbf{a}&lt;/em&gt;{-i}) \pi*{-i}(\mathbf{a}*{-i}|s) $$
注意公式里的 $\pi_{-i}$（队友的策略）。在训练过程中，&lt;strong&gt;队友也在学习，$\pi_{-i}$ 一直在变&lt;/strong&gt;。
这意味着：&lt;strong&gt;对于智能体 $i$ 来说，昨天有用的策略，今天可能就没用了，因为环境（队友）变了。&lt;/strong&gt;
这破坏了马尔可夫假设，理论上算法无法收敛。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;总结与对比&lt;/h2&gt;
&lt;p&gt;| 维度           | 联合动作学习 (JAL)           | 独立学习 (IPPO)                                  |
| :------------- | :--------------------------- | :----------------------------------------------- | --- | ---------------------- | --- | --- |
| &lt;strong&gt;控制方式&lt;/strong&gt;   | &lt;strong&gt;完全中心化&lt;/strong&gt; (Super Agent) | &lt;strong&gt;完全去中心化&lt;/strong&gt; (Independent Agents)            |
| &lt;strong&gt;动作空间&lt;/strong&gt;   | 联合动作 $\mathbf{a}$        | 独立动作 $a_i$                                   |
| &lt;strong&gt;空间复杂度&lt;/strong&gt; | &lt;strong&gt;指数级爆炸&lt;/strong&gt; $             | A                                                | ^N$ | &lt;strong&gt;线性增长&lt;/strong&gt; $N \times | A   | $   |
| &lt;strong&gt;环境性质&lt;/strong&gt;   | &lt;strong&gt;平稳 (Stationary)&lt;/strong&gt;        | &lt;strong&gt;非平稳 (Non-Stationary)&lt;/strong&gt;                      |
| &lt;strong&gt;理论保证&lt;/strong&gt;   | 有收敛保证                   | 无收敛保证                                       |
| &lt;strong&gt;实际表现&lt;/strong&gt;   | 规模稍大即无法运行           | 尽管没理论保证，但在实践中往往是 Strong Baseline |&lt;/p&gt;
&lt;h3&gt;这一章的启示&lt;/h3&gt;
&lt;p&gt;我们陷入了两难境地：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;选 JAL，理论稳但算不动。&lt;/li&gt;
&lt;li&gt;选 IPPO，算得快但理论虚。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这就引出了 MARL 研究的圣杯——&lt;strong&gt;CTDE (集中式训练，分布式执行)&lt;/strong&gt;。
我们需要一种折中的方法：&lt;strong&gt;在训练时利用 JAL 的上帝视角来缓解非平稳性，在执行时利用 IPPO 的独立结构来避免维度灾难。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这正是后续 &lt;strong&gt;MAPPO&lt;/strong&gt; 和 &lt;strong&gt;QMIX&lt;/strong&gt; 等算法的核心动机。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/102841826_p0_master1200.4clh7xw95k.webp"/><enclosure url="https://pic.hana0721.top/102841826_p0_master1200.4clh7xw95k.webp"/></item><item><title>RL笔记（21）：目标导向的强化学习 (Goal-Conditioned RL)</title><link>https://hana-blog.top/blog/rl-note-21</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-21</guid><description>从解决单一任务到解决一类任务：详解目标导向 RL 的数学形式化。涵盖通用价值函数近似 (UVFA) 理论，以及解决稀疏奖励难题的核心技术——事后经验回放 (HER)。</description><pubDate>Tue, 30 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在标准的强化学习设定中，我们通常训练一个智能体去完成&lt;strong&gt;一个特定的任务&lt;/strong&gt;（例如：打赢一局游戏，或者让机器人走到特定的坐标 $(x,y)$）。如果任务目标变了（例如：走到新的坐标 $(x&apos;, y&apos;)$），我们通常需要重新训练网络。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标导向的强化学习 (Goal-Conditioned RL, GCRL)&lt;/strong&gt; 旨在打破这一限制。我们希望训练一个智能体，它不仅能根据状态 $s$ 做出决策，还能根据&lt;strong&gt;输入的目标 $g$&lt;/strong&gt; 动态调整策略。&lt;/p&gt;
&lt;p&gt;即策略函数变为 $\pi(a|s, g)$，价值函数变为 $Q(s, a, g)$。这使得智能体具备了&lt;strong&gt;多任务泛化&lt;/strong&gt;的能力。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;问题形式化 (Problem Formulation)&lt;/h2&gt;
&lt;p&gt;我们将标准的马尔可夫决策过程 (MDP) 扩展为 &lt;strong&gt;目标增强的 MDP (Augmented MDP)&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;扩展元组&lt;/h3&gt;
&lt;p&gt;新的元组定义为 $\mathcal{M} = (\mathcal{S}, \mathcal{A}, \mathcal{P}, \mathcal{G}, r, \gamma)$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\mathcal{G}$：目标空间（通常是状态空间 $\mathcal{S}$ 的子集）。&lt;/li&gt;
&lt;li&gt;$r(s, a, g)$：目标导向的奖励函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;奖励函数的设计&lt;/h3&gt;
&lt;p&gt;在 GCRL 中，最常见的奖励函数有两种形式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;稀疏奖励 (Sparse Reward)&lt;/strong&gt;：
只有当智能体真正达成目标时才给奖励（通常用于机械臂抓取等精确任务）。&lt;/p&gt;
&lt;p&gt;$$
r(s, a, g) = \mathbb{I}(\phi(s) = g) = \begin{cases} 0 &amp;#x26; \text{if } ||\phi(s) - g|| &amp;#x3C; \epsilon \ -1 &amp;#x26; \text{otherwise} \end{cases}
$$&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(注：这里用 0/-1 奖励结构比 1/0 更常见，因为这样价值函数代表了“到达目标的期望步数”的负值)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;稠密奖励 (Dense Reward)&lt;/strong&gt;：
基于当前状态与目标的距离。
$$
r(s, a, g) = -||\phi(s) - g||_2
$$&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;目标：UVFA&lt;/h3&gt;
&lt;p&gt;我们的优化目标是找到一个最优策略 $\pi^*$，最大化所有可能的起始状态和目标的期望回报：&lt;/p&gt;
&lt;p&gt;$$
J(\pi) = \mathbb{E}&lt;em&gt;{g \sim p(g), s_0 \sim p(s_0), \tau \sim \pi} \left[ \sum&lt;/em&gt;{t=0}^\infty \gamma^t r(s_t, a_t, g) \right]
$$&lt;/p&gt;
&lt;p&gt;这种能够同时泛化状态 $s$ 和目标 $g$ 的价值函数近似器，被称为 &lt;strong&gt;通用价值函数近似 (Universal Value Function Approximators, UVFA)&lt;/strong&gt;。
$$ V(s, g; \theta) \approx \mathbb{E} [R | s, g] $$
神经网络结构通常是将状态特征 $\phi(s)$ 和目标特征 $\psi(g)$ 拼接后输入。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;稀疏奖励难题&lt;/h2&gt;
&lt;p&gt;尽管 UVFA 提供了理论框架，但在实际训练中，如果使用&lt;strong&gt;稀疏奖励&lt;/strong&gt;，学习会极其困难。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：在一个高维环境中（例如机械臂控制），随机探索碰到目标 $g$ 的概率几乎为 0。
这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;经验回放池 $\mathcal{D}$ 中几乎所有的样本，其奖励 $r$ 都是 -1（或 0）。&lt;/li&gt;
&lt;li&gt;智能体收不到任何正反馈，梯度无法指引优化的方向。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这被称为 &lt;strong&gt;稀疏奖励问题 (Sparse Reward Problem)&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;事后经验回放 (Hindsight Experience Replay, HER)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HER&lt;/strong&gt; 是 GCRL 领域最核心的算法创新。它的灵感来源于人类的“精神胜利法”。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉&lt;/strong&gt;：
假设你的目标是射中靶心，但你射偏了，射到了旁边的树上。
虽然作为“射靶心”的任务你失败了，但如果我们事后回顾（Hindsight），假设原本的目标就是“射中那棵树”，那你通过刚才的动作序列，完美地完成了任务！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;理论推导&lt;/h3&gt;
&lt;p&gt;HER 的核心在于&lt;strong&gt;重标记 (Relabeling)&lt;/strong&gt; 经验回放池中的目标。&lt;/p&gt;
&lt;p&gt;假设智能体在目标 $g$ 的指导下，产生了一条轨迹：
$$ \tau = {s_0, a_0, r_0, s_1, \dots, s_T} $$
其中，最后的状态 $s_T$ 并没有到达预设目标 $g$，所以所有的 $r_t = -1$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HER 的操作&lt;/strong&gt;：
我们构造一个新的虚假目标 $g&apos; = s_T$（即把最后实际达到的状态当作目标）。
对于轨迹中的每一个转换 $(s_t, a_t, s_{t+1})$，我们根据新目标 $g&apos;$ 重新计算奖励：&lt;/p&gt;
&lt;p&gt;$$
r&apos;&lt;em&gt;t = r(s_t, a_t, g&apos;) = \begin{cases} 0 &amp;#x26; \text{if } s&lt;/em&gt;{t+1} = g&apos; \ -1 &amp;#x26; \text{otherwise} \end{cases}
$$&lt;/p&gt;
&lt;p&gt;由于 $g&apos;$ 就是 $s_T$，那么在轨迹结束时，智能体&lt;strong&gt;必然&lt;/strong&gt;获得了正奖励。&lt;/p&gt;
&lt;p&gt;我们将原始数据 $(s_t, a_t, r_t, s_{t+1}, g)$ 和修改后的数据 $(s_t, a_t, r&apos;&lt;em&gt;t, s&lt;/em&gt;{t+1}, g&apos;)$ 都存入 Replay Buffer。&lt;/p&gt;
&lt;h3&gt;为什么数学上是成立的？(Off-Policy 的重要性)&lt;/h3&gt;
&lt;p&gt;HER 只能用于 &lt;strong&gt;离线策略 (Off-Policy)&lt;/strong&gt; 算法（如 DQN, DDPG, SAC）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：
我们原本产生的轨迹 $\tau$ 是由策略 $\pi(\cdot | s, g)$ 生成的。
但在训练时，我们将其强行解释为是由策略 $\pi(\cdot | s, g&apos;)$ 生成的。
这本质上是一种极端的 Off-Policy 学习：&lt;strong&gt;行为策略的目标与更新时的目标完全不同。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;只要算法支持 Off-Policy（即不仅可以利用自己产生的旧数据，还可以利用“别人”产生的数据），HER 就是数学上合法的。因为对于 $(s_t, a_t, s_{t+1})$ 这个物理转换来说，它只遵循环境动力学 $P(s&apos;|s,a)$，与目标 $g$ 无关。环境的物理规律是不随心中的目标而改变的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;HER 的变种与理论解释&lt;/h2&gt;
&lt;h3&gt;不同的重标记策略 (Replay Strategy)&lt;/h3&gt;
&lt;p&gt;在将数据存入 Buffer 时，我们可以选择不同的重标记方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Future&lt;/strong&gt;: 从当前时间步 $t$ 之后的轨迹中随机选取一个状态作为新目标 $g&apos;$。（这是标准做法，效果最好）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Final&lt;/strong&gt;: 仅使用轨迹的最后一个状态 $s_T$ 作为 $g&apos;$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Episode&lt;/strong&gt;: 从整个轨迹中随机选取一个状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random&lt;/strong&gt;: 随机生成一个全新的目标（效果通常不好）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;隐式课程学习 (Implicit Curriculum Learning)&lt;/h3&gt;
&lt;p&gt;从理论上讲，HER 实现了一种自动的课程学习。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;初期&lt;/strong&gt;：智能体很笨，只能碰到离起始点很近的状态。HER 把这些近的状态设为目标，智能体学会了如何“走一小步”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中期&lt;/strong&gt;：学会走一小步后，智能体能探索到稍远一点的状态。HER 再次把稍远的状态设为目标。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后期&lt;/strong&gt;：随着能力扩展，智能体最终能学会到达真正的远距离目标 $g$。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种奖励信号从容易到困难的自动传播，是 HER 解决稀疏奖励问题的本质原因。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;目标导向 RL 将强化学习从“解决一个问题”提升到了“解决一类问题”。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UVFA&lt;/strong&gt; 提供了网络结构的理论基础，让 $V(s,g)$ 成为可能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HER&lt;/strong&gt; 解决了最为棘手的稀疏奖励问题，通过&lt;strong&gt;事后重标记&lt;/strong&gt;，将每一次失败的尝试都转化为另一次成功的训练数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;至此，我们已经掌握了让智能体学会“指哪打哪”的关键技术。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/90399055_p0_master1200.4g535npbv6.webp"/><enclosure url="https://pic.hana0721.top/90399055_p0_master1200.4g535npbv6.webp"/></item><item><title>Paper Reading: LLM 1</title><link>https://hana-blog.top/blog/paper-reading-llm1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-llm1</guid><description>读一些大型语言模型相关的论文</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;GPT 1.0&lt;/h2&gt;
&lt;h2&gt;BERT&lt;/h2&gt;
&lt;h2&gt;GPT 2.0&lt;/h2&gt;
&lt;h2&gt;Megatron-LM&lt;/h2&gt;
&lt;h2&gt;T5&lt;/h2&gt;
&lt;h2&gt;ZeRO&lt;/h2&gt;
&lt;h2&gt;Scaling Law&lt;/h2&gt;
&lt;h2&gt;GPT 3.0&lt;/h2&gt;
&lt;h2&gt;Switch Transformers&lt;/h2&gt;
&lt;h2&gt;Codex&lt;/h2&gt;
&lt;h2&gt;COT&lt;/h2&gt;
&lt;h2&gt;InstructGPT&lt;/h2&gt;
&lt;h2&gt;PaLM&lt;/h2&gt;
&lt;h2&gt;LLaMA&lt;/h2&gt;
&lt;h2&gt;GPT 4&lt;/h2&gt;
&lt;h2&gt;DPO&lt;/h2&gt;
&lt;h2&gt;ToT&lt;/h2&gt;
&lt;h2&gt;LLaMA2&lt;/h2&gt;
&lt;h2&gt;Mistral 7B&lt;/h2&gt;
&lt;h2&gt;Mamba&lt;/h2&gt;
&lt;h2&gt;Mamba2&lt;/h2&gt;
&lt;h2&gt;Qwen2.5&lt;/h2&gt;
&lt;h2&gt;DeepSeek-V3&lt;/h2&gt;
&lt;h2&gt;DeepSeek-R1&lt;/h2&gt;
&lt;h2&gt;Kimi K2&lt;/h2&gt;
&lt;h2&gt;DFT&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/68232005_p0_master1200.60uu51sjdo.webp"/><enclosure url="https://pic.hana0721.top/68232005_p0_master1200.60uu51sjdo.webp"/></item><item><title>Paper Reading: LLM 2</title><link>https://hana-blog.top/blog/paper-reading-llm2</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-llm2</guid><description>读一些大型语言模型相关的论文</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;MMaDA&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/69461006_p0_master1200.1e974ctzq9.webp"/><enclosure url="https://pic.hana0721.top/69461006_p0_master1200.1e974ctzq9.webp"/></item><item><title>Paper Reading: MLLM 1</title><link>https://hana-blog.top/blog/paper-reading-mllm1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-mllm1</guid><description>读一些多模态相关的论文</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;CLIP&lt;/h2&gt;
&lt;h2&gt;SigLip&lt;/h2&gt;
&lt;h2&gt;SigLipV2&lt;/h2&gt;
&lt;h2&gt;Dino V1&lt;/h2&gt;
&lt;h2&gt;Dino V2&lt;/h2&gt;
&lt;h2&gt;Dino V3&lt;/h2&gt;
&lt;h2&gt;ViLT&lt;/h2&gt;
&lt;h2&gt;ALBEF&lt;/h2&gt;
&lt;h2&gt;VLMo&lt;/h2&gt;
&lt;h2&gt;BLIP&lt;/h2&gt;
&lt;h2&gt;CoCa&lt;/h2&gt;
&lt;h2&gt;BEiT V3&lt;/h2&gt;
&lt;h2&gt;BLIP2&lt;/h2&gt;
&lt;h2&gt;LLava&lt;/h2&gt;
&lt;h2&gt;Flamingo&lt;/h2&gt;
&lt;h2&gt;Visual Instruction Tuning&lt;/h2&gt;
&lt;h2&gt;Qwen-VL&lt;/h2&gt;
&lt;h2&gt;Gemini&lt;/h2&gt;
&lt;h2&gt;GPT-4V&lt;/h2&gt;
&lt;h2&gt;LISA&lt;/h2&gt;
&lt;h2&gt;Cambrian-1&lt;/h2&gt;
&lt;h2&gt;Qwen2-VL&lt;/h2&gt;
&lt;h2&gt;Qwen2.5-VL&lt;/h2&gt;
&lt;h2&gt;InstructBLIP&lt;/h2&gt;
&lt;h2&gt;LLaMA-Adapter&lt;/h2&gt;
&lt;h2&gt;VisionLLM&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/70434530_p0_master1200.b9htgy5uw.webp"/><enclosure url="https://pic.hana0721.top/70434530_p0_master1200.b9htgy5uw.webp"/></item><item><title>Paper Reading: MLLM 2</title><link>https://hana-blog.top/blog/paper-reading-mllm2</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-mllm2</guid><description>读一些多模态相关的论文</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;LLaVA-OneVision&lt;/h2&gt;
&lt;h2&gt;GPT 5&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/70977889_p0_master1200.9gx5x526gc.webp"/><enclosure url="https://pic.hana0721.top/70977889_p0_master1200.9gx5x526gc.webp"/></item><item><title>Paper Reading: Unify MLLM 1</title><link>https://hana-blog.top/blog/paper-reading-umllm1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-umllm1</guid><description>读一些统一多模态大模型相关的论文</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;速览一些统一多模态大模型的论文。&lt;/p&gt;
&lt;h2&gt;SEED&lt;/h2&gt;
&lt;h2&gt;LaVIT&lt;/h2&gt;
&lt;h2&gt;SEED-X&lt;/h2&gt;
&lt;h2&gt;Emu&lt;/h2&gt;
&lt;h2&gt;Emu2&lt;/h2&gt;
&lt;h2&gt;Chameleon&lt;/h2&gt;
&lt;h2&gt;Transfusion&lt;/h2&gt;
&lt;h2&gt;Emu3&lt;/h2&gt;
&lt;h2&gt;MMAR&lt;/h2&gt;
&lt;h2&gt;Janus&lt;/h2&gt;
&lt;h2&gt;PUMA&lt;/h2&gt;
&lt;h2&gt;Show-o&lt;/h2&gt;
&lt;h2&gt;VILA-U&lt;/h2&gt;
&lt;h2&gt;MIO&lt;/h2&gt;
&lt;h2&gt;JanusFlow&lt;/h2&gt;
&lt;h2&gt;Orthus&lt;/h2&gt;
&lt;h2&gt;TokenFlow&lt;/h2&gt;
&lt;h2&gt;Liquid&lt;/h2&gt;
&lt;h2&gt;MUSE-VL&lt;/h2&gt;
&lt;h2&gt;SILMM&lt;/h2&gt;
&lt;h2&gt;ILLUME&lt;/h2&gt;
&lt;h2&gt;Visual Lexicon&lt;/h2&gt;
&lt;h2&gt;LatentLM&lt;/h2&gt;
&lt;h2&gt;SynerGen-VL&lt;/h2&gt;
&lt;h2&gt;MetaMorph&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/71875434_p0_master1200.7pvvr603z.webp"/><enclosure url="https://pic.hana0721.top/71875434_p0_master1200.7pvvr603z.webp"/></item><item><title>Paper Reading: Unify MLLM 1</title><link>https://hana-blog.top/blog/paper-reading-umllm2</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-umllm2</guid><description>读一些统一多模态大模型相关的论文</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;速览一些统一多模态大模型的论文。&lt;/p&gt;
&lt;h2&gt;LMFusion&lt;/h2&gt;
&lt;h2&gt;UniToken&lt;/h2&gt;
&lt;h2&gt;VARGPT&lt;/h2&gt;
&lt;h2&gt;Janus-Pro&lt;/h2&gt;
&lt;h2&gt;BLIP3-o&lt;/h2&gt;
&lt;h2&gt;Show-o2&lt;/h2&gt;
&lt;h2&gt;Qwen-Image&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/49383596_p0_master1200.6t7pmts45l.webp"/><enclosure url="https://pic.hana0721.top/49383596_p0_master1200.6t7pmts45l.webp"/></item><item><title>RL笔记（20）：Decision Transformer</title><link>https://hana-blog.top/blog/rl-note-20</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-20</guid><description>范式转移：当强化学习遇上 Transformer。详解 Decision Transformer 如何抛弃贝尔曼方程，利用 Return-to-Go 将 RL 重构为条件序列建模问题。</description><pubDate>Mon, 29 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的笔记中，无论是有模型还是无模型，在线还是离线，我们解决 RL 问题的核心思路都是基于 &lt;strong&gt;动态规划 (Dynamic Programming)&lt;/strong&gt; 的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们要估计价值函数 $V(s)$ 或 $Q(s,a)$。&lt;/li&gt;
&lt;li&gt;利用贝尔曼方程进行迭代更新（自举）。&lt;/li&gt;
&lt;li&gt;通过最大化价值来获得策略。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Decision Transformer (DT)&lt;/strong&gt; 提出了一种完全不同的视角：
如果我们拥有大量的离线轨迹数据，为什么不把它看作是一个 &lt;strong&gt;序列建模 (Sequence Modeling)&lt;/strong&gt; 问题呢？
就像 GPT 预测下一个单词一样，能不能根据&lt;strong&gt;过去的轨迹&lt;/strong&gt;和&lt;strong&gt;期望的回报&lt;/strong&gt;，直接预测&lt;strong&gt;下一个动作&lt;/strong&gt;？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：
强化学习 $\approx$ 在给定&lt;strong&gt;期望回报 (Target Return)&lt;/strong&gt; 条件下的&lt;strong&gt;行为克隆 (Conditional Behavior Cloning)&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;轨迹表示与 Return-to-Go&lt;/h2&gt;
&lt;p&gt;为了将 RL 问题转化为 Transformer 可以处理的序列问题，我们需要重新定义模型的输入。&lt;/p&gt;
&lt;h3&gt;轨迹 (Trajectory)&lt;/h3&gt;
&lt;p&gt;一条标准的 RL 轨迹由状态、动作和奖励组成：
$$ \tau = (s_1, a_1, r_1, s_2, a_2, r_2, \dots, s_T, a_T, r_T) $$&lt;/p&gt;
&lt;h3&gt;剩余回报 (Return-to-Go, RTG)&lt;/h3&gt;
&lt;p&gt;传统的 RL 使用即时奖励 $r_t$。但在做序列预测时，我们更关心“&lt;strong&gt;未来还能拿多少分&lt;/strong&gt;”。
定义 $t$ 时刻的 &lt;strong&gt;Return-to-Go (RTG)&lt;/strong&gt; $\hat{R}_t$ 为从当前时刻到回合结束的累积回报：&lt;/p&gt;
&lt;p&gt;$$
\hat{R}&lt;em&gt;t = \sum&lt;/em&gt;{k=t}^T r_k
$$&lt;/p&gt;
&lt;h3&gt;模型的输入序列&lt;/h3&gt;
&lt;p&gt;DT 的核心创新在于将 RTG 作为一种&lt;strong&gt;条件 (Condition)&lt;/strong&gt; 输入给模型。
输入序列被组织为三元组的序列（K-V-Q 模式）：&lt;/p&gt;
&lt;p&gt;$$
\tau_{input} = (\hat{R}_1, s_1, a_1, \hat{R}_2, s_2, a_2, \dots, \hat{R}_T, s_T, a_T)
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直觉&lt;/strong&gt;：这就像是在告诉模型：“我现在状态是 $s_1$，我想在未来总共获得 $\hat{R}_1$ 分，请告诉我该做什么动作 $a_1$？”&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;网络架构&lt;/h2&gt;
&lt;p&gt;DT 直接使用了 &lt;strong&gt;GPT (Generative Pre-trained Transformer)&lt;/strong&gt; 的架构，即因果掩码 Transformer (Causal Transformer)。&lt;/p&gt;
&lt;h3&gt;嵌入层 (Embeddings)&lt;/h3&gt;
&lt;p&gt;由于 $R, s, a$ 的模态不同（标量、图像/向量、离散/连续），我们需要先将它们映射到同一个维度 $d$：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;状态嵌入&lt;/strong&gt;：CNN 或 MLP 处理 $s_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作嵌入&lt;/strong&gt;：Embedding 层或 MLP 处理 $a_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回报嵌入&lt;/strong&gt;：MLP 处理 $\hat{R}_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时间步嵌入 (Timestep Embedding)&lt;/strong&gt;：为了让模型知道当前处于轨迹的哪个阶段，额外加入一个可学习的时间位置编码。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;上下文窗口 (Context Window)&lt;/h3&gt;
&lt;p&gt;由于轨迹可能很长，Transformer 无法处理整个 Episode。DT 使用一个固定的上下文窗口 $K$（context length），只把最近的 $K$ 步输入模型：&lt;/p&gt;
&lt;p&gt;$$
\text{Input}&lt;em&gt;t = [\hat{R}&lt;/em&gt;{t-K}, s_{t-K}, a_{t-K}, \dots, \hat{R}_t, s_t]
$$&lt;/p&gt;
&lt;h3&gt;预测目标&lt;/h3&gt;
&lt;p&gt;模型的目标是预测下一个 token。在 DT 中，我们主要关注预测&lt;strong&gt;动作&lt;/strong&gt; $a_t$。&lt;/p&gt;
&lt;p&gt;$$
a_t = \text{DecisionTransformer}(\hat{R}&lt;em&gt;{t-K}, s&lt;/em&gt;{t-K}, a_{t-K}, \dots, \hat{R}_t, s_t)
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;训练与推断&lt;/h2&gt;
&lt;h3&gt;训练 (Training)&lt;/h3&gt;
&lt;p&gt;DT 的训练过程完全是&lt;strong&gt;监督学习 (Supervised Learning)&lt;/strong&gt;，不需要计算梯度，不需要贝尔曼误差，不需要目标网络。&lt;/p&gt;
&lt;p&gt;从离线数据集 $\mathcal{D}$ 中采样轨迹片段，最小化预测动作与真实动作的误差：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;离散动作&lt;/strong&gt;：交叉熵损失 (Cross-Entropy Loss)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连续动作&lt;/strong&gt;：均方误差 (MSE Loss)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
\mathcal{L}(\theta) = \mathbb{E}&lt;em&gt;{\tau \sim \mathcal{D}} \left[ \sum&lt;/em&gt;{t=1}^T (a_t - \hat{a}_t)^2 \right]
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 注意&lt;/strong&gt;：虽然数据集中可能包含低分的轨迹（“臭棋”），但模型学习的是条件概率 $P(a_t | \hat{R}_t, s_t, \dots)$。也就是说，模型学会了“如果想要低分该怎么做”以及“如果想要高分该怎么做”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;推断 (Inference)&lt;/h3&gt;
&lt;p&gt;这是 DT 最神奇的地方。训练完成后，我们可以通过&lt;strong&gt;提示 (Prompting)&lt;/strong&gt; 来控制智能体。&lt;/p&gt;
&lt;p&gt;我们给智能体设定一个&lt;strong&gt;目标回报 (Target Return)&lt;/strong&gt; $\hat{R}_{target}$（通常设为数据集中最高分的那个回报，或者更高）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;自回归生成过程&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;初始时刻 $t=1$&lt;/strong&gt;：输入 $(\hat{R}_{target}, s_1)$，模型输出动作 $a_1$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;环境交互&lt;/strong&gt;：执行 $a_1$，环境返回即时奖励 $r_1$ 和新状态 $s_2$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新目标&lt;/strong&gt;：既然已经拿到了 $r_1$，剩下的目标就要减去它：
$$ \hat{R}_2 = \hat{R}_1 - r_1 $$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;下一时刻&lt;/strong&gt;：输入 $(\dots, \hat{R}_1, s_1, a_1, \hat{R}_2, s_2)$，预测 $a_2$。&lt;/li&gt;
&lt;li&gt;重复直至结束。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;理论对比：DT vs. CQL&lt;/h2&gt;
&lt;p&gt;为了理解 DT 的位置，我们将它与上一篇笔记中的 CQL 进行对比：&lt;/p&gt;
&lt;p&gt;| 维度               | Conservative Q-Learning (CQL)          | Decision Transformer (DT)                |
| :----------------- | :------------------------------------- | :--------------------------------------- |
| &lt;strong&gt;核心范式&lt;/strong&gt;       | &lt;strong&gt;动态规划 (DP)&lt;/strong&gt;                      | &lt;strong&gt;序列建模 (SL)&lt;/strong&gt;                        |
| &lt;strong&gt;学习目标&lt;/strong&gt;       | 逼近最优价值函数 $Q^*(s,a)$            | 拟合条件分布 $P(a|\hat{R}, s)$          |
| &lt;strong&gt;训练方式&lt;/strong&gt;       | 最小化贝尔曼误差 (TD Error)            | 最大化动作似然 (Supervised)              |
| &lt;strong&gt;OOD 处理&lt;/strong&gt;       | 显式惩罚未知动作的 Q 值 (悲观主义)     | 依靠 Transformer 的泛化能力 (无显式约束) |
| &lt;strong&gt;长时序信用分配&lt;/strong&gt; | 依靠 $Q$ 值的自举传播 (Bootstrapping)  | 依靠 Attention 机制直接关联过去与未来    |
| &lt;strong&gt;优点&lt;/strong&gt;           | 理论下界保证，擅长拼接轨迹 (Stitching) | 训练极其稳定，易于扩展，能够处理稀疏奖励 |&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Decision Transformer 的提出证明了：&lt;strong&gt;只要模型足够强（Transformer），强化学习可以被简化为监督学习。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它不需要复杂的 Actor-Critic 架构。&lt;/li&gt;
&lt;li&gt;它不需要处理“过高估计”等死板的数值问题。&lt;/li&gt;
&lt;li&gt;它通过 &lt;strong&gt;Return-to-Go&lt;/strong&gt; 实现了类似于“事后诸葛亮”的条件控制：利用这一局最终的得分作为条件，来学习这一局中的动作。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://pic.hana0721.top/131135880_p0_master1200.9rjzqc0dp5.webp"/><enclosure url="https://pic.hana0721.top/131135880_p0_master1200.9rjzqc0dp5.webp"/></item><item><title>RL笔记（19）：离线强化学习 (Offline RL)</title><link>https://hana-blog.top/blog/rl-note-19</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-19</guid><description>数据驱动的强化学习：当不能与环境交互时，如何从静态数据集中学习？深度解析分布偏移 (Distribution Shift) 问题，以及 BCQ 和 CQL 算法的理论推导。</description><pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的章节（DQN, SAC, DDPG）中，我们讨论的都是 &lt;strong&gt;在线 (Online)&lt;/strong&gt; 或 &lt;strong&gt;离线策略 (Off-Policy)&lt;/strong&gt; 算法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Online&lt;/strong&gt;: 边玩边学。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Off-Policy&lt;/strong&gt;: 可以利用之前的经验回放池（Replay Buffer），但仍然需要不断与环境交互来补充新数据，纠正价值估计。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;离线强化学习 (Offline RL)&lt;/strong&gt;，又称 Batch RL，面临的是一个更严苛的场景：
&lt;strong&gt;智能体完全不能与环境交互，只能从一个固定的、历史的数据集 $\mathcal{D}$ 中学习策略。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就像是“仅仅通过看别人下棋的棋谱（而且可能含有臭棋），就要学会成为棋圣”。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;核心挑战：分布偏移 (Distribution Shift)&lt;/h2&gt;
&lt;p&gt;既然 Off-Policy 算法（如 SAC）可以使用 Replay Buffer，为什么不能直接把静态数据集 $\mathcal{D}$ 当作 Buffer 跑 SAC 呢？&lt;/p&gt;
&lt;p&gt;答案是：&lt;strong&gt;外推误差 (Extrapolation Error)&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;贝尔曼更新的陷阱&lt;/h3&gt;
&lt;p&gt;回顾 Q-Learning 的贝尔曼更新目标：&lt;/p&gt;
&lt;p&gt;$$
\mathcal{T}Q(s, a) = r + \gamma \max_{a&apos;} Q(s&apos;, a&apos;)
$$&lt;/p&gt;
&lt;p&gt;或者 Actor-Critic 中的：&lt;/p&gt;
&lt;p&gt;$$
y = r + \gamma Q(s&apos;, \pi(s&apos;))
$$&lt;/p&gt;
&lt;p&gt;这里存在一个致命的 &lt;strong&gt;反事实查询 (Counterfactual Query)&lt;/strong&gt; 问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据集 $\mathcal{D}$ 是由行为策略 $\pi_\beta$ 产生的。&lt;/li&gt;
&lt;li&gt;我们学习的新策略 $\pi$ 通常会与 $\pi_\beta$ 不同（为了变得更好）。&lt;/li&gt;
&lt;li&gt;在计算目标值时，我们需要查询 $Q(s&apos;, \pi(s&apos;))$。&lt;/li&gt;
&lt;li&gt;如果 $\pi(s&apos;)$ 选出的动作 $a&apos;$ &lt;strong&gt;从未在数据集 $\mathcal{D}$ 中出现过&lt;/strong&gt;（OOD, Out-of-Distribution），Q 网络对这个从未见过的 $(s&apos;, a&apos;)$ 会输出什么？
&lt;ul&gt;
&lt;li&gt;根据神经网络的特性，它会输出一个随机的、无意义的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最大化的诅咒&lt;/strong&gt;：由于 $\max$ 操作的存在，优化器会倾向于选择那些被&lt;strong&gt;错误高估&lt;/strong&gt;的 OOD 动作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;误差传播&lt;/strong&gt;：这个高估的误差会通过贝尔曼方程回传，导致整个 Q 函数崩溃。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这就是 &lt;strong&gt;分布偏移&lt;/strong&gt;：训练数据的分布 $(s, a) \sim \pi_\beta$ 与学习策略的分布 $(s, a) \sim \pi$ 不一致，导致 Q 值在未知区域严重高估。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;解决方案一：基于约束的方法 (BCQ)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;BCQ (Batch-Constrained deep Q-learning)&lt;/strong&gt; 的思路非常直观：
既然 OOD 动作会导致 Q 值高估，那我就&lt;strong&gt;禁止智能体选择那些在数据集中没出现过的动作&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;理论推导&lt;/h3&gt;
&lt;p&gt;我们希望优化策略 $\pi$，使其在最大化 $Q$ 值的动作，同时满足它生成的动作分布接近行为策略 $\pi_\beta$：&lt;/p&gt;
&lt;p&gt;$$
\pi(s) = \arg\max_{a} Q(s,a) \quad \text{s.t.} \quad P(a|s, \mathcal{D}) &gt; \tau
$$&lt;/p&gt;
&lt;h3&gt;算法实现逻辑&lt;/h3&gt;
&lt;p&gt;由于 $\pi_\beta$ 未知，BCQ 训练一个 &lt;strong&gt;生成模型 (VAE)&lt;/strong&gt; 来拟合数据集中的状态-动作分布。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;生成 (Generation)&lt;/strong&gt;：使用 CVAE 根据状态 $s$ 生成一组“在该状态下可能出现过的”候选动作 ${a_1, \dots, a_n}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扰动 (Perturbation)&lt;/strong&gt;：训练一个扰动网络 $\xi_\phi(s,a)$，对生成的动作进行微调（在小范围内寻找更优解），得到 ${a_1+\xi_1, \dots, a_n+\xi_n}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选择 (Selection)&lt;/strong&gt;：在这些动作中，选择 Q 值最大的那个作为最终输出。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 评价&lt;/strong&gt;：BCQ 是一种保守策略，它消除了外推误差，但也限制了策略的泛化能力（只能在见过的数据附近微调）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;解决方案二：基于价值的保守方法 (CQL)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CQL (Conservative Q-Learning)&lt;/strong&gt; 是目前理论最完备的 Offline RL 算法之一。
它的思路是：&lt;strong&gt;修改贝尔曼更新的目标函数，显式地惩罚 OOD 动作的 Q 值。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;目标函数设计&lt;/h3&gt;
&lt;p&gt;标准的贝尔曼误差是最小化 $\mathbb{E}_{(s,a)\sim\mathcal{D}} [(Q - \mathcal{T}Q)^2]$。
CQL 增加了一个正则项，用于&lt;strong&gt;最小化&lt;/strong&gt;策略 $\mu$（新策略）产生的动作的 Q 值，同时&lt;strong&gt;最大化&lt;/strong&gt;数据集 $\mathcal{D}$ 中动作的 Q 值：&lt;/p&gt;
&lt;p&gt;$$
\min_Q \alpha \left( \underbrace{\mathbb{E}&lt;em&gt;{s\sim\mathcal{D}, a\sim\mu(a|s)} [Q(s,a)]}&lt;/em&gt;{\text{压低 OOD 动作的 Q 值}} - \underbrace{\mathbb{E}&lt;em&gt;{s\sim\mathcal{D}, a\sim\pi&lt;/em&gt;\beta(a|s)} [Q(s,a)]}&lt;em&gt;{\text{拉高数据中动作的 Q 值}} \right) + \frac{1}{2} \mathbb{E}&lt;/em&gt;{\mathcal{D}} [(Q - \mathcal{B}^{\pi}Q)^2]
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\mu(a|s)$：可以是当前学习的策略，也可以是随机分布。&lt;/li&gt;
&lt;li&gt;通过这种“压低未知，拉高已知”，CQL 迫使 Q 函数在 OOD 区域产生低估。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;理论推导：Q 值下界 (Lower Bound)&lt;/h3&gt;
&lt;p&gt;CQL 的核心理论贡献在于证明了上述目标函数学习到的 Q 值 $\hat{Q}$ 是真实 Q 值 $Q^\pi$ 的&lt;strong&gt;下界&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;令 $\mathcal{B}^\pi$ 为贝尔曼算子。CQL 的不动点迭代可以近似写作：&lt;/p&gt;
&lt;p&gt;$$
\hat{Q}_{k+1} \leftarrow \mathcal{B}^\pi \hat{Q}&lt;em&gt;k - \alpha (\mu - \pi&lt;/em&gt;\beta)
$$&lt;/p&gt;
&lt;p&gt;(这里省略了复杂的采样误差项，仅关注期望行为)&lt;/p&gt;
&lt;p&gt;当 $\alpha$ 足够大时，可以证明：&lt;/p&gt;
&lt;p&gt;$$
\mathbb{E}&lt;em&gt;{\pi(a|s)} [\hat{Q}(s,a)] \le \mathbb{E}&lt;/em&gt;{\pi(a|s)} [Q^\pi(s,a)]
$$&lt;/p&gt;
&lt;p&gt;这意味着 CQL 学习到的 Q 值是&lt;strong&gt;保守的 (Conservative)&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果策略想去一个未知区域，CQL 会告诉它：“那个地方 Q 值很低（哪怕实际上可能很高）”。&lt;/li&gt;
&lt;li&gt;因此，策略会倾向于留在数据覆盖的“安全区域”内。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;广义 CQL (Generalization)&lt;/h3&gt;
&lt;p&gt;对于连续动作空间，CQL 将第一项正则化改为 $\log\sum\exp$ 形式（Soft-max），以覆盖所有可能的动作：&lt;/p&gt;
&lt;p&gt;$$
\min_Q \alpha \mathbb{E}&lt;em&gt;{s \sim \mathcal{D}} \left[ \log \sum_a \exp(Q(s,a)) - \mathbb{E}&lt;/em&gt;{a \sim \pi_\beta}[Q(s,a)] \right] + \text{Bellman Error}
$$&lt;/p&gt;
&lt;p&gt;这等价于压低整个 Q 值函数的配分函数，确保没有任何一个 OOD 动作能由于外推误差而获得异常高的 Q 值。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结：从乐观到悲观&lt;/h2&gt;
&lt;p&gt;| 维度         | 在线/离线策略 RL (DQN/SAC)         | 离线 RL (Offline RL)                                         |
| :----------- | :--------------------------------- | :----------------------------------------------------------- |
| &lt;strong&gt;心态&lt;/strong&gt;     | &lt;strong&gt;乐观 (Optimistic)&lt;/strong&gt;              | &lt;strong&gt;悲观 (Pessimistic) / 保守&lt;/strong&gt;                                |
| &lt;strong&gt;对待未知&lt;/strong&gt; | 未知动作 = 潜在的高回报 (探索红利) | 未知动作 = 潜在的风险 (外推误差)                             |
| &lt;strong&gt;核心算法&lt;/strong&gt; | $\max Q$                           | $\max Q$ s.t. $a \in \mathcal{D}$ (BCQ) $\min Q_{OOD}$ (CQL) |
| &lt;strong&gt;适用场景&lt;/strong&gt; | 模拟器、低成本试错环境             | 医疗、工业控制、自动驾驶 (试错成本极高)                      |&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;离线强化学习的本质&lt;/strong&gt;：
在无法验证真伪的情况下，&lt;strong&gt;宁可错过（低估），不可做错（高估）。&lt;/strong&gt; 只有当数据充分证明某个策略好时，我们才敢信任它。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/103975128_p0_master1200.8vniavqp93.webp"/><enclosure url="https://pic.hana0721.top/103975128_p0_master1200.8vniavqp93.webp"/></item><item><title>RL笔记（18）：基于模型的策略优化 (MBPO)</title><link>https://hana-blog.top/blog/rl-note-18</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-18</guid><description>Model-Based RL 的集大成者：深度解析 MBPO 的理论边界。从单调性保证到分支推演 (Branched Rollout)，论证如何通过控制推演步长来解决模型偏差带来的二次误差累积问题。</description><pubDate>Sat, 27 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在强化学习的谱系中，存在一对矛盾：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model-Free (如 SAC)&lt;/strong&gt;：渐进性能好（能收敛到很高的分数），但样本效率极低（需要几百万次交互）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model-Based (如 Dyna)&lt;/strong&gt;：样本效率高（利用模型生成数据），但往往受限于&lt;strong&gt;模型偏差 (Model Bias)&lt;/strong&gt;，导致渐进性能较差。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;模型偏差的诅咒&lt;/strong&gt;在于：模型也是从数据中拟合出来的，它必然存在误差。当智能体在模型中进行长距离推演时，微小的误差会像滚雪球一样迅速放大（Compound Error），导致智能体在“虚构的完美世界”里表现很好，但在“残酷的现实世界”里一塌糊涂。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MBPO (Model-Based Policy Optimization)&lt;/strong&gt; 的核心贡献在于它通过严格的理论推导，定量地分析了模型误差与策略性能的关系，并提出了一种&lt;strong&gt;分支推演 (Branched Rollout)&lt;/strong&gt; 机制，在保证单调提升的前提下，最大化地利用模型。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;理论推导：回报差的上界&lt;/h2&gt;
&lt;p&gt;我们希望找到一个策略 $\pi$，最大化真实环境中的期望回报 $\eta[\pi]$。
但我们只能优化模型环境中的期望回报 $\hat{\eta}[\pi]$。
我们需要推导两者之间的误差上界，从而找到一个安全优化的 &lt;strong&gt;下界 (Lower Bound)&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;定义与假设&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;真实环境的状态转移分布：$p(s&apos;|s,a)$&lt;/li&gt;
&lt;li&gt;学习模型的状态转移分布：$\hat{p}(s&apos;|s,a)$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型误差 $\epsilon_m$&lt;/strong&gt;：假设模型在分布上的最大误差是有界的（TV 散度）：
$$ \max*{s,a} D*{TV}(p(\cdot|s,a) || \hat{p}(\cdot|s,a)) \le \epsilon_m $$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;状态分布的差异&lt;/h3&gt;
&lt;p&gt;策略 $\pi$ 在真实动力学下的状态访问分布为 $\rho_\pi$，在模型动力学下的状态访问分布为 $\hat{\rho}_\pi$。
根据 &lt;em&gt;Simulation Lemma&lt;/em&gt;，两者之间的差异上界为：&lt;/p&gt;
&lt;p&gt;$$
D_{TV}(\rho_\pi || \hat{\rho}_\pi) \le \frac{\gamma \epsilon_m}{(1-\gamma)^2}
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 警示&lt;/strong&gt;：注意这里的 $(1-\gamma)^2$。这意味着状态分布的误差是随着时间视界（Horizon）&lt;strong&gt;二次方&lt;/strong&gt;增长的。这就是为什么在传统 Model-Based RL 中，长距离推演会导致极其严重的偏差。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;真实回报与模型回报的差异&lt;/h3&gt;
&lt;p&gt;基于上述状态分布差异，我们可以推导出真实回报 $\eta[\pi]$ 与模型回报 $\hat{\eta}[\pi]$ 的差值上界：&lt;/p&gt;
&lt;p&gt;$$
\left| \eta[\pi] - \hat{\eta}[\pi] \right| \le 2 r_{\max} \frac{\gamma \epsilon_m}{(1-\gamma)^2}
$$&lt;/p&gt;
&lt;p&gt;这个上界告诉我们：如果我们只是简单地训练一个模型，然后在模型里从头跑到尾（Full Rollout）来训练策略，那么真实性能和模型性能的差距可能会非常大，优化 $\hat{\eta}$ 并不代表能提升 $\eta$。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;核心突破：分支推演 (Branched Rollout)&lt;/h2&gt;
&lt;p&gt;为了打破上述的“二次方误差诅咒”，MBPO 提出改变推演的方式。&lt;/p&gt;
&lt;h3&gt;什么是分支推演？&lt;/h3&gt;
&lt;p&gt;传统的 Dyna 风格是从初始状态 $s_0$ 开始，一直推演到 $s_T$。
MBPO 的 &lt;strong&gt;$k$-步分支推演&lt;/strong&gt; 是：
从策略在&lt;strong&gt;真实环境&lt;/strong&gt;中访问过的状态分布 $\mathcal{D}_{\text{env}}$ 中采样一个状态 $s$，作为推演的起点，然后仅在模型中推演 $k$ 步。&lt;/p&gt;
&lt;h3&gt;修正后的误差边界&lt;/h3&gt;
&lt;p&gt;在 $k$-步分支推演下，回报差的上界被显著收紧了：&lt;/p&gt;
&lt;p&gt;$$
\left| \eta[\pi] - \hat{\eta}&lt;em&gt;k[\pi] \right| \le 2 r&lt;/em&gt;{\max} \left[ \frac{\gamma^{k+1} \epsilon_\pi}{(1-\gamma)^2} + \frac{\gamma^k \epsilon_m}{1-\gamma} + \frac{k}{1-\gamma}\epsilon_m \right]
$$&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(注：公式略作简化以直观展示核心项)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;关键在于最后一项 $\frac{k}{1-\gamma}\epsilon_m$。
这表明：&lt;strong&gt;在分支推演下，模型误差 $\epsilon_m$ 对总回报误差的贡献是随 $k$ 线性增长的，而不是二次方增长。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;$k$ 的权衡 (Trade-off)&lt;/h3&gt;
&lt;p&gt;这个理论边界揭示了超参数 $k$ 的物理意义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;当 $k$ 很小时&lt;/strong&gt;：误差界很紧（线性），我们可以放心地信任模型数据。但是模型能提供的“远见”有限。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;当 $k$ 很大时&lt;/strong&gt;：误差界变松（趋向二次方），模型偏差开始主导，导致策略性能下降。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，MBPO 动态调整 $k$：在训练初期模型不准时，$k=1$；随着模型精度提高，逐渐增加 $k$（通常从 1 增加到 15 左右），在保证偏差可控的前提下最大化利用模型。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;算法架构设计&lt;/h2&gt;
&lt;p&gt;MBPO 的实现是围绕上述理论构建的，由三个核心模块组成。&lt;/p&gt;
&lt;h3&gt;概率模型集成 (Ensemble of Probabilistic Models)&lt;/h3&gt;
&lt;p&gt;为了准确估计模型误差并防止过拟合，MBPO 训练一组（如 7 个）高斯概率网络。
$$ \hat{p}&lt;em&gt;\theta(s&apos;|s,a) = \mathcal{N}(\mu&lt;/em&gt;\theta(s,a), \Sigma_\theta(s,a)) $$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不确定性感知&lt;/strong&gt;：预测时随机从集成模型中选择一个成员，这模拟了贝叶斯后验采样，能够捕捉&lt;strong&gt;认知不确定性 (Epistemic Uncertainty)&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;混合策略优化 (Model-Based Policy Optimization)&lt;/h3&gt;
&lt;p&gt;MBPO 使用 &lt;strong&gt;SAC (Soft Actor-Critic)&lt;/strong&gt; 作为底层的策略优化器。
为了克服模型偏差，SAC 的训练数据由两部分组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;真实数据 $\mathcal{D}_{\text{env}}$&lt;/strong&gt;：占比很小（如 5%）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型数据 $\mathcal{D}_{\text{model}}$&lt;/strong&gt;：由分支推演产生的海量数据，占比很大（如 95%）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;算法流程 (伪代码)&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize policy } \pi_\phi, \text{ ensemble models } {p_{\theta_1}, \dots, p_{\theta_B}} \
&amp;#x26; \bullet ; \text{Initialize empty buffers } \mathcal{D}&lt;em&gt;{\text{env}}, \mathcal{D}&lt;/em&gt;{\text{model}} \
&amp;#x26; \bullet ; \textbf{For } N \text{ epochs} \textbf{ do}: \
&amp;#x26; \bullet \qquad \textbf{1. Collect Real Data (Exploration)} \
&amp;#x26; \bullet \qquad \text{Rollout policy in real environment, add transitions to } \mathcal{D}&lt;em&gt;{\text{env}} \
&amp;#x26; \bullet \qquad \textbf{2. Train Models (Supervised Learning)} \
&amp;#x26; \bullet \qquad \text{Train ensemble } {p&lt;/em&gt;\theta} \text{ on } \mathcal{D}&lt;em&gt;{\text{env}} \text{ via Max Likelihood} \
&amp;#x26; \bullet \qquad \textbf{3. Branched Rollout (Data Augmentation)} \
&amp;#x26; \bullet \qquad \text{Sample states } s \sim \mathcal{D}&lt;/em&gt;{\text{env}} \
&amp;#x26; \bullet \qquad \text{Rollout } k \text{ steps using models, add to } \mathcal{D}&lt;em&gt;{\text{model}} \
&amp;#x26; \bullet \qquad \textbf{4. Policy Optimization (SAC)} \
&amp;#x26; \bullet \qquad \textbf{For } G \text{ gradient steps} \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Sample batch } B \text{ from } \mathcal{D}&lt;/em&gt;{\text{env}} \cup \mathcal{D}&lt;em&gt;{\text{model}} \text{ (e.g., 95% model data)} \
&amp;#x26; \bullet \qquad \qquad \text{Update } \pi&lt;/em&gt;\phi, Q_\psi \text{ using SAC loss} \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结：MBPO 的启示&lt;/h2&gt;
&lt;p&gt;MBPO 是 Model-Based RL 的一个转折点，它通过理论证明回答了**“我们该多大程度信任模型？”**这个问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;单调性保证&lt;/strong&gt;：通过限制推演步长 $k$，MBPO 将模型误差控制在线性增长范围内，从而保证了策略改进的单调性（类似 TRPO 的 Trust Region 思想，但应用在 Rollout Length 上）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据增强&lt;/strong&gt;：MBPO 本质上是把学到的模型当作一个&lt;strong&gt;超级数据增强器&lt;/strong&gt;。它从有限的真实数据出发，通过短步推演，扩散出海量的虚拟数据供 SAC 训练。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结果&lt;/strong&gt;：MBPO 在 MuJoCo 等基准测试中，以 1/10 甚至更少的样本量，达到了与 SAC 相当的渐进性能，打破了“Model-Based 也就是学得快但分不高”的刻板印象。&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://pic.hana0721.top/98259515_p0_master1200.3ns7nvxpav.webp"/><enclosure url="https://pic.hana0721.top/98259515_p0_master1200.3ns7nvxpav.webp"/></item><item><title>Paper Reading: VLM 1</title><link>https://hana-blog.top/blog/paper-reading-vlm1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-vlm1</guid><description>从零开始的VLM研究生活。</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;浅浅学习一下VLM相关的知识。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/56422576_p0_master1200.7i0z6ufn65.webp"/><enclosure url="https://pic.hana0721.top/56422576_p0_master1200.7i0z6ufn65.webp"/></item><item><title>Paper Reading: VLM 2</title><link>https://hana-blog.top/blog/paper-reading-vlm2</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-vlm2</guid><description>从零开始的VLM研究生活。</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;浅浅学习一下VLM相关的知识。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/57793944_p0_master1200.4xv4u7fok2.webp"/><enclosure url="https://pic.hana0721.top/57793944_p0_master1200.4xv4u7fok2.webp"/></item><item><title>Paper Reading: Basic Method 1</title><link>https://hana-blog.top/blog/paper-reading-bm1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-bm1</guid><description>从基础开始吧！</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;这里主要是一些比较基础性的文章。&lt;/p&gt;
&lt;h2&gt;VAE&lt;/h2&gt;
&lt;h2&gt;Transformer&lt;/h2&gt;
&lt;h2&gt;Diffusion&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/62423081_p0_master1200.3ns7nvxp9b.webp"/><enclosure url="https://pic.hana0721.top/62423081_p0_master1200.3ns7nvxp9b.webp"/></item><item><title>Paper Reading: MARL 1</title><link>https://hana-blog.top/blog/paper-reading-marl1</link><guid isPermaLink="true">https://hana-blog.top/blog/paper-reading-marl1</guid><description>回来吧MARL，我最骄傲的信仰。</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { ArxivRating, RatingCriteria } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;博主制作这篇博客时，已弃坑MARL，仅留作纪念，怀念最初的感觉。&lt;/p&gt;
&lt;h2&gt;VDN&lt;/h2&gt;
&lt;h2&gt;QMIX&lt;/h2&gt;
&lt;h2&gt;Qatten&lt;/h2&gt;
&lt;h2&gt;QTRAN&lt;/h2&gt;
&lt;h2&gt;QPLEX&lt;/h2&gt;
&lt;h2&gt;RQN&lt;/h2&gt;
&lt;h2&gt;GraphMix&lt;/h2&gt;
&lt;h2&gt;Weighted QMIX&lt;/h2&gt;
&lt;h2&gt;MAVEN&lt;/h2&gt;
&lt;h2&gt;HAPPO&lt;/h2&gt;
&lt;h2&gt;MAT&lt;/h2&gt;
&lt;h2&gt;RODE&lt;/h2&gt;
&lt;h2&gt;EMC&lt;/h2&gt;
&lt;h2&gt;EMU&lt;/h2&gt;
&lt;h2&gt;HPN&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/69606244_p0_master1200.64eg2rlm3q.webp"/><enclosure url="https://pic.hana0721.top/69606244_p0_master1200.64eg2rlm3q.webp"/></item><item><title>RL笔记（17）：模型预测控制 (MPC)</title><link>https://hana-blog.top/blog/rl-note-17</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-17</guid><description>在已知环境模型的情况下，如何高效规划？详解模型预测控制 (MPC) 的原理：预测未来、滚动优化与MPC-SAC。</description><pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的笔记中，我们探讨了&lt;strong&gt;无模型 (Model-Free)&lt;/strong&gt; 算法（如 Q-Learning, PPO, SAC）如何通过试错学习。我们也初步接触了&lt;strong&gt;基于模型 (Model-Based)&lt;/strong&gt; 的 Dyna-Q，它通过学习环境模型来生成模拟数据辅助学习。&lt;/p&gt;
&lt;p&gt;今天我们深入 Model-Based RL 的另一个重要分支：&lt;strong&gt;模型预测控制 (Model Predictive Control, MPC)&lt;/strong&gt;。
MPC 的核心优势在于，它&lt;strong&gt;直接利用环境模型进行前向规划&lt;/strong&gt;，像一个经验丰富的棋手一样“预判”未来，然后根据预测结果做出当前最优的决策。&lt;/p&gt;
&lt;p&gt;MPC 不仅仅是生成模拟数据（像 Dyna-Q），它是在每一个状态下，都&lt;strong&gt;重新规划&lt;/strong&gt;一次未来的动作序列。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;MPC 的核心思想&lt;/h2&gt;
&lt;p&gt;MPC 是一种控制策略，它假设我们拥有一个精确的环境模型 $M(s,a) \to (r, s&apos;)$。&lt;/p&gt;
&lt;h3&gt;预测与规划 (Prediction &amp;#x26; Planning)&lt;/h3&gt;
&lt;p&gt;在当前状态 $s_t$，MPC 并不直接选择一个动作。而是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;预测未来&lt;/strong&gt;：假设从当前状态 $s_t$ 开始，执行一系列动作 $\pi = {a_t, a_{t+1}, \dots, a_{t+H}}$（称为&lt;strong&gt;预测时域 (Prediction Horizon)&lt;/strong&gt; $H$）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模拟轨迹&lt;/strong&gt;：利用模型 $M$，模拟出这一系列动作可能产生的轨迹及其累积奖励。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优化规划&lt;/strong&gt;：找到能最大化累积奖励的动作序列 $\pi^*$。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;滚动优化 (Rolling Optimization)&lt;/h3&gt;
&lt;p&gt;MPC 的决策过程是&lt;strong&gt;滚动&lt;/strong&gt;的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在状态 $s_t$，MPC 找到最优动作序列 $\pi^* = {a_t^&lt;em&gt;, a_{t+1}^&lt;/em&gt;, \dots, a_{t+H}^*}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只执行第一个动作 $a_t^*$&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;智能体进入新状态 $s_{t+1}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重新规划&lt;/strong&gt;：基于新的状态 $s_{t+1}$，重复步骤 1-3。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉&lt;/strong&gt;：
MPC 就像一个有预见性的决策者。它不会一次性把所有步骤都定死，而是每走一步，都根据当前情况重新规划下一步的最佳路径。这使得它能很好地应对环境变化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;MPC 与强化学习的结合&lt;/h2&gt;
&lt;p&gt;MPC 本身是一种控制方法，如何与 RL 结合呢？&lt;/p&gt;
&lt;h3&gt;学习模型&lt;/h3&gt;
&lt;p&gt;在 Model-Based RL 中，我们首先需要学习一个环境模型 $M_\theta$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;监督学习&lt;/strong&gt;：用收集到的真实数据 $(s, a, r, s&apos;)$ 来训练模型。
&lt;ul&gt;
&lt;li&gt;预测奖励：$r \approx R_\phi(s,a)$&lt;/li&gt;
&lt;li&gt;预测状态：$s&apos; \approx M_\theta(s,a)$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型类型&lt;/strong&gt;：可以是&lt;strong&gt;概率模型&lt;/strong&gt;（如 PETS 使用的概率 GP 回归）来估计模型的不确定性，也可以是确定性模型（如简单的神经网络）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;MPC 规划&lt;/h3&gt;
&lt;p&gt;一旦有了模型 $M_\theta$，就可以进行规划：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在当前状态 $s_t$，MPC 在所有可能的 $H$ 步动作序列中搜索，找到能最大化模拟累积奖励的序列 $\pi^* = {a_t^&lt;em&gt;, a_{t+1}^&lt;/em&gt;, \dots, a_{t+H}^*}$。&lt;/li&gt;
&lt;li&gt;执行 $a_t^*$。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;优化策略&lt;/h3&gt;
&lt;p&gt;MPC 产生的动作序列 $\pi^&lt;em&gt;$ 本身就可以看作是当前状态下的一个“策略”。我们可以用标准的 RL 算法（如 PPO, SAC）来优化**生成这个 $\pi^&lt;/em&gt;$ 的参数**。&lt;/p&gt;
&lt;p&gt;例如，&lt;strong&gt;MPC-SAC&lt;/strong&gt; 的做法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;训练一个模型&lt;/strong&gt; $M_\theta$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;训练 Actor-Critic&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;Critic 学习 $Q$ 值。&lt;/li&gt;
&lt;li&gt;Actor 学习策略 $\pi_\phi$。&lt;/li&gt;
&lt;li&gt;在 Actor 的更新时，&lt;strong&gt;使用 MPC 规划 $H$ 步&lt;/strong&gt;，获得更准确的奖励信号来更新 Actor。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;PETS 算法示例 (Probabilistic Embeded Trajectory Sampling)&lt;/h2&gt;
&lt;p&gt;PETS 是一个经典的 Model-Based RL 算法，它使用了&lt;strong&gt;概率模型&lt;/strong&gt;来处理模型不确定性。&lt;/p&gt;
&lt;h3&gt;概率模型&lt;/h3&gt;
&lt;p&gt;PETS 使用&lt;strong&gt;高斯过程 (Gaussian Process, GP)&lt;/strong&gt; 或其他概率模型来学习奖励函数 $R_\phi$ 和状态转移函数 $M_\theta$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：GP 不仅能给出预测值，还能给出预测的&lt;strong&gt;不确定性&lt;/strong&gt;（方差）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;采样与规划&lt;/h3&gt;
&lt;p&gt;在 MPC 规划时，PETS 会从学到的概率模型中&lt;strong&gt;采样&lt;/strong&gt;多条可能的未来轨迹。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果模型预测某一步不确定性很高（方差大），意味着未来可能有很多种情况，MPC 会倾向于选择风险更小的路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;策略学习&lt;/h3&gt;
&lt;p&gt;PETS 也会用学习到的模型和规划结果来训练一个 Actor-Critic 策略。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;MPC 的优势与劣势&lt;/h2&gt;
&lt;h3&gt;优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高样本效率&lt;/strong&gt;：通过模型可以“预演”大量场景，大大减少与真实环境的交互次数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可解释性&lt;/strong&gt;：模型可以被检查，知道智能体为什么这么规划。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全性&lt;/strong&gt;：在规划时可以预见潜在的危险状态，并加以避免。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;劣势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;模型偏差 (Model Bias)&lt;/strong&gt;：这是 Model-Based 方法的软肋。如果模型不准确，规划的结果可能完全错误。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;计算成本&lt;/strong&gt;：MPC 规划本身需要大量的计算，尤其是在动作空间连续、预测时域长的情况下。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;MPC 提供了一种强大的在&lt;strong&gt;已知模型&lt;/strong&gt;下进行序列决策的方法。它通过向前看（预测）和滚动优化，能够做出比直接 RL 更明智的决策。
将 MPC 与 RL 结合，是 Model-Based RL 的重要研究方向，其目标是学好模型，并用好模型，从而以最高的样本效率解决复杂任务。&lt;/p&gt;
&lt;p&gt;我们之前的笔记主要聚焦于 Model-Free 方法（Q-Learning, REINFORCE, PPO, SAC）。Model-Based 方法（Dyna-Q, MPC）提供了另一种思路，它们往往在样本效率上具有优势，但在模型精度要求上较高。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/95619535_p0_master1200.8okafg4jti.webp"/><enclosure url="https://pic.hana0721.top/95619535_p0_master1200.8okafg4jti.webp"/></item><item><title>RL笔记（16）：模仿学习 (Imitation Learning)</title><link>https://hana-blog.top/blog/rl-note-16</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-16</guid><description>没有奖励函数怎么办？详解模仿学习的三大流派：行为克隆 (BC) 的简单粗暴与局限、逆强化学习 (IRL) 的理论推导，以及生成式对抗模仿学习 (GAIL) 的对抗博弈思想。</description><pubDate>Thu, 25 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在传统的强化学习中，智能体通过最大化累积奖励来学习。然而，在很多复杂任务中，“定义奖励”比“解决任务”本身还要难。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;例子&lt;/strong&gt;：自动驾驶。你很难用数学公式精确定义什么叫“开得稳且安全”。但是，我们可以很容易地收集到人类老司机的驾驶数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;模仿学习 (Imitation Learning)&lt;/strong&gt; 的目标是：给定一组专家演示数据 $\tau_E = {(s_1, a_1), (s_2, a_2), \dots }$，训练一个策略 $\pi_\theta$，使其行为尽可能接近专家策略 $\pi_E$。&lt;/p&gt;
&lt;p&gt;我们不需要知道背后的奖励函数是什么，只需要“照猫画虎”。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;行为克隆 (Behavioral Cloning, BC)&lt;/h2&gt;
&lt;p&gt;这是最简单、最直观的模仿学习方法。&lt;/p&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;把模仿学习看作一个&lt;strong&gt;监督学习 (Supervised Learning)&lt;/strong&gt; 问题。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入&lt;/strong&gt;：状态 $s$（特征）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标签&lt;/strong&gt;：动作 $a$（专家在 $s$ 下做的动作）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们的目标是训练一个分类器（离散动作）或回归器（连续动作）$\pi_\theta(s)$，使其输出拟合专家的动作。&lt;/p&gt;
&lt;h3&gt;目标函数&lt;/h3&gt;
&lt;p&gt;使用最大似然估计 (MLE)：&lt;/p&gt;
&lt;p&gt;$$
\max_\theta \mathbb{E}&lt;em&gt;{(s,a) \sim \pi_E} [\log \pi&lt;/em&gt;\theta(a|s)]
$$&lt;/p&gt;
&lt;p&gt;即最小化负对数似然损失：&lt;/p&gt;
&lt;p&gt;$$
L(\theta) = - \sum_{i=1}^N \log \pi_\theta(a_i|s_i)
$$&lt;/p&gt;
&lt;h3&gt;局限性：协变量偏移 (Covariate Shift)&lt;/h3&gt;
&lt;p&gt;虽然 BC 简单有效，但它有一个致命弱点：&lt;strong&gt;误差累积&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;训练分布&lt;/strong&gt;：策略是在专家访问过的状态 $s \sim P(\pi_E)$ 上训练的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试分布&lt;/strong&gt;：在实际运行时，策略 $\pi_\theta$ 可能会犯一点小错。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;恶性循环&lt;/strong&gt;：这一点小错会导致智能体进入一个&lt;strong&gt;专家从未去过&lt;/strong&gt;的状态（Out-of-Distribution）。在这种陌生状态下，BC 策略可能会胡乱行动，导致更大的错误，最终迅速偏离轨道（比如自动驾驶车偏离一点点后，不知道怎么修回去，直接冲出跑道）。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 总结&lt;/strong&gt;：BC 只是单纯地拟合动作，而不理解“为什么要这么做”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;逆强化学习 (Inverse RL, IRL)&lt;/h2&gt;
&lt;p&gt;为了解决 BC 的问题，IRL 提出了一个更深层的思路：&lt;strong&gt;专家之所以这么做，是因为他在最大化某个未知的奖励函数 $R$。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果我们能把这个 $R$ 反推（Inverse）出来，再用标准的 RL 算法（如 PPO/SAC）去最大化这个 $R$，不就能学会专家的策略了吗？&lt;/p&gt;
&lt;h3&gt;核心流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;假设&lt;/strong&gt;：存在一个参数化的奖励函数 $R_\phi(s,a)$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：找到参数 $\phi$，使得在该奖励函数下，专家的轨迹获得的累积回报高于其他任何策略。
$$ \mathbb{E}&lt;em&gt;{\pi_E} [R&lt;/em&gt;\phi] \ge \mathbb{E}&lt;em&gt;{\pi} [R&lt;/em&gt;\phi] $$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内层循环&lt;/strong&gt;：给定当前的 $R_\phi$，通过 RL 训练一个最优策略 $\pi^*$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外层循环&lt;/strong&gt;：更新 $R_\phi$，使得专家轨迹和 $\pi^*$ 轨迹的差距变大（让专家得分更高）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;难点&lt;/h3&gt;
&lt;p&gt;IRL 是一个&lt;strong&gt;不适定 (Ill-posed)&lt;/strong&gt; 问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;很多奖励函数都能解释同一种行为（例如：如果 $R=0$，任何策略都是最优的）。&lt;/li&gt;
&lt;li&gt;因此，IRL 通常需要引入&lt;strong&gt;最大熵 (Maximum Entropy)&lt;/strong&gt; 假设：在所有能解释专家行为的奖励函数中，选择那个对策略分布约束最少（熵最大）的。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 总结&lt;/strong&gt;：IRL 试图理解专家的“动机”。但它计算极其昂贵，因为每更新一次奖励函数，就要重新跑一遍完整的 RL 训练。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;生成式对抗模仿学习 (GAIL)&lt;/h2&gt;
&lt;p&gt;Ho &amp;#x26; Ermon (2016) 证明了：&lt;strong&gt;我们其实不需要显式地把奖励函数 $R$ 恢复出来。&lt;/strong&gt;
如果我们直接训练一个策略，让它的状态-动作分布 $\rho_\pi(s,a)$ 和专家的分布 $\rho_E(s,a)$ 难以区分，不就达到目的了吗？&lt;/p&gt;
&lt;p&gt;这正是 &lt;strong&gt;GAN (生成对抗网络)&lt;/strong&gt; 的思想。&lt;/p&gt;
&lt;h3&gt;核心架构&lt;/h3&gt;
&lt;p&gt;GAIL 包含两个网络进行博弈：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;判别器 (Discriminator) $D_w(s,a)$&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;任务是&lt;strong&gt;找茬&lt;/strong&gt;。它接收一个 $(s,a)$ 对，判断这是&lt;strong&gt;专家&lt;/strong&gt;产生的（输出 1），还是&lt;strong&gt;智能体&lt;/strong&gt;产生的（输出 0）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生成器 (Generator) $\pi_\theta(a|s)$&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;即我们的策略网络。任务是&lt;strong&gt;欺骗&lt;/strong&gt;判别器，让自己的行为看起来像专家。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;目标函数&lt;/h3&gt;
&lt;p&gt;GAIL 的优化目标与 GAN 几乎一致：&lt;/p&gt;
&lt;p&gt;$$
\min_\pi \max_D V(\pi, D) = \mathbb{E}&lt;em&gt;{\pi_E}[\log D(s,a)] + \mathbb{E}&lt;/em&gt;{\pi}[\log(1 - D(s,a))]
$$&lt;/p&gt;
&lt;h3&gt;训练流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;训练判别器 $D$&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;采样专家数据 $(s_E, a_E)$ 和 智能体数据 $(s_\pi, a_\pi)$。&lt;/li&gt;
&lt;li&gt;让 $D(s_E, a_E) \to 1$，让 $D(s_\pi, a_\pi) \to 0$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;训练策略 $\pi$&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;我们使用 $D$ 的输出作为一种&lt;strong&gt;替代奖励 (Surrogate Reward)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;定义奖励函数：$r(s,a) = - \log(1 - D(s,a))$ （或者其他变体，如 $\log D(s,a)$）。&lt;/li&gt;
&lt;li&gt;如果 $D$ 认为当前动作像专家（$D \approx 1$），奖励就高；不像专家，奖励就低。&lt;/li&gt;
&lt;li&gt;使用 PPO 或 TRPO 来最大化这个奖励。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解决了 Covariate Shift&lt;/strong&gt;：因为 $\pi$ 是在环境中交互训练的（RL），遇到偏离的状态时，判别器会给低分，RL 算法会学会如何修正回到正轨。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;效率高&lt;/strong&gt;：不需要像 IRL 那样反复求解最优策略，策略和判别器是同步更新的。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;总结与对比&lt;/h2&gt;
&lt;p&gt;| 维度         | 行为克隆 (BC)                     | 逆强化学习 (IRL)            | 生成对抗模仿 (GAIL)                            |
| :----------- | :-------------------------------- | :-------------------------- | :--------------------------------------------- |
| &lt;strong&gt;核心思想&lt;/strong&gt; | &lt;strong&gt;监督学习&lt;/strong&gt;：拟合 $s \to a$ 映射 | &lt;strong&gt;逆向推理&lt;/strong&gt;：反推 $R(s,a)$ | &lt;strong&gt;对抗博弈&lt;/strong&gt;：让分布 $\rho_\pi \approx \rho_E$ |
| &lt;strong&gt;交互需求&lt;/strong&gt; | 不需要与环境交互 (Offline)        | 需要 (Online)               | 需要 (Online)                                  |
| &lt;strong&gt;奖励函数&lt;/strong&gt; | 无                                | 显式学出 $R$                | 隐式奖励 ($D$ 的输出)                          |
| &lt;strong&gt;优点&lt;/strong&gt;     | 简单、训练快                      | 可解释性强、鲁棒            | 效果好、无需解内层 RL 循环                     |
| &lt;strong&gt;缺点&lt;/strong&gt;     | 误差累积 (Covariate Shift)        | 计算极其昂贵                | 训练不稳定 (GAN 通病)                          |&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 思考&lt;/strong&gt;：
如果专家的数据也不是完美的（比如偶尔手滑），BC 会傻傻地把错误也学进去，而 IRL/GAIL 由于有奖励函数的平滑作用，通常能学出比专家更好的策略。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="https://pic.hana0721.top/94819769_p0_master1200.4xv4u7folx.webp"/><enclosure url="https://pic.hana0721.top/94819769_p0_master1200.4xv4u7folx.webp"/></item><item><title>RL笔记（15）：SAC</title><link>https://hana-blog.top/blog/rl-note-15</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-15</guid><description>深入解析 Soft Actor-Critic (SAC)：从最大熵原理出发，详细推导 Soft 策略迭代与收敛性证明，并解析重参数化技巧与自动熵调节等关键实现细节。</description><pubDate>Wed, 24 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Soft Actor-Critic (SAC)&lt;/strong&gt; 是一种基于最大熵强化学习的 &lt;strong&gt;Off-Policy（离线策略）&lt;/strong&gt; 算法，于 2018 年提出。
SAC 的前身是 Soft Q-learning (SQL)。相比于 SQL 需要复杂的采样过程，SAC 引入了 Actor-Critic 架构，使其训练更加稳定且高效。SAC 在各类 Benchmark 及真实机器人任务中表现出色，以其&lt;strong&gt;极强的抗干扰能力&lt;/strong&gt;和&lt;strong&gt;对超参数的鲁棒性&lt;/strong&gt;著称，是现代深度强化学习的基石算法之一。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;最大熵强化学习 (Maximum Entropy RL)&lt;/h2&gt;
&lt;h3&gt;熵的定义&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;熵 (Entropy)&lt;/strong&gt; 表示对一个随机变量的随机程度的度量。如果 $X$ 是一个随机变量，它的概率密度记为 $p$，那么它的熵 $H$ 定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
H(X)&amp;#x26;=\mathbb{E}&lt;em&gt;{x\sim p}[-\log p(x)]\notag \
&amp;#x26;=-\int&lt;/em&gt;{x}p(x)\log p(x) \mathrm{d}x \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;在强化学习中，可以使用 $H(\pi(\cdot|s))$ 来表示策略 $\pi$ 在状态 $s$ 下的随机程度。&lt;/p&gt;
&lt;h3&gt;目标函数&lt;/h3&gt;
&lt;p&gt;最大熵强化学习（maximum entropy RL）的思想是除了最大化累计奖励，还要使得策略更加随机。如此，强化学习的目标中加入了一项熵的正则项，定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\pi_{\textbf{MaxEnt}}^*=\arg\max_{\pi}\mathbb{E}&lt;em&gt;{\pi}\left[\sum&lt;/em&gt;{t=0}^\infty r(s_t,a_t)+\alpha H(\pi(\cdot|s_t))\right]\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中，$\alpha$ 是一项正则项系数，用来权衡熵的重要程度。
SAC 算法加入熵正则项，有利于增加强化学习算法的探索，$\alpha$ 越大，探索性越强，有助于加速策略的学习，降低策略陷入局部最优的可能性。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;基于能量的模型 (Energy-Based Model, EBM)&lt;/h2&gt;
&lt;p&gt;基于能量的模型（Energy-Based Model，EBM）是一类基于统计物理学原理的概率模型，通过能量函数为每个可能的状态分配一个标量能量值，低能量区域对应高概率区域。在强化学习中，EBM 将状态-动作对 $(s,a)$ 映射到能量值 $\mathcal{E}$，从而表示策略分布，定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\pi(a|s)=\frac{\exp(-\mathcal{E}(s,a))}{Z(s)}\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中，$Z(s)=\int_{A}\exp(-\mathcal{E}(s,a))\mathrm{d}a$ 是配分函数，负责归一化能量值以形成有效的概率分布。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Soft 策略迭代 (Soft Policy Iteration)&lt;/h2&gt;
&lt;h3&gt;Soft 贝尔曼方程&lt;/h3&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^\pi(s)&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}[Q^{\pi}&lt;/em&gt;{\textbf{soft}}(s,a)-\alpha\log\pi(a|s)]\notag \
&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim \pi(a|s)}[Q^{\pi}&lt;/em&gt;{\textbf{soft}}(s,a)]+\alpha H(\pi(\cdot|s)) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q^{\pi}&lt;em&gt;{\textbf{soft}}(s,a)=r(s,a)+\gamma \mathbb{E}&lt;/em&gt;{s^\prime\sim p(\cdot|s,a)}[V_{\textbf{soft}}^\pi(s^\prime)]\notag \
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;Soft 策略评估 (Soft Policy Evaluation)&lt;/h3&gt;
&lt;p&gt;在 SAC 算法中，Soft 价值函数定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^\pi(s)\triangleq\mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}[Q^{\pi}&lt;/em&gt;{\textbf{soft}}(s,a)-\alpha\log\pi(a|s)]\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;Soft 动作价值函数定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q^{\pi}&lt;em&gt;{\textbf{soft}}(s,a)\triangleq r(s,a)+\gamma \mathbb{E}&lt;/em&gt;{s^\prime\sim p(\cdot|s,a)}[V_{\textbf{soft}}^\pi(s^\prime)]\notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;动作价值函数的 Soft 贝尔曼迭代算子定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathcal{T}^\pi Q^{k}&lt;em&gt;{\textbf{soft}}(s,a)&amp;#x26;\triangleq r(s,a)+\gamma \mathbb{E}&lt;/em&gt;{s^\prime\sim p(\cdot|s,a)}[V_{\textbf{soft}}^{k}(s^\prime)]\notag \
&amp;#x26;\triangleq  r(s,a)+\gamma \mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a)}[\mathbb{E}&lt;/em&gt;{a^\prime\sim\pi(\cdot|s^\prime)}[Q^{k}_{\textbf{soft}}(s^\prime,a^\prime)-\alpha\log\pi(a^\prime|s^\prime)]]\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Soft 策略评估定理&lt;/strong&gt;：基于上述的算子 $\mathcal{T}^\pi$ 以及初始化的映射 $Q^0_{\textbf{soft}}:S\times A\rightarrow \mathbb{R}$，且 $|A|&amp;#x3C;\infty$，$Q^{k+1}&lt;em&gt;{\textbf{soft}}=\mathcal{T}^\pi Q^k&lt;/em&gt;{\textbf{soft}}$。那么当 $k\rightarrow\infty$ 时，序列 ${Q^k_{\textbf{soft}}}$ 会收敛至 $Q_{\textbf{soft}}^\pi$。&lt;/p&gt;
&lt;p&gt;接下来证明这是一个压缩映射。文章重新定义了奖励 $r_\pi(s,a)$：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
r_{\pi}(s,a)\triangleq r(s,a)+\mathbb{E}_{s^\prime\sim p(\cdot|s,a)}[H(\pi(\cdot|s^\prime))]\notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;迭代式可以重写为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathcal{T}^\pi Q^{k}&lt;em&gt;{\textbf{soft}}(s,a)\triangleq r&lt;/em&gt;\pi(s,a)+\gamma \mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a), a^\prime \sim \pi(\cdot|s^\prime)}[Q^{k}&lt;/em&gt;{\textbf{soft}}(s^\prime,a^\prime)]\notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;在算法中，我们假设 $|A|&amp;#x3C;\infty$，那么 $r_\pi(s,a)$ 就是有界的，那么只需要证明此不等式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\big|\big|\mathcal{T}^\pi Q^N_{\textbf{soft}}(s,a)-\mathcal{T}^\pi Q^M_{\textbf{soft}}(s,a)\big|\big|&lt;em&gt;\infty \le k\big|\big| Q^N&lt;/em&gt;{\textbf{soft}}(s,a)-Q^M_{\textbf{soft}}(s,a)\big|\big|_\infty ,\quad\exists k\in(0,1)\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;左式化简为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\big|\mathcal{T}^\pi Q^N_{\textbf{soft}}(s,a)-\mathcal{T}^\pi Q^M_{\textbf{soft}}(s,a)\big|
&amp;#x26;=\gamma\big|\mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a), a^\prime\sim\pi(\cdot|s^\prime)}\left[Q^N&lt;/em&gt;{\textbf{soft}}(s^\prime,a^\prime)-Q^M_{\textbf{soft}}(s^\prime,a^\prime)\right]\big|\notag \
&amp;#x26;\le \gamma\mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a), a^\prime\sim\pi(\cdot|s^\prime)}\left[\big|Q^N&lt;/em&gt;{\textbf{soft}}(s^\prime,a^\prime)-Q^M_{\textbf{soft}}(s^\prime,a^\prime)\big|\right] \notag \
&amp;#x26;\le \gamma\mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a)}\left[\max&lt;/em&gt;{a^\prime\in A}\big|Q^N_{\textbf{soft}}(s^\prime,a^\prime)-Q^M_{\textbf{soft}}(s^\prime,a^\prime)\big|\right] \notag \
&amp;#x26;\le \gamma \max_{s^\prime\in S,a^\prime\in A} \big|Q^N_{\textbf{soft}}(s^\prime,a^\prime)-Q^M_{\textbf{soft}}(s^\prime,a^\prime)\big| \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;最终可以得出：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\max_{s\in S,a\in A}\big|\mathcal{T}^\pi Q^N_{\textbf{soft}}(s,a)-\mathcal{T}^\pi Q^M_{\textbf{soft}}(s,a)\big| \le \gamma \max_{s^\prime\in S,a^\prime\in A} \big|Q^N_{\textbf{soft}}(s^\prime,a^\prime)-Q^M_{\textbf{soft}}(s^\prime,a^\prime)\big| \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中，折扣因子 $\gamma\in(0,1)$，可以证明 $\mathcal{T}^\pi$ 是压缩映射。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Soft 策略提升 (Soft Policy Improvement)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Soft 策略提升定理&lt;/strong&gt;：对于任意 $\pi_{\text{old}} \in \Pi$，并以下式优化 $\pi_{\text{new}}$。假设 $|A|&amp;#x3C;\infty$，那么对于 $\forall (s,a) \in S \times A$，有 $Q_{\textbf{soft}}^{\pi_{\text{new}}}(s,a)\ge Q_{\textbf{soft}}^{\pi_{\text{old}}}(s,a)$。&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\pi_{\text{new}}(\cdot|s)=\arg \min_{\pi^\prime\in\Pi}D_{\textbf{KL}}\left(\pi^\prime(\cdot|s)\Big|\Big|\frac{\exp(\frac{1}{\alpha}Q^{\pi_{\text{old}}}&lt;em&gt;{\textbf{soft}}(s,\cdot))}{Z^{\pi&lt;/em&gt;{\text{old}}}(s)}\right)\notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中 $Z^{\pi_{\text{old}}}(s)$ 是配分函数，负责归一化分布。&lt;/p&gt;
&lt;p&gt;定义 $J(\pi(\cdot|s);\pi_{\text{old}})$ 为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
J(\pi(\cdot|s);\pi_{\text{old}})&amp;#x26;= D_{\textbf{KL}}\left(\pi(\cdot|s)\Big|\Big|\frac{\exp(\frac{1}{\alpha}Q_{\textbf{soft}}^{\pi_\text{old}}(s,\cdot))}{Z^{\pi_\text{old}}(s)}\right)\notag \
&amp;#x26;=\int_a \pi(a|s)\log\frac{\pi(a|s)}{\exp(\frac{1}{\alpha}Q_{\textbf{soft}}^{\pi_\text{old}}(s,a)-\log Z^{\pi_{\text{old}}}(s))}\text{d}a \notag \
&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}\left[\log\pi(a|s)-\frac{1}{\alpha}Q&lt;/em&gt;{\textbf{soft}}^{\pi_\text{old}}(s,a)+\log Z^{\pi_\text{old}}(s)\right] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;那么对于 $\pi_\text{old}$ 和 $\pi_\text{new}$，满足 $J(\pi_\text{new}(\cdot|s);\pi_\text{old})\le J(\pi_\text{old}(\cdot|s);\pi_\text{old})$，即：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathbb{E}&lt;em&gt;{a\sim\pi&lt;/em&gt;\text{new}(\cdot|s)}\left[\log\pi_\text{new}(a|s)-\frac{1}{\alpha}Q_{\textbf{soft}}^{\pi_\text{old}}(s,a)+\log Z^{\pi_\text{old}}(s)\right] \le \mathbb{E}&lt;em&gt;{a\sim\pi&lt;/em&gt;\text{old}(\cdot|s)}\left[\log\pi_\text{old}(a|s)-\frac{1}{\alpha}Q_{\textbf{soft}}^{\pi_\text{old}}(s,a)+\log Z^{\pi_\text{old}}(s)\right]  \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;$Z^{\pi_\text{old}}(s)$ 与 $a$ 无关，相约后得：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathbb{E}&lt;em&gt;{a\sim\pi&lt;/em&gt;\text{new}(\cdot|s)}\left[\log\pi_\text{new}(a|s)-\frac{1}{\alpha}Q_{\textbf{soft}}^{\pi_\text{old}}(s,a)\right] \le \mathbb{E}&lt;em&gt;{a\sim\pi&lt;/em&gt;\text{old}(\cdot|s)}\left[\log\pi_\text{old}(a|s)-\frac{1}{\alpha}Q_{\textbf{soft}}^{\pi_\text{old}}(s,a)\right]  \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;化简为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathbb{E}&lt;em&gt;{a\sim\pi&lt;/em&gt;\text{new}(\cdot|s)}\left[Q_{\textbf{soft}}^{\pi_\text{old}}(s,a)-\alpha\log\pi_\text{new}(a|s)\right] \ge \mathbb{E}&lt;em&gt;{a\sim\pi&lt;/em&gt;\text{old}(\cdot|s)}\left[Q_{\textbf{soft}}^{\pi_\text{old}}(s,a)-\alpha\log\pi_\text{old}(a|s)\right] = V_{\textbf{soft}}^{\pi_\text{old}}(s) \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;接下来证明 $Q_{\textbf{soft}}^{\pi_{\text{new}}}(s,a)\ge Q_{\textbf{soft}}^{\pi_{\text{old}}}(s,a)$，我们利用上式进行迭代展开：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q_{\textbf{soft}}^{\pi_{\text{old}}}(s,a) &amp;#x26;= r_0 + \gamma \mathbb{E}&lt;em&gt;{s_1\sim p(\cdot|s,a)}\left[ V&lt;/em&gt;{\textbf{soft}}^{\pi_\text{old}}(s_1)\right]\notag \
&amp;#x26;\le r_0 + \gamma \mathbb{E}&lt;em&gt;{s_1\sim p(\cdot|s,a)}\left[\mathbb{E}&lt;/em&gt;{a_1\sim \pi_\text{new}(\cdot|s_1)}\left[Q_{\textbf{soft}}^{\pi_\text{old}}(s_1,a_1) - \alpha \log \pi_\text{new}(a_1|s_1)\right]\right] \notag \
&amp;#x26;= r_0 + \gamma \mathbb{E}&lt;em&gt;{s_1\sim p(\cdot|s,a)}\left[\mathbb{E}&lt;/em&gt;{a_1\sim \pi_\text{new}(\cdot|s_1)}\left[r(s_1, a_1) + \gamma \mathbb{E}&lt;em&gt;{s_2}[V&lt;/em&gt;{\textbf{soft}}^{\pi_\text{old}}(s_2)] + \alpha H(\pi_\text{new}(\cdot|s_1))\right]\right] \notag \
&amp;#x26;\cdots \notag \
&amp;#x26;\le \sum_{t=0}^\infty \gamma^t \mathbb{E}&lt;em&gt;{(s_t,a_t)\sim \rho^{\pi&lt;/em&gt;\text{new}}}\left[r(s_t,a_t)+\alpha H(\pi_\text{new}(\cdot|s_t))\right]\notag \
&amp;#x26;=Q_{\textbf{soft}}^{\pi_\text{new}}(s,a) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;证明完毕。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Soft 策略迭代定理&lt;/h3&gt;
&lt;p&gt;假设 $|A|&amp;#x3C;\infty$，对于任意 $\pi \in \Pi$，重复应用 Soft 策略评估和 Soft 策略提升，会收敛到 $\pi^&lt;em&gt;$，使得对于任意 $\pi \in \Pi$ 以及 $(s,a)\in S\times A$，都有 $Q^{\pi^&lt;/em&gt;}&lt;em&gt;\textbf{soft}(s,a)\ge Q^{\pi}&lt;/em&gt;\textbf{soft}(s,a)$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;证明过程&lt;/strong&gt;：
令 $\pi_i$ 为第 $i$ 轮迭代的策略，根据 Soft 策略迭代定理，序列 ${Q^{\pi_i}&lt;em&gt;\textbf{soft}}$ 是单调递增的。对于任意 $\pi \in \Pi$， $Q^\pi&lt;/em&gt;\textbf{soft}$ 是有上界的（奖励与熵均是有界的），因此序列会收敛到某个 $\pi^&lt;em&gt;$，接下来需要证明 $\pi^&lt;/em&gt;$ 是最优的。
在收敛时，必然满足：对于任意 $\pi\in\Pi$ 且 $\pi\neq \pi^&lt;em&gt;$，都有 $J(\pi^&lt;/em&gt;(\cdot|s);\pi^&lt;em&gt;) &amp;#x3C; J(\pi(\cdot|s);\pi^&lt;/em&gt;)$，即：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathbb{E}&lt;em&gt;{a\sim\pi^&lt;em&gt;(\cdot|s)}\left[\log\pi^&lt;/em&gt;(a|s)-\frac{1}{\alpha}Q&lt;/em&gt;{\textbf{soft}}^{\pi^&lt;em&gt;}(s,a)\right] &amp;#x3C; \mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}\left[\log\pi(a|s)-\frac{1}{\alpha}Q&lt;/em&gt;{\textbf{soft}}^{\pi^&lt;/em&gt;}(s,a)\right]  \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;化简为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_\textbf{soft}^{\pi^&lt;em&gt;}(s) &gt; \mathbb{E}&lt;em&gt;{a\sim \pi(\cdot|s)}\left[Q&lt;/em&gt;\textbf{soft}^{\pi^&lt;/em&gt;}(s,a)-\alpha \log \pi(a|s)\right]\notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;接下来证明 $\pi^&lt;em&gt;$ 是最优的，即证明 $Q_\textbf{soft}^{\pi^&lt;/em&gt; }(s,a) &gt; Q^\pi_\textbf{soft}(s,a)$：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q_\textbf{soft}^{\pi^* }(s,a)&amp;#x26;=r_0+\gamma\mathbb{E}&lt;em&gt;{s_1\sim p(\cdot|s,a)}\left[V&lt;/em&gt;\textbf{soft}^{\pi^&lt;em&gt;}(s_1)\right]\notag \
&amp;#x26;&gt;r_0+\gamma\mathbb{E}&lt;em&gt;{s_1\sim p(\cdot|s,a)}\left[\mathbb{E}&lt;/em&gt;{a_1\sim \pi(\cdot|s_1)}\left[Q_\textbf{soft}^{\pi^&lt;/em&gt;}(s_1,a_1)-\alpha \log \pi(a_1|s_1)\right]\right] \notag \
&amp;#x26;= r_0+\gamma\mathbb{E}&lt;em&gt;{s_1\sim p(\cdot|s,a)}\left[\mathbb{E}&lt;/em&gt;{a_1\sim \pi(\cdot|s_1)}\left[r(s_1, a_1) + \gamma \mathbb{E}&lt;em&gt;{s_2}[V&lt;/em&gt;\textbf{soft}^{\pi^*}(s_2)] + \alpha H(\pi(\cdot|s_1))\right]\right] \notag \
&amp;#x26;&gt; \sum_{t=0}^\infty \gamma^t \mathbb{E}&lt;em&gt;{(s_t,a_t)\sim\rho^\pi}\left[r(s_t,a_t)+\alpha H(\pi(\cdot|s_t))\right] \notag \
&amp;#x26;=Q^\pi&lt;/em&gt;\textbf{soft}(s,a)\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;证明完毕。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;算法实现&lt;/h2&gt;
&lt;p&gt;在 SAC 算法中，我们为两个动作价值函数 $Q_\textbf{soft}$（参数分别 $\omega_1$ 为和 $\omega_2$）和一个策略函数 $\pi$（参数为 $\theta$）建模。基于 Double DQN 的思想，SAC 使用 $Q_\textbf{soft}$ 两个网络，但每次用 $Q_\textbf{soft}$ 网络时会挑选一个 $Q_\textbf{soft}$ 值小的网络，从而缓解 $Q_\textbf{soft}$ 值过高估计的问题。&lt;/p&gt;
&lt;h3&gt;动作价值函数 (Critic)&lt;/h3&gt;
&lt;p&gt;任意一个函数 $Q_\textbf{soft}$ 的损失函数为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
L_{Q_\textbf{soft}}(\omega)&amp;#x26;=\mathbb{E}&lt;em&gt;{(s_t,a_t,r_t,s&lt;/em&gt;{t+1})\sim R}\left[\frac{1}{2}\left(Q_\textbf{soft}^\omega(s_t,a_t)-\left(r_t+\gamma V_{\textbf{soft}}^{\omega^-}(s_{t+1})\right)\right)^2\right]\notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;{(s_t,a_t,r_t,s&lt;/em&gt;{t+1})\sim R}\left[\frac{1}{2}\left(Q_\textbf{soft}^\omega(s_t,a_t)-\left(r_t+\gamma\mathbb{E}&lt;em&gt;{a&lt;/em&gt;{t+1}\sim \pi_\theta(\cdot|s_{t+1})}\left[\min_{j=1,2} Q_\textbf{soft}^{\omega_j^-}(s_{t+1},a_{t+1})-\alpha \log \pi_{\theta}(a_{t+1}|s_{t+1})\right]\right)\right)^2\right]\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中，$R$ 是策略过去收集的数据，因为 SAC 是一种离线策略算法。为了让训练更加稳定，这里使用了两个目标网络 $Q_\textbf{soft}^{\omega_j^-}$。目标网络的更新方式是 Soft 更新：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\omega_j^-\leftarrow \tau \omega_j + (1-\tau) \omega_j^-, \quad j=1,2\notag
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;策略函数 (Actor)&lt;/h3&gt;
&lt;p&gt;接下来根据 Soft 策略提升定理中的 KL 散度来推导策略 $\pi_\theta$ 的损失函数 $L_{\pi}(\theta)$：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\theta&amp;#x26;=\arg \min_{\theta} L_\pi(\theta) \notag \
&amp;#x26;=\arg \min_{\theta} \mathbb{E}&lt;em&gt;{s_t\sim R}\left[D&lt;/em&gt;\textbf{KL}\left(\pi_\theta(\cdot|s_t)\Big|\Big|\frac{\exp(\frac{1}{\alpha }Q_\textbf{soft}^\omega(s_t,\cdot))}{Z(s_t)}\right)\right]\notag \
&amp;#x26;= \arg\min_\theta \mathbb{E}&lt;em&gt;{s_t\sim R}\left[\mathbb{E}&lt;/em&gt;{a_t\sim \pi_\theta(\cdot|s_t)}\left[\log\left(\frac{\pi_\theta(a_t|s_t)Z(s_t)}{\exp(\frac{1}{\alpha}Q_\textbf{soft}^\omega(s_t,a_t))}\right)\right]\right] \notag \
&amp;#x26;=\arg\min_\theta \mathbb{E}&lt;em&gt;{s_t\sim R, a_t\sim \pi&lt;/em&gt;\theta(\cdot|s_t)}\left[\log \pi_\theta(a_t|s_t)-\frac{1}{\alpha}Q_\textbf{soft}^\omega(s_t,a_t)+\log Z(s_t)\right] \notag \
&amp;#x26;=\arg\min_\theta \mathbb{E}&lt;em&gt;{s_t\sim R, a_t\sim \pi&lt;/em&gt;\theta(\cdot|s_t)}\left[\log \pi_\theta(a_t|s_t)-\frac{1}{\alpha}Q_\textbf{soft}^\omega(s_t,a_t)\right] \notag \
&amp;#x26;=\arg\min_\theta \mathbb{E}&lt;em&gt;{s_t\sim R, a_t\sim \pi&lt;/em&gt;\theta(\cdot|s_t)}\left[\alpha\log \pi_\theta(a_t|s_t)-Q_\textbf{soft}^\omega(s_t,a_t)\right] \notag \
&amp;#x26;=\arg\max_\theta \mathbb{E}&lt;em&gt;{s_t\sim R}\left[V&lt;/em&gt;\textbf{soft}^\omega (s_t)\right]\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;考虑到两个 $Q_\textbf{soft}^\omega$ 网络，损失函数写为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
L_\pi(\theta)=\arg\min_\theta \mathbb{E}&lt;em&gt;{s_t\sim R, a_t\sim \pi&lt;/em&gt;\theta(\cdot|s_t)}\left[\alpha\log \pi_\theta(a_t|s_t)-\min_{j=1,2}Q_\textbf{soft}^{\omega_j}(s_t,a_t)\right] \notag \
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;重参数化技巧 (Reparameterization Trick)&lt;/h3&gt;
&lt;p&gt;对于连续动作空间的环境，策略输出 Gauss 分布的均值和标准差，但是 Gauss 分布采样动作的过程是不可导的。因此，我们使用了重参数化技巧。
重参数化的做法是先从标准的 Gauss 分布 $\mathcal{N}$ 进行采样，然后再将采样值乘以标准差后加上均值，如下：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
a_t&amp;#x26;=f_\theta(\epsilon_t;s_t) \notag \
&amp;#x26;= \mu_\theta(s_t)+\epsilon_t\sigma_\theta(s_t), \quad \epsilon\sim\mathcal{N}(0,I)\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;这样就可以认为是从策略高斯分布采样，并且这样对于策略函数是可导的，损失函数写为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
L_\pi(\theta)=\arg\min_\theta \mathbb{E}&lt;em&gt;{s_t\sim R, \epsilon_t \sim \mathcal{N}}\left[\alpha\log \pi&lt;/em&gt;\theta(f_\theta(\epsilon_t;s_t)|s_t)-\min_{j=1,2}Q_\textbf{soft}^{\omega_j}(s_t,f_\theta(\epsilon_t;s_t))\right] \notag \
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;自动调整熵系数 (Automating Entropy Adjustment)&lt;/h3&gt;
&lt;p&gt;在 SAC 算法中，如何选择熵正则项的系数非常重要。在不同的状态下需要不同大小的熵：在最优动作不确定的某个状态下，熵的取值应该大一点；而在某个最优动作比较确定的状态下，熵的取值可以小一点。
为了自动调整熵正则项，SAC 将强化学习的目标改写为一个带约束的优化问题：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\arg\max_\theta\mathbb{E}&lt;em&gt;{(s_t,a_t)\sim \rho^{\pi&lt;/em&gt;\theta}}\left[\sum_{t=0}^\infty r(s_t,a_t)\right] \quad \text{s.t.} \quad \mathbb{E}&lt;em&gt;{(s_t,a_t)\sim\rho^{\pi&lt;/em&gt;\theta}}\left[-\log\pi_\theta(a_t|s_t)\right]\ge H_0\notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;将上述问题转化为 KKT（Karush-Kuhn-Tucker）条件下的约束问题：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\arg\min_\theta\mathbb{E}&lt;em&gt;{(s_t,a_t)\sim \rho^{\pi&lt;/em&gt;\theta}}\left[-\sum_{t=0}^\infty r(s_t,a_t)\right] \quad \text{s.t.} \quad H_0-\mathbb{E}&lt;em&gt;{(s_t,a_t)\sim\rho^{\pi&lt;/em&gt;\theta}}\left[-\log\pi_\theta(a_t|s_t)\right]\le 0\notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;通过拉格朗日乘数法，转化为无约束问题，拉格朗日函数为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
L(\theta,\alpha)=\mathbb{E}&lt;em&gt;{(s_t,a_t)\sim \rho^{\pi&lt;/em&gt;\theta}}\left[-\sum_{t=0}^\infty r(s_t,a_t)\right] + \alpha\left[H_0-\mathbb{E}&lt;em&gt;{(s_t,a_t)\sim\rho^{\pi&lt;/em&gt;\theta}}\left[-\log\pi_\theta(a_t|s_t)\right]\right] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中 $\alpha\ge0$ 是拉格朗日乘数，将拉格朗日函数中与 $\alpha$ 相关的提取出来：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
L(\alpha)=\mathbb{E}&lt;em&gt;{s_t\sim R,a_t\sim\pi&lt;/em&gt;\theta(\cdot|s_t)}\left[-\alpha\log\pi_\theta(a_t|s_t)-\alpha H_0\right] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;即当策略的熵低于目标值 $H_0$ 时，训练目标 $L(\alpha)$ 会使 $\alpha$ 的值增大，进而在上述最小化损失函数 $L_\pi(\theta)$ 的过程中增加了策略熵对应项的重要性；而当策略的熵高于目标值 $H_0$ 时，训练目标 $L(\alpha)$ 会使 $\alpha$ 的值减小，进而使得策略训练时更专注于价值提升。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/92079701_p0_master1200.64eg2t4l77.webp"/><enclosure url="https://pic.hana0721.top/92079701_p0_master1200.64eg2t4l77.webp"/></item><item><title>RL笔记（14）：SQL</title><link>https://hana-blog.top/blog/rl-note-14</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-14</guid><description>深入解析 Soft Q-Learning (SQL)：从最大熵强化学习原理出发，详细推导 Soft Bellman 方程、策略提升定理及收敛性证明，并探讨基于能量模型的策略采样与实现细节。</description><pubDate>Tue, 23 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;Soft Q-Learning (SQL) 是强化学习中结合 &lt;strong&gt;最大熵强化学习（Maximum Entropy Reinforcement Learning）&lt;/strong&gt; 思想的 Q-Learning 扩展算法。
它的核心目标不仅仅是找到&lt;strong&gt;那一条&lt;/strong&gt;获得最高分的路，而是要找到&lt;strong&gt;所有&lt;/strong&gt;能获得高分的路。它在最大化累积奖励的同时，强制策略保持随机性（即最大化策略的熵），从而提升策略的探索能力、鲁棒性和泛化性。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;最大熵强化学习（Maximum Entropy RL）&lt;/h2&gt;
&lt;h3&gt;基本定义&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;熵（Entropy）&lt;/strong&gt; 是表示随机变量不确定性的度量。对于随机变量 $X$，如果其概率密度为 $p$，熵 $H$ 定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
H(X)&amp;#x26;=\mathbb{E}&lt;em&gt;{x\sim p}[-\log p(x)]\notag \
&amp;#x26;=-\int&lt;/em&gt;{X}p(x)\log p(x) \mathrm{d}x \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;在强化学习中，我们关注的是策略 $\pi$ 在状态 $s$ 下的随机程度，记为 $H(\pi(\cdot|s))$。&lt;/p&gt;
&lt;h3&gt;目标函数&lt;/h3&gt;
&lt;p&gt;最大熵强化学习的思想是：除了最大化累计奖励，还要使得策略更加随机。因此，目标函数中加入了一项&lt;strong&gt;熵正则项&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\pi_{\textbf{MaxEnt}}^*=\arg\max_{\pi}\sum_{t=0}^\infty \mathbb{E}_{\pi}[ r(s_t,a_t)+\alpha H(\pi(\cdot|s_t))]\notag
\end{align}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\alpha$ (Temperature)：正则项系数，用来权衡熵的重要程度。
&lt;ul&gt;
&lt;li&gt;$\alpha \to 0$：退化为标准 RL（贪婪）。&lt;/li&gt;
&lt;li&gt;$\alpha$ 越大：越鼓励探索，策略分布越平坦。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉理解&lt;/strong&gt;：
这一项 $\alpha H$ 的加入，意味着智能体如果不去探索新的动作，或者太早确定一个动作（熵变低），就会受到“惩罚”。这能有效防止智能体陷入局部最优。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;基于能量的模型 (Energy-Based Model, EBM)&lt;/h2&gt;
&lt;p&gt;SQL 使用基于能量的模型来建模策略。这源于统计物理学：能量越低的状态，出现的概率越高。&lt;/p&gt;
&lt;p&gt;在强化学习中，我们将状态-动作对 $(s,a)$ 映射到能量值 $\mathcal{E}$。策略分布定义为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\pi(a|s)=\frac{\exp(-\mathcal{E}(s,a))}{Z(s)}\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中，$Z(s)$ 是&lt;strong&gt;配分函数（Partition Function）&lt;/strong&gt;，用于归一化，确保概率之和为 1：&lt;/p&gt;
&lt;p&gt;$$
Z(s)=\int_{\mathcal{A}}\exp(-\mathcal{E}(s,a))\mathrm{d}a
$$&lt;/p&gt;
&lt;p&gt;在 Soft Q-Learning 中，我们通常用 Soft Q 值来代替负能量 $-\mathcal{E}(s,a)$，即 $Q_{soft}(s,a)$ 越大，选择该动作的概率越大。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Soft 贝尔曼方程 (Soft Bellman Equation)&lt;/h2&gt;
&lt;p&gt;为了推导 SQL，我们需要重新定义价值函数 $V$ 和 $Q$，将熵包含进去。&lt;/p&gt;
&lt;h3&gt;回顾：普通贝尔曼方程&lt;/h3&gt;
&lt;p&gt;普通 $Q$ 值：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q^\pi(s,a) &amp;#x26;= r(s,a)+\gamma\mathbb{E}_{s^\prime \sim p(\cdot|s,a)}[V^\pi(s^\prime)] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;普通 $V$ 值：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V^\pi(s) &amp;#x26;=\mathbb{E}_{a\sim \pi(\cdot|s)}[Q^\pi(s,a)] \notag
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;定义：Soft 贝尔曼方程&lt;/h3&gt;
&lt;p&gt;在最大熵框架下，价值函数的定义发生了变化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Soft 状态价值函数 $V_\text{soft}$ 推导：&lt;/strong&gt;
$V_\text{soft}$ 不仅包含未来的奖励，还包含未来的熵。&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^\pi(s)
&amp;#x26;=\mathbb{E}&lt;em&gt;{\pi}[\sum&lt;/em&gt;{l=0}^\infty \gamma^l(R_{t+l}+\alpha H(\pi(\cdot|S_{t+l})))|S_t=s] \notag \
&amp;#x26;=\mathbb{E}&lt;em&gt;{\pi}[R_t+\alpha H(\pi(\cdot|S_t))|S_t=s]+\gamma \mathbb{E}&lt;/em&gt;{\pi}[\sum_{l=0}^\infty \gamma^l(R_{t+1+l}+\alpha H(\pi(\cdot|S_{t+1+l})))] \notag \
&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}[r(s,a)]+\alpha H(\pi(\cdot|s))+\gamma \mathbb{E}&lt;/em&gt;{a\sim\pi(\cdot|s),s^\prime\sim p(\cdot|s,a)}[V^{\pi}_{\textbf{soft}}(s^\prime)] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;将 $H(\pi)$ 展开为期望形式 $\mathbb{E}[-\log \pi]$，可以合并项：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^\pi(s)
&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}[r(s,a)-\alpha\log\pi(a|s)+\gamma\mathbb{E}&lt;/em&gt;{s^\prime\sim p(\cdot|s,a)}[V^{\pi}&lt;em&gt;{\textbf{soft}}(s^\prime)]] \notag \
&amp;#x26;=\mathbb{E}&lt;/em&gt;{a\sim\pi(\cdot|s)}[r(s,a)+\gamma \mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a)}[V^{\pi}&lt;/em&gt;{\textbf{soft}}(s^\prime)]]+\alpha H(\pi(\cdot|s)) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Soft 动作价值函数 $Q_\text{soft}$ 推导：&lt;/strong&gt;
与 $V$ 类似，展开递归形式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q_{\textbf{soft}}^\pi(s,a)
&amp;#x26;=r(s,a)+\gamma \mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a),a^\prime\sim\pi(\cdot|s^\prime)}[Q^{\pi}&lt;/em&gt;{\textbf{soft}}(s^\prime,a^\prime)-\alpha\log\pi(a^\prime|s^\prime)] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两者关系总结：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;V 与 Q 的关系&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^\pi(s)&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}[Q^{\pi}&lt;/em&gt;{\textbf{soft}}(s,a)-\alpha\log\pi(a|s)]\notag \
&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim \pi(a|s)}[Q^{\pi}&lt;/em&gt;{\textbf{soft}}(s,a)]+\alpha H(\pi(\cdot|s)) \notag
\end{align}
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 笔记&lt;/strong&gt;：这说明 $V$ 值等于 $Q$ 值的期望加上当前的熵红利。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Q 与 V 的关系&lt;/strong&gt;（Soft Bellman）：
$$
\begin{align}
Q^{\pi}&lt;em&gt;{\textbf{soft}}(s,a)=r(s,a)+\gamma \mathbb{E}&lt;/em&gt;{s^\prime\sim p(\cdot|s,a)}[V_{\textbf{soft}}^\pi(s^\prime)]\notag \
\end{align}
$$&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Soft 策略提升定理 (Soft Policy Improvement)&lt;/h2&gt;
&lt;p&gt;我们如何保证更新策略后，效果一定会变好？这里给出了详细证明。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;定理&lt;/strong&gt;：对于任意状态 $s$，如果我们按照以下规则更新策略：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\tilde{\pi}(\cdot|s) \propto \exp(\frac{1}{\alpha}Q^{\pi}_{\textbf{soft}}(s,\cdot)), \quad \forall s \in \mathcal{S} \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;那么新策略 $\tilde{\pi}$ 的 Soft Q 值一定优于旧策略 $\pi$。&lt;/p&gt;
&lt;h3&gt;证明过程&lt;/h3&gt;
&lt;p&gt;首先，我们计算旧策略 $\pi$ 下的 $V$ 值，并尝试引入新策略 $\tilde{\pi}$ 的形式。&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^\pi(s)&amp;#x26;=\mathbb{E}&lt;em&gt;{a\sim \pi(\cdot|s)}[Q&lt;/em&gt;{\textbf{soft}}^\pi(s,a)]+\alpha H(\pi(\cdot|s)) \notag \
&amp;#x26;=\int_a\pi(a|s)Q_{\textbf{soft}}^\pi(s,a)\text{d}a-\alpha\int_a\pi(a|s)\log\pi(a|s)\text{d}a\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;这里利用一个恒等变换。因为更新规则 $\tilde{\pi}(a|s) = \frac{\exp(\frac{1}{\alpha}Q(s,a))}{Z(s)}$，所以 $Q(s,a) = \alpha \log \tilde{\pi}(a|s) + \alpha \log Z(s)$。
代入积分中：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^\pi(s) &amp;#x26;= \alpha\int_a \pi(a|s) \left[ \log \tilde{\pi}(a|s) + \log \int_{a^\prime}\exp(\frac{1}{\alpha}Q_{\textbf{soft}}^\pi(s,a^\prime))\text{d}a^\prime \right] \text{d}a - \alpha\int_a\pi(a|s)\log\pi(a|s)\text{d}a \notag \
&amp;#x26;= \alpha \log \left[ \int_{a^\prime}\exp(\frac{1}{\alpha}Q_{\textbf{soft}}^\pi(s,a^\prime))\text{d}a^\prime \right] + \alpha \int_a \pi(a|s) \log \frac{\tilde{\pi}(a|s)}{\pi(a|s)} \text{d}a \notag \
&amp;#x26;= \alpha \log \int_a\exp(\frac{1}{\alpha}Q_{\textbf{soft}}^\pi(s,a))\text{d}a - \alpha D_{\textbf{KL}}(\pi(\cdot|s)||\tilde{\pi}(\cdot|s)) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;当 $\pi = \tilde{\pi}$ 时，KL 散度为 0，我们得到&lt;strong&gt;Soft Value Function 的解析解（LogSumExp 形式）&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$
V_{\textbf{soft}}^{\tilde{\pi}}(s) = \alpha \log \int_a \exp(\frac{1}{\alpha}Q_{\textbf{soft}}^\pi(s,a))\text{d}a
$$&lt;/p&gt;
&lt;p&gt;因为 KL 散度非负（$D_{KL} \ge 0$），且 $\alpha &gt; 0$，所以去掉 KL 项后，不等式成立：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\alpha \log \int_a \exp(\frac{1}{\alpha}Q)\text{d}a \ge V_{\textbf{soft}}^\pi(s) + \alpha D_{\textbf{KL}} \ge V_{\textbf{soft}}^\pi(s) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;这导出了关键不等式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathbb{E}&lt;em&gt;{a\sim\tilde{\pi}(\cdot|s)}[Q&lt;/em&gt;{\textbf{soft}}^\pi(s,a)]+ \alpha H(\tilde{\pi}(\cdot|s))\ge
\mathbb{E}&lt;em&gt;{a\sim\pi(\cdot|s)}[Q&lt;/em&gt;{\textbf{soft}}^\pi(s,a)]+ \alpha H(\pi(\cdot|s)) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;接下来的迭代推导证明了 $Q$ 值的单调递增性（通过不断展开贝尔曼算子）：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q_{\textbf{soft}}^\pi(s,a) &amp;#x26;= r_0 + \gamma V_{soft}^\pi(s_1) \notag \
&amp;#x26;\le r_0 + \gamma (\mathbb{E}&lt;em&gt;{a_1 \sim \tilde{\pi}}[Q^\pi(s_1, a_1)] + \alpha H(\tilde{\pi})) \quad \text{(根据上述不等式)} \notag \
&amp;#x26;\le \dots \notag \
&amp;#x26;\le Q&lt;/em&gt;{\textbf{soft}}^{\tilde{\pi}}(s,a) \notag
\end{align}
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 结论&lt;/strong&gt;：
只要我们将策略更新为正比于 $\exp(Q/\alpha)$，新的策略在 Soft Q 值的评估下一定比旧策略更好（或持平）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;Soft 策略评估定理与收敛性证明&lt;/h2&gt;
&lt;p&gt;我们定义&lt;strong&gt;Soft 贝尔曼算子 $\mathcal{T}$&lt;/strong&gt;，并证明反复应用它会收敛到最优值。&lt;/p&gt;
&lt;p&gt;定义算子操作：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathcal{T} Q_{\textbf{soft}}^\pi(s,a) \triangleq r(s,a)+\gamma \mathbb{E}&lt;em&gt;{s^\prime\sim p(\cdot|s,a)}\left[\alpha\log\int&lt;/em&gt;{a^\prime}\exp (\frac{1}{\alpha}Q_{\textbf{soft}}^\pi(s^\prime,a^\prime))\text{d}a^\prime\right]\notag
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;压缩映射证明 (Contraction Mapping)&lt;/h3&gt;
&lt;p&gt;我们要证明 $||\mathcal{T}Q_1 - \mathcal{T}Q_2|| \le k ||Q_1 - Q_2||$，其中 $k &amp;#x3C; 1$。&lt;/p&gt;
&lt;p&gt;定义度量距离 $\epsilon = \max_{s,a}|Q_1(s,a) - Q_2(s,a)|$。
这意味对于任意 $(s,a)$：&lt;/p&gt;
&lt;p&gt;$$
Q_1(s,a) \le Q_2(s,a) + \epsilon
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键推导步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;利用指数单调性：&lt;/p&gt;
&lt;p&gt;$$
\exp(\frac{1}{\alpha}Q_1) \le \exp(\frac{1}{\alpha}(Q_2 + \epsilon)) = \exp(\frac{Q_2}{\alpha}) \cdot \exp(\frac{\epsilon}{\alpha})
$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;积分保序性：&lt;/p&gt;
&lt;p&gt;$$
\int \exp(\frac{Q_1}{\alpha}) \text{d}a&apos; \le \exp(\frac{\epsilon}{\alpha}) \int \exp(\frac{Q_2}{\alpha}) \text{d}a&apos;
$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;取 $\alpha \log$（&lt;strong&gt;注意这里的常数提取&lt;/strong&gt;）：
$$
\begin{align}
\alpha \log \int \exp(\frac{Q_1}{\alpha}) \text{d}a&apos;
&amp;#x26;\le \alpha \log \left[ \exp(\frac{\epsilon}{\alpha}) \int \exp(\frac{Q_2}{\alpha}) \text{d}a&apos; \right] \notag \
&amp;#x26;= \alpha \left[ \log(\exp(\frac{\epsilon}{\alpha})) + \log \int \exp(\frac{Q_2}{\alpha}) \text{d}a&apos; \right] \notag \
&amp;#x26;= \alpha \left[ \frac{\epsilon}{\alpha} + \log \int \exp(\frac{Q_2}{\alpha}) \text{d}a&apos; \right] \notag \
&amp;#x26;= \epsilon + \alpha \log \int \exp(\frac{Q_2}{\alpha}) \text{d}a&apos; \notag
\end{align}
$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;计算算子差值：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
|\mathcal{T}Q_1 - \mathcal{T}Q_2| &amp;#x26;= \gamma \left| \mathbb{E}&lt;em&gt;{s&apos;} \left[ \alpha \log \int&lt;/em&gt;{a^\prime} \exp(\frac{Q_1(s^\prime,a^\prime)}{\alpha}) \text{d}a^\prime - \alpha \log \int_{a^\prime} \exp(\frac{Q_2(s^\prime,a^\prime)}{\alpha}) \text{d}a^\prime \right] \right| \notag \
&amp;#x26;\le \gamma \mathbb{E}&lt;em&gt;{s&apos;} [\epsilon] \notag \
&amp;#x26;= \gamma \max&lt;/em&gt;{s,a} |Q_1(s,a)-Q_2(s,a)| \notag
\end{align}
$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;结论：
$$
||\mathcal{T}Q_1 - \mathcal{T}Q_2||&lt;em&gt;\infty \le \gamma ||Q_1 - Q_2||&lt;/em&gt;\infty
$$&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;由于折扣因子 $\gamma \in (0,1)$，算子 $\mathcal{T}$ 是一个&lt;strong&gt;压缩映射&lt;/strong&gt;，必然收敛到唯一的不动点 $Q^*_\text{soft}$。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;实现困难与解决方案&lt;/h2&gt;
&lt;p&gt;理论很完美，但实际用神经网络实现时（Deep RL），有两个主要困难。&lt;/p&gt;
&lt;h3&gt;困难 1：处理积分 (The Integral Problem)&lt;/h3&gt;
&lt;p&gt;在计算 $V_\text{soft}(s)$ 时，需要计算 $\log \int \exp(Q) \text{d}a$。
如果动作空间是连续的，这个积分通常无法解析计算。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：重要性采样 (Importance Sampling)&lt;/strong&gt;
我们把积分转化为期望，通过采样来近似。
引入一个采样分布 $q_a(a)$（通常可以是当前的策略网络）：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_{\textbf{soft}}^{\theta}(s)&amp;#x26;= \alpha \log \int_a \exp(\frac{1}{\alpha}Q_{\textbf{soft}}^{\theta}(s,a))\text{d}a\notag \
&amp;#x26;=\alpha\log\int_a q_a(a)\frac{\exp(\frac{1}{\alpha}Q_{\textbf{soft}}^{\theta}(s,a))}{q_a(a)} \text{d}a \notag \
&amp;#x26;\approx \alpha \log \mathbb{E}&lt;em&gt;{a\sim q_a(\cdot)}\left[\frac{\exp(\frac{1}{\alpha}Q&lt;/em&gt;{\textbf{soft}}^{\theta}(s,a))}{q_a(a)}\right] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;于是，Soft Q-Learning 的目标函数（Bellman Error）变为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
J_Q(\theta)&amp;#x26;=\mathbb{E}&lt;em&gt;{\mathcal{D}}\left[\frac{1}{2}\left(\hat{Q}&lt;/em&gt;{\textbf{soft}}(s_t,a_t)-Q_{\textbf{soft}}^\theta(s_t,a_t)\right)^2\right]\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;其中目标值 $\hat{Q}$ 使用上述的重要性采样估计来计算 $V$。&lt;/p&gt;
&lt;h3&gt;困难 2：策略采样 (Sampling from Energy-Based Policy)&lt;/h3&gt;
&lt;p&gt;理论上的最优策略是基于能量的：$\pi(a|s) \propto \exp(\frac{1}{\alpha}Q(s,a))$。
这是一种复杂的分布，我们无法直接从 Q 网络中高效采样动作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：Amortized Inference (近似推断)&lt;/strong&gt;
我们训练一个显式的策略网络（Actor）$\pi_\phi(a|s)$（通常是高斯分布或由神经网络生成的分布），让它去逼近那个能量分布。&lt;/p&gt;
&lt;p&gt;目标是最小化两者之间的 KL 散度：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
J_\pi(\phi;s_t)=D_{\textbf{KL}}\left(\pi_\phi(\cdot|s_t)\Big|\Big|\exp\left(\frac{1}{\alpha}(Q_{\textbf{soft}}^\theta(s_t,\cdot)-V_{\textbf{soft}}^\theta(s_t))\right)\right)\notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;将 KL 散度展开并忽略常数项，等价于最大化：&lt;/p&gt;
&lt;p&gt;$$
\mathbb{E}&lt;em&gt;{a \sim \pi&lt;/em&gt;\phi}[Q^\theta(s,a) - \alpha \log \pi_\phi(a|s)]
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 实现细节&lt;/strong&gt;：
在 SQL 原文中，作者使用了 &lt;strong&gt;SVGD (Stein Variational Gradient Descent)&lt;/strong&gt; 方法来更新这个策略网络，这允许策略生成非常复杂的多模态分布。后续的 SAC 算法则使用了重参数化技巧（Reparameterization Trick）来简化这一步。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="https://pic.hana0721.top/86800103_p0_master1200.4qrwyrtj6a.webp"/><enclosure url="https://pic.hana0721.top/86800103_p0_master1200.4qrwyrtj6a.webp"/></item><item><title>RL笔记（13）：DDPG</title><link>https://hana-blog.top/blog/rl-note-13</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-13</guid><description>深度确定性策略梯度：将 DQN 扩展到连续动作空间。详解 DDPG 的软更新与噪声探索，以及 TD3 如何通过双 Q 网络和延迟更新解决过估计问题。</description><pubDate>Mon, 22 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;我们之前学过 &lt;strong&gt;DQN&lt;/strong&gt;（处理离散动作的价值方法）和 &lt;strong&gt;REINFORCE/PPO&lt;/strong&gt;（处理连续/离散动作的随机策略方法）。
如果我们将 DQN 的思想（Off-policy, Replay Buffer）应用到连续控制任务中，会遇到什么问题？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DQN 需要对动作取最大值 $\max_a Q(s,a)$。在连续空间中，这个最大化操作本身就是一个复杂的优化问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;DDPG (Deep Deterministic Policy Gradient)&lt;/strong&gt; 的出现解决了这个问题。你可以简单地把它理解为 &lt;strong&gt;“DQN + Actor-Critic”&lt;/strong&gt;。它使用一个 Actor 网络来直接输出那个“让 Q 值最大的动作”，从而避免了复杂的积分或最大化计算。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;策略类型的本质区别&lt;/h2&gt;
&lt;p&gt;在深入 DDPG 之前，我们需要明确两种策略的数学形式差异。&lt;/p&gt;
&lt;h3&gt;随机策略 (Stochastic Policy)&lt;/h3&gt;
&lt;p&gt;输出动作的&lt;strong&gt;概率分布&lt;/strong&gt;（如高斯分布的均值和方差）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;离散动作&lt;/strong&gt;：$\pi(a|s;\theta)=\frac{\exp(Q_\theta(s,a))}{\sum_{a^\prime}\exp(Q_\theta(s,a^\prime))}$ (Softmax)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连续动作&lt;/strong&gt;：$\pi(a|s;\theta) \sim \mathcal{N}(\mu_\theta(s), \sigma_\theta(s))$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;确定性策略 (Deterministic Policy)&lt;/h3&gt;
&lt;p&gt;对于同一个状态，永远输出&lt;strong&gt;同一个动作&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;离散动作&lt;/strong&gt;：$\pi(s;\theta)=\operatorname{argmax}&lt;em&gt;{a} Q&lt;/em&gt;\theta(s,a)$ (不可微，无法用梯度下降直接优化)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连续动作&lt;/strong&gt;：$a=\mu_\theta(s)$ (可微，这正是 DDPG 的基础)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 核心区别&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;随机策略&lt;/strong&gt;通过&lt;strong&gt;对数似然 (Log-Likelihood)&lt;/strong&gt; 来更新参数：$\nabla \log \pi \cdot Q$。它试探性地增加高分动作的概率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;确定性策略&lt;/strong&gt;通过&lt;strong&gt;链式法则 (Chain Rule)&lt;/strong&gt; 来更新参数：$\nabla_a Q \cdot \nabla_\theta \mu$。它直接告诉动作“往哪个方向挪一点，Q 值会变大”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;确定性策略梯度定理 (DPG)&lt;/h2&gt;
&lt;p&gt;这是 DDPG 的理论基石。我们希望找到策略参数 $\theta$，最大化目标函数 $J(\theta) = \mathbb{E}[Q(s, \mu_\theta(s))]$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;定理推导&lt;/strong&gt;：
假设我们有一个 Critic 网络 $Q^w(s,a)$ 估得足够准。我们想调整 Actor $\mu_\theta(s)$，使得输出的动作 $a$ 能获得更大的 $Q$ 值。
根据链式法则：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta J(\mu_\theta) &amp;#x26;= \mathbb{E}&lt;em&gt;{s \sim \rho^\pi} [\nabla&lt;/em&gt;\theta Q^w(s, \mu_\theta(s))] \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;{s \sim \rho^\pi} [\underbrace{\nabla_a Q^w(s,a)|&lt;/em&gt;{a=\mu_\theta(s)}}&lt;em&gt;{\text{Critic 指出的方向}} \cdot \underbrace{\nabla&lt;/em&gt;\theta \mu_\theta(s)}_{\text{Actor 的梯度}}] \notag
\end{align}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直觉&lt;/strong&gt;：Critic 告诉 Actor：“动作 $a$ 往大变一点，Q 值能增加”。Actor 就计算如何调整 $\theta$ 才能让 $a$ 变大。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;深度确定性策略梯度 (DDPG)&lt;/h2&gt;
&lt;p&gt;DDPG 是 Deep Q-Network (DQN) 在连续动作空间的自然延伸。&lt;/p&gt;
&lt;h3&gt;核心组件&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Actor-Critic 架构&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Actor $\mu(s; \theta)$：输出确定性动作。&lt;/li&gt;
&lt;li&gt;Critic $Q(s, a; w)$：评估 (状态, 动作) 的价值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;经验回放 (Replay Buffer)&lt;/strong&gt;：与 DQN 一样，存储 $(s, a, r, s&apos;)$，打破数据相关性，实现 Off-Policy 训练。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;目标网络 (Target Networks)&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为了稳定训练，建立了 Target Actor $\mu&apos;$ 和 Target Critic $Q&apos;$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键改进：软更新 (Soft Update)&lt;/strong&gt;。
DQN 是每隔 $C$ 步硬拷贝参数。DDPG 使用滑动平均：
$$ \theta&apos; \leftarrow \tau \theta + (1-\tau)\theta&apos; $$
其中 $\tau \ll 1$ (如 0.001)。这使得目标值变化非常平滑。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;探索策略 (Exploration)&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确定性策略本身不会探索。DDPG 必须在动作上&lt;strong&gt;加噪声&lt;/strong&gt;来进行探索：
$$ a&lt;em&gt;t = \mu&lt;/em&gt;\theta(s_t) + \mathcal{N}_t $$&lt;/li&gt;
&lt;li&gt;原论文使用了 &lt;strong&gt;Ornstein-Uhlenbeck (OU)&lt;/strong&gt; 噪声（具有时间相关性，适合惯性系统）。现在的实践通常直接使用简单的&lt;strong&gt;高斯噪声&lt;/strong&gt; $\mathcal{N}(0, \sigma)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;DDPG 算法流程&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize Actor } \mu_\theta, \text{Critic } Q_w, \text{Target nets } \mu&apos;, Q&apos; \
&amp;#x26; \bullet ; \textbf{For } \text{episode } = 1 \to E \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Receive initial state } s \
&amp;#x26; \bullet \qquad \textbf{For } t = 1 \to T \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Select action } a = \mu_\theta(s) + \text{Noise} \
&amp;#x26; \bullet \qquad \qquad \text{Execute } a, \text{ observe } r, s&apos; \
&amp;#x26; \bullet \qquad \qquad \text{Store } (s, a, r, s&apos;) \text{ in Buffer } \mathcal{R} \
&amp;#x26; \bullet \qquad \qquad \text{Sample batch } N \text{ from } \mathcal{R} \
&amp;#x26; \bullet \qquad \qquad \textbf{// Update Critic (minimize MSBE)} \
&amp;#x26; \bullet \qquad \qquad y = r + \gamma Q&apos;(s&apos;, \mu&apos;(s&apos;)) \
&amp;#x26; \bullet \qquad \qquad L_w = \frac{1}{N}\sum (y - Q_w(s,a))^2 \
&amp;#x26; \bullet \qquad \qquad \textbf{// Update Actor (maximize Q)} \
&amp;#x26; \bullet \qquad \qquad \nabla_\theta J = \frac{1}{N} \sum \nabla_a Q_w(s,a) \nabla_\theta \mu_\theta(s) \
&amp;#x26; \bullet \qquad \qquad \textbf{// Soft Update Targets} \
&amp;#x26; \bullet \qquad \qquad \theta&apos; \leftarrow \tau \theta + (1-\tau)\theta&apos; \
&amp;#x26; \bullet \qquad \qquad w&apos; \leftarrow \tau w + (1-\tau)w&apos; \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;双价值函数策略延时更新 (TD3)&lt;/h2&gt;
&lt;p&gt;DDPG 很强，但非常&lt;strong&gt;脆弱&lt;/strong&gt;，对超参数敏感。它继承了 DQN 的&lt;strong&gt;过高估计 (Overestimation)&lt;/strong&gt; 问题，甚至更严重。TD3 提出了三个技巧来修正它。&lt;/p&gt;
&lt;h3&gt;技巧 1：截断双 Q 学习 (Clipped Double Q-Learning)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：DQN 中 $y = r + \gamma \max Q$ 会导致价值高估。DDPG 中虽然没有 max，但 Actor 也是朝着 Q 值最大的方向更新的，效果一样。
&lt;strong&gt;解决&lt;/strong&gt;：学习两个 Critic ($Q_1, Q_2$)，计算目标值时&lt;strong&gt;取最小值&lt;/strong&gt;。
$$ y = r + \gamma \min*{i=1,2} Q*{\phi_i&apos;}(s&apos;, \tilde{a}) $$
这体现了“悲观主义”原则，宁可低估也不要高估。&lt;/p&gt;
&lt;h3&gt;技巧 2：策略参数延迟更新 (Delayed Policy Updates)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：Critic 还在学习中，波动很大。如果 Actor 频繁根据错误的 Critic 调整方向，会导致策略震荡发散。
&lt;strong&gt;解决&lt;/strong&gt;：让 Critic 多练几次，Actor 再动。
通常 Critic 更新 2 次（或更多），Actor 和 目标网络才更新 1 次。&lt;/p&gt;
&lt;h3&gt;目标策略平滑 (Target Policy Smoothing)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：Critic 可能会对某些尖峰值（错误的 Q 值峰值）过拟合。
&lt;strong&gt;解决&lt;/strong&gt;：在计算目标值时，给动作加一点&lt;strong&gt;被截断的噪声&lt;/strong&gt;，让 Q 函数在动作周围更平滑。&lt;/p&gt;
&lt;p&gt;$$
\tilde{a} = \mu_{\theta&apos;}(s&apos;) + \epsilon, \quad \epsilon \sim \text{clip}(\mathcal{N}(0, \sigma), -c, c)
$$&lt;/p&gt;
&lt;p&gt;$$ y = r + \gamma \min Q&apos;(s&apos;, \tilde{a}) $$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉&lt;/strong&gt;：相似的动作应该有相似的价值。如果动作稍微变一点点，价值就剧烈变化，说明这个 Critic 是有问题的（过拟合）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;深度解析：DDPG/TD3 的定位&lt;/h2&gt;
&lt;p&gt;我们可以将 DDPG/TD3 放在整个 RL 家族谱系中进行对比：&lt;/p&gt;
&lt;p&gt;| 维度         | DDPG / TD3                                    | PPO / A2C                            | DQN              |
| :----------- | :-------------------------------------------- | :----------------------------------- | :--------------- | ----------------- |
| &lt;strong&gt;动作空间&lt;/strong&gt; | &lt;strong&gt;连续&lt;/strong&gt; (Continuous)                         | 离散 或 连续                         | 离散 (Discrete)  |
| &lt;strong&gt;策略类型&lt;/strong&gt; | &lt;strong&gt;确定性策略&lt;/strong&gt; ($\mu(s)$)                     | 随机策略 ($\pi(a                     | s)$)             | 确定性 (argmax Q) |
| &lt;strong&gt;样本效率&lt;/strong&gt; | &lt;strong&gt;高 (Off-Policy)&lt;/strong&gt;                           | 低 (On-Policy)                       | 高 (Off-Policy)  |
| &lt;strong&gt;更新方式&lt;/strong&gt; | &lt;strong&gt;链式法则&lt;/strong&gt; ($\nabla_a Q \nabla_\theta \mu$) | 对数似然 ($\nabla \log \pi \cdot A$) | 最小化 TD Error  |
| &lt;strong&gt;稳定性&lt;/strong&gt;   | 较低 (需精细调参)                             | &lt;strong&gt;高&lt;/strong&gt; (鲁棒性强)                    | 中               |
| &lt;strong&gt;核心痛点&lt;/strong&gt; | Q 值高估、超参数敏感                          | 采样慢、无法利用旧数据               | 无法处理连续动作 |&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DDPG&lt;/strong&gt; 是 DQN 在连续控制领域的继承者，引入了 Actor-Critic 架构。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TD3&lt;/strong&gt; 是 DDPG 的“补丁版”，通过双 Q 网络和延迟更新，大大提升了算法的稳定性。&lt;/li&gt;
&lt;li&gt;它们都是 &lt;strong&gt;Off-Policy&lt;/strong&gt; 算法，适合需要极高样本效率的场景（如机器人控制）。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://pic.hana0721.top/78391247_p0_master1200.5j4sgia4wg.webp"/><enclosure url="https://pic.hana0721.top/78391247_p0_master1200.5j4sgia4wg.webp"/></item><item><title>RL笔记（12）：PPO</title><link>https://hana-blog.top/blog/rl-note-12</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-12</guid><description>OpenAI 的默认算法：详解 PPO 如何通过 Clip 技巧简化 TRPO。涵盖 PPO-Clip 与 PPO-Penalty 两种变体、GAE 优势估计及完整的损失函数设计。圣PPO伟大无需多言！</description><pubDate>Sun, 21 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;上一章提到的 &lt;strong&gt;TRPO&lt;/strong&gt; 虽然理论优美（保证单调不减），但计算太复杂了（需要共轭梯度法求解 Hessian-Vector Product）。
OpenAI 提出的 &lt;strong&gt;PPO (Proximal Policy Optimization)&lt;/strong&gt; 是 TRPO 的一阶近似版本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：TRPO 使用 KL 散度作为&lt;strong&gt;硬约束 (Constraint)&lt;/strong&gt;，而 PPO 将约束转化为&lt;strong&gt;惩罚项 (Penalty)&lt;/strong&gt; 或者直接通过&lt;strong&gt;截断 (Clipping)&lt;/strong&gt; 来限制策略更新幅度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;地位&lt;/strong&gt;：PPO 是目前 Deep RL 的基准算法，平衡了实现复杂度、样本效率和性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;变体 1：PPO-Clip (主流)&lt;/h2&gt;
&lt;p&gt;这是目前最常用的 PPO 版本，它不需要计算 KL 散度，直接在目标函数里动手脚。&lt;/p&gt;
&lt;h3&gt;重要性采样比率&lt;/h3&gt;
&lt;p&gt;定义新旧策略的比率为 $r_t(\theta)$：&lt;/p&gt;
&lt;p&gt;$$
r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当 $\theta = \theta_{\text{old}}$ 时，$r_t = 1$。&lt;/li&gt;
&lt;li&gt;我们希望 $r_t$ 不要偏离 1 太多，这意味着新策略没有发生剧烈变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;截断目标函数&lt;/h3&gt;
&lt;p&gt;PPO-Clip 的核心目标函数如下：&lt;/p&gt;
&lt;p&gt;$$
L^{CLIP}(\theta)=\hat{\mathbb{E}}_t \left[ \min \left( r_t(\theta)\hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_t \right) \right]
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\epsilon$ 是一个超参数（通常为 0.2），表示允许策略变化的幅度（Trust Region）。&lt;/li&gt;
&lt;li&gt;$\text{clip}(r, 1-\epsilon, 1+\epsilon)$：把比率 $r$ 强制限制在 $[0.8, 1.2]$ 之间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;直观理解：为什么要 Min？&lt;/h3&gt;
&lt;p&gt;我们需要分两种情况来看优势函数 $\hat{A}_t$ 的正负：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;情况 A：动作是好的 ($\hat{A}_t &gt; 0$)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;我们希望增加这个动作的概率，即 $r_t(\theta)$ 增大。&lt;/li&gt;
&lt;li&gt;但不能无限增大。如果 $r_t &gt; 1+\epsilon$，&lt;code&gt;clip&lt;/code&gt; 函数会将其锁死在 $1+\epsilon$。&lt;/li&gt;
&lt;li&gt;此时 $\min$ 操作生效，目标函数不再随 $r_t$ 增加而增加。这防止了策略更新步子太大。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;情况 B：动作是坏的 ($\hat{A}_t &amp;#x3C; 0$)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;我们希望减小这个动作的概率，即 $r_t(\theta)$ 减小。&lt;/li&gt;
&lt;li&gt;但不能无限减小。如果 $r_t &amp;#x3C; 1-\epsilon$，&lt;code&gt;clip&lt;/code&gt; 函数会将其锁死在 $1-\epsilon$。&lt;/li&gt;
&lt;li&gt;此时 $\min$ 操作生效，防止策略过度修正（以此避免策略坍塌）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 总结&lt;/strong&gt;：
只有当策略变化在“安全区域”内时，我们才进行奖励优化；一旦超出安全区域，就不再给予额外的梯度奖励。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;变体 2：PPO-Penalty (自适应 KL)&lt;/h2&gt;
&lt;p&gt;这是另一种接近 TRPO 原义的方法，将 KL 散度作为正则项加入 Loss，并动态调整系数 $\beta$。&lt;/p&gt;
&lt;h3&gt;目标函数&lt;/h3&gt;
&lt;p&gt;$$
L^{KLPEN}(\theta)=\hat{\mathbb{E}}&lt;em&gt;t \left[ r_t(\theta)\hat{A}&lt;em&gt;t - \beta D&lt;/em&gt;{KL}(\pi&lt;/em&gt;{\theta_{\text{old}}}(\cdot|s_t) || \pi_\theta(\cdot|s_t)) \right]
$$&lt;/p&gt;
&lt;h3&gt;自适应 $\beta$ 更新规则&lt;/h3&gt;
&lt;p&gt;我们在每次更新后计算平均 KL 散度 $d = \hat{\mathbb{E}}&lt;em&gt;t [D&lt;/em&gt;{KL}]$，并与目标值 $d_{\text{targ}}$ 比较：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;如果 $d &amp;#x3C; d_{\text{targ}} / 1.5$&lt;/strong&gt;：说明策略变动太小，步子太保守。&lt;strong&gt;减小惩罚&lt;/strong&gt; $\beta \leftarrow \beta / 2$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如果 $d &gt; d_{\text{targ}} \times 1.5$&lt;/strong&gt;：说明策略变动太大，步子太危险。&lt;strong&gt;增大惩罚&lt;/strong&gt; $\beta \leftarrow \beta \times 2$。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;广义优势估计 (GAE)&lt;/h2&gt;
&lt;p&gt;在计算优势函数 $\hat{A}_t$ 时，简单的多步 TD 或蒙特卡洛都有局限。PPO 通常使用 &lt;strong&gt;GAE (Generalized Advantage Estimation)&lt;/strong&gt; 来平衡偏差和方差。&lt;/p&gt;
&lt;p&gt;我们定义 TD Error $\delta_t = r_t + \gamma V(s_{t+1}) - V(s_t)$。
GAE 是 $\delta$ 的加权几何平均：&lt;/p&gt;
&lt;p&gt;$$
\hat{A}&lt;em&gt;t^{GAE} = \sum&lt;/em&gt;{k=0}^{\infty} (\gamma \lambda)^k \delta_{t+k}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\lambda = 0$：即单步 TD（偏差大，方差小）。&lt;/li&gt;
&lt;li&gt;$\lambda = 1$：即蒙特卡洛（无偏差，方差大）。&lt;/li&gt;
&lt;li&gt;$\lambda \in (0, 1)$：通常取 0.95，在两者之间取得最佳平衡。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;完整的 PPO 损失函数&lt;/h2&gt;
&lt;p&gt;在实际代码实现（如 Actor-Critic 架构）中，PPO 的总 Loss 包含三部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;策略损失 (Policy Loss)&lt;/strong&gt;：即 $L^{CLIP}$，让策略变好。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;价值损失 (Value Loss)&lt;/strong&gt;：$L^{VF} = (V_\theta(s_t) - V_{target})^2$，让 Critic 估值更准。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;熵奖励 (Entropy Bonus)&lt;/strong&gt;：$S[\pi_\theta]$，鼓励策略保持随机性，防止过早收敛到局部最优。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;$$
L^{Total}_t(\theta) = - L^{CLIP}_t(\theta) + c_1 L^{VF}_t(\theta) - c_2 S&lt;a href=&quot;s_t&quot;&gt;\pi_\theta&lt;/a&gt;
$$&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(注：通常深度学习框架是最小化 Loss，所以最大化目标前加负号)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;PPO 算法流程&lt;/h2&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize policy parameters } \theta \text{ and value parameters } \phi \
&amp;#x26; \bullet ; \textbf{For } \text{iteration } k = 1, 2, \dots \textbf{ do}: \
&amp;#x26; \bullet \qquad \textbf{1. Data Collection:} \
&amp;#x26; \bullet \qquad \text{Run policy } \pi_{\theta_{old}} \text{ in environment for } T \text{ steps} \
&amp;#x26; \bullet \qquad \text{Compute advantage estimates } \hat{A}&lt;em&gt;1, \dots, \hat{A}&lt;em&gt;T \text{ using GAE} \
&amp;#x26; \bullet \qquad \textbf{2. Optimization:} \
&amp;#x26; \bullet \qquad \textbf{For } \text{epoch } = 1 \to K \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Shuffle data and divide into mini-batches} \
&amp;#x26; \bullet \qquad \qquad \textbf{For } \text{each mini-batch } B \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \qquad L = L^{CLIP}(\theta) - c_1 L^{VF}(\phi) + c_2 S[\pi&lt;/em&gt;\theta] \
&amp;#x26; \bullet \qquad \qquad \qquad \text{Update } \theta, \phi \text{ using Adam optimizer} \
&amp;#x26; \bullet \qquad \qquad \textbf{End For} \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet \qquad \theta&lt;/em&gt;{old} \leftarrow \theta \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;PPO 之所以能成为 OpenAI 的默认算法（Default Algorithm），是因为它：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;简单&lt;/strong&gt;：只需要对梯度进行简单的截断（Clip），不需要复杂的二阶优化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;稳定&lt;/strong&gt;：截断机制保证了策略不会因为一次糟糕的更新而崩溃。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通用&lt;/strong&gt;：既适用于离散动作（Atari），也适用于连续动作（机器人控制）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;至此，经典的 Policy Gradient 家族（REINFORCE $\to$ Actor-Critic $\to$ TRPO $\to$ PPO）已经梳理完毕。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/78075718_p0_master1200.51eqrx8rbi.webp"/><enclosure url="https://pic.hana0721.top/78075718_p0_master1200.51eqrx8rbi.webp"/></item><item><title>RL笔记（11）：TRPO</title><link>https://hana-blog.top/blog/rl-note-11</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-11</guid><description>深度强化学习的里程碑：详解 TRPO 如何通过信任区域约束保证策略更新的单调性。涵盖目标函数推导、二阶泰勒近似、共轭梯度法及 HVP 技巧。</description><pubDate>Sat, 20 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在 REINFORCE 和 A2C 等策略梯度算法中，我们通常使用固定的学习率 $\alpha$ 来更新参数：$\theta \leftarrow \theta + \alpha \nabla J$。
但这有一个严重问题：&lt;strong&gt;策略参数的变化 $\Delta \theta$ 并不等同于策略行为的变化&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有时候参数改一点点，策略分布变动巨大（导致性能崩塌）。&lt;/li&gt;
&lt;li&gt;有时候参数改很多，策略分布几乎没变（训练效率低）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;TRPO (Trust Region Policy Optimization)&lt;/strong&gt; 的核心思想是：我们不直接控制参数的变化量，而是控制&lt;strong&gt;策略分布的变化量&lt;/strong&gt;（用 KL 散度衡量）。我们在一个“信任区域”内进行优化，确保每一步更新后，策略的表现都是&lt;strong&gt;单调不减&lt;/strong&gt;的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;策略优化的单调性证明&lt;/h2&gt;
&lt;p&gt;我们的目标是找到一个新策略 $\pi_{\theta&apos;}$，使得它的期望回报 $J(\theta&apos;)$ 比旧策略 $J(\theta)$ 高。&lt;/p&gt;
&lt;h3&gt;性能差异引理 (Performance Difference Lemma)&lt;/h3&gt;
&lt;p&gt;首先推导新旧策略的目标函数之差：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
J(\theta^\prime)-J(\theta)
&amp;#x26;= \mathbb{E}&lt;em&gt;{s_0}[V^{\pi&lt;/em&gt;{\theta^\prime}}(s_0)]-\mathbb{E}&lt;em&gt;{s_0}[V^{\pi&lt;/em&gt;{\theta}}(s_0)]\notag\
&amp;#x26;=\mathbb{E}&lt;em&gt;{\pi&lt;/em&gt;{\theta^\prime}}[\sum_{t=0}^\infty \gamma^t r(s_t,a_t)] - \mathbb{E}&lt;em&gt;{s_0}[\sum&lt;/em&gt;{t=0}^\infty \gamma^t V^{\pi_\theta}(s_t)-\sum_{t=1}^\infty \gamma^t V^{\pi_\theta}(s_t)] \notag \
&amp;#x26;=\mathbb{E}&lt;em&gt;{\pi&lt;/em&gt;{\theta^\prime}}[\sum_{t=0}^\infty \gamma^t r(s_t,a_t)] - \mathbb{E}&lt;em&gt;{\pi&lt;/em&gt;{\theta^\prime}}[\sum_{t=0}^\infty \gamma^t (V^{\pi_\theta}(s_t)-\gamma V^{\pi_\theta}(s_{t+1}))] \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;{\pi&lt;/em&gt;{\theta^\prime}}[\sum_{t=0}^\infty \gamma^t (r(s_t,a_t)+\gamma V^{\pi_\theta}(s_{t+1})-V^{\pi_\theta}(s_t))] \notag \
&amp;#x26;=\mathbb{E}&lt;em&gt;{\pi&lt;/em&gt;{\theta^\prime}}[\sum_{t=0}^\infty \gamma^t A^{\pi_\theta}(s_t,a_t )] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;将期望展开为状态访问分布 $\nu^{\pi_{\theta&apos;}}$ 的形式：&lt;/p&gt;
&lt;p&gt;$$
J(\theta^\prime)-J(\theta) = \sum_{t=0}^\infty \gamma^t \mathbb{E}&lt;em&gt;{s_t\sim p^{\pi&lt;/em&gt;{\theta^\prime}} \, ; a_t\sim\pi_{\theta^\prime}(\cdot|s_t)}[ A^{\pi_\theta}(s_t,a_t)]
= \frac{1}{1-\gamma} \mathbb{E}&lt;em&gt;{s\sim\nu^{\pi&lt;/em&gt;{\theta^\prime}}\, ;a\sim\pi_{\theta^\prime}(\cdot|s)}[A^{\pi_\theta}(s,a)]
$$&lt;/p&gt;
&lt;h3&gt;状态分布近似&lt;/h3&gt;
&lt;p&gt;上式很难计算，因为 $\nu^{\pi_{\theta&apos;}}$ 依赖于新策略（而新策略还没求出来）。
TRPO 做了一个关键假设：&lt;strong&gt;当新旧策略差距很小时，状态访问分布变化不大&lt;/strong&gt;，即 $\nu^{\pi_{\theta&apos;}} \approx \nu^{\pi_\theta}$。&lt;/p&gt;
&lt;p&gt;目标函数近似为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
J(\theta^\prime)-J(\theta) &amp;#x26;\approx \frac{1}{1-\gamma}\mathbb{E}&lt;em&gt;{s\sim\nu^{\pi&lt;/em&gt;\theta}\,; a\sim\pi_{\theta^\prime}(\cdot|s)}[A^{\pi_\theta}(s,a)] \notag \
&amp;#x26;= \frac{1}{1-\gamma}\mathbb{E}&lt;em&gt;{s\sim\nu^{\pi&lt;/em&gt;\theta}\,; a\sim\pi_{\theta}(\cdot|s)}\left[\frac{\pi_{\theta^\prime}(a|s)}{\pi_\theta(a|s)}A^{\pi_\theta}(s,a)\right] \quad \text{(重要性采样)} \notag
\end{align}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;信任区域约束&lt;/h2&gt;
&lt;p&gt;为了让上述近似成立，必须限制 $\pi_{\theta&apos;}$ 和 $\pi_\theta$ 不能差太多。我们使用 &lt;strong&gt;KL 散度&lt;/strong&gt; 作为约束条件。&lt;/p&gt;
&lt;p&gt;优化目标：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\max_{\theta^\prime} \quad &amp;#x26; \mathbb{E}&lt;em&gt;{s\sim\nu^{\pi&lt;/em&gt;\theta}\,; a\sim\pi_{\theta}(\cdot|s)}\left[\frac{\pi_{\theta^\prime}(a|s)}{\pi_\theta(a|s)}A^{\pi_\theta}(s,a)\right] \notag \
\text{s.t.} \quad &amp;#x26; \bar{D}&lt;em&gt;{KL}(\theta || \theta&apos;) = \mathbb{E}&lt;/em&gt;{s\sim\nu^{\pi_\theta}}[D_{KL}(\pi_{\theta}(\cdot|s) || \pi_{\theta&apos;}(\cdot|s))] \leqslant \delta \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;(注：忽略了常数系数 $\frac{1}{1-\gamma}$，因为它不影响梯度方向)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;数学近似求解&lt;/h2&gt;
&lt;p&gt;为了求解这个带约束的优化问题，我们对其进行泰勒展开。&lt;/p&gt;
&lt;h3&gt;目标函数的一阶近似&lt;/h3&gt;
&lt;p&gt;令 $L(\theta&apos;) = \mathbb{E}[\frac{\pi_{\theta&apos;}}{\pi_\theta} A^{\pi_\theta}]$。在 $\theta&apos; = \theta$ 处展开：&lt;/p&gt;
&lt;p&gt;$$
L(\theta&apos;) \approx L(\theta) + \nabla_{\theta&apos;} L(\theta&apos;)|_{\theta&apos;=\theta}^T (\theta&apos; - \theta)
$$&lt;/p&gt;
&lt;p&gt;由于 $L(\theta) = \mathbb{E}[1 \cdot A^{\pi_\theta}] = 0$ (优势函数的期望为0)，且 $\nabla_{\theta&apos;} L(\theta&apos;)|&lt;em&gt;{\theta} = \nabla&lt;/em&gt;\theta J(\theta)$ (策略梯度)。
记梯度为 $g$：
$$ L(\theta&apos;) \approx g^T (\theta&apos; - \theta) $$&lt;/p&gt;
&lt;h3&gt;约束条件的二阶近似&lt;/h3&gt;
&lt;p&gt;KL 散度在 $\theta&apos;=\theta$ 处的一阶导数为 0（因为是极小值点），所以必须展开到二阶。&lt;/p&gt;
&lt;p&gt;$$
\bar{D}_{KL}(\theta || \theta&apos;) \approx \frac{1}{2} (\theta&apos; - \theta)^T \mathbf{H} (\theta&apos; - \theta)
$$&lt;/p&gt;
&lt;p&gt;其中 $\mathbf{H}$ 是 KL 散度的 &lt;strong&gt;黑塞矩阵 (Hessian Matrix)&lt;/strong&gt;（也是 Fisher Information Matrix）。&lt;/p&gt;
&lt;h3&gt;近似后的优化问题&lt;/h3&gt;
&lt;p&gt;令更新步长 $\Delta \theta = \theta&apos; - \theta$，问题转化为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\max_{\Delta \theta} \quad &amp;#x26; g^T \Delta \theta \notag \
\text{s.t.} \quad &amp;#x26; \frac{1}{2} \Delta \theta^T \mathbf{H} \Delta \theta \leqslant \delta \notag
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;解析解&lt;/h3&gt;
&lt;p&gt;利用拉格朗日乘数法（推导见你原笔记），解得：&lt;/p&gt;
&lt;p&gt;$$
\Delta \theta = \sqrt{\frac{2\delta}{g^T \mathbf{H}^{-1} g}} \mathbf{H}^{-1} g
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;共轭梯度法 (Conjugate Gradient)&lt;/h2&gt;
&lt;p&gt;公式里需要计算 $\mathbf{H}^{-1} g$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：$\mathbf{H}$ 是神经网络参数的 Hessian 矩阵。如果参数有 100万个，$\mathbf{H}$ 就是 $100w \times 100w$ 的矩阵，显式计算它的逆是不可能的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;思路&lt;/strong&gt;：我们不需要算 $\mathbf{H}^{-1}$，我们只需要算向量 $x = \mathbf{H}^{-1} g$。这等价于求解线性方程组 $\mathbf{H}x = g$。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;共轭梯度法 (CG)&lt;/strong&gt; 是一种迭代算法，它可以只通过计算矩阵-向量乘积 ($\mathbf{H}v$)，在 $k$ 步内（$k \ll \text{参数维度}$）找到 $x$ 的近似解。&lt;/p&gt;
&lt;h3&gt;Hessian-Vector Product (HVP)&lt;/h3&gt;
&lt;p&gt;CG 算法需要计算 $\mathbf{H}v$。虽然我们算不起 $\mathbf{H}$，但可以利用反向传播算 $\mathbf{H}v$。&lt;/p&gt;
&lt;p&gt;$$
\mathbf{H}v = \nabla_\theta (\nabla_\theta \bar{D}_{KL} \cdot v)
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;代码实现技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先算 KL 散度的梯度 $\nabla \bar{D}_{KL}$。&lt;/li&gt;
&lt;li&gt;算梯度和向量 $v$ 的点积：$scalar = \nabla \bar{D}_{KL}^T v$。&lt;/li&gt;
&lt;li&gt;对这个标量再求一次梯度：$\nabla_\theta (scalar)$。
这样只需要两次反向传播，就能算出 $\mathbf{H}v$，完全避免了存储巨大的 Hessian 矩阵。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;线搜索 (Line Search)&lt;/h2&gt;
&lt;p&gt;虽然我们算出了更新方向 $\Delta \theta$ 和最大步长 $\beta = \sqrt{\frac{2\delta}{g^T \mathbf{H}^{-1} g}}$，但由于泰勒展开只是&lt;strong&gt;局部近似&lt;/strong&gt;，直接走这么远可能会导致策略性能下降（KL 散度超标或目标函数下降）。&lt;/p&gt;
&lt;p&gt;TRPO 采用 &lt;strong&gt;回溯线搜索 (Backtracking Line Search)&lt;/strong&gt; 来确定最终步长：
设 $\theta_{new} = \theta + \alpha^j \beta \Delta \theta$，其中 $\alpha \in (0, 1)$ 是衰减系数（如 0.5）。
从 $j=0, 1, 2, \dots$ 开始尝试，直到满足以下两个条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;KL 约束满足&lt;/strong&gt;：$\bar{D}&lt;em&gt;{KL}(\theta || \theta&lt;/em&gt;{new}) \leqslant \delta$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目标函数提升&lt;/strong&gt;：$L(\theta_{new}) \geqslant 0$ (即性能确实提升了)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;TRPO 算法流程总结&lt;/h2&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize policy parameters } \theta_0 \
&amp;#x26; \bullet ; \textbf{For } k = 0, 1, 2, \dots \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Collect trajectories } \tau \text{ using } \pi_{\theta_k} \
&amp;#x26; \bullet \qquad \text{Estimate advantages } A^{\pi_{\theta_k}} \text{ (using GAE)} \
&amp;#x26; \bullet \qquad \text{Compute policy gradient } g = \nabla_\theta L(\theta)|&lt;em&gt;{\theta_k} \
&amp;#x26; \bullet \qquad \text{Use CG to compute } x \approx \mathbf{H}^{-1} g \text{ with HVP} \
&amp;#x26; \bullet \qquad \text{Compute max step length } \beta = \sqrt{\frac{2\delta}{x^T \mathbf{H} x}} \
&amp;#x26; \bullet \qquad \text{Perform Line Search to find best step size } \eta \in {\beta, 0.5\beta, 0.25\beta \dots} \
&amp;#x26; \bullet \qquad \text{Update } \theta&lt;/em&gt;{k+1} = \theta_k + \eta x \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;优缺点分析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;理论保证&lt;/strong&gt;：单调提升，不再担心学习率太大导致训练崩溃。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据效率&lt;/strong&gt;：比 REINFORCE 高，因为利用了二阶信息（自然梯度）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算复杂&lt;/strong&gt;：CG 和 HVP 实现复杂，且计算量大。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;约束太硬&lt;/strong&gt;：KL 散度是硬约束，处理起来不够灵活。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 预告&lt;/strong&gt;：
TRPO 虽然好，但太复杂了。有没有一种方法，既能保留 TRPO 的“信任区域”思想（稳定），又能像 SGD 一样简单（一阶优化）？
这就是 &lt;strong&gt;PPO (Proximal Policy Optimization)&lt;/strong&gt; 的由来（下一章内容）。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="https://pic.hana0721.top/76117294_p0_master1200.2rvq8fo0ul.webp"/><enclosure url="https://pic.hana0721.top/76117294_p0_master1200.2rvq8fo0ul.webp"/></item><item><title>RL笔记（10）：Actor-Critic</title><link>https://hana-blog.top/blog/rl-note-10</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-10</guid><description>策略梯度与价值函数的完美结合：详解 Actor-Critic 架构。从 Baseline 减小方差的数学证明，到优势函数 (Advantage) 的推导及 A2C 算法流程。</description><pubDate>Fri, 19 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的笔记中，我们学习了两种截然不同的强化学习路径：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Value-Based (如 Q-Learning)&lt;/strong&gt;：通过学习价值函数 $Q(s,a)$ 来间接导出策略。优点是方差小（利用了 TD 的一步更新），缺点是无法处理连续动作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Policy-Based (如 REINFORCE)&lt;/strong&gt;：直接学习策略 $\pi_\theta$。优点是可以处理连续动作，缺点是方差极大（因为使用了蒙特卡洛回报 $G_t$），且只能在回合结束后更新。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Actor-Critic (AC)&lt;/strong&gt; 架构旨在结合两者的优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Actor (演员)&lt;/strong&gt;：即策略网络 $\pi_\theta(a|s)$，负责由状态输出动作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Critic (评论家)&lt;/strong&gt;：即价值网络 $V_w(s)$ 或 $Q_w(s,a)$，负责评估 Actor 的动作好不好，帮助 Actor 减小方差加速学习。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;降低方差的技巧：基线 (Baseline)&lt;/h2&gt;
&lt;p&gt;回顾 REINFORCE 的梯度公式：&lt;/p&gt;
&lt;p&gt;$$
\nabla_\theta J(\theta) = \mathbb{E}&lt;em&gt;{\tau \sim \pi&lt;/em&gt;\theta} \left[ \sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t) G_t \right]
$$&lt;/p&gt;
&lt;p&gt;这里的 $G_t$ 是从 $t$ 时刻开始的累积回报。由于环境随机性和策略随机性，$G_t$ 的波动非常大，导致梯度估计不稳定。&lt;/p&gt;
&lt;p&gt;为了减小方差，我们引入一个 &lt;strong&gt;基线函数 (Baseline) $b(s)$&lt;/strong&gt;。基线函数只与状态有关，与动作无关。
我们把梯度修改为：&lt;/p&gt;
&lt;p&gt;$$
\nabla_\theta J(\theta) = \mathbb{E} \left[ \sum_{t=0}^T \nabla_\theta \log \pi_\theta(a_t|s_t) (G_t - b(s_t)) \right]
$$&lt;/p&gt;
&lt;h3&gt;数学证明：引入基线不改变梯度期望&lt;/h3&gt;
&lt;p&gt;我们需要证明减去一项 $b(s_t)$ 后，梯度的期望值不变。即证明：&lt;/p&gt;
&lt;p&gt;$$
\mathbb{E}&lt;em&gt;{a_t \sim \pi&lt;/em&gt;\theta} [\nabla_\theta \log \pi_\theta(a_t|s_t) \cdot b(s_t)] = 0
$$&lt;/p&gt;
&lt;p&gt;证明如下：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathbb{E}&lt;em&gt;{a \sim \pi} [\nabla&lt;/em&gt;\theta \log \pi(a|s) \cdot b(s)]
&amp;#x26;= \sum_{a} \pi(a|s) \frac{\nabla_\theta \pi(a|s)}{\pi(a|s)} b(s) \notag \
&amp;#x26;= b(s) \sum_{a} \nabla_\theta \pi(a|s) \notag \
&amp;#x26;= b(s) \nabla_\theta \sum_{a} \pi(a|s) \notag \
&amp;#x26;= b(s) \nabla_\theta (1) \notag \
&amp;#x26;= 0 \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;结论：只要 $b(s)$ 不依赖于动作 $a$，我们可以随意减去它而不改变梯度的方向，但能显著改变梯度的方差。通常，我们选择状态价值函数 $V(s)$ 作为基线。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Actor-Critic 架构演变&lt;/h2&gt;
&lt;h3&gt;Q Actor-Critic&lt;/h3&gt;
&lt;p&gt;如果我们用一个神经网络 $Q_w(s,a)$ 来近似 REINFORCE 中的 $G_t$，梯度变为：&lt;/p&gt;
&lt;p&gt;$$
\nabla_\theta J(\theta) \approx \mathbb{E} [\nabla_\theta \log \pi_\theta(a|s) Q_w(s,a)]
$$&lt;/p&gt;
&lt;p&gt;但这并没有解决问题，因为单纯拟合 $Q$ 值还是很难。&lt;/p&gt;
&lt;h3&gt;Advantage Actor-Critic (A2C)&lt;/h3&gt;
&lt;p&gt;为了利用 Baseline 减小方差，我们希望梯度形式为：&lt;/p&gt;
&lt;p&gt;$$
\nabla_\theta J(\theta) \approx \mathbb{E} [\nabla_\theta \log \pi_\theta(a|s) (Q_w(s,a) - V_v(s))]
$$&lt;/p&gt;
&lt;p&gt;其中 $Q(s,a) - V(s)$ 被称为 &lt;strong&gt;优势函数 (Advantage Function)&lt;/strong&gt;，记为 $A(s,a)$。它表示“在状态 $s$ 下，动作 $a$ 比平均情况好了多少”。&lt;/p&gt;
&lt;p&gt;但是，如果我们需要同时维护两个网络（一个算 $Q$，一个算 $V$），训练会很麻烦。我们可以利用 &lt;strong&gt;TD Error&lt;/strong&gt; 来近似优势函数。&lt;/p&gt;
&lt;p&gt;根据贝尔曼方程：
$$ Q(s&lt;em&gt;t, a_t) \approx r_t + \gamma V(s&lt;/em&gt;{t+1}) $$
因此，优势函数可以写为：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
A(s_t, a_t) &amp;#x26;= Q(s_t, a_t) - V(s_t) \notag \
&amp;#x26;\approx r_t + \gamma V(s_{t+1}) - V(s_t) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;这恰好就是 &lt;strong&gt;TD Error ($\delta_t$)&lt;/strong&gt;！&lt;/p&gt;
&lt;p&gt;因此，我们只需要维护一个 &lt;strong&gt;Actor 网络 $\pi_\theta(a|s)$&lt;/strong&gt; 和一个 &lt;strong&gt;Critic 网络 $V_w(s)$&lt;/strong&gt; 即可。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;A2C 算法流程&lt;/h2&gt;
&lt;p&gt;在 Advantage Actor-Critic 中，Critic 的任务是把 $V_w(s)$ 估得越准越好，Actor 的任务是利用 Critic 提供的 TD Error 来更新策略。&lt;/p&gt;
&lt;h3&gt;Critic 的更新 (Value Update)&lt;/h3&gt;
&lt;p&gt;Critic 的目标是最小化 TD Error 的平方（即回归问题）：&lt;/p&gt;
&lt;p&gt;$$
L_{critic}(w) = \frac{1}{2} \left( r_t + \gamma V_w(s_{t+1}) - V_w(s_t) \right)^2
$$&lt;/p&gt;
&lt;p&gt;梯度更新：
$$ w \leftarrow w + \alpha_c \delta_t \nabla_w V_w(s_t) $$&lt;/p&gt;
&lt;h3&gt;Actor 的更新 (Policy Update)&lt;/h3&gt;
&lt;p&gt;Actor 使用 TD Error 作为优势函数的估计值进行梯度上升：&lt;/p&gt;
&lt;p&gt;$$
\nabla_\theta J(\theta) \approx \nabla_\theta \log \pi_\theta(a_t|s_t) \delta_t
$$&lt;/p&gt;
&lt;p&gt;梯度更新：
$$ \theta \leftarrow \theta + \alpha&lt;em&gt;a \delta_t \nabla&lt;/em&gt;\theta \log \pi_\theta(a_t|s_t) $$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉理解&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 $\delta_t &gt; 0$（惊喜，结果比预期好）：增加动作 $a_t$ 的概率。&lt;/li&gt;
&lt;li&gt;如果 $\delta_t &amp;#x3C; 0$（失望，结果比预期差）：减小动作 $a_t$ 的概率。&lt;/li&gt;
&lt;li&gt;这里的“预期”就是 Critic 提供的 $V(s_t)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;A2C 伪代码&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize Actor } \pi_\theta \text{ and Critic } V_w \
&amp;#x26; \bullet ; \textbf{For } \text{episode } = 1 \to E \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Initialize state } s \
&amp;#x26; \bullet \qquad \textbf{For } \text{step } t = 1 \to T \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Sample action } a \sim \pi_\theta(\cdot|s) \
&amp;#x26; \bullet \qquad \qquad \text{Execute } a, \text{ observe } r, s&apos; \
&amp;#x26; \bullet \qquad \qquad \textbf{// Calculate TD Error (Advantage)} \
&amp;#x26; \bullet \qquad \qquad \delta \leftarrow r + \gamma V_w(s&apos;) - V_w(s) \
&amp;#x26; \bullet \qquad \qquad \textbf{// Update Critic} \
&amp;#x26; \bullet \qquad \qquad w \leftarrow w + \alpha_c \delta \nabla_w V_w(s) \
&amp;#x26; \bullet \qquad \qquad \textbf{// Update Actor} \
&amp;#x26; \bullet \qquad \qquad \theta \leftarrow \theta + \alpha_a \delta \nabla_\theta \log \pi_\theta(a|s) \
&amp;#x26; \bullet \qquad \qquad s \leftarrow s&apos; \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;偏差与方差的权衡 (Bias-Variance Tradeoff)&lt;/h2&gt;
&lt;p&gt;我们可以总结一下不同计算 Advantage 的方法，它们体现了 RL 中核心的权衡：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;蒙特卡洛 (REINFORCE)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;$A_t \approx G_t - V(s_t)$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无偏差&lt;/strong&gt;：$G_t$ 是真实回报。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高方差&lt;/strong&gt;：受整个序列随机性影响。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actor-Critic (TD)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;$A_t \approx r_t + \gamma V(s_{t+1}) - V(s_t)$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;低方差&lt;/strong&gt;：只受一步随机性影响。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有偏差&lt;/strong&gt;：依赖于 Critic 的估计 $V(s_{t+1})$，如果 Critic 还没练好，Actor 就会被带偏。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了平衡两者，我们可以使用 &lt;strong&gt;多步 TD&lt;/strong&gt; 或者 &lt;strong&gt;GAE (Generalized Advantage Estimation)&lt;/strong&gt;，这是 PPO 等进阶算法的核心技巧。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Actor-Critic 架构是现代深度强化学习的主流架构。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它解决了 REINFORCE 方差大、更新慢的问题。&lt;/li&gt;
&lt;li&gt;它解决了 DQN 无法处理连续动作的问题。&lt;/li&gt;
&lt;li&gt;它是后续 &lt;strong&gt;A3C, DDPG, TRPO, PPO, SAC&lt;/strong&gt; 等高级算法的共同祖先。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://pic.hana0721.top/75863098_p0_master1200.8aduokw8y2.webp"/><enclosure url="https://pic.hana0721.top/75863098_p0_master1200.8aduokw8y2.webp"/></item><item><title>RL笔记（9）：REINFORCE</title><link>https://hana-blog.top/blog/rl-note-9</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-9</guid><description>从价值到策略：详解策略梯度 (Policy Gradient) 定理的完整数学推导，并介绍最基础的策略梯度算法——REINFORCE。</description><pubDate>Thu, 18 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的 DQN 等算法中，我们都是先学习价值函数 $Q(s,a)$，再根据价值函数推导出策略（例如 $\epsilon$-Greedy）。这类方法称为 &lt;strong&gt;Value-Based&lt;/strong&gt; 方法。&lt;/p&gt;
&lt;p&gt;但这类方法有一些局限：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;无法处理连续动作空间&lt;/strong&gt;：在连续动作中找 $\max_a Q(s,a)$ 非常困难。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无法学习随机策略&lt;/strong&gt;：DQN 最终学到的是确定性策略，但在某些博弈场景（如剪刀石头布），随机策略才是最优的。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，我们引入 &lt;strong&gt;Policy-Based&lt;/strong&gt; 方法：直接参数化策略 $\pi_\theta(a|s)$，通过调整参数 $\theta$ 来最大化期望回报。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;策略的表示&lt;/h2&gt;
&lt;p&gt;我们需要用一个函数（通常是神经网络）来表示策略。&lt;/p&gt;
&lt;h3&gt;随机策略 (Stochastic Policy)&lt;/h3&gt;
&lt;p&gt;输入状态 $s$，输出动作的概率分布。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;离散动作&lt;/strong&gt;（如 Softmax）：
$$ \pi(a|s;\theta)=\frac{\exp(Q*\theta(s,a))}{\sum*{a^\prime}\exp(Q_\theta(s,a^\prime))} $$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连续动作&lt;/strong&gt;（如高斯分布）：
$$ \pi(a|s;\theta) = \frac{1}{\sqrt{2\pi}\sigma} \exp\left(-\frac{(a-\mu*\theta(s))^2}{2\sigma^2}\right) $$
通常网络输出均值 $\mu*\theta(s)$ 和标准差 $\sigma_\theta(s)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;确定性策略 (Deterministic Policy)&lt;/h3&gt;
&lt;p&gt;输入状态 $s$，直接输出动作值。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;离散动作&lt;/strong&gt;：$$ a=\operatorname{argmax}&lt;em&gt;{a} Q&lt;/em&gt;\theta(s,a) $$ （不可微，无法直接用梯度下降）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连续动作&lt;/strong&gt;：$$ a=\mu_\theta(s) $$ （可微，用于 DDPG 等算法）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;策略梯度定理 (Policy Gradient Theorem)&lt;/h2&gt;
&lt;p&gt;这是 Policy-Based 方法的基石。我们的目标是最大化期望回报 $J(\theta)$，通过计算梯度 $\nabla_\theta J(\theta)$ 来更新参数。&lt;/p&gt;
&lt;h3&gt;目标函数&lt;/h3&gt;
&lt;p&gt;$$
J(\theta) = \mathbb{E}&lt;em&gt;{s_0}[V^{\pi&lt;/em&gt;\theta}(s_0)]
$$&lt;/p&gt;
&lt;p&gt;其中 $s_0$ 是初始状态。&lt;/p&gt;
&lt;h3&gt;梯度推导 (The &quot;Hard&quot; Part)&lt;/h3&gt;
&lt;p&gt;我们需要计算 $\nabla_\theta V^{\pi_\theta}(s)$。根据贝尔曼方程展开：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta V^{\pi_\theta}(s)
&amp;#x26;= \nabla_\theta \sum_{a\in \mathcal{A}} \pi_\theta (a|s) Q^{\pi_\theta}(s,a) \notag \
&amp;#x26;= \sum_{a\in\mathcal{A}}\left[\nabla_\theta \pi_\theta (a|s) Q^{\pi_\theta}(s,a) +\pi_\theta (a|s) \nabla_\theta Q^{\pi_\theta}(s,a) \right] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;这里的难点在于 $\nabla_\theta Q^{\pi_\theta}(s,a)$，因为 $Q$ 值本身也依赖于策略参数 $\theta$（未来的动作会变）。我们继续展开 $Q$：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta Q^{\pi_\theta}(s,a) &amp;#x26;= \nabla_\theta \sum_{r,s^\prime} P(s^\prime, r| s, a) (r + \gamma V^{\pi_\theta}(s^\prime)) \notag \
&amp;#x26;= \sum_{s^\prime} P(s^\prime| s, a) \gamma \nabla_\theta V^{\pi_\theta}(s^\prime) \notag
\end{align}
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：环境的动力学 $P$ 和奖励 $r$ 与 $\theta$ 无关，所以导数为 0。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;代回原式，得递归形式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta V^{\pi_\theta}(s)
&amp;#x26;= \underbrace{\sum_{a\in\mathcal{A}}\nabla_\theta\pi_\theta(a|s) Q^{\pi_\theta}(s,a)}&lt;em&gt;{\phi(s)} + \gamma \sum&lt;/em&gt;{a\in\mathcal{A}}\pi_\theta (a|s) \sum_{s^\prime \in \mathcal{S}} P(s^\prime| s, a) \nabla_\theta V^{\pi_\theta}(s^\prime) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;定义转移概率 $d^{\pi_\theta}(s \to s&apos;, k)$ 为策略 $\pi_\theta$ 从 $s$ 出发走 $k$ 步到达 $s&apos;$ 的概率。反复迭代上述递归式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta V^{\pi_\theta}(s)
&amp;#x26;= \phi(s) + \gamma \sum_{s&apos;} d(s \to s&apos;, 1) \nabla_\theta V(s&apos;) \notag \
&amp;#x26;= \phi(s) + \gamma \sum_{s&apos;} d(s \to s&apos;, 1) [\phi(s&apos;) + \gamma \sum_{s&apos;&apos;} d(s&apos; \to s&apos;&apos;, 1) \nabla_\theta V(s&apos;&apos;)] \notag \
&amp;#x26;= \dots \notag \
&amp;#x26;= \sum_{x \in \mathcal{S}} \sum_{k=0}^\infty \gamma^k d^{\pi_\theta}(s \to x, k) \phi(x) \notag
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;引入状态访问分布&lt;/h3&gt;
&lt;p&gt;回到总目标函数 $J(\theta)$ 的梯度：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta J(\theta) &amp;#x26;= \mathbb{E}&lt;em&gt;{s_0} [\nabla&lt;/em&gt;\theta V^{\pi_\theta}(s_0)] \notag \
&amp;#x26;= \sum_{s \in \mathcal{S}} \left( \sum_{k=0}^\infty \gamma^k d^{\pi_\theta}(s_0 \to s, k) \right) \phi(s) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;利用状态访问分布 $\nu^{\pi_\theta}(s)$ 的定义：
$$ \sum*{k=0}^\infty \gamma^k d^{\pi*\theta}(s&lt;em&gt;0 \to s, k) \propto \nu^{\pi&lt;/em&gt;\theta}(s) $$&lt;/p&gt;
&lt;p&gt;我们得到了极其优雅的 &lt;strong&gt;策略梯度定理&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta J(\theta)
&amp;#x26;\propto \sum_{s \in \mathcal{S}} \nu^{\pi_\theta}(s) \phi(s) \notag \
&amp;#x26;= \sum_{s \in \mathcal{S}} \nu^{\pi_\theta}(s) \sum_{a \in \mathcal{A}} \nabla_\theta \pi_\theta(a|s) Q^{\pi_\theta}(s,a) \notag
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;对数导数技巧 (Log-Derivative Trick)&lt;/h3&gt;
&lt;p&gt;为了能在采样中计算，我们利用恒等式 $\nabla \pi = \pi \frac{\nabla \pi}{\pi} = \pi \nabla \log \pi$：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\nabla_\theta J(\theta)
&amp;#x26;= \mathbb{E}&lt;em&gt;{s \sim \nu^{\pi&lt;/em&gt;\theta}} \left[ \sum_{a \in \mathcal{A}} \pi_\theta(a|s) \nabla_\theta \log \pi_\theta(a|s) Q^{\pi_\theta}(s,a) \right] \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;{\pi&lt;/em&gt;\theta} [\nabla_\theta \log \pi_\theta(a|s) Q^{\pi_\theta}(s,a)] \notag
\end{align}
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;核心结论&lt;/strong&gt;：我们不需要知道环境的状态转移 $P$，也不需要对状态分布 $\nu(s)$ 求导。只要让智能体在环境里玩，收集数据，就可以计算梯度！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;REINFORCE 算法&lt;/h2&gt;
&lt;p&gt;REINFORCE 是最基础的策略梯度算法，它使用 &lt;strong&gt;蒙特卡洛方法 (Monte-Carlo)&lt;/strong&gt; 来估计 $Q^{\pi_\theta}(s,a)$。&lt;/p&gt;
&lt;p&gt;对于一条完整的轨迹 $\tau = {s_1, a_1, r_1, \dots, s_T, a_T, r_T}$，时刻 $t$ 的真实回报 $G_t$ 是 $Q(s_t, a_t)$ 的无偏估计：
$$ Q^{\pi*\theta}(s_t, a_t) \approx G_t = \sum*{k=t}^T \gamma^{k-t} r_k $$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;参数更新公式&lt;/strong&gt;：
$$ \theta \leftarrow \theta + \alpha \gamma^t G&lt;em&gt;t \nabla&lt;/em&gt;\theta \log \pi_\theta(a_t|s_t) $$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直觉&lt;/strong&gt;：如果某个动作 $a_t$ 带来了高回报 $G_t$，我们就增加它的概率（$\nabla \log \pi$ 方向）；如果回报低（甚至是负的），就减少它的概率。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;算法伪代码&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize policy parameters } \theta \
&amp;#x26; \bullet ; \textbf{For } \text{episode } e = 1 \to E \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Generate trajectory } \tau = {s_1, a_1, r_1, \dots, s_T, a_T, r_T} \sim \pi_\theta \
&amp;#x26; \bullet \qquad \textbf{For } t = 1 \to T \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad G_t \leftarrow \sum_{k=t}^T \gamma^{k-t} r_k \
&amp;#x26; \bullet \qquad \qquad \theta \leftarrow \theta + \alpha \gamma^t G_t \nabla_\theta \log \pi_\theta(a_t|s_t) \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;h3&gt;优缺点分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：可以直接处理连续动作；学习到的随机策略可以探索；数学推导优美。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;方差极大&lt;/strong&gt;：一局游戏的结果随机性很大，导致梯度估计不稳定。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-Policy&lt;/strong&gt;：必须采集一条数据就更新一次，旧数据无法重复利用，效率低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回合更新&lt;/strong&gt;：必须等到游戏结束才能计算 $G_t$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 思考&lt;/strong&gt;：为了减小方差，我们通常会减去一个 &lt;strong&gt;基线 (Baseline)&lt;/strong&gt; $b(s)$，比如状态价值 $V(s)$。这不会改变梯度的期望，但能显著降低方差。这就是 &lt;strong&gt;Actor-Critic&lt;/strong&gt; 算法的雏形（下一章内容）。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="https://pic.hana0721.top/74502138_p0_master1200.7i0z6ufn7r.webp"/><enclosure url="https://pic.hana0721.top/74502138_p0_master1200.7i0z6ufn7r.webp"/></item><item><title>RL笔记（8）：DQN</title><link>https://hana-blog.top/blog/rl-note-8</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-8</guid><description>深度强化学习的开山之作。详解 DQN 如何利用神经网络拟合 Q 值，以及两大核心创新：经验回放与目标网络。进阶涵盖 Double DQN 与 Dueling DQN。</description><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的 Q-Learning 算法中，我们使用一个矩阵（Q-Table）来记录每个状态动作对 $(s,a)$ 的价值。
这种方法有两个致命局限：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;内存限制&lt;/strong&gt;：如果状态空间很大（例如围棋 $10^{170}$），表格根本存不下。这被称为&lt;strong&gt;维度灾难 (Curse of Dimensionality)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;泛化能力差&lt;/strong&gt;：对于没见过的状态，表格无法给出估计，而在连续状态空间中，几乎很难遇到完全一样的状态。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了解决这个问题，我们引入&lt;strong&gt;函数拟合 (Function Approximation)&lt;/strong&gt;。&lt;strong&gt;深度Q网络 (Deep Q-Network, DQN)&lt;/strong&gt; 使用一个神经网络 $Q_\omega(s, a)$ 来近似 $Q^*(s, a)$，输入状态 $s$，输出所有离散动作的 Q 值。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;深度 Q 网络 (DQN)&lt;/h2&gt;
&lt;h3&gt;核心定义&lt;/h3&gt;
&lt;p&gt;在 DQN 中，我们使用参数为 $\omega$ 的神经网络来拟合动作价值函数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入&lt;/strong&gt;：状态 $s$（例如游戏的屏幕像素）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输出&lt;/strong&gt;：每个动作 $a \in \mathcal{A}$ 对应的价值 $Q(s,a)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;损失函数&lt;/h3&gt;
&lt;p&gt;类似于 Q-Learning，我们希望神经网络的输出逼近 TD Target。
对于一条数据 $(s_i, a_i, r_i, s_i^\prime)$，目标是最小化均方误差：&lt;/p&gt;
&lt;p&gt;$$
J(\omega)=\frac{1}{2N}\sum_{i=1}^{N}\left[ \underbrace{Q_\omega(s_i,a_i)}&lt;em&gt;{\text{预测值}} - \underbrace{\left(r_i+\gamma \max&lt;/em&gt;{a^\prime}Q_\omega(s_i^\prime,a^\prime)\right)}_{\text{TD Target}} \right]^2
$$&lt;/p&gt;
&lt;p&gt;然而，直接这样训练是不稳定的。DQN 引入了两大“法宝”来解决这个问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;创新一：经验回放 (Experience Replay)&lt;/h2&gt;
&lt;p&gt;在一般的监督学习中，我们假设训练数据是&lt;strong&gt;独立同分布 (i.i.d)&lt;/strong&gt; 的。但在强化学习中，智能体采集的数据是序列相关的（现在的状态依赖于上一秒的状态）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法&lt;/strong&gt;：
维护一个&lt;strong&gt;回放缓冲区 (Replay Buffer)&lt;/strong&gt; $\mathcal{R}$。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;智能体与环境交互，将产生的数据 $(s_t, a_t, r_t, s_{t+1})$ 存入缓冲区。&lt;/li&gt;
&lt;li&gt;训练时，从缓冲区中&lt;strong&gt;随机采样&lt;/strong&gt;一个批次 (Batch) 的数据进行梯度下降。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;作用&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;打破相关性&lt;/strong&gt;：随机采样消除了数据之间的时间相关性，满足独立同分布假设，稳定网络训练。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提高样本效率&lt;/strong&gt;：一条经验数据可以被多次采样利用，而不是用完即弃。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;创新二：目标网络 (Target Network)&lt;/h2&gt;
&lt;p&gt;在原版 Q-Learning 中，TD Target 的计算也依赖于当前网络参数 $\omega$：&lt;/p&gt;
&lt;p&gt;$$
y_i = r_i + \gamma \max_{a&apos;} Q_\omega(s&apos;_i, a&apos;)
$$&lt;/p&gt;
&lt;p&gt;这就好比“在射箭的同时，靶子也在动”。更新 $\omega$ 会同时改变预测值和目标值，容易导致震荡发散。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法&lt;/strong&gt;：
引入一个结构相同但参数独立的&lt;strong&gt;目标网络 (Target Network)&lt;/strong&gt;，参数记为 $\omega^-$。
计算目标值时使用 $\omega^-$，更新网络时优化 $\omega$。&lt;/p&gt;
&lt;p&gt;$$
y_i = r_i + \gamma \max_{a&apos;} Q_{\omega^-}(s&apos;_i, a&apos;)
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;更新规则&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;训练网络 $\omega$&lt;/strong&gt;：每个 step 都进行梯度下降更新。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目标网络 $\omega^-$&lt;/strong&gt;：每隔 $C$ 步（例如 1000 步），将 $\omega$ 的值复制给 $\omega^-$ ($\omega^- \leftarrow \omega$)。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉&lt;/strong&gt;：固定住靶子一会儿，让射手（训练网络）安心瞄准，等射手练好了，再把靶子挪到新的位置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;DQN 算法伪代码&lt;/h2&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize main network } Q_\omega \text{ and target network } Q_{\omega^-} \text{ with weights } \omega \
&amp;#x26; \bullet ; \text{Initialize replay buffer } \mathcal{R} \
&amp;#x26; \bullet ; \textbf{For } \text{episode } e = 1 \to E \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Initialize state } s \
&amp;#x26; \bullet \qquad \textbf{For } \text{step } t = 1 \to T \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Select action } a \text{ using } \epsilon\text{-greedy based on } Q_\omega(s) \
&amp;#x26; \bullet \qquad \qquad \text{Execute } a, \text{ observe } r, s&apos; \
&amp;#x26; \bullet \qquad \qquad \text{Store transition } (s, a, r, s&apos;) \text{ in } \mathcal{R} \
&amp;#x26; \bullet \qquad \qquad \textbf{If } |\mathcal{R}| &gt; \text{batch_size}: \
&amp;#x26; \bullet \qquad \qquad \qquad \text{Sample batch } {(s_i, a_i, r_i, s&apos;&lt;em&gt;i)}&lt;/em&gt;{i=1}^N \text{ from } \mathcal{R} \
&amp;#x26; \bullet \qquad \qquad \qquad \text{Calculate targets: } y_i = r_i + \gamma \max_{a&apos;} Q_{\omega^-}(s&apos;&lt;em&gt;i, a&apos;) \
&amp;#x26; \bullet \qquad \qquad \qquad \text{Update } \omega \text{ by minimizing } \frac{1}{N}\sum (Q&lt;/em&gt;\omega(s_i, a_i) - y_i)^2 \
&amp;#x26; \bullet \qquad \qquad \qquad \textbf{Every } C \text{ steps: } \omega^- \leftarrow \omega \
&amp;#x26; \bullet \qquad \qquad s \leftarrow s&apos; \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;进阶 1：Double DQN&lt;/h2&gt;
&lt;h3&gt;问题：过高估计 (Overestimation)&lt;/h3&gt;
&lt;p&gt;DQN 在计算目标值时使用了 $\max$ 操作：$y_i = r + \gamma \max_{a&apos;} Q(s&apos;, a&apos;)$。
由于神经网络本身存在估计误差，$\max$ 操作倾向于选择那些被&lt;strong&gt;高估&lt;/strong&gt;的值。这种&lt;strong&gt;最大化偏差 (Maximization Bias)&lt;/strong&gt; 会导致 Q 值普遍偏大，影响策略学习。&lt;/p&gt;
&lt;h3&gt;解决方案&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;解耦选择与评估&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;选择动作&lt;/strong&gt;：使用当前网络 $Q_\omega$ 来决定哪个动作最好（即 argmax）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;评估价值&lt;/strong&gt;：使用目标网络 $Q_{\omega^-}$ 来计算那个动作的价值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Double DQN 目标公式&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$
y_i = r_i + \gamma Q_{\omega^-}(s&apos;&lt;em&gt;i, \underset{a&apos;}{\operatorname{argmax}} Q&lt;/em&gt;\omega(s&apos;_i, a&apos;))
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;进阶 2：Dueling DQN&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;在很多状态下，&lt;strong&gt;状态本身的价值&lt;/strong&gt;比&lt;strong&gt;选择什么动作&lt;/strong&gt;更重要。
例如：在赛车游戏中，如果前面是死胡同（状态差），无论你向左转还是向右转（动作），价值都很低。
Dueling DQN 改变了网络结构，将 Q 值分解为两部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;状态价值函数 (Value Function)&lt;/strong&gt; $V(s)$：仅与状态有关。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势函数 (Advantage Function)&lt;/strong&gt; $A(s,a)$：与动作有关，表示动作 $a$ 相比平均情况好多少。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;$$
Q(s, a) = V(s) + A(s, a)
$$&lt;/p&gt;
&lt;h3&gt;可辨识性问题 (Identifiability)&lt;/h3&gt;
&lt;p&gt;如果直接用 $V+A$，网络会出现唯一性问题（$V$ 加 10，$A$ 减 10，总和 $Q$ 不变）。为了让 $V$ 和 $A$ 能被唯一确定，我们需要强行约束 $A$ 的某种性质。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;聚合公式 (Aggregation)&lt;/strong&gt;：
通常让优势函数 $A$ 对于某个状态的均值为 0，或者最大值为 0。&lt;/p&gt;
&lt;p&gt;$$
Q(s,a;\theta,\alpha,\beta)=V(s;\theta,\beta) + \left( A(s,a;\theta,\alpha) - \frac{1}{|\mathcal{A}|}\sum_{a^\prime}A(s,a^\prime;\theta,\alpha) \right)
$$&lt;/p&gt;
&lt;p&gt;或者：&lt;/p&gt;
&lt;p&gt;$$
Q(s,a;\theta,\alpha,\beta)=V(s;\theta,\beta) + \left( A(s,a;\theta,\alpha) - \max_{a^\prime}A(s,a^\prime;\theta,\alpha) \right)
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在动作对环境影响不大的状态下，能更快地学习状态价值 $V$。&lt;/li&gt;
&lt;li&gt;极大地提高了训练效率和稳定性。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://pic.hana0721.top/74378589_p0_master1200.5j4sgia4w5.webp"/><enclosure url="https://pic.hana0721.top/74378589_p0_master1200.5j4sgia4w5.webp"/></item><item><title>RL笔记（7）：Dyna-Q</title><link>https://hana-blog.top/blog/rl-note-7</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-7</guid><description>从试错到规划：基于模型的强化学习 (Model-Based RL) 入门。详解 Dyna-Q 算法如何利用环境模型生成模拟数据，加速策略学习。</description><pubDate>Tue, 16 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在之前的 Q-Learning 和 SARSA 中，智能体只能通过&lt;strong&gt;真实&lt;/strong&gt;地与环境交互（摔跟头、吃金币）来学习，这被称为 &lt;strong&gt;无模型强化学习 (Model-Free RL)&lt;/strong&gt;。这种方式虽然稳健，但效率较低，因为真实交互往往昂贵且缓慢。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基于模型的强化学习 (Model-Based RL)&lt;/strong&gt; 引入了一个新的思路：如果智能体能学会环境的运行规律（建立一个模型），它就可以在脑海中“推演”未来，从而减少对真实世界的依赖。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉理解&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model-Free (Q-Learning)&lt;/strong&gt;：像是在练习投篮，必须每次真把球投出去才知道进没进。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model-Based (Dyna-Q)&lt;/strong&gt;：像是下棋高手，不仅在实战中学习，还在脑海中复盘和推演（Planning），“如果我走这一步，对手可能会那样走...”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;核心概念&lt;/h2&gt;
&lt;h3&gt;什么是模型 (Model)？&lt;/h3&gt;
&lt;p&gt;在 RL 中，模型 $M$ 指的是对环境动态的模拟。给定状态 $s$ 和动作 $a$，模型能预测出下一个状态 $s&apos;$ 和奖励 $r$：&lt;/p&gt;
&lt;p&gt;$$
s&apos;, r \leftarrow M(s, a)
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;预知模型&lt;/strong&gt;：如下棋，规则是完全已知的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习模型&lt;/strong&gt;：如机器人走路，需要通过观测数据 $(s, a, r, s&apos;)$ 来拟合环境规律。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;两个关键指标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;收敛效果&lt;/strong&gt;：算法收敛后能够获得的期望回报。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;样本复杂度 (Sample Complexity)&lt;/strong&gt;：达到同样的性能，需要在真实环境中交互多少次。
&lt;ul&gt;
&lt;li&gt;Model-Based 的核心优势就是&lt;strong&gt;降低样本复杂度&lt;/strong&gt;（少走弯路）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;学习与规划&lt;/h3&gt;
&lt;p&gt;Dyna-Q 架构将 RL 过程分为了两部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;直接强化学习 (Direct RL)&lt;/strong&gt;：利用&lt;strong&gt;真实经验&lt;/strong&gt;更新价值函数（和 Q-Learning 一样）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规划 (Planning)&lt;/strong&gt;：利用&lt;strong&gt;模拟经验&lt;/strong&gt;（模型生成的）更新价值函数。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Dyna-Q 算法&lt;/h2&gt;
&lt;p&gt;Dyna-Q 是将 Q-Learning 与规划结合的最简单范例。它维护一个简单的&lt;strong&gt;查表式模型 (Table-based Model)&lt;/strong&gt;，记录在这个状态 $s$ 做动作 $a$ 曾经发生了什么。&lt;/p&gt;
&lt;h3&gt;算法流程&lt;/h3&gt;
&lt;p&gt;在每个时间步 $t$：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;行动&lt;/strong&gt;：在真实环境中执行动作，获得 $(s, a, r, s&apos;)$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;直接学习&lt;/strong&gt;：用真实数据更新 $Q(s,a)$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型学习&lt;/strong&gt;：把 $(s, a) \to (r, s&apos;)$ 记入模型（如果是确定性环境，直接覆盖；如果是随机环境，可能需要记录分布）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规划 (Planning)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;重复 $N$ 次（比如 10 次）：&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;做梦&lt;/strong&gt;：随机从记忆中挑选一个&lt;strong&gt;曾经去过的&lt;/strong&gt;状态 $s_{sim}$ 和&lt;strong&gt;曾经做过的&lt;/strong&gt;动作 $a_{sim}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推演&lt;/strong&gt;：询问模型得到模拟结果 $r_{sim}, s&apos;_{sim}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;间接学习&lt;/strong&gt;：用模拟数据更新 $Q(s_{sim}, a_{sim})$。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;算法伪代码&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize } Q(s,a), \text{Model } M(s,a) \
&amp;#x26; \bullet ; \textbf{For } \text{episode } e = 1 \to E \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Initialize state } s \
&amp;#x26; \bullet \qquad \textbf{For } \text{step } t = 1 \to T \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Choose action } a \text{ using } \epsilon\text{-greedy} \
&amp;#x26; \bullet \qquad \qquad \text{Execute } a, \text{ observe } r, s&apos; \
&amp;#x26; \bullet \qquad \qquad \textbf{1. Direct RL (Q-Learning update):} \
&amp;#x26; \bullet \qquad \qquad Q(s,a) \leftarrow Q(s,a) + \alpha [r + \gamma \max_{a&apos;} Q(s&apos;, a&apos;) - Q(s,a)] \
&amp;#x26; \bullet \qquad \qquad \textbf{2. Model Learning:} \
&amp;#x26; \bullet \qquad \qquad M(s, a) \leftarrow (r, s&apos;) \
&amp;#x26; \bullet \qquad \qquad \textbf{3. Planning (Loop N times):} \
&amp;#x26; \bullet \qquad \qquad \textbf{For } n = 1 \to N \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \qquad \text{Randomly select } s_{sim} \text{ previously observed} \
&amp;#x26; \bullet \qquad \qquad \qquad \text{Randomly select } a_{sim} \text{ previously taken in } s_{sim} \
&amp;#x26; \bullet \qquad \qquad \qquad r_{sim}, s&apos;&lt;em&gt;{sim} \leftarrow M(s&lt;/em&gt;{sim}, a_{sim}) \
&amp;#x26; \bullet \qquad \qquad \qquad Q(s_{sim}, a_{sim}) \leftarrow Q(s_{sim}, a_{sim}) + \alpha [r_{sim} + \gamma \max_{a&apos;} Q(s&apos;&lt;em&gt;{sim}, a&apos;) - Q(s&lt;/em&gt;{sim}, a_{sim})] \
&amp;#x26; \bullet \qquad \qquad \textbf{End For} \
&amp;#x26; \bullet \qquad \qquad s \leftarrow s&apos; \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;优缺点分析&lt;/h2&gt;
&lt;h3&gt;优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;样本效率极高&lt;/strong&gt;：在每次与环境交互后，Dyna-Q 会进行 $N$ 次规划。这意味着&lt;strong&gt;一条真实经验被复用了 $N+1$ 次&lt;/strong&gt;。对于真实交互很昂贵的场景（如机器人实验），这非常有用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;劣势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;模型偏差 (Model Bias)&lt;/strong&gt;：这是 Model-Based RL 的死穴。
&lt;ul&gt;
&lt;li&gt;如果模型学错了（比如现实中走这一步会掉坑里，模型却认为会飞过去），那么规划得越多，智能体就在错误的道路上越走越远。&lt;/li&gt;
&lt;li&gt;解决方案通常涉及&lt;strong&gt;不确定性估计&lt;/strong&gt;（如果对模型预测不自信，就不要信它）或&lt;strong&gt;探索奖励&lt;/strong&gt;（Dyna-Q+，鼓励去验证模型不确定的地方）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Dyna-Q 完美展示了强化学习如何结合“行万里路”（真实交互）与“读万卷书”（模型规划）。
在下一章，我们将正式告别“表格型 (Tabular)”强化学习，引入神经网络，进入 &lt;strong&gt;深度强化学习 (Deep Reinforcement Learning)&lt;/strong&gt; 的时代。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/72300463_p0_master1200.83amt5a3ia.webp"/><enclosure url="https://pic.hana0721.top/72300463_p0_master1200.83amt5a3ia.webp"/></item><item><title>RL笔记（6）：时序差分</title><link>https://hana-blog.top/blog/rl-note-6</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-6</guid><description>结合了蒙特卡洛与动态规划的精华：详解时序差分 (TD) 学习。涵盖 SARSA、Q-Learning 及其多步扩展，深入对比 On-Policy 与 Off-Policy 的本质区别。</description><pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在大部分强化学习的现实场景中，MDP 中的状态转移概率 $\mathcal{P}$ 和奖励函数 $\mathcal{R}$ 通常是未知的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;因为没有模型，我们无法直接用&lt;strong&gt;动态规划 (DP)&lt;/strong&gt; 来算出最优解。&lt;/li&gt;
&lt;li&gt;智能体必须像&lt;strong&gt;蒙特卡洛 (MC)&lt;/strong&gt; 那样，通过与环境交互、采样数据来学习。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类方法统称为 &lt;strong&gt;无模型强化学习 (Model-Free RL)&lt;/strong&gt;。本章将介绍其中最重要的一类方法：&lt;strong&gt;时序差分 (Temporal Difference, TD)&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;时序差分方法 (Temporal Difference)&lt;/h2&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;时序差分 (TD) 结合了蒙特卡洛 (MC) 和动态规划 (DP) 的思想：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;像 MC&lt;/strong&gt;：直接从经验（采样数据）中学习，不需要环境模型。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;像 DP&lt;/strong&gt;：利用&lt;strong&gt;自举 (Bootstrapping)&lt;/strong&gt; 的思想，用后继状态的估计值来更新当前状态的估计值，而不需要等到回合结束。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;价值函数的更新&lt;/h3&gt;
&lt;p&gt;回顾蒙特卡洛 (MC) 的增量更新公式：&lt;/p&gt;
&lt;p&gt;$$
V(s_t) \leftarrow V(s_t) + \alpha [G_t - V(s_t)]
$$&lt;/p&gt;
&lt;p&gt;其中 $G_t$ 是从 $t$ 时刻开始直到回合结束的真实回报。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TD 的改进&lt;/strong&gt;：
TD 不想等到回合结束。它利用贝尔曼方程的性质，用 $r_t + \gamma V(s_{t+1})$ 来&lt;strong&gt;替代&lt;/strong&gt; $G_t$：&lt;/p&gt;
&lt;p&gt;$$
V(s_t) \leftarrow V(s_t) + \alpha [\underbrace{r_t + \gamma V(s_{t+1})}_{\text{TD Target}} - V(s_t)]
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TD Target&lt;/strong&gt;: $r_t + \gamma V(s_{t+1})$，这是我们对真实回报 $G_t$ 的一个有偏但方差更小的估计。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TD Error&lt;/strong&gt;: $\delta_t = r_t + \gamma V(s_{t+1}) - V(s_t)$，表示“当下的惊喜”——实际发生的情况比预期的好了多少。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;为什么可以替代？&lt;/h3&gt;
&lt;p&gt;根据 $V^\pi$ 的定义推导：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V^\pi(s) &amp;#x26;= \mathbb{E}&lt;em&gt;\pi[G_t | S_t=s] \notag \
&amp;#x26;= \mathbb{E}&lt;/em&gt;\pi [R_t + \gamma G_{t+1} | S_t=s] \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;\pi [R_t + \gamma V^\pi(S&lt;/em&gt;{t+1}) | S_t=s] \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;可见，$R_t + \gamma V(S_{t+1})$ 是 $V(s)$ 的无偏估计（假设 $V(S_{t+1})$ 准确）。随着迭代进行，最终 $V$ 会收敛到 $V^\pi$。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;SARSA 算法&lt;/h2&gt;
&lt;p&gt;SARSA 是 &lt;strong&gt;S&lt;/strong&gt;tate-&lt;strong&gt;A&lt;/strong&gt;ction-&lt;strong&gt;R&lt;/strong&gt;eward-&lt;strong&gt;S&lt;/strong&gt;tate-&lt;strong&gt;A&lt;/strong&gt;ction 的缩写，因为它利用五元组 $(s_t, a_t, r_t, s_{t+1}, a_{t+1})$ 进行更新。&lt;/p&gt;
&lt;h3&gt;从 V 到 Q&lt;/h3&gt;
&lt;p&gt;在 Model-Free 场景下，我们通常直接估计 &lt;strong&gt;动作价值函数 $Q(s,a)$&lt;/strong&gt;，而不是 $V(s)$，以便直接选取动作。
更新公式：&lt;/p&gt;
&lt;p&gt;$$
Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha [r_t + \gamma Q(s_{t+1},a_{t+1}) - Q(s_t,a_t)]
$$&lt;/p&gt;
&lt;h3&gt;探索与利用&lt;/h3&gt;
&lt;p&gt;SARSA 需要解决两个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;估计不准&lt;/strong&gt;：在训练初期，Q 值是不准确的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;采样覆盖&lt;/strong&gt;：如果一直贪婪地选动作，可能永远无法发现更好的策略。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，SARSA 使用 &lt;strong&gt;$\epsilon$-贪婪策略 ($\epsilon$-Greedy)&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以 $1-\epsilon$ 的概率选择 $\arg\max_a Q(s,a)$。&lt;/li&gt;
&lt;li&gt;以 $\epsilon$ 的概率随机选择动作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;算法伪代码&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize } Q(s,a) \
&amp;#x26; \bullet ; \textbf{For } \text{episode } = 1 \to E \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Initialize state } s \
&amp;#x26; \bullet \qquad \text{Choose action } a \text{ from } s \text{ using } \epsilon\text{-greedy} \
&amp;#x26; \bullet \qquad \textbf{For } \text{step } = 1 \to T \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Take action } a, \text{ observe } r, s&apos; \
&amp;#x26; \bullet \qquad \qquad \text{Choose action } a&apos; \text{ from } s&apos; \text{ using } \epsilon\text{-greedy} \
&amp;#x26; \bullet \qquad \qquad Q(s,a) \leftarrow Q(s,a) + \alpha [r + \gamma Q(s&apos;,a&apos;) - Q(s,a)] \
&amp;#x26; \bullet \qquad \qquad s \leftarrow s&apos;, a \leftarrow a&apos; \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;多步 SARSA ($n$-step SARSA)&lt;/h2&gt;
&lt;p&gt;MC 是无偏但方差大（要等很久），TD(0) 是偏差大但方差小（看一步）。我们可以折中一下，看 $n$ 步。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;单步 TD (SARSA)&lt;/strong&gt;:
$$G_t^{(1)} = r_t + \gamma Q(s_{t+1}, a_{t+1})$$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$n$ 步 TD&lt;/strong&gt;:
$$G_t^{(n)} = r_t + \gamma r_{t+1} + \dots + \gamma^n Q(s_{t+n}, a_{t+n})$$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;$n$ 步 SARSA 更新规则&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$
Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha [G_t^{(n)} - Q(s_t,a_t)]
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Q-Learning 算法&lt;/h2&gt;
&lt;p&gt;Q-Learning 是强化学习中最著名的算法之一，它与 SARSA 非常像，但有一个关键区别。&lt;/p&gt;
&lt;h3&gt;更新规则&lt;/h3&gt;
&lt;p&gt;$$
Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha [r_t + \gamma \max_{a&apos;} Q(s_{t+1}, a&apos;) - Q(s_t,a_t)]
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键区别&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SARSA&lt;/strong&gt;：使用 $Q(s_{t+1}, a_{t+1})$。这里的 $a_{t+1}$ 是智能体&lt;strong&gt;实际采取&lt;/strong&gt;的动作（可能包含了 $\epsilon$ 的随机探索）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Q-Learning&lt;/strong&gt;：使用 $\max_{a&apos;} Q(s_{t+1}, a&apos;)$。不管智能体下一步实际做了什么，我们在更新时都假设它会做&lt;strong&gt;最好的&lt;/strong&gt;那个动作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;算法伪代码&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;#x26; \bullet ; \text{Initialize } Q(s,a) \
&amp;#x26; \bullet ; \textbf{For } \text{episode } e = 1 \to E \textbf{ do}: \
&amp;#x26; \bullet \qquad \text{Initialize state } s \
&amp;#x26; \bullet \qquad \textbf{For } \text{step } t = 1 \to T \textbf{ do}: \
&amp;#x26; \bullet \qquad \qquad \text{Choose action } a \text{ from } s \text{ using } \epsilon\text{-greedy} \
&amp;#x26; \bullet \qquad \qquad \text{Take action } a, \text{ observe } r, s&apos; \
&amp;#x26; \bullet \qquad \qquad Q(s,a) \leftarrow Q(s,a) + \alpha [r + \gamma \max_{a&apos;} Q(s&apos;, a&apos;) - Q(s,a)] \
&amp;#x26; \bullet \qquad \qquad s \leftarrow s&apos; \
&amp;#x26; \bullet \qquad \textbf{End For} \
&amp;#x26; \bullet ; \textbf{End For}
\end{aligned}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;核心对比：在线策略 vs. 离线策略&lt;/h2&gt;
&lt;p&gt;这是强化学习中最重要的分类之一。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;行为策略 (Behavior Policy)&lt;/strong&gt;：智能体与环境交互、产生数据时使用的策略（通常包含随机性，如 $\epsilon$-greedy）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目标策略 (Target Policy)&lt;/strong&gt;：我们想要学习和优化的策略（通常是贪婪策略，即最优策略）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;| 特性         | 在线策略 (On-Policy)                                                                             | 离线策略 (Off-Policy)                                                |
| :----------- | :----------------------------------------------------------------------------------------------- | :------------------------------------------------------------------- |
| &lt;strong&gt;定义&lt;/strong&gt;     | 行为策略 &lt;strong&gt;==&lt;/strong&gt; 目标策略                                                                         | 行为策略 &lt;strong&gt;!=&lt;/strong&gt; 目标策略                                             |
| &lt;strong&gt;代表算法&lt;/strong&gt; | &lt;strong&gt;SARSA&lt;/strong&gt;                                                                                        | &lt;strong&gt;Q-Learning&lt;/strong&gt;                                                       |
| &lt;strong&gt;更新依据&lt;/strong&gt; | 使用&lt;strong&gt;实际执行&lt;/strong&gt;的下一个动作 $a_{t+1}$ 的价值                                                    | 使用&lt;strong&gt;理论上最优&lt;/strong&gt;的动作 $\max Q$ 的价值                             |
| &lt;strong&gt;优缺点&lt;/strong&gt;   | 比较胆小。因为它知道自己下一步可能会乱走（随机探索），所以它会避开悬崖边缘（哪怕悬崖边有宝藏）。 | 比较大胆。它假设自己下一步一定会走最优路径，所以会勇敢地贴着悬崖走。 |
| &lt;strong&gt;数据效率&lt;/strong&gt; | 数据必须现采现用，不能使用旧策略产生的数据（Replay Buffer）。                                    | 可以使用过去的数据，或者别人玩的数据（适合结合 Experience Replay）。 |&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉理解&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SARSA&lt;/strong&gt; 像是一个&lt;strong&gt;谨慎的学生&lt;/strong&gt;：他在学习时会考虑到自己考试时可能会因为紧张（$\epsilon$ 随机性）而犯错，所以他平时练习时就尽量选容错率高的解法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Q-Learning&lt;/strong&gt; 像是一个&lt;strong&gt;理想主义者&lt;/strong&gt;：他在学习时假设自己考试时绝对不会犯错（全选最优解 $\max Q$），所以他会学习那条理论上分数最高、但可能风险很大的路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="https://pic.hana0721.top/72072963_p0_master1200.mo0d1wsi.webp"/><enclosure url="https://pic.hana0721.top/72072963_p0_master1200.mo0d1wsi.webp"/></item><item><title>RL笔记（5）：蒙特卡洛</title><link>https://hana-blog.top/blog/rl-note-5</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-5</guid><description>从模型到经验：如何不依赖状态转移矩阵，仅通过‘玩游戏’来估计价值？详解蒙特卡洛预测与控制、增量更新及 GLIE 性质。</description><pubDate>Sun, 14 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在上一章 &lt;strong&gt;动态规划&lt;/strong&gt; 中，我们假设环境是已知的（即我们知道状态转移概率 $\mathcal{P}$ 和奖励函数 $\mathcal{R}$）。但在现实中，比如围棋或机器人在火星行走，我们往往无法预知“做了动作后具体会发生什么”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;蒙特卡洛方法 (Monte-Carlo methods, MC)&lt;/strong&gt; 标志着我们进入了 &lt;strong&gt;无模型 (Model-Free)&lt;/strong&gt; 强化学习的领域。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：不需要知道环境的动力学方程，只需要让智能体在环境中&lt;strong&gt;采样（玩游戏）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大数定律&lt;/strong&gt;：只要玩的次数够多，观测到的平均回报就会趋近于真实的期望回报。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：MC 方法仅适用于&lt;strong&gt;情节任务 (Episodic Tasks)&lt;/strong&gt;，即任务必须有终止状态（如一盘棋下完、游戏通关或挂掉）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;蒙特卡洛预测 (Monte-Carlo Prediction)&lt;/h2&gt;
&lt;p&gt;我们的第一个目标是：给定一个策略 $\pi$，估算它的状态价值函数 $V^\pi(s)$。&lt;/p&gt;
&lt;p&gt;回顾 $V$ 的定义：&lt;/p&gt;
&lt;p&gt;$$
V^\pi(s)=\mathbb{E}_\pi[G_t|S_t=s]
$$&lt;/p&gt;
&lt;p&gt;在没有模型的情况下，我们无法通过贝尔曼方程的 $\sum P(s&apos;|s)\dots$ 来计算期望。
MC 的做法是：直接用&lt;strong&gt;经验平均值&lt;/strong&gt;来代替期望。&lt;/p&gt;
&lt;h3&gt;算法流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;使用策略 $\pi$ 采样 $N$ 条完整的轨迹（Episode）：
$$s_0 \xrightarrow{a_0} r_0, s_1 \xrightarrow{a_1} r_1, \dots, s_{T-1} \xrightarrow{a_{T-1}} r_{T-1}, s_T$$&lt;/li&gt;
&lt;li&gt;计算每条轨迹中，状态 $s$ 出现后的累积回报 $G_t$。&lt;/li&gt;
&lt;li&gt;取平均值：
$$V(s) \approx \frac{1}{N(s)} \sum_{i=1}^{N(s)} G_t^{(i)}$$&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;首次访问 vs. 每次访问&lt;/h3&gt;
&lt;p&gt;在一条轨迹中，状态 $s$ 可能会出现多次。如何计算回报？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;首次访问 (First-visit MC)&lt;/strong&gt;：只计算状态 $s$ &lt;strong&gt;第一次&lt;/strong&gt;出现时的回报 $G_t$。这是无偏估计。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每次访问 (Every-visit MC)&lt;/strong&gt;：状态 $s$ 每次出现，都计算一次回报 $G_t$ 并纳入平均。这也是无偏估计（但收敛性证明略有不同）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;增量式更新 (Incremental Update)&lt;/h2&gt;
&lt;p&gt;在实际计算中，我们不需要要把几百万条轨迹的数据都存下来最后求平均，而是可以使用&lt;strong&gt;增量更新&lt;/strong&gt;的方法（类似于第二章多臂老虎机中的更新）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推导&lt;/strong&gt;：
假设 $V_{k-1}$ 是前 $k-1$ 次的平均值，$G_k$ 是第 $k$ 次观测到的回报：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V_k &amp;#x26;= \frac{1}{k} \sum_{i=1}^k G_i \notag \
&amp;#x26;= \frac{1}{k} (G_k + \sum_{i=1}^{k-1} G_i) \notag \
&amp;#x26;= \frac{1}{k} (G_k + (k-1)V_{k-1}) \notag \
&amp;#x26;= V_{k-1} + \frac{1}{k} (G_k - V_{k-1}) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;算法描述&lt;/strong&gt;：
对于每条采样出的轨迹，对其中的每个状态 $S_t$ 和回报 $G_t$：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;计数器加一：$N(S_t) \leftarrow N(S_t) + 1$&lt;/li&gt;
&lt;li&gt;更新价值：
$$
V(S_t) \leftarrow V(S_t) + \frac{1}{N(S_t)} \underbrace{(G_t - V(S_t))}_{\text{误差 Error}}
$$
或者是使用固定的步长 $\alpha$（适用于非平稳问题）：
$$
V(S_t) \leftarrow V(S_t) + \alpha (G_t - V(S_t))
$$&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;蒙特卡洛控制 (Monte-Carlo Control)&lt;/h2&gt;
&lt;p&gt;知道怎么评估价值还不够，我们的最终目标是找到&lt;strong&gt;最优策略&lt;/strong&gt; $\pi^*$。
这就是 &lt;strong&gt;广义策略迭代 (Generalized Policy Iteration, GPI)&lt;/strong&gt; 的思想：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;评估&lt;/strong&gt;：用 MC 方法估算当前策略的 $Q(s,a)$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提升&lt;/strong&gt;：根据估算的 $Q(s,a)$ 改进策略（通常是贪婪策略）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;为什么要用 $Q$ 而不是 $V$？&lt;/h3&gt;
&lt;p&gt;在 Model-Free 场景下，如果我们只知道 $V(s)$，却不知道状态转移 $P(s&apos;|s,a)$，我们是无法推断出哪个动作 $a$ 导致了更好的 $s&apos;$。
因此，必须显式地估算 &lt;strong&gt;动作价值函数 $Q(s,a)$&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;探索的必要性&lt;/h3&gt;
&lt;p&gt;如果我们在提升策略时使用完全贪婪策略（greedy）：$\pi(s) = \arg\max_a Q(s,a)$，智能体可能会因为过早收敛而错过最优解（没见过的动作永远不会去尝试）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：$\epsilon$-Greedy 策略&lt;/strong&gt;
保持持续的探索（Exploration）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以 $1-\epsilon$ 的概率选择当前 $Q$ 值最大的动作。&lt;/li&gt;
&lt;li&gt;以 $\epsilon$ 的概率随机选择动作。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;GLIE (Greedy in the Limit with Infinite Exploration)&lt;/strong&gt;：
如果我们让 $\epsilon$ 随着时间推移逐渐趋近于 0（例如 $\epsilon_k = 1/k$），那么 MC 控制算法最终会收敛到最优策略。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结与对比&lt;/h2&gt;
&lt;p&gt;蒙特卡洛方法是强化学习中第一种不需要了解环境模型的算法。&lt;/p&gt;
&lt;p&gt;| 维度          | 动态规划 (DP)                                        | 蒙特卡洛 (MC)                                          |
| :------------ | :--------------------------------------------------- | :----------------------------------------------------- |
| &lt;strong&gt;环境模型&lt;/strong&gt;  | 需要已知 $P, R$ (Model-Based)                        | 未知，仅需经验 (Model-Free)                            |
| &lt;strong&gt;更新方式&lt;/strong&gt;  | **自举 (Bootstrapping)**用后继状态的估计值更新当前值 | &lt;strong&gt;全采样&lt;/strong&gt;必须等到 Episode 结束拿到真实 $G_t$ 才能更新 |
| &lt;strong&gt;适用范围&lt;/strong&gt;  | 状态空间较小，已知规则                               | 情节性任务 (Episodic)，未知规则                        |
| &lt;strong&gt;偏差/方差&lt;/strong&gt; | 有偏差 (Bias)，低方差                                | 无偏差 (Unbiased)，高方差                              |&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 思考&lt;/strong&gt;：
MC 必须等到游戏结束才能更新，这在像自动驾驶这样没有明确“终点”或者流程极长的任务中非常低效。有没有一种方法，既不需要模型（像 MC），又不需要等到结束就能更新（像 DP）呢？
这就是下一章 &lt;strong&gt;时序差分 (Temporal Difference, TD)&lt;/strong&gt; 要解决的问题。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="https://pic.hana0721.top/71849616_p0.1sfmv9l9of.webp"/><enclosure url="https://pic.hana0721.top/71849616_p0.1sfmv9l9of.webp"/></item><item><title>RL笔记（4）：动态规划</title><link>https://hana-blog.top/blog/rl-note-4</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-4</guid><description>详解强化学习中的动态规划（DP）方法，涵盖策略迭代与价值迭代的算法原理、贝尔曼算子的收敛性证明（Banach不动点定理）及DP方法的局限性分析。</description><pubDate>Sat, 13 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;动态规划（DP）在强化学习中指的是在环境模型已知（即状态转移概率 $\mathcal{P}$ 和奖励函数 $\mathcal{R}$ 已知）的情况下，计算最优策略的一类算法。
DP 的核心思想是将复杂问题分解为子问题，通过求解子问题来求解原问题。在 MDP 中，这体现为利用贝尔曼方程进行迭代更新。&lt;/p&gt;
&lt;h2&gt;策略迭代 (Policy Iteration)&lt;/h2&gt;
&lt;p&gt;核心思想： “先以此为据，算个清楚；再择优而行。”
策略迭代将求解过程严格拆分为两个交替的步骤，直到策略不再改变。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;步骤一：策略评估 (Policy Evaluation)：在当前策略 $\pi$ 固定不变的情况下，计算出该策略下精确的状态价值函数 $V^\pi$。这意味着在这个步骤里，我们通常需要多次迭代（或者解线性方程组）直到 $V^\pi$ 完全收敛。&lt;/li&gt;
&lt;li&gt;步骤二：策略提升 (Policy Improvement)：基于刚刚算出来的精确 $V^\pi$，贪心地更新策略。即对于每个状态，选择那个能带来最大期望回报的动作 $\pi^\prime(s)=\arg\max_{a} Q^\pi(s,a)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;特点： 收敛所需的迭代步数少，但每一步内部的计算量大（因为策略评估需要很久）。&lt;/p&gt;
&lt;h3&gt;策略评估（Policy Evaluation）&lt;/h3&gt;
&lt;p&gt;策略评估的目标是求解给定策略 $\pi$ 的状态价值函数 $V^\pi$。
对于策略 $\pi$，状态价值函数满足贝尔曼期望方程：&lt;/p&gt;
&lt;p&gt;$$
V^\pi(s)=\sum_{a\in \mathcal{A}}\pi(a|s)(\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in \mathcal{S}}\mathcal{P}(s^\prime|s,a)V^\pi(s^\prime))
$$&lt;/p&gt;
&lt;p&gt;这构成了一个线性方程组，但在状态空间较大时直接求解困难，因此采用迭代法。&lt;/p&gt;
&lt;h4&gt;贝尔曼迭代（Bellman Iteration）&lt;/h4&gt;
&lt;p&gt;将当前时刻估计的价值函数 $V_k$ 代入贝尔曼方程右侧，计算下一轮的估计 $V_{k+1}$ ：&lt;/p&gt;
&lt;p&gt;$$
V_{k+1}(s)\leftarrow\sum_{a\in\mathcal{A}}\pi(a|s)(\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)V_{k}(s^\prime))
$$&lt;/p&gt;
&lt;p&gt;随机选定初始值 $V_0$ ，当 $k\rightarrow\infin$ 时，序列 ${ V_k }$ 会收敛到 $V^\pi$ 。
为了方便证明收敛性，定义贝尔曼算子为 $\mathcal{T}^\pi$：&lt;/p&gt;
&lt;p&gt;$$
\mathcal{T}^\pi V_k = \sum_{a\in\mathcal{A}}\pi(a|s)(\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)V_{k}(s^\prime))
$$&lt;/p&gt;
&lt;h4&gt;收敛性证明（Banach不动点定理）&lt;/h4&gt;
&lt;p&gt;为了证明序列 ${ V_k }$ 收敛于 $V^\pi$，我们将迭代过程看作算子 $\mathcal{T}^\pi$ 的应用：&lt;/p&gt;
&lt;p&gt;$$
V_{k+1}=\mathcal{T}^\pi V_k
$$&lt;/p&gt;
&lt;p&gt;证明步骤如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;定义范数：采用无穷范数 $\parallel V \parallel_\infty=\max_{s\in \mathcal{S}} | V(s)|$；&lt;/li&gt;
&lt;li&gt;压缩映射性质：对于任意两个价值函数 $U, V$，有：
$$
\begin{align}
\parallel \mathcal{T}^\pi U - \mathcal{T}^\pi V \parallel_\infty
&amp;#x26;= \max_{s} | \gamma \sum_{a}\pi(a|s)\sum_{s^\prime}\mathcal{P}(s^\prime|s,a)(U(s^\prime)-V(s^\prime)) | \notag \
&amp;#x26;\leqslant \gamma \max_{s} \sum_{a}\pi(a|s)\sum_{s^\prime}\mathcal{P}(s^\prime|s,a) |U(s^\prime)-V(s^\prime)| \notag \
&amp;#x26;\leqslant \gamma \max_{s} \sum_{a}\pi(a|s)\sum_{s^\prime}\mathcal{P}(s^\prime|s,a) \parallel U - V \parallel_\infty \notag \
&amp;#x26;= \gamma \parallel U - V \parallel_\infty \notag
\end{align}
$$&lt;/li&gt;
&lt;li&gt;结论：只要折扣因子 satisfying $0 \le \gamma &amp;#x3C; 1$，算子 $\mathcal{T}^\pi$ 就是一个$\gamma$-压缩映射（Contraction Mapping）。根据 Banach 不动点定理，序列 ${V_k}$ 必收敛于唯一的固定点 $V^\pi$。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;策略提升（Policy Improvement）&lt;/h3&gt;
&lt;p&gt;在计算出 $V^\pi$ 后，我们需要判断当前策略是否最优。如果不是，如何改进？&lt;/p&gt;
&lt;h4&gt;策略提升理论（Policy Improvement Theorem）&lt;/h4&gt;
&lt;p&gt;给定策略 $\pi$ 和其价值函数 $V^\pi$，构造新策略 $\pi^\prime$ 使得其在每个状态下都贪心地选择动作价值最大的动作：&lt;/p&gt;
&lt;p&gt;$$
\pi^\prime(s) = \argmax_{a\in\mathcal{A}} Q^\pi(s,a) = \argmax_{a\in\mathcal{A}} \left( r(s,a)+\gamma\sum_{s^\prime \in\mathcal{S}}\mathcal{P}(s^\prime|s,a)V^\pi(s^\prime) \right)
$$&lt;/p&gt;
&lt;p&gt;定理保证：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;$V^{\pi^\prime}(s) \geqslant V^\pi(s), \forall s \in \mathcal{S}$（策略单调递增）。&lt;/li&gt;
&lt;li&gt;如果 $V^{\pi^\prime}(s) = V^\pi(s)$，则 $\pi$ 已经是与最优策略 $V^*$ 等价的策略。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;理论证明（Theorem Proof）&lt;/h4&gt;
&lt;p&gt;证明：对于任何状态 $s$，满足$V^{\pi^\prime}(s)\geqslant V^\pi(s)$。
证明思路：利用 $V^\pi(s) \leqslant \max_a Q^\pi(s,a) = Q^\pi(s, \pi^\prime(s))$，反复展开 $Q^\pi$ 即可证。&lt;/p&gt;
&lt;p&gt;状态价值函数和动作价值函数之间的关系：&lt;/p&gt;
&lt;p&gt;$$
V^\pi(s)=\sum_{a\in\mathcal{A}}\pi(a|s)Q^\pi(s,a)
$$&lt;/p&gt;
&lt;p&gt;经过推导，可以得出：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V^\pi(s)&amp;#x26;=\sum_{a\in\mathcal{A}}\pi(a|s)Q^\pi(s,a) \notag \
&amp;#x26;\leqslant \max_{a\in\mathcal{A}} Q^\pi(s,a) \notag \
&amp;#x26;=\max_{a\in\mathcal{A}} \left( r(s,a) + \gamma \sum_{s^\prime\in\mathcal{S}} \mathcal{P}(s^\prime|s,a) V^\pi(s^\prime)\right) \notag \
&amp;#x26;= Q^\pi(s,\pi^\prime(s)) \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;有了 $V^\pi(s)\leqslant Q^\pi(s,\pi^\prime(s))$，可以进一步推导出：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V^\pi(s) &amp;#x26;\leqslant Q^\pi(s,\pi^\prime(s)) \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;{\pi^\prime}[R&lt;/em&gt;{t}+\gamma V^\pi(S_{t+1})|S_t=s] \notag \
&amp;#x26;\leqslant \mathbb{E}&lt;em&gt;{\pi^\prime}[R_t+\gamma Q^\pi(S&lt;/em&gt;{t+1},\pi^\prime(S_{t+1}))|S_t=s]\notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;{\pi^\prime}[R_t+\gamma R&lt;/em&gt;{t+1}+\gamma^2V^\pi(S_{t+2})|S_t=s] \notag \
&amp;#x26;\leqslant \mathbb{E}&lt;em&gt;{\pi^\prime}[R_t+\gamma R&lt;/em&gt;{t+1}+\gamma^2 R_{t+2} + \gamma^3 V^\pi(S_{t+3})|S_t=s]  \notag \
&amp;#x26;\cdots \notag \
&amp;#x26;\leqslant \mathbb{E}&lt;em&gt;{\pi^\prime}[R_t+\gamma R&lt;/em&gt;{t+1}+\gamma^2 R_{t+2} + \gamma^3 R_{t+3} + \cdots|S_t=s]  \notag \
&amp;#x26;= V^{\pi^\prime}(s) \notag
\end{align}
$$&lt;/p&gt;
&lt;h3&gt;迭代算法（Iteration Algorithm）&lt;/h3&gt;
&lt;p&gt;策略迭代交替进行“策略评估”和“策略提升”，直到策略不再变化。&lt;/p&gt;
&lt;p&gt;流程：&lt;/p&gt;
&lt;p&gt;$$
\pi_0 \xrightarrow{E} V^{\pi_0} \xrightarrow{I} \pi_1 \xrightarrow{E} V^{\pi_1} \xrightarrow{I} \dots \xrightarrow{I} \pi_* \xrightarrow{E} V^*
$$&lt;/p&gt;
&lt;p&gt;算法伪代码：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初始化：$V(s) \in \mathbb{R}$，$\pi(s) \in \mathcal{A}$ 任意。&lt;/li&gt;
&lt;li&gt;策略评估（循环直到收敛）：
&lt;ul&gt;
&lt;li&gt;$\Delta \leftarrow 0$&lt;/li&gt;
&lt;li&gt;For each $s \in \mathcal{S}$:
&lt;ul&gt;
&lt;li&gt;$v \leftarrow V(s)$&lt;/li&gt;
&lt;li&gt;$V(s) \leftarrow \sum_{s^\prime} \mathcal{P}(s^\prime|s,\pi(s))[\mathcal{R}(s,\pi(s)) + \gamma V(s^\prime)]$&lt;/li&gt;
&lt;li&gt;$\Delta \leftarrow \max(\Delta, |v - V(s)|)$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;若 $\Delta &amp;#x3C; \theta$，停止评估。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;策略提升：
&lt;ul&gt;
&lt;li&gt;$policy_stable \leftarrow true$&lt;/li&gt;
&lt;li&gt;For each $s \in \mathcal{S}$:
&lt;ul&gt;
&lt;li&gt;$old_action \leftarrow \pi(s)$&lt;/li&gt;
&lt;li&gt;$\pi(s) \leftarrow \argmax_a \sum_{s^\prime} \mathcal{P}(s^\prime|s,a)[\mathcal{R}(s,a) + \gamma V(s^\prime)]$&lt;/li&gt;
&lt;li&gt;If $old_action \neq \pi(s)$, then $policy_stable \leftarrow false$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If $policy_stable$ is true，停止并返回 $V^&lt;em&gt;, \pi^&lt;/em&gt;$；否则跳转至步骤 2。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：由于有限状态 MDP 的策略总数是有限的（$|\mathcal{A}|^{|\mathcal{S}|}$），且每次提升策略都严格（或非严格）变好，策略迭代保证在有限步内收敛。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;价值迭代（Value Iteration）&lt;/h2&gt;
&lt;p&gt;策略迭代的缺点是每次“策略评估”都需要迭代很多次直到完全收敛。
事实上，我们不需要等到 $V^\pi$ 完全收敛才进行策略提升。
如果将策略评估的迭代次数缩减为 1次，并结合策略提升，就得到了价值迭代。&lt;/p&gt;
&lt;h3&gt;贝尔曼最优迭代（Bellman Optimal Iteration）&lt;/h3&gt;
&lt;p&gt;价值迭代直接迭代求解最优价值函数，基于贝尔曼最优方程进行迭代：&lt;/p&gt;
&lt;p&gt;$$
V_{k+1}(s) \leftarrow \max_{a\in\mathcal{A}} \left{ \mathcal{R}(s,a) + \gamma \sum_{s^\prime \in \mathcal{S}} \mathcal{P}(s^\prime|s,a) V_k(s^\prime) \right}
$$&lt;/p&gt;
&lt;p&gt;随机选定初始值 $V_0$ ，当 $k\rightarrow\infin$ 时，序列 ${ V_k }$ 会收敛到 $V^&lt;em&gt;$ 。为了方便证明收敛性，定义贝尔曼最优算子为 $\mathcal{T}^&lt;/em&gt;$：&lt;/p&gt;
&lt;p&gt;$$
\mathcal{T}^* V_{k} = \max_{a\in\mathcal{A}} \left{ \mathcal{R}(s,a) + \gamma \sum_{s^\prime \in \mathcal{S}} \mathcal{P}(s^\prime|s,a) V_k(s^\prime) \right}
$$&lt;/p&gt;
&lt;h3&gt;收敛性证明（Banach不动点定理）&lt;/h3&gt;
&lt;p&gt;为了证明序列 ${V_k}$ 收敛于 $V^&lt;em&gt;$ ，将迭代过程看作算子 $\mathcal{T}^&lt;/em&gt;$ 的应用：&lt;/p&gt;
&lt;p&gt;$$
V_{k+1} = \mathcal{T}^* V_k
$$&lt;/p&gt;
&lt;p&gt;证明步骤如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;定义范数：采用无穷范数 $||V||&lt;em&gt;{\infty}=\max&lt;/em&gt;{s\in\mathcal{S}}|V(s)|$；&lt;/li&gt;
&lt;li&gt;压缩映射性质：对于 $\forall s\in\mathcal{S}$ 以及任意两个价值函数 $U,V$，有：
$$
\begin{align}
|\mathcal{T}^* U(s) - \mathcal{T}^* V(s)|
&amp;#x26;=\left|\max_{a\in\mathcal{A}}\left{\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)U(s^\prime)\right}-\max_{a\in\mathcal{A}}\left{\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)U(s^\prime)\right}\right| \notag \
&amp;#x26;\leqslant \max_{a\in\mathcal{A}}\left|\gamma\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)(U(s^\prime)-V(s^\prime))\right| \notag \
&amp;#x26;= \gamma\max_{a\in\mathcal{A}}\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)\left|U(s)-V(s)\right| \notag \
&amp;#x26;\leqslant \gamma\max_{a\in\mathcal{A}}\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)||U-V||&lt;em&gt;{\infty} \notag \
&amp;#x26;= \gamma ||U-V||&lt;/em&gt;{\infty} \notag
\end{align}
$$&lt;/li&gt;
&lt;li&gt;对左式取最大值： $||\mathcal{T}^*U-\mathcal{T}^*V||&lt;em&gt;{\infty}\leqslant\gamma ||U-V||&lt;/em&gt;{\infty}$&lt;/li&gt;
&lt;li&gt;结论：只要折扣因子 satisfying $0 \le \gamma &amp;#x3C; 1$，算子 $\mathcal{T}^&lt;em&gt;$ 就是一个$\gamma$-压缩映射（Contraction Mapping）。根据 Banach 不动点定理，序列 ${V_k}$ 必收敛于唯一的固定点 $V^&lt;/em&gt;$。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;迭代算法（Iteration Algorithm）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;初始化&lt;/strong&gt;：$V(s)$ 任意。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迭代更新&lt;/strong&gt;（Loop until $\Delta &amp;#x3C; \theta$）：
&lt;ul&gt;
&lt;li&gt;$\Delta \leftarrow 0$&lt;/li&gt;
&lt;li&gt;For each $s \in \mathcal{S}$:
&lt;ul&gt;
&lt;li&gt;$v \leftarrow V(s)$&lt;/li&gt;
&lt;li&gt;$V(s) \leftarrow \max_{a} \sum_{s^\prime\in\mathcal{S}} \mathcal{P}(s^\prime|s,a)[\mathcal{R}(s,a) + \gamma V(s^\prime)]$&lt;/li&gt;
&lt;li&gt;$\Delta \leftarrow \max(\Delta, |v - V(s)|)$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输出策略&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;$\pi^*(s) = \argmax_{a} \sum_{s^\prime\in\mathcal{S}} \mathcal{P}(s^\prime|s,a)[\mathcal{R}(s,a) + \gamma V(s^\prime)]$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;局限性（Limitation）&lt;/h2&gt;
&lt;p&gt;动态规划方法（如策略迭代和价值迭代）是强化学习的理论基石，其核心优势在于理论完备性，利用自举（Bootstrapping）机制能够保证在有限步内稳定收敛至全局最优策略。
然而，其在实际应用中存在两大核心局限：一是强依赖完备的环境模型（Model-based），即必须预先知晓精确的状态转移概率和奖励函数，这在现实场景中往往难以满足；
二是受困于维数灾难（Curse of Dimensionality），随着状态变量增加，状态空间呈指数级膨胀，导致遍历所有状态进行全宽更新（Full-width Backup）在计算资源和存储空间上变得不可行，因此无法直接应用于大规模或连续状态空间的复杂问题。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/70217040_p0_master1200.77e5dp0f1s.webp"/><enclosure url="https://pic.hana0721.top/70217040_p0_master1200.77e5dp0f1s.webp"/></item><item><title>RL笔记（3）：马尔可夫决策过程</title><link>https://hana-blog.top/blog/rl-note-3</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-3</guid><description>梳理从马尔可夫过程(MP)、奖励过程(MRP)到决策过程(MDP)的演变，详解价值函数、贝尔曼方程推导、占用度量及最优策略定义。</description><pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在上一章《多臂老虎机》中，我们解决了一个简化版的强化学习问题：在一个&lt;strong&gt;只有一个不变状态&lt;/strong&gt;的环境中，如何平衡探索与利用以获得最大收益。&lt;/p&gt;
&lt;p&gt;然而，现实世界要复杂得多。
下棋时，你走出的一步棋不仅决定了当下的局势，更改变了棋盘的格局，影响了你后续所有的选择。你的每一个动作（Action）不仅会带来即时的奖励（Reward），还会通过**状态转移（State Transition）**改变环境的状态（State）。&lt;/p&gt;
&lt;p&gt;这就是**序列决策（Sequential Decision Making）**的核心难题：今天的选择，决定明天的处境。&lt;/p&gt;
&lt;p&gt;为了描述这种“牵一发而动全身”的动态过程，我们需要引入强化学习的数学基石——&lt;strong&gt;马尔可夫决策过程（MDP）&lt;/strong&gt;。本章将从最基础的马尔可夫过程出发，层层递进，最终推导出描述价值流转的&lt;strong&gt;贝尔曼方程（Bellman Equation）&lt;/strong&gt;。这是理解后续所有 RL 算法（如 DQN, PPO, SAC）的绝对前提。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;马尔可夫过程（Markov Process）&lt;/h2&gt;
&lt;h3&gt;随机过程（Stochastic Process）&lt;/h3&gt;
&lt;p&gt;随机过程是研究随时间演变的随机现象。
在随机过程中，随机现象在$$t$$时刻的取值为$S_t$，并且$S_t$通常会取决于之前的状态 $(S_1,...,S_{t-1})$。
在已知 $(S_1,...,S_{t-1})$的情况下，下一个时刻 $t$的状态为 $S_t$的概率可以表示为$P(S_t | S_1,...,S_{t-1})$。&lt;/p&gt;
&lt;h3&gt;马尔可夫性质（Markov Property）&lt;/h3&gt;
&lt;p&gt;当且仅当某个时刻的状态只取决于上个时刻的状态时，这个随机过程满足马尔可夫性质，即$P(S_t | S_{t-1}) = P(S_t | S_1, ..., S_{t-1})$。&lt;/p&gt;
&lt;h3&gt;马尔可夫过程（Markov Process）&lt;/h3&gt;
&lt;p&gt;马尔可夫过程就是满足了马尔可夫性质的随机过程，也叫马尔可夫链（Markov Chain）。
通常使用一个二元组 $&amp;#x3C;\mathcal{S}, \mathcal{P}&gt;$ 来描述马尔可夫过程，其中 $\mathcal{S}$是有限数量的状态集合， $\mathcal{P}$是状态转移矩阵（State Transition Matrix）。
假设 $|\mathcal{S}|=n$，那么有：&lt;/p&gt;
&lt;p&gt;$$
\mathcal{P}=\begin{bmatrix} P(s_1|s_1) &amp;#x26; \cdots &amp;#x26; P(s_n|s_1)
\cr \vdots &amp;#x26; \ddots &amp;#x26; \vdots \cr P(s_n|s_1) &amp;#x26; \cdots &amp;#x26; P(s_n|s_n) \end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;且对于任意 $i$行，满足 $\sum_{j=1}^{n} P(s_j|s_i)=1$。
特别的，当一个状态 $s_t$ 满足 $P(s_t|s_t)=1$，那么称 $s_t$为终止状态（Terminal State）。
给定马尔可夫过程，从某个状态 $S_1$ 出发，根据状态转移矩阵 $\mathcal{P}$ 得出一段状态序列 $S_1 S_2...S_T$（Episode），这个过程也叫做采样（Sampling）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;马尔可夫奖励过程（Markov Reward Process）&lt;/h2&gt;
&lt;p&gt;马尔可夫奖励过程基于马尔可夫过程加入了奖励函数 $\mathcal{R}$（Reward Function）与折扣因子 $$\gamma$$（Discount Factor），通常由四元组 $&amp;#x3C;\mathcal{S},\mathcal{P},\mathcal{R},\gamma&gt;$ 构成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\mathcal{S}$ 是有限状态集；&lt;/li&gt;
&lt;li&gt;$\mathcal{P}$ 是状态转移矩阵；&lt;/li&gt;
&lt;li&gt;$\mathcal{R}$ 是奖励函数，$\mathcal{R}(s)$ 表示达到状态 $s$ 可以获得的奖励；&lt;/li&gt;
&lt;li&gt;$\gamma$ 是折扣因子，取值为 $[0,1)$。 $\gamma$ 的越小，对于未来的利益折扣越大；&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;回报（Return）&lt;/h3&gt;
&lt;p&gt;在一个马尔可夫奖励过程中，在某个时刻 $t$，其回报 $G_t$ 的定义如下：&lt;/p&gt;
&lt;p&gt;$$
G_t=R_t+\gamma R_{t+1}+\gamma^2 R_{t+2}+...=\sum_{k=0}^\infin \gamma^k R_{t+k}
$$&lt;/p&gt;
&lt;p&gt;其中， $R_t$ 表示 $t$ 时刻获得的奖励。&lt;/p&gt;
&lt;h3&gt;价值函数（Value Function）&lt;/h3&gt;
&lt;p&gt;某个状态 $s$的期望回报 $\mathbb{E}[G_t|S_t=s]$，被称为这个状态的价值（Value）。那么所有状态的价值组成了价值函数（Value Function），展开如下：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V(s)&amp;#x26;=\mathbb{E}[G_t|S_t=s] \notag \
&amp;#x26;=\mathbb{E}[R_t+\gamma R_{t+1}+\gamma^2 R_{t+2}+...|S_t=s] \notag \
&amp;#x26;=\mathbb{E}[R_t+\gamma(R_{t+1}+\gamma R_{t+2}+...)|S_t=s] \notag \
&amp;#x26;=\mathbb{E}[R_t+\gamma G_{t+1}|S_t=s] \notag \
&amp;#x26;= \mathbb{E}[R_t+\gamma V(S_{t+1})|S_t=s] \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;经过几步简单的推导，可以得出贝尔曼方程（Bellman Equation）：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V(s)&amp;#x26;= \mathbb{E}[R_t+\gamma V(S_{t+1})|S_t=s] \notag \
&amp;#x26;= \mathbb{E}[R_t|S_t=s]+\gamma\mathbb{E}[V(S_{t+1})|S_t=s] \notag \
&amp;#x26;= \mathcal{R}(s)+\gamma\sum_{s^\prime \in \mathcal{S}} P(s^\prime|s) V(s^\prime) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;可以写成矩阵形式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\mathcal{V}&amp;#x26;=\mathcal{R}+\gamma\mathcal{P}\mathcal{V} \notag \
(I-\gamma\mathcal{P})\mathcal{V}&amp;#x26;=\mathcal{R} \notag \
\mathcal{V}&amp;#x26;=(I-\gamma\mathcal{P})^{-1}\mathcal{R} \notag
\end{align}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;马尔可夫决策过程（Markov Decision Process）&lt;/h2&gt;
&lt;p&gt;在马尔可夫奖励过程的基础上，加入外界的刺激，即智能体（Agent）的动作（Action），就有了马尔可夫决策过程（Markov Decision Process）。
马尔可夫决策由五元组 $&amp;#x3C;\mathcal{S},\mathcal{A},\mathcal{P},\mathcal{R},\gamma&gt;$构成，分别是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\mathcal{S}$ 是有限状态集；&lt;/li&gt;
&lt;li&gt;$\mathcal{A}$ 是动作的集合；&lt;/li&gt;
&lt;li&gt;$\gamma$ 是折扣因子；&lt;/li&gt;
&lt;li&gt;$\mathcal{R}(s,a)$ 是奖励函数；&lt;/li&gt;
&lt;li&gt;$\mathcal{P}(s^\prime|s,a)$ 是状态转移函数；&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;策略（Policy）&lt;/h3&gt;
&lt;p&gt;策略的定义：智能体根据当前的状态 $S_t$，并从动作集合 $\mathcal{A}$中选取一个 $A_t$ 的函数，一般记为 $\pi$。
$\pi(a|s)=P(A_t=a|S_t=s)$ 表示状态 $s$ 下，选择动作 $a$ 的概率。&lt;/p&gt;
&lt;h4&gt;确定性策略（Deterministic Policy）&lt;/h4&gt;
&lt;p&gt;如果一个策略 $\pi$ ，对于每一个状态 $s\in\mathcal{S}$ ，有且仅有唯一动作 $a\in\mathcal{A}$ ，使得 $\pi(a|s)=1$ ，那么这个策略就是一个确定性策略。&lt;/p&gt;
&lt;h4&gt;随机性策略（Stochastic Policy）&lt;/h4&gt;
&lt;p&gt;随机性策略和确定性策略相反，它输出的各个动作的概率分布（Probability Distribution），每次采取动作时会从分布中进行采样。&lt;/p&gt;
&lt;h3&gt;状态价值函数（State-Value Function）&lt;/h3&gt;
&lt;p&gt;基于策略 $\pi$ 的状态价值函数被记为 $V^\pi(s)=\mathbb{E}_\pi[G_t|S_t=s]$ 。
注意：若 $\pi \ne \pi^\prime$ ，则 $V^\pi \ne V^{\pi^\prime}$ 。&lt;/p&gt;
&lt;h3&gt;动作价值函数（Action-Value Function）&lt;/h3&gt;
&lt;p&gt;马尔可夫决策过程引入了动作，我们额外定义了一个动作价值函数（Action-Value Function）。
基于策略 $\pi$ 的动作价值函数被记为 $Q^\pi(s,a)$ ，表达式为 $Q^\pi(s,a)=\mathbb{E}_{\pi}[G_t|S_t=s,A_t=a]$ 。
经过简单的推导，可以得出如下公式：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q^\pi(s,a)&amp;#x26;=\mathbb{E}&lt;em&gt;\pi[G_t|S_t=s,A_t=a] \notag \
&amp;#x26;=\mathbb{E}&lt;/em&gt;\pi[R_t+\gamma R_{t+1}+\gamma^2 R_{t+2}+...|S_t=s,A_t=a] \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;\pi[R_t+\gamma(R&lt;/em&gt;{t+1}+\gamma R_{t+2}+...)|S_t=s,A_t=a] \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;\pi[R_t+\gamma V(S&lt;/em&gt;{t+1})|S_t=s,A_t=a] \notag \
&amp;#x26;= \mathbb{E}&lt;em&gt;\pi[R_t]+\gamma \mathbb{E}&lt;/em&gt;\pi[V^\pi(S_{t+1})|S_t=s,A_t=a] \notag \
&amp;#x26;= \mathcal{R}(s,a)+\gamma \sum_{s^\prime \in \mathcal{S}}\mathcal{P}(s^\prime|s,a)V^\pi(s^\prime) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;状态价值函数和动作价值函数之间的关系：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V^\pi(s)&amp;#x26;= \mathbb{E}&lt;em&gt;{a\sim \pi( \cdot | s)}[Q^\pi(s,a)] \notag \
&amp;#x26;=\sum&lt;/em&gt;{a\in \mathcal{A}}\pi(a|s)Q^\pi(s,a) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;注意：若 $\pi \ne \pi^\prime$ ，则 $Q^\pi \ne Q^{\pi^\prime}$ 。&lt;/p&gt;
&lt;h3&gt;贝尔曼期望方程（Bellman Expectation Equation）&lt;/h3&gt;
&lt;p&gt;对状态价值函数进行边缘化（Marginalization），可以得出 $V^\pi(s)$ 的贝尔曼期望方程：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
V^\pi(s)&amp;#x26;=\mathbb{E}&lt;em&gt;\pi[R_t+\gamma V^\pi(S&lt;/em&gt;{t+1})|S_t=s] \notag \
&amp;#x26;=\mathcal{R}(s)+\gamma\sum_{s^\prime\in \mathcal{S}}\mathcal{P}(s^\prime|s)V^\pi(s^\prime) \notag \
&amp;#x26;=\sum_{a\in \mathcal{A}}\pi(a|s)(\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in \mathcal{S}}\mathcal{P}(s^\prime|s,a)V^\pi(s^\prime)) \notag
\end{align}
$$&lt;/p&gt;
&lt;p&gt;将 $V^\pi(s)=\sum_{a\in\mathcal{A}}\pi(a|s)Q^\pi(s,a)$ 代入，可以得出 $Q^\pi(s,a)$ 的贝尔曼期望方程：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
Q^\pi(s,a)&amp;#x26;=\mathbb{E}&lt;em&gt;{\pi}[G_t|S_t=s,A_t=a] \notag \
&amp;#x26;=\mathcal{R}(s,a) + \gamma \sum&lt;/em&gt;{s^\prime\in \mathcal{S}}\mathcal{P}(s^\prime|s,a)V^\pi(s^\prime) \notag \
&amp;#x26;= \mathcal{R}(s,a)+\gamma\sum_{s\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)\sum_{a^\prime\in\mathcal{A}}\pi(a^\prime|s^\prime)Q^\pi(s^\prime,a^\prime) \notag
\end{align}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;状态访问分布（State Visitation Distribution）&lt;/h2&gt;
&lt;p&gt;在同一个马尔可夫过程中，不同的策略，它们的价值函数是不一样的。
因为在不同的策略，智能体能够访问的状态的概率分布是不同的。
我们使用 $P_t^\pi(s)$ 来表示智能体使用策略 $\pi$ 在 $t$ 时刻访问状态 $s$ 的概率。
策略 $\pi$ 的状态访问分布（State Visitation Distribution）定义如下：&lt;/p&gt;
&lt;p&gt;$$
\nu^\pi(s)=(1-\gamma)\sum_{t=0}^{\infin}\gamma^t P_t^\pi(s)
$$&lt;/p&gt;
&lt;p&gt;$1-\gamma$ 是归一化因子，它保证 $\nu^\pi(s)$ 是一个有效的概率分布，即 $\sum_s \nu^\pi(s)=1$，推导如下：&lt;/p&gt;
&lt;p&gt;$$
\begin{align}
\sum_s \nu^\pi(s)&amp;#x26;=\sum_s (1-\gamma) \sum_{t=0}^\infin \gamma^t P_t^\pi(s) \notag \
&amp;#x26;= (1-\gamma)\sum_{t=0}^\infin \gamma^t \sum_s P_t^\pi(s) \notag \
\end{align}
$$&lt;/p&gt;
&lt;p&gt;在任意时刻 $t$，满足 $\sum_s P_t^\pi(s)=1$，可以推导出：&lt;/p&gt;
&lt;p&gt;$$
\sum_s \nu^\pi(s)=(1-\gamma) \sum_{t=0}^\infin \gamma^t
$$&lt;/p&gt;
&lt;p&gt;这里有个几何级数，满足 $\sum_{t=0}^\infin\gamma^t=\frac{1}{1-\gamma}$，可以推导出：&lt;/p&gt;
&lt;p&gt;$$
\sum_s \nu^\pi(s)=(1-\gamma) \frac{1}{1-\gamma}=1
$$&lt;/p&gt;
&lt;p&gt;特别地，定义初始状态分布为 $\nu_0(s)$，且 $P_0^\pi(s)=\nu_0(s)$。&lt;/p&gt;
&lt;p&gt;使用上述公式计算该分布涉及到了无穷步的交互，但是实际上的交互数量是有限的。同时，状态访问分布满足以下性质：&lt;/p&gt;
&lt;p&gt;$$
\nu^\pi(s^\prime)=(1-\gamma)\nu_0(s^\prime)+\gamma  \int \mathcal{P}(s^\prime|s,a)\pi(a|s)\nu^\pi(s) {\rm d} s {\rm d} a
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;占用度量（Occupancy Measure）&lt;/h2&gt;
&lt;p&gt;进一步，为了表示动作状态 $(s,a)$ 访问的分布，定义策略 $\pi$ 的占用度量（Occupancy Measure）如下：&lt;/p&gt;
&lt;p&gt;$$
\rho^\pi(s,a)=(1-\gamma)\sum_{t=0}^\infin \gamma^t P_t^\pi(s)\pi(a|s)
$$&lt;/p&gt;
&lt;p&gt;两者存在以下关系：&lt;/p&gt;
&lt;p&gt;$$
\rho^\pi(s,a)=\nu^\pi(s)\pi(a|s)
$$&lt;/p&gt;
&lt;p&gt;最终，可以推导出两个定理：&lt;/p&gt;
&lt;p&gt;定理1：给定环境（MDP）的条件下，策略 $\pi_1$和策略 $\pi_2$ 得出的占用度量 $\rho^{\pi_1}$ 和 $\rho^{\pi_2}$ 满足：&lt;/p&gt;
&lt;p&gt;$$
\pi_1 = \pi_2 \Longleftrightarrow \rho^{\pi_1} = \rho^{\pi_2}
$$&lt;/p&gt;
&lt;p&gt;定理2：给定一合法的占用度量 $\rho$，生成该占用度量的唯一策略是：&lt;/p&gt;
&lt;p&gt;$$
\pi_\rho(a|s)=\frac{\rho(s,a)}{\sum_{a^\prime}\rho(s,a^\prime)}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;最优策略（Optimal Policy）&lt;/h2&gt;
&lt;p&gt;强化学习的目标：找到一个策略，使得智能体从初始状态出发获取最大的累计奖励。
首先定义策略之间的偏序关系：当且仅当对于任何状态 $s$ 都满足 $V^\pi(s) \ge V^{\pi^\prime}(s)$，则 $\pi &gt; \pi^\prime$。
最优策略（Optimal Policy）：在一个MDP中，至少存在一个策略优于其他策略或者至少存在一个策略不差于其他策略，这个策略就是最优策略。&lt;/p&gt;
&lt;p&gt;最优策略都有相同的状态价值函数，被称为最优状态价值函数：&lt;/p&gt;
&lt;p&gt;$$
V^*(s)=\max_\pi V^\pi(s),; \forall s \in \mathcal{S}
$$&lt;/p&gt;
&lt;p&gt;同理，定义最优动作价值函数：&lt;/p&gt;
&lt;p&gt;$$
Q^*(s,a)=\max_\pi Q^\pi(s,a), ; \forall s\in\mathcal{S},a\in\mathcal{A}
$$&lt;/p&gt;
&lt;p&gt;最优状态价值函数和最优动作价值函数之间的关系：&lt;/p&gt;
&lt;p&gt;$$
Q^&lt;em&gt;(s,a)=\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in\mathcal{S}} \mathcal{P}(s^\prime|s,a)V^&lt;/em&gt;(s^\prime)
$$&lt;/p&gt;
&lt;p&gt;$$
V^&lt;em&gt;(s)=\max_{a\in\mathcal{A}}Q^&lt;/em&gt;(s,a)
$$&lt;/p&gt;
&lt;p&gt;贝尔曼最优方程（Bellman optimality equation）：&lt;/p&gt;
&lt;p&gt;$$
V^&lt;em&gt;(s)=\max_{a\in\mathcal{A}} { \mathcal{R}(s,a)+\gamma\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a)V^&lt;/em&gt;(s^\prime)}
$$&lt;/p&gt;
&lt;p&gt;$$
Q^&lt;em&gt;(s,a)=\mathcal{R}(s,a)+\gamma\sum_{s^\prime\in\mathcal{S}}\mathcal{P}(s^\prime|s,a) \max_{a^\prime\in\mathcal{A}} Q^&lt;/em&gt;(s^\prime,a^\prime)
$$&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/66952181_p0_master1200.6ikvtocw16.webp"/><enclosure url="https://pic.hana0721.top/66952181_p0_master1200.6ikvtocw16.webp"/></item><item><title>RL笔记（2）：多臂老虎机</title><link>https://hana-blog.top/blog/rl-note-2</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-2</guid><description>从赌场到推荐系统：详解多臂老虎机问题。涵盖累积懊悔、Epsilon-Greedy、UCB 以及基于贝叶斯的汤普森采样。</description><pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;在上一章中，我们提到了 RL 的核心矛盾：&lt;strong&gt;探索与利用 (Exploration vs. Exploitation)&lt;/strong&gt;。
为了研究这个问题，我们先暂时把“状态转移”拿掉，看一个最简化的场景——&lt;strong&gt;多臂老虎机 (Multi-Armed Bandit, MAB)&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个问题来源于赌场：
假设你面前有 $K$ 台老虎机。每台老虎机吐钱的概率分布不一样（有的容易赢，有的容易输），但你事先不知道。你的目标是：在有限的操作次数 $T$ 内，获得最大的累积奖励。&lt;/p&gt;
&lt;p&gt;这不仅是赌博问题，它在现实中无处不在：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;推荐系统&lt;/strong&gt;：把哪部电影推给用户？（探索新电影 vs 利用已知的高分电影）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;临床试验&lt;/strong&gt;：给病人用哪种药？（试新药 vs 用疗效确定的旧药）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;问题定义&lt;/h2&gt;
&lt;h3&gt;形式化描述&lt;/h3&gt;
&lt;p&gt;多臂老虎机问题是一个元组 $\langle \mathcal{A}, \mathcal{R} \rangle$：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\mathcal{A}$：动作集合，即 $K$ 个拉杆，${a_1, \dots, a_K}$。&lt;/li&gt;
&lt;li&gt;$\mathcal{R}$：奖励概率分布。对于每个动作 $a$，奖励 $r \sim \mathcal{R}(r|a)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;累积懊悔 (Cumulative Regret)&lt;/h3&gt;
&lt;p&gt;怎么衡量一个算法好不好？我们引入&lt;strong&gt;懊悔 (Regret)&lt;/strong&gt; 的概念。
假设我们开了上帝视角，知道哪台机器最好，那台机器的期望奖励是 $Q^*$。
如果我们选了差的机器，我们就会“后悔”。在 $T$ 步内的总懊悔定义为：&lt;/p&gt;
&lt;p&gt;$$
R_T = \sum_{t=1}^T (Q^* - q_*(a_t))
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$Q^*$：全局最优拉杆的期望奖励。&lt;/li&gt;
&lt;li&gt;$q_*(a_t)$：我们第 $t$ 步实际选的那个拉杆的期望奖励。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;我们的目标&lt;/strong&gt;：设计一个算法，让累积懊悔 $R_T$ 增长得越慢越好（最好是亚线性的，即 $\lim_{T \to \infty} \frac{R_T}{T} = 0$）。这意味着随着时间推移，我们几乎总是选中最好的那台机器。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;基础：估计期望奖励&lt;/h2&gt;
&lt;p&gt;不论用什么算法，我们都需要估计每个拉杆的价值。最简单的方法是&lt;strong&gt;增量式蒙特卡洛更新&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;假设第 $k$ 个拉杆被选中了 $n$ 次，每次的奖励是 $r_1, \dots, r_n$。
当前的估计值 $Q_n$ 是平均值。当有新的奖励 $r_{n+1}$ 进来时，更新公式为：&lt;/p&gt;
&lt;p&gt;$$
Q_{n+1} = Q_n + \frac{1}{n+1} (r_{n+1} - Q_n)
$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 直觉&lt;/strong&gt;：
$\text{新估计值} = \text{旧估计值} + \text{步长} \times \text{误差}$
这个公式非常重要，它是后续所有强化学习价值更新的雏形。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;算法 1：$\epsilon$-Greedy 算法&lt;/h2&gt;
&lt;p&gt;这是最直观的平衡策略。&lt;/p&gt;
&lt;h3&gt;规则&lt;/h3&gt;
&lt;p&gt;每次选择动作时，生成一个 $[0, 1]$ 的随机数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;以 $1-\epsilon$ 的概率（利用）&lt;/strong&gt;：选择当前估计值 $Q(a)$ 最高的那个拉杆。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;以 $\epsilon$ 的概率（探索）&lt;/strong&gt;：随机选择任意一个拉杆。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;衰减的 $\epsilon$&lt;/h3&gt;
&lt;p&gt;普通的 $\epsilon$-Greedy 有个缺点：即使已经玩了一万次，明明知道哪个最好，它还是会以固定的 $\epsilon$ 概率去乱选，导致懊悔线性增长。
&lt;strong&gt;改进&lt;/strong&gt;：让 $\epsilon$ 随时间减小，例如 $\epsilon_t = \frac{1}{t}$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初期 $\epsilon$ 大：疯狂探索。&lt;/li&gt;
&lt;li&gt;后期 $\epsilon$ 小：主要利用，趋近于最优策略。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;算法 2：上置信界 (UCB)&lt;/h2&gt;
&lt;p&gt;$\epsilon$-Greedy 的探索是盲目的。&lt;strong&gt;UCB (Upper Confidence Bound)&lt;/strong&gt; 提出了一种“&lt;strong&gt;面对不确定性保持乐观 (Optimism in the Face of Uncertainty)&lt;/strong&gt;”的原则。&lt;/p&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;我们给每个拉杆的价值算一个&lt;strong&gt;上界&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果一个拉杆估计值高，上界就高。&lt;/li&gt;
&lt;li&gt;如果一个拉杆&lt;strong&gt;被选的次数很少&lt;/strong&gt;（不确定性大），它的波动范围就大，上界也会很高。
我们每次都选&lt;strong&gt;上界最高&lt;/strong&gt;的那个拉杆。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;UCB 公式&lt;/h3&gt;
&lt;p&gt;在时刻 $t$，对于动作 $a$，我们计算它的 UCB 分数：&lt;/p&gt;
&lt;p&gt;$$
\text{Score}_t(a) = \hat{Q}_t(a) + c \sqrt{\frac{\ln t}{N_t(a) + 1}}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\hat{Q}_t(a)$：当前的平均收益（利用项）。&lt;/li&gt;
&lt;li&gt;$\sqrt{\dots}$：&lt;strong&gt;不确定性红利（探索项）&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;$N_t(a)$ 是动作 $a$ 被选中的次数。分母越小（玩得少），这一项越大。&lt;/li&gt;
&lt;li&gt;$t$ 是总步数。随着时间推移，如果某个动作一直没被选，分子 $\ln t$ 变大，也会强行把它的分数拉高。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$c$：超参数，控制探索的权重。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 效果&lt;/strong&gt;：
那些“潜力股”（玩得少）和“绩优股”（均值高）都会被选中，而那些“玩了很多次且证明了很烂”的拉杆会被逐渐抛弃。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;算法 3：汤普森采样 (Thompson Sampling)&lt;/h2&gt;
&lt;p&gt;汤普森采样 (Thompson Sampling)基于&lt;strong&gt;贝叶斯 (Bayesian)&lt;/strong&gt; 思想，比 UCB 更具统计学美感。&lt;/p&gt;
&lt;h3&gt;核心思想&lt;/h3&gt;
&lt;p&gt;我们不维护一个具体的数值 $Q(a)$，而是维护每个拉杆奖励的&lt;strong&gt;概率分布&lt;/strong&gt;。
假设每台老虎机只有“赢”和“输”两种结果（伯努利分布）。我们可以用 &lt;strong&gt;Beta 分布&lt;/strong&gt; 来描述这种不确定性。&lt;/p&gt;
&lt;h3&gt;流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;初始化&lt;/strong&gt;：为每个拉杆 $k$ 维护两个数：$Win_k$（赢的次数）和 $Lose_k$（输的次数）。初始都为 1。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;采样&lt;/strong&gt;：在每一步，对于每个拉杆，从它的 Beta 分布中随机采样一个数：
$$
\theta_k \sim \text{Beta}(Win_k + 1, Lose_k + 1)
$$
&lt;blockquote&gt;
&lt;p&gt;注意：不是选 Beta 分布的均值，而是&lt;strong&gt;采样&lt;/strong&gt;产生一个随机数。如果一个拉杆尝试少，Beta 分布很宽，采样出来的数可能很高也可能很低（探索）。如果尝试多且赢得多，Beta 分布很尖且靠右，采样出来的数大概率很高（利用）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;决策&lt;/strong&gt;：选择采样值 $\theta_k$ 最大的那个拉杆 $a_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新&lt;/strong&gt;：观察实际奖励 $r_t$，如果赢了则 $Win_{a_t} += 1$，输了则 $Lose_{a_t} += 1$。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;优势&lt;/h3&gt;
&lt;p&gt;汤普森采样在很多实际应用中表现比 UCB 更好，因为它引入了随机性，能更平滑地处理探索与利用，而且对非平稳环境适应性较强。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;多臂老虎机是强化学习的“单状态”特例，它教会了我们处理 RL 核心难题的几种范式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;盲目探索&lt;/strong&gt;：$\epsilon$-Greedy（简单粗暴，但有效）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;乐观探索&lt;/strong&gt;：UCB（基于不确定性的确定性策略，数学理论完备）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后验采样&lt;/strong&gt;：Thompson Sampling（基于概率分布的随机策略，贝叶斯流派）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;解决了怎么选动作的问题，下一章我们将引入&lt;strong&gt;状态转移&lt;/strong&gt;，去面对真正复杂的&lt;strong&gt;序列决策问题&lt;/strong&gt;——马尔可夫决策过程 (MDP)。&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/66119164_p0_master1200.3d5duqih46.webp"/><enclosure url="https://pic.hana0721.top/66119164_p0_master1200.3d5duqih46.webp"/></item><item><title>RL笔记（1）：初入强化学习</title><link>https://hana-blog.top/blog/rl-note-1</link><guid isPermaLink="true">https://hana-blog.top/blog/rl-note-1</guid><description>什么是强化学习？它与监督学习有何不同？核心要素与基本世界观的构建。</description><pubDate>Wed, 10 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言（Introduction）&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;强化学习 (Reinforcement Learning, RL)&lt;/strong&gt; 是机器学习中一个独特的领域，它关注的是&lt;strong&gt;智能体 (Agent)&lt;/strong&gt; 如何在&lt;strong&gt;环境 (Environment)&lt;/strong&gt; 中通过&lt;strong&gt;试错 (Trial-and-Error)&lt;/strong&gt; 来学习最佳策略。&lt;/p&gt;
&lt;p&gt;如果说监督学习是“老师手把手教你做题”（有标签），那么强化学习就是“把你扔进游戏里自己玩，赢了得分，输了扣分”。智能体必须通过与环境的交互，自己总结出怎么做才能获得最高的&lt;strong&gt;累积奖励&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;核心直觉&lt;/strong&gt;：
就像训练小狗：小狗做动作（Action），你给它骨头或训斥（Reward）。小狗为了多吃骨头，逐渐学会了“听到口令坐下”这个策略（Policy）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;强化学习 vs. 其他机器学习&lt;/h2&gt;
&lt;p&gt;为了搞懂 RL，我们先看它和我们熟悉的监督学习有什么本质区别：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;没有“正确答案” (No Ground Truth)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;监督学习&lt;/strong&gt;：输入一张猫的图片，标签明确告诉你“这是猫”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RL&lt;/strong&gt;：你走了一步棋，没人告诉你这一步是“对”还是“错”，你只知道这局棋最后是赢了还是输了。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反馈是延迟的 (Delayed Reward)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;你现在做的一个决定（比如买股票），可能要很久之后（卖出时）才知道好坏。这被称为&lt;strong&gt;信用分配问题 (Credit Assignment Problem)&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据是动态分布的 (Non-i.i.d Data)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;你的策略变了，你看到的画面（状态）也就变了。智能体的动作会直接改变它未来接收到的数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;核心组件：RL 的世界观&lt;/h2&gt;
&lt;p&gt;强化学习的一切都发生在一个循环交互中。我们需要掌握以下几个核心术语，它们是后续所有章节的基石。&lt;/p&gt;
&lt;h3&gt;1. 交互循环 (The Loop)&lt;/h3&gt;
&lt;p&gt;在每一个时刻 $t$：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;观察&lt;/strong&gt;：智能体看到环境的状态 $S_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作&lt;/strong&gt;：智能体根据策略，选择一个动作 $A_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反馈&lt;/strong&gt;：环境受到动作影响，变成新状态 $S_{t+1}$，并给出一个即时奖励 $R_t$。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2. 状态 (State, $S$)&lt;/h3&gt;
&lt;p&gt;状态是对环境现状的描述。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;例子&lt;/strong&gt;：在玩《王者荣耀》时，屏幕上的所有画面（英雄位置、血量、小地图）就是状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全观测 vs. 部分观测&lt;/strong&gt;：如果你能看到整个棋盘，这叫全观测；如果你在打扑克，你看不到对手的牌，这叫部分观测（POMDP）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 动作 (Action, $A$)&lt;/h3&gt;
&lt;p&gt;智能体能做的事情。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;离散动作&lt;/strong&gt;：上下左右、跳跃、开火（如超级马里奥）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连续动作&lt;/strong&gt;：方向盘转动 30.5 度、油门踩 70%（如自动驾驶）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 奖励 (Reward, $R$)&lt;/h3&gt;
&lt;p&gt;奖励是一个标量数值，是环境给智能体的唯一反馈信号。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;奖励假设 (The Reward Hypothesis)&lt;/strong&gt;：所有目标都可以被描述为“最大化累积奖励的期望”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意&lt;/strong&gt;：奖励定义了“好坏”，但没告诉智能体“怎么做”。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 策略 (Policy, $\pi$)&lt;/h3&gt;
&lt;p&gt;策略是智能体的大脑，它定义了&lt;strong&gt;在某个状态下该采取什么动作&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;确定性策略&lt;/strong&gt;：看到 $s$，就做 $a$。即 $a = \pi(s)$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;随机性策略&lt;/strong&gt;：看到 $s$，有 70% 概率做 $a_1$，30% 概率做 $a_2$。即 $\pi(a|s) = P(A_t=a|S_t=s)$。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 价值 (Value, $V$)&lt;/h3&gt;
&lt;p&gt;这是 RL 中最关键的概念之一（后续笔记会详细讲）。
&lt;strong&gt;奖励&lt;/strong&gt;是眼前的利益，&lt;strong&gt;价值&lt;/strong&gt;是长远的目光。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;例子&lt;/strong&gt;：为了赢下棋局（高价值），你可能需要牺牲一个车（负的即时奖励）。&lt;/li&gt;
&lt;li&gt;价值函数预测了：从当前状态出发，未来&lt;strong&gt;总共&lt;/strong&gt;能拿多少分。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;两个核心难题&lt;/h2&gt;
&lt;p&gt;在深入学习具体算法前，我们需要理解阻碍智能体变强的两大难题：&lt;/p&gt;
&lt;h3&gt;1. 序列决策 (Sequential Decision Making)&lt;/h3&gt;
&lt;p&gt;智能体的动作有&lt;strong&gt;长远影响&lt;/strong&gt;。现在的选择决定了未来的状态，进而限制了未来的选择。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;解决思路&lt;/em&gt;：我们需要引入 &lt;strong&gt;马尔可夫决策过程 (MDP)&lt;/strong&gt; 来数学化描述这个过程（详见笔记 3），并通过 &lt;strong&gt;动态规划&lt;/strong&gt;（笔记 5）或 &lt;strong&gt;时序差分&lt;/strong&gt;（笔记 6）来求解未来的价值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 探索与利用 (Exploration vs. Exploitation)&lt;/h3&gt;
&lt;p&gt;这是 RL 独有的矛盾。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;利用 (Exploitation)&lt;/strong&gt;：根据现有经验，做那个我认为最好的动作（拿确定的分数）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;探索 (Exploration)&lt;/strong&gt;：尝试一个没做过的动作，虽然可能导致扣分，但也可能发现一条通往更高分的新捷径。&lt;/li&gt;
&lt;li&gt;&lt;em&gt;解决思路&lt;/em&gt;：就像去餐厅吃饭，是去吃那家确定的好吃的店（利用），还是去试一家新开的店（探索）？&lt;/li&gt;
&lt;li&gt;这个问题在 &lt;strong&gt;多臂老虎机 (Multi-armed Bandit)&lt;/strong&gt; 问题中最为纯粹（详见笔记 2）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;学习路线图 (Roadmap)&lt;/h2&gt;
&lt;p&gt;本系列笔记将按照以下逻辑逐步深入：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;无状态的基础&lt;/strong&gt;：从最简单的&lt;strong&gt;多臂老虎机&lt;/strong&gt;（笔记 2）开始，只考虑动作选择，不考虑状态转移。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;建立数学模型&lt;/strong&gt;：引入&lt;strong&gt;马尔可夫决策过程 (MDP)&lt;/strong&gt;（笔记 3），正式描述状态转移和序列决策。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;求解 MDP&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;如果你知道环境的一切规则（上帝视角），使用&lt;strong&gt;动态规划&lt;/strong&gt;（笔记 5）。&lt;/li&gt;
&lt;li&gt;如果你只能通过玩游戏来学习（蒙眼摸象），使用&lt;strong&gt;蒙特卡洛&lt;/strong&gt;（笔记 4）和&lt;strong&gt;时序差分&lt;/strong&gt;（笔记 6）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进阶与深度强化学习&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;当状态太多存不下表格时，我们引入神经网络，进入 Deep RL 时代（DQN, Policy Gradient, Actor-Critic 等）。&lt;/li&gt;
&lt;li&gt;一直到最前沿的 SAC, PPO 等算法。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;让我们开始这段旅程吧！&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top//65089776_p0_master1200.5c1kl2nzfo.webp"/><enclosure url="https://pic.hana0721.top//65089776_p0_master1200.5c1kl2nzfo.webp"/></item><item><title>Slay the Spire: Silent Cards 评测</title><link>https://hana-blog.top/blog/sts-silent-cards-rating</link><guid isPermaLink="true">https://hana-blog.top/blog/sts-silent-cards-rating</guid><description>Silent 全卡评测索引页：按稀有度整理卡牌，包含基础信息、卡图与评分占位，便于后续逐卡完善攻略。</description><pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { StsCardRating, StsSilentTierBoard } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;h2&gt;Silent Cards Rank 表&lt;/h2&gt;
&lt;p&gt;个人情况：我在《杀戮尖塔》累计游玩约 &lt;code&gt;2000h&lt;/code&gt;，最好成绩为 &lt;code&gt;SL 179 连胜&lt;/code&gt;、&lt;code&gt;NOSL 10 连胜&lt;/code&gt;。平时会在 B 站直播杀戮尖塔，主页：&lt;a href=&quot;https://space.bilibili.com/44972476&quot;&gt;juhuahua233&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;说明：本 Rank 表按个人评测结果整理。评级解读时建议参考稀有度（Starter / Common / Uncommon / Rare）与成型阶段，不同稀有度的抓取机会与机会成本差异很大。&lt;/p&gt;
&lt;p&gt;免责声明：该分级仅代表个人当前版本体验，不构成通用强度结论；不同路线、遗物、路径、事件、升级时机、Boss 都会显著改变卡牌价值。请结合你的局内状态灵活取舍。&lt;/p&gt;
&lt;h2&gt;起始卡 Starter Cards&lt;/h2&gt;
&lt;h3&gt;防御 Defend&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Defend&apos;
rarity=&apos;Starter&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Gain 5(8) Block.&apos;
score={5}
image=&apos;https://pic.hana0721.top//Defend_G.8vni909zoh.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;猎人的防御价值会比较高，评分也会比其他角色的防御卡更高一些。
首先，取决于猎人的毒系终端，毒会偏向于防杀。
其次，猎人有一张轮椅卡——灵动，可以有效提高防御的数值。
综上，猎人的防御是普遍比打击有价值的。
&lt;/p&gt;
&lt;h3&gt;中和 Neutralize&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Neutralize&apos;
rarity=&apos;Starter&apos;
type=&apos;Attack&apos;
cost=&apos;0&apos;
effect=&apos;Deal 3(4) damage. Apply 1(2) Weak.&apos;
score={7.5}
image=&apos;https://pic.hana0721.top//Neutralize.67y1yngycu.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;0费的1层虚弱，敲后2层。
虚弱覆盖率是 Silent 在 NOSL 玩法中需要重点关注的。
因此，一般在进入一层 boss 前会升级中和，若没有其他的虚弱卡。
同时，轮椅遗物纸鹤的存在更加提高了虚弱的价值。&lt;/p&gt;
&lt;h3&gt;打击 Strike&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Strike&apos;
rarity=&apos;Starter&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 6(9) damage.&apos;
score={2}
image=&apos;https://pic.hana0721.top//Strike_G.5trm7s8nhy.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;打击，不说了。但是在 Silent 套牌中，打击还真不是最垃圾的，原来还有高手。&lt;/p&gt;
&lt;h3&gt;生存者 Survivor&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Survivor&apos;
rarity=&apos;Starter&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Gain 8(11) Block. Discard a card.&apos;
score={6}
image=&apos;https://pic.hana0721.top//Survivor.6m4hpip987.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;Silent 的费防比最高的卡，特效是丢弃一张牌。
在前期，丢弃是非常好的，因为你根本出不完手牌，还能听一些丢弃触发的卡牌。
在后期，当你过牌很足时，丢弃也是一种很好的卸牌手段。
针对保留，生存者可能时好时坏。
在你有金字塔的情况下一般是好的，保留量大，可以卸牌。
在你使用计划妥当的情况下，生存者可能会丢弃掉一些你想要保留的牌。
&lt;/p&gt;
&lt;h2&gt;普通卡 Common Cards&lt;/h2&gt;
&lt;h3&gt;杂技 Acrobatics&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Acrobatics&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Draw 3(4) cards. Discard 1 card.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Acrobatics.39lrv58oug.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;后空翻 Backflip&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Backflip&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Gain 5(8) Block. Draw 2 cards.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Backflip.2dpafoz0eq.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;灾祸 Bane&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Bane&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 7(10) damage. If the enemy is Poison, deal 7(10) damage again.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Bane.6wrbio4hck.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;刀刃之舞 Blade Dance&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Blade Dance&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Add 3(4) Shiv to your hand.&apos;
score={0}
image=&apos;https://pic.hana0721.top//BladeDance.102rbnnydx.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;斗篷与匕首 Cloak And Dagger&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Cloak And Dagger&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Gain 6 Block. Add 1(2) Shiv to your hand.&apos;
score={0}
image=&apos;https://pic.hana0721.top//CloakAndDagger.92qq4fw53w.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;匕首雨 Dagger Spray&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Dagger Spray&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 4(6) damage to ALL enemies twice.&apos;
score={0}
image=&apos;https://pic.hana0721.top//DaggerSpray.1apl4t36jj.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;投掷匕首 Dagger Throw&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Dagger Throw&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 9(12) damage. Draw 1 card. Discard 1 card.&apos;
score={0}
image=&apos;https://pic.hana0721.top//R_dagger-throw.1apl4t36k5.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;致命毒药 Deadly Poison&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Deadly Poison&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Apply 5(7) Poison.&apos;
score={0}
image=&apos;https://pic.hana0721.top//DeadlyPoison.60uu37uswx.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;偏折 Deflect&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Deflect&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;0&apos;
effect=&apos;Gain 4(7) Block.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Deflect.9o0dqqqleq.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;闪躲翻滚 Dodge and Roll&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Dodge and Roll&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Gain 4(6) Block. Next turn, gain 4(6) Block.&apos;
score={0}
image=&apos;https://pic.hana0721.top//DodgeandRoll.102rbnnyea.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;飞膝 Flying Knee&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Flying Knee&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 8(11) damage. Next turn, gain 1 Energy.&apos;
score={0}
image=&apos;https://pic.hana0721.top//FlyingKnee.3rbtjqa2g5.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;抢占先机 Outmaneuver&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Outmaneuver&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Next turn, gain 2(3) Energy.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Outmaneuver.26m2k9cv02.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;尖啸 Piercing Wail&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Piercing Wail&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;ALL enemies lose 6(8) Strength for 1 turn. Exhaust.&apos;
score={0}
image=&apos;https://pic.hana0721.top//PiercingWail.7pvtx7coj.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;带毒刺击 Poisoned Stab&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Poisoned Stab&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 6(8) damage. Apply 3(4) Poison.&apos;
score={0}
image=&apos;https://pic.hana0721.top//PoisonedStab.3625xffm5v.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;早有准备 Prepared&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Prepared&apos;
rarity=&apos;Common&apos;
type=&apos;Skill&apos;
cost=&apos;0&apos;
effect=&apos;Draw 1(2) card(s). Discard 1(2) card(s).&apos;
score={0}
image=&apos;https://pic.hana0721.top//Prepared.1ziuotqpkk.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;快斩 Quick Slash&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Quick Slash&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 8(12) damage. Draw 1 card.&apos;
score={0}
image=&apos;https://pic.hana0721.top//QuickSlash.70axgdxk38.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;切割 Slice&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Slice&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;0&apos;
effect=&apos;Deal 6(9) damage.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Slice.3ns7m0gzqt.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;隐秘打击 Sneaky Strike&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Sneaky Strike&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;2&apos;
effect=&apos;Deal 12(16) damage. If you have discarded a card this turn, gain 2 Energy.&apos;
score={0}
image=&apos;https://pic.hana0721.top//SneakyStrike.13md9dh14m.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;突然一拳 Sucker Punch&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Sucker Punch&apos;
rarity=&apos;Common&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 7(9) damage. Apply 1(2) Weak.&apos;
score={0}
image=&apos;https://pic.hana0721.top//SuckerPunch.szjg81sza.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h2&gt;罕见卡 Uncommon Cards&lt;/h2&gt;
&lt;h3&gt;精准 Accuracy&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Accuracy&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Power&apos;
cost=&apos;1&apos;
effect=&apos;Shiv deal 4(6) additional damage.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Accuracy.1hst08pbyf.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;全力攻击 All-Out Attack&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;All-Out Attack&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 10(14) damage to ALL enemies. Discard 1 card at random.&apos;
score={0}
image=&apos;https://pic.hana0721.top//All-OutAttack.4clh614iq7.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;背刺 Backstab&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Backstab&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;0&apos;
effect=&apos;Deal 11(15) damage. Innate. Exhaust.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Backstab.102rbnnydv.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;残影 Blur&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Blur&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Gain 5(8) Block. Block is not removed at the start of your next turn.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Blur.9rjzogjo46.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;弹跳药瓶 Bouncing Flask&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Bouncing Flask&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;2&apos;
effect=&apos;Apply 3 Poison to a random enemy 3(4) times.&apos;
score={0}
image=&apos;https://pic.hana0721.top//BouncingFlask.7w7evu78if.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;计算下注 Calculated Gamble&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Calculated Gamble&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;0&apos;
effect=&quot;Discard your hand, then draw that many cards. Exhaust. (Don&apos;t exhaust when upgraded.)&quot;
score={0}
image=&apos;https://pic.hana0721.top//CalculatedGamble.b9hrn0fdm.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;铁蒺藜 Caltrops&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Caltrops&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Power&apos;
cost=&apos;1&apos;
effect=&apos;Whenever you are attacked, deal 3(5) damage back.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Caltrops.3d5dsv1rkr.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;催化剂 Catalyst&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Catalyst&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&quot;Double(Triple) an enemy&apos;s Poison. Exhaust.&quot;
score={0}
image=&apos;https://pic.hana0721.top//Catalyst.5j4semtfbv.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;勒脖 Choke&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Choke&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;2&apos;
effect=&apos;Deal 12 damage. Whenever you play a card this turn, the targeted enemy loses 3(5) HP.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Choke.3625xffm58.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;全神贯注 Concentrate&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Concentrate&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;0&apos;
effect=&apos;Discard 3(2) cards. Gain 2 Energy.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Concentrate.60uu37uswt.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;致残毒云 Crippling Cloud&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Crippling Cloud&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;2&apos;
effect=&apos;Apply 4(7) Poison and 2 Weak to ALL enemies. Exhaust.&apos;
score={0}
image=&apos;https://pic.hana0721.top//CripplingCloud.3gozqkuuan.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;冲刺 Dash&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Dash&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;2&apos;
effect=&apos;Gain 10(13) Block. Deal 10(13) damage.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Dash.9ddjxlbd9c.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;声东击西 Distraction&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Distraction&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;1(0)&apos;
effect=&apos;Add a random Skill to your hand. It costs 0 this turn. Exhaust.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Distraction.4g533qxlgi.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;无尽苦痛 Endless Agony&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Endless Agony&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;0&apos;
effect=&apos;Whenever you draw this card, add a copy of it to your hand. Deal 4(6) damage. Exhaust.&apos;
score={0}
image=&apos;https://pic.hana0721.top//EndlessAgony.2rvq6k7bab.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;逃脱计划 Escape Plan&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Escape Plan&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;0&apos;
effect=&apos;Draw 1 card. If you draw a Skill, gain 3(5) Block.&apos;
score={0}
image=&apos;https://pic.hana0721.top//EscapePlan.232gmjjs9w.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;内脏切除 Eviscerate&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Eviscerate&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;3&apos;
effect=&apos;Costs 1 less Energy for each card discarded this turn. Deal 7(9) damage three times.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Eviscerate.96ac25p7tw.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;独门技术 Expertise&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Expertise&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Draw cards until you have 6(7) in your hand.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Expertise.3ns7m0gzqc.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;终结技 Finisher&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Finisher&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 6(8) damage for each Attack played this turn.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Finisher.5fl6gx0cmc.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;飞镖 Flechettes&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Flechettes&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 4(6) damage for each Skill in your hand.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Flechettes.175z73a3tx.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;灵动步法 Footwork&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Footwork&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Power&apos;
cost=&apos;1&apos;
effect=&apos;Gain 2(3) Dexterity.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Footwork.3yf1f5w7vp.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;足跟勾 Heel Hook&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Heel Hook&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 5(8) damage. If the enemy is Weak, gain 1 Energy and draw 1 card.&apos;
score={0}
image=&apos;https://pic.hana0721.top//HeelHook.2a5ohz5xpi.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;无限刀刃 Infinite Blades&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Infinite Blades&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Power&apos;
cost=&apos;1&apos;
effect=&apos;(Innate when upgraded.) At the start of your turn, add a Shiv to your hand.&apos;
score={0}
image=&apos;https://pic.hana0721.top//InfiniteBlades.67y1yngycn.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;扫腿 Leg Sweep&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Leg Sweep&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;2&apos;
effect=&apos;Apply 2(3) Weak. Gain 11(14) Block.&apos;
score={0}
image=&apos;https://pic.hana0721.top//LegSweep.83amr9tdyf.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;精巧刺击 Masterful Stab&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Masterful Stab&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;0&apos;
effect=&apos;Costs 1 additional Energy for each time you lose HP this combat. Deal 12(16) damage.&apos;
score={0}
image=&apos;https://pic.hana0721.top//MasterfulStab.39lrv58ovb.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;毒雾 Noxious Fumes&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Noxious Fumes&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Power&apos;
cost=&apos;1&apos;
effect=&apos;At the start of your turn, apply 2(3) Poison to ALL enemies.&apos;
score={0}
image=&apos;https://pic.hana0721.top//NoxiousFumes.41yncvpalu.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;猎杀者 Predator&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Predator&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;2&apos;
effect=&apos;Deal 15(20) damage. Draw 2 more cards next turn.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Predator.2a5ohz5xpw.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;本能反应 Reflex&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Reflex&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;Unplayable&apos;
effect=&apos;If this card is discarded from your hand, draw 2(3) cards.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Reflex.lwbksfnjp.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;千穿百刺 Riddle With Holes&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Riddle With Holes&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;2&apos;
effect=&apos;Deal 3(4) damage 5 times.&apos;
score={0}
image=&apos;https://pic.hana0721.top//RiddleWithHoles.73uje3qmt1.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;部署 Setup&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Setup&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;1(0)&apos;
effect=&apos;Place a card in your hand on top of your draw pile. It costs 0 until it is played.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Setup.5moeccmi2d.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;串刺 Skewer&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Skewer&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Attack&apos;
cost=&apos;X&apos;
effect=&apos;Deal 7(10) damage X times.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Skewer.1hst08pbzp.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;战术大师 Tactician&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Tactician&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;Unplayable&apos;
effect=&apos;If this card is discarded from your hand, gain 1(2) Energy.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Tactician.pfxii8q9j.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;恐怖 Terror&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Terror&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Skill&apos;
cost=&apos;1(0)&apos;
effect=&apos;Apply 99 Vulnerable. Exhaust.&apos;
score={0}
image=&apos;https://pic.hana0721.top//Terror.5fl6gx0cmv.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h3&gt;计划妥当 Well-Laid Plans&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Well-Laid Plans&apos;
rarity=&apos;Uncommon&apos;
type=&apos;Power&apos;
cost=&apos;1&apos;
effect=&apos;At the end of your turn, Retain up to 1(2) card(s).&apos;
score={0}
image=&apos;https://pic.hana0721.top//Well-LaidPlans.3d5dsv1rlk.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;TODO: 写这张卡在 Silent 套牌中的定位、抓取优先级、升级优先级、和关键联动。
&lt;/p&gt;
&lt;h2&gt;稀有卡 Rare Cards&lt;/h2&gt;
&lt;h3&gt;凌迟 A Thousand Cuts&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;A Thousand Cuts&apos;
rarity=&apos;Rare&apos;
type=&apos;Power&apos;
cost=&apos;2&apos;
effect=&apos;Whenever you play a card, deal 1(2) damage to ALL enemies.&apos;
score={7.5}
image=&apos;https://pic.hana0721.top//AThousandCuts.5trm7s8ngx.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第三梯队；但如果一层 Boss 是史莱姆老大，抓位仅次于玻璃刀、炼药和余像。
凌迟的问题在于 2 费起手偏重，未升级前数值一般，投资回报不高；升级后出伤就会达到合格甚至超模水平。前期可能因难以下场而增加战损，但随着对局推进，它能稳定应对高压力的精英和 Boss 战，例如三奴隶、地精首领、蛇女等。
打三奴隶时通常不如尸爆术，但在需要顺带清爪牙的战斗（如地精首领和蛇女）里，凌迟的发挥很稳定。
敲位很高，基本默认要敲；不升级很难达到合格输出。
&lt;/p&gt;
&lt;h3&gt;肾上腺素 Adrenaline&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Adrenaline&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;0&apos;
effect=&apos;Gain 1(2) Energy. Draw 2 cards. Exhaust.&apos;
score={8.5}
image=&apos;https://pic.hana0721.top//Adrenaline.2h8wdes349.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第二梯队。虽然是第二梯队，但不妨碍肾上腺素是我爹。
肾上腺素同时提供过牌和费用，本质上是启动卡和卡组润滑剂，本身不直接提供数值。
爆发 + 肾上腺素能在当前回合提供大量资源，方便启动与打数值；夜宴 + 肾上腺素则能保证次回合稳定展开。
敲位：一层通常要让位给过渡输出；到二层后，很多输出升级已难再跨攻杀阈值，肾上腺素的敲位会明显上升，既能强化展开，也更利于长线作战。
&lt;/p&gt;
&lt;h3&gt;余像 After Image&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;After Image&apos;
rarity=&apos;Rare&apos;
type=&apos;Power&apos;
cost=&apos;1&apos;
effect=&apos;(Innate when upgraded.) Whenever you play a card, gain 1 Block.&apos;
score={9.5}
image=&apos;https://pic.hana0721.top//AfterImage.7p470el32j.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第二梯队。虽然是第二梯队，但不妨碍余像是我爹。
开局抓到余像时，能显著降低一层小怪战损；有余像时，刀刃之舞、斗篷与匕首等多动刀牌，以及切割、偏折、逃脱计划等 0 费牌的抓位都会上升。随着层数提高和加费遗物增多，每回合出牌数提升，余像收益也会持续走高。一张余像能过渡，两张基本能接管防御；若能夜宴余像，很多对局的防御终端都能解决。
敲位较高。数值不足时可让位给过渡输出；输出够、或者能稳住防御打长线时，升级余像能稳定降低战损。天鹅绒颈圈限制每回合出牌数，与余像相性较差。&lt;/p&gt;
&lt;h3&gt;炼制药水 Alchemize&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Alchemize&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;1(0)&apos;
effect=&apos;Obtain a random potion. Exhaust.&apos;
score={9.5}
image=&apos;https://pic.hana0721.top//Alchemize.4n8az6jqvg.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：开局最强金卡之一，不拿通常是治理问题。
这是猎手少有的局外收益卡。药水虽然有随机性，但过渡能力很强，能帮你滚雪球；高质量药水也可以留到关键战斗保命或提速。
炼药作为局外收益卡，虽不能被尼利的宝典或发现复制，但能被夜宴指定；在防御稳、怪物无成长时，可打出双夜宴多炼药的上限玩法。卡组里有爆发时，也可以等两张牌同回合抽到，打出双炼药收益。
&lt;/p&gt;
&lt;h3&gt;子弹时间 Bullet Time&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Bullet Time&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;3(2)&apos;
effect=&apos;You cannot draw additional cards this turn. Reduce the cost of all cards in your hand to 0 this turn.&apos;
score={7.5}
image=&apos;https://pic.hana0721.top//BulletTime.5q80a2fkrc.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第三梯队。没有配合时接近“伤口”，有条件可以听高费牌。
没有稳定过牌前，通常先不急着敲；有过牌体系后建议升级，避免过牌后费用不够下子弹时间（确保过牌后仍有费用出牌）。
它能把手牌高费卡统一压成 0 费，配合猎杀者、跳瓶、冲刺、夜宴、扫腿等都能打出很强的当回合资源转换；但和计算下注同回合时可能冲突，也会拮抗 0 费体系。
注意，子弹时间的禁止抽牌可以被人工抵消，也可以被橙色药丸解除。&lt;/p&gt;
&lt;h3&gt;爆发 Burst&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Burst&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;This turn, your next 1(2) Skill(s) is(are) played twice.&apos;
score={7.5}
image=&apos;https://pic.hana0721.top//Burst.491v8bbg0p.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第三梯队，偏防御向，但能有效提高技能牌利用率。
敲位极高：不升级只能复制 1 张技能，升级后可复制 2 张，质变很明显。
可以配合肾上腺素、萎靡、跳瓶、尸爆术、残影、催化剂等高价值技能牌。
本质上是把关键技能的收益翻倍。&lt;/p&gt;
&lt;h3&gt;尸爆术 Corpse Explosion&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Corpse Explosion&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;2&apos;
effect=&apos;Apply 6(9) Poison. When the enemy dies, deal damage equal to its max HP to ALL enemies.&apos;
score={8.5}
image=&apos;https://pic.hana0721.top//CorpseExplosion.2a5ohz5xpa.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第二梯队，机制型 AOE，毒贼核心之一。
敲位很高，但通常略低于纯 AOE 数值卡的必敲优先级。
在一层，毒层机制能针对乐加维林和小红，爆炸机制也能处理三柱等群怪场景。
面对群怪时表现极好，同时还能顺带拆部分人工制品。
较早拿到尸爆术时，可以考虑向毒体系构筑，提高催化剂抓位。
后期补入尸爆术，也能显著增强 AOE，尤其针对矛盾、觉醒者以及甜圈八体。
&lt;/p&gt;
&lt;h3&gt;死吧死吧死吧 Die Die Die&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Die Die Die&apos;
rarity=&apos;Rare&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 13(17) damage to ALL enemies. Exhaust.&apos;
score={8}
image=&apos;https://pic.hana0721.top//DieDieDie.232gmjjs9u.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第二梯队，抓位略低于尸爆术。
纯粹的数值卡，通常不急着敲，前期过渡能力很强。
如果畏惧蛇女的诛仙剑阵，可以考虑带一张。
&lt;/p&gt;
&lt;h3&gt;双重存在 Doppelganger&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Doppelganger&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;X&apos;
effect=&apos;Next turn, draw X(+1) cards and gain X(+1) Energy.&apos;
score={8}
image=&apos;https://pic.hana0721.top//Doppelganger.175z73a3tv.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第三梯队。
跨回合收益可以利用没花完的费用，但前期费用紧张时，打它往往接近“眩晕”。
面对 Boss 的高压回合时价值会上升，比如勇士的处刑回合以及铜人的爆发回合。
可以配合爆发复制双重存在，做出高费用启动回合。
双重存在敲位较高；有 X 药时可适当下调，一般敲位高于肾上腺素。&lt;/p&gt;
&lt;h3&gt;荼毒 Envenom&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Envenom&apos;
rarity=&apos;Rare&apos;
type=&apos;Power&apos;
cost=&apos;2(1)&apos;
effect=&apos;Whenever an attack deals unblocked damage, apply 1 Poison.&apos;
score={4}
image=&apos;https://pic.hana0721.top//Envenom.1ziuotqpk3.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：等于开局塞一张诅咒，前期基本打不出；六火环境可定向针对，但多数时候不如毒雾。
敲位极高，不升级经常下不去，除非你有蛇眼。
遗物异蛇头骨可以显著提高这张卡的抓位。
可以配合多段攻击卡，例如飞镖、刀舞、切割、终结技等（即使这样，也不建议为它硬抓低质量攻击牌）。&lt;/p&gt;
&lt;h3&gt;玻璃刀刃 Glass Knife&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Glass Knife&apos;
rarity=&apos;Rare&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 8(12) damage twice. Decrease the damage of this card by 2 this combat.&apos;
score={8.5}
image=&apos;https://pic.hana0721.top//GlassKnife.4jop1gqo6e.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：开局第二强金卡，具有极其爆炸的过渡数值，适合打大部分短线战斗。
纯粹的数值卡，敲位极高，升级后直接提升 8 点基础伤害，收益非常扎实。
开局拿到后可以积极冲精英，能显著提高一层攻杀稳定性。
&lt;/p&gt;
&lt;h3&gt;华丽收场 Grand Finale&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Grand Finale&apos;
rarity=&apos;Rare&apos;
type=&apos;Attack&apos;
cost=&apos;0&apos;
effect=&apos;Can only be played if there are no cards in your draw pile. Deal 50(60) damage to ALL enemies.&apos;
score={1}
image=&apos;https://pic.hana0721.top//GrandFinale.2yyy1ztgpy.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：开局塞一张诅咒。
这张卡只能在抽牌堆空时打出，伤害上限很高，但稳定性很差。
通常只有金字塔，或强运转 + 计划妥当时才考虑带，且更偏小卡组，不然很难稳定打出。
敲位较低，先解决“能稳定打出”再谈升级收益。
&lt;/p&gt;
&lt;h3&gt;萎靡 Malaise&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Malaise&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;X&apos;
effect=&apos;Enemy loses X(+1) Strength. Apply X(+1) Weak. Exhaust.&apos;
score={9}
image=&apos;https://pic.hana0721.top//Malaise.2yyy1ztgpz.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第二梯队末端，六火蹲起特攻，对一层小怪精英相对弱势。
对连击怪发挥极佳，同时能稳定覆盖虚弱；道中可显著降低战损，Boss 战能大幅压低输出能力。对小手、地精首领、天罚等特定怪效果尤其好；打觉醒者、老头和心脏也很强。敲位极高（除非你有 X 药）；可以配合爆发打双萎靡，或配合全神贯注、双重存在、抢占先机打高费用萎靡。
&lt;/p&gt;
&lt;h3&gt;夜宴 Nightmare&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Nightmare&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;3(2)&apos;
effect=&apos;Choose a card. Next turn, add 3 copies of that card into your hand. Exhaust.&apos;
score={7}
image=&apos;https://pic.hana0721.top//Nightmare.1hst08pbzl.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：给你塞诅咒了，但未来可期。
这张卡非常难开，更适合在中后期补上限；若前期过渡已经足够强，也可以提前带一张。
可以配合灵动、幽魂、余像等防御核心，也能配合催化剂、精准等爆发输出；复制偏折、逃脱计划、残影、后空翻也能补防御浓度。
在 3-4 费环境下敲位很高，5 费后可适当下调。它也能配合子弹时间、部署强行下场，或借计划妥当、金字塔配合抢占先机提高稳定性。
&lt;/p&gt;
&lt;h3&gt;幻影杀手 Phantasmal Killer&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Phantasmal Killer&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;1(0)&apos;
effect=&apos;On your next turn, your Attacks deal double damage.&apos;
score={7}
image=&apos;https://pic.hana0721.top//PhantasmalKiller.3ns7m0gzqr.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第三梯队，出伤非常不稳定，看你昨晚有没有给发牌员。
敲位极高，升级后更容易保证出手权，但不保证每回合都能吃满收益。
数值上限很高，但稳定性不足；通常需要计划妥当、金字塔，或高运转卡组来确保收益延续。
下回合生效的效果，不利于某些攻杀的环境，当然也不一定是坏事，比如配合猎杀者、双重存在以及抢占先机这些跨回合卡。&lt;/p&gt;
&lt;h3&gt;钢铁风暴 Storm of Steel&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Storm of Steel&apos;
rarity=&apos;Rare&apos;
type=&apos;Skill&apos;
cost=&apos;1&apos;
effect=&apos;Discard your hand. Add 1 Shiv into your hand for each card discarded.&apos;
score={3}
image=&apos;https://pic.hana0721.top//StormofSteel.8s3wbagwza.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：很弱。
弃掉所有手牌在大多数场景会直接丢失出手权。少数情况下（例如有精准核心并配合金字塔）可以打出接近“绿色恶魔火”的爆发；有绷带时也能转化出很高防御。
它和树枝联动后上限会抬高，但整体还是明显弱于余像。
&lt;/p&gt;
&lt;h3&gt;必备工具 Tools of the Trade&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Tools of the Trade&apos;
rarity=&apos;Rare&apos;
type=&apos;Power&apos;
cost=&apos;1(0)&apos;
effect=&apos;At the start of your turn, draw 1 card and discard 1 card.&apos;
score={7.5}
image=&apos;https://pic.hana0721.top//ToolsoftheTrade.1lcexyiepj.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：第三梯队。
道中无竞争时可以抓，打出后能稳定触发弃牌特效，配合内切、隐秘、战术等都不错。每回合多看一张牌，但不净增手牌数，会同步弃一张。
与金字塔相性较差，这时必备工具常常只剩卸牌作用。
&lt;/p&gt;
&lt;h3&gt;乾坤一掷 Unload&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Unload&apos;
rarity=&apos;Rare&apos;
type=&apos;Attack&apos;
cost=&apos;1&apos;
effect=&apos;Deal 14(18) damage. Discard all non-Attack cards in your hand.&apos;
score={7}
image=&apos;https://pic.hana0721.top//Unload.45i9alidbr.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：属于第三梯队。
过渡能力很强，但弃牌副作用偏重。虽然可以配合内切、隐秘等卡提升过渡效率，但后期经常丢失出手权，尤其在计划妥当或金字塔体系里更明显。
&lt;/p&gt;
&lt;h3&gt;幽魂形态 Wraith Form&lt;/h3&gt;
&lt;p&gt;&amp;#x3C;StsCardRating
name=&apos;Wraith Form&apos;
rarity=&apos;Rare&apos;
type=&apos;Power&apos;
cost=&apos;3&apos;
effect=&apos;Gain 2(3) Intangible. At the end of your turn, lose 1 Dexterity.&apos;
score={9}
image=&apos;https://pic.hana0721.top//WraithForm.2rvq6k7baw.webp&apos;&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;开局金卡：属于是第二梯队，比余像弱一点。
道中高优先级抓取，敲位极高，能显著提升短线作战能力，是短线毒体系的重要支点。
&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/125160058_p0_master1200.6ikvtm0o6l.webp"/><enclosure url="https://pic.hana0721.top/125160058_p0_master1200.6ikvtm0o6l.webp"/></item><item><title>Galgame 简评系列 1</title><link>https://hana-blog.top/blog/galgame-1</link><guid isPermaLink="true">https://hana-blog.top/blog/galgame-1</guid><description>小资历玩家的 Galgame 简评，仅代表个人观点。</description><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { GameRating, GameRatingCriteria, ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;Galgame Rating Patches&apos;,
items: [
{ title: &apos;Patch 1&apos;, href: &apos;/blog/galgame-1&apos;, order: &apos;1&apos; },
{ title: &apos;Patch 2&apos;, href: &apos;/blog/galgame-2&apos;, order: &apos;2&apos; }
]
}
]}
/&gt;&lt;/p&gt;
&lt;p&gt;免责说明：以下评分仅代表个人观点，旨在提供参考。每款游戏的体验因人而异，建议根据自己的喜好和需求进行选择。&lt;/p&gt;
&lt;h2&gt;MUV-LUV EXTRA+UNLIMITED&lt;/h2&gt;
&lt;p&gt;&amp;#x3C;GameRating
title=&apos;MUV-LUV EXTRA+UNLIMITED&apos;
score={8.5}
bangumiId=&apos;4827&apos;
tldr=&apos;EXTRA 为废萌日常，UNLIMITED 就是 ALTERNATIVE 世界的故事了。&apos;
tags={[&apos;科幻&apos;, &apos;日常&apos;, &apos;剧情向&apos;, &apos;重口味&apos;]}&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/muvluvlogo.7pvslv04a.webp&quot; alt=&quot;&quot;&gt;
乍一看是部普通的废萌日常作，男主也是个典型的 Galgame 男主，至少在 EXTRA 部分是这样的。 但到了
UNLIMITED 部分，剧情就完全变了，把男主扔到了 BETA 的世界里。
这男主就特别有意思，看到战术机残骸在家里，第一感觉不是惊恐，而是觉得太几把帅了（笑死）。 UNLIMITED
篇主要也是将 BETA 的世界观介绍了一下，但是留下了许多谜团，是为 ALTERNATIVE 做铺垫的。
整体看下来，有续作加持，博主的评价可能会偏高。
&lt;/p&gt;
&lt;h2&gt;MUV-LUV ALTERNATIVE&lt;/h2&gt;
&lt;p&gt;&amp;#x3C;GameRating
title=&apos;MUV-LUV ALTERNATIVE&apos;
score={10.0}
bangumiId=&apos;4828&apos;
tldr=&apos;史上最燃 Galgame，伟大无需多言。巨人、魔圆以及石头门都有借鉴。&apos;
tags={[&apos;科幻&apos;, &apos;战争&apos;, &apos;剧情向&apos;, &apos;重口味&apos;]}&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/rl-note-3.3yex1xdpri.webp&quot; alt=&quot;&quot;&gt;
这作可以说是博主最喜欢的作品了，讲述了人类与外星生物 BETA 之间的战争故事。
可战争与热血只是这作的表面，真正让人印象深刻的是它对爱情、友情、牺牲和人性的深刻描绘。
角色塑造非常出色，每个角色都有独特的个性和动机，尤其是武爷的成长历程令人动容。
最喜欢的角色是纯夏，宇宙最强青梅，可以说整个故事都是因她而起的。
玩完了 ALTERNATIVE，才懂得 EXTRA 中日常的可贵，真希望大家都能活在没有 BETA 的世界里。&lt;/p&gt;
&lt;h2&gt;樱之诗-在樱花之森上飞舞&lt;/h2&gt;
&lt;p&gt;&amp;#x3C;GameRating
title=&apos;樱之诗-在樱花之森上飞舞&apos;
score={9.5}
bangumiId=&apos;22423&apos;
tldr=&apos;氛围废萌作，日常可能比较无聊，但后续剧情十分上头。&apos;
tags={[&apos;枕社&apos;, &apos;扶她自&apos;, &apos;治愈&apos;, &apos;剧情向&apos;]}&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/Sakura_no_uta.2ksi9r6jpk.webp&quot; alt=&quot;&quot;&gt; 这是博主早期玩的 Galgame
之一，很典型的亚撒西男主救赎系作品。
其实博主在玩的时候，并没有太喜欢这个游戏，因为扶她自的黄段子日常实在是太无聊。
但是呢，第二次游玩这个游戏时，其实早期的日常也埋了许多伏笔。
而且嘛，扶她自其实完全按照了《快乐王子》的剧本去写的，前期疯狂掉书袋，讲一些听不懂的话，但是氛围是不错的。
与女主们相比，男性角色反而会更有魅力，例如直哉、圭以及健一郎。
整体看下来，CG、音乐以及脚本都非常不错，虽然日常部分可能会让人觉得有些无聊，但后续的剧情发展绝对值得一玩。
&lt;/p&gt;
&lt;h2&gt;美好的每一天 ～不连续的存在～&lt;/h2&gt;
&lt;p&gt;&amp;#x3C;GameRating
title=&apos;美好的每一天 ～不连续的存在～&apos;
score={9.0}
bangumiId=&apos;4639&apos;
tldr=&apos;多视角叙事的剧情向作品，但不推荐心智不成熟的人玩。&apos;
tags={[&apos;枕社&apos;, &apos;治愈&apos;, &apos;重口&apos;, &apos;剧情向&apos;]}&lt;/p&gt;
&lt;blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://pic.hana0721.top/wonderful.5trm6g6vbw.webp&quot; alt=&quot;&quot;&gt;
素晴日的剧情、音乐以及作画都不错，但是近年过于出圈出现太多哲逼小鬼了，导致路人缘可能不太好。
有人说前期剧情太过于黑暗，但是我认为其内核是最后几章的救赎。
作品采用了多视角叙事，讲述了短短几天发生的事情，采用了许多叙事性诡计，随着游戏进行，会一步步揭开整个事件的真相。
这部的剧情非常紧凑，几乎没有浪费文字的地方，除了扶她自的恶趣味xp。
整体看下来，还是非常推荐的，尤其是喜欢哲学的（呵呵），不过不太推荐心智不成熟的人玩，感觉会变成魔怔小鬼罢。
&lt;/p&gt;
&lt;h2&gt;壳之少女&lt;/h2&gt;
&lt;h2&gt;虚之少女&lt;/h2&gt;
&lt;h2&gt;天之少女&lt;/h2&gt;
&lt;h2&gt;Erewhon&lt;/h2&gt;
&lt;h2&gt;千恋*万花&lt;/h2&gt;
&lt;h2&gt;夏空彼方&lt;/h2&gt;
&lt;h2&gt;樱之刻-于樱花之森下漫步&lt;/h2&gt;
&lt;h2&gt;近月少女的礼仪&lt;/h2&gt;
&lt;h2&gt;少女理论及其周边 -巴黎学园篇-&lt;/h2&gt;
&lt;h2&gt;eden* They were only two, on the planet&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;魔女的夜宴&lt;/h2&gt;
&lt;h2&gt;少女*领域&lt;/h2&gt;
&lt;h2&gt;RIDDLE JOKER&lt;/h2&gt;
&lt;h2&gt;饿殍：明末千里行&lt;/h2&gt;
&lt;h2&gt;永不枯萎的世界与终结之花&lt;/h2&gt;
&lt;h2&gt;ATRI -My Dear Moment-&lt;/h2&gt;
&lt;h2&gt;Clover Days&lt;/h2&gt;
&lt;h2&gt;逝去的你、于馆中萌生的憎恶&lt;/h2&gt;
&lt;h2&gt;濒死的骑士、于异世界中响彻的绝唱&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;星光咖啡馆与死神之蝶&lt;/h2&gt;
&lt;h2&gt;美少女万华镜 -被诅咒的传说少女-&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;</content:encoded><h:img src="https://pic.hana0721.top/71187447_p0_master1200.13mdb6lism.webp"/><enclosure url="https://pic.hana0721.top/71187447_p0_master1200.13mdb6lism.webp"/></item><item><title>Galgame 简评系列 2</title><link>https://hana-blog.top/blog/galgame-2</link><guid isPermaLink="true">https://hana-blog.top/blog/galgame-2</guid><description>小资历玩家的 Galgame 简评，仅代表个人观点。</description><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { GameRating, GameRatingCriteria, ManualTOC } from &apos;@/components/advanced&apos;&lt;/p&gt;
&lt;p&gt;免责说明：以下评分仅代表个人观点，旨在提供参考。每款游戏的体验因人而异，建议根据自己的喜好和需求进行选择。&lt;/p&gt;
&lt;p&gt;&amp;#x3C;ManualTOC
title=&apos;&apos;
categories={[
{
title: &apos;Galgame Rating Patches&apos;,
items: [
{ title: &apos;Patch 1&apos;, href: &apos;/blog/galgame-1&apos;, order: &apos;1&apos; },
{ title: &apos;Patch 2&apos;, href: &apos;/blog/galgame-2&apos;, order: &apos;2&apos; }
]
}
]}
/&gt;&lt;/p&gt;
&lt;h2&gt;美少女万华镜 -勿忘草与永远的少女-&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;美少女万华镜 -理与迷宫的少女-&lt;/h2&gt;
&lt;h2&gt;友爱俱乐部&lt;/h2&gt;
&lt;h2&gt;命运石之门&lt;/h2&gt;
&lt;h2&gt;命运石之门 0&lt;/h2&gt;
&lt;h2&gt;弹丸论破 希望的学园和绝望高中生&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;超级弹丸论破2 再见了绝望学园&lt;/h2&gt;
&lt;h2&gt;灰色的果实&lt;/h2&gt;
&lt;h2&gt;灰色的迷宫&lt;/h2&gt;
&lt;h2&gt;灰色的乐园&lt;/h2&gt;</content:encoded><h:img src="https://pic.hana0721.top/124956717_p0_master1200.83amt2xvmx.webp"/><enclosure url="https://pic.hana0721.top/124956717_p0_master1200.83amt2xvmx.webp"/></item></channel></rss>