Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

RL——离线强化学习整体介绍

  • 参考文献:
    • 综述1:A Survey on Offline Reinforcement Learning: Taxonomy, Review, and Open Problems(2022,最新更新2024年8月,持续更新)
    • 综述2:Offline Reinforcement Learning: Tutorial, Review, and Perspectives on Open Problems(2020)
    • 离线强化学习(A Survey on Offline Reinforcement Learning)

离线强化学习是什么?

  • 一句话描述:不与环境进行交互,只在固定数据集上进行策略学习的强化学习,也称为 Offline RL 或 Batch RL,自2020年以后基本都叫做Offline RL
  • 直接将off-policy方法使用到Offline RL场景会面临的核心问题是外推误差 ,外推误差(Extrapolation Error)的定义:off-policy值学习中,当前策略真实状态动作访问分布和数据集中的状态动作分布不匹配导致的一种误差,具体来说,包括Absent Data(状态动作对缺失),Training Mismatch(训练预测分布不一致),Model Bias(随机MDP的状态转移概率有偏差)等问题

离线强化学习的优缺点

  • 优点:
    • 应用安全:不需要与真实环境交互,能防止未知风险;
    • 训练高效:样本利用率高;
  • 缺点:
    • 外推误差:容易面临外推误差问题,导致策略学不好;
    • 样本限制:样本量多和行为策略探索性强的数据集较好,对于基于模仿学习的离线强化学习方法,一般需要行为策略是专家策略(本质也于外推误差相关,如果样本量太少,或者行为策略过于局限,都可能只收集到固定的某个局部状态或动作,加重外推误差问题)

离线强化学习数据集D4RL

  • 数据集原始论文:D4RL: Datasets for Deep Data-Driven Reinforcement Learning
  • D4RL的一些介绍:
    • D4RL: DATASETS FOR DEEP DATA-DRIVEN REINFORCEMENT LEARNING,知乎
    • offline RL | D4RL:最常用的 offline 数据集之一
  • 数据集安装:离线强化学习(Offline RL)系列2: (环境篇)D4RL数据集简介、安装及错误解决 - Jensen Wang的文章 - 知乎,直接安装D4RL会遇到一些问题,需要逐步解决
  • 数据集一般都会包含一些特性:
    • Narrow and Biased Data Distributions (NB) :少量且有偏数据集,即OOD问题可能很严重的数据
    • Undirected and Multitask Data (UM) :无方向和多任务数据,即当前收集到的数据不是为了解决问题而收集的。(理解:比如要出找到出口,但是行为策略不找出口而是随机开门关门打开柜子等操作)
    • Sparse Rewards (SR) :系数奖励,即奖励稀疏的场景
    • Suboptimal Data (SD) :次优数据,即使用次优策略收集的数据
    • Nonrepresentable Behavior Policies (NR) :不可表示的行为策略。(理解:当函数逼近器(function approximator)很难完全捕捉基础行为的复杂性时,策略难以被神经网络或其他函数逼近器表示出来)
    • Non-Markovian Behavior Policies (NM) :非马尔可夫行为策略,一般出现在人类Agent或者手动工程控制中?不遵循马尔可夫性?【TODO:待补充】
    • Realistic Domains (RD) :现实领域。即现实数据,不是模拟数据,可能会出现噪声等信息
    • Nonstationarity (NS) :非平稳性,即不稳定的MDP过程。在这个数据集中,Agent可能会遇到传感器故障、执行器退化或奖励函数更新的情况,导致随时间变化的MDP中的扰动(例如,泵的效率随时间下降)
  • D4RL和RL Unplugged环境

离线强化学习的分类

说明:目前没有非常官方的离线强化学习分类方法,本文的分类方法来源于论文A Survey on Offline Reinforcement Learning: Taxonomy, Review, and Open Problems(2022,最新更新2024年8月,持续更新)

  • 本文按照修改类型分类(Modification Types),即按照离线强化学习方法在标准多步AC(Multi-step Actor Critic)的基础上做了哪些修改(注:修改类型的对照基线模型是标准Multi-step AC方法),可以将修改类型分成以下几类
  • 各种修改类型的一些图示
  • 不同算法的修改类型归属(同一个算法可能同时使用多个修改类型)

Policy Constraints

  • 策略约束分为直接策略约束(Direct Policy Constriants)和隐式策略约束(Implicit Policy Constraints)两种
  • 直接策略约束方法 :学习策略 \(\pi_\theta\) 的同时估计行为策略 \(\pi_\beta\),同时限制策略 \(\pi_\theta\) 贴近行为策略 \(\pi_\beta\)
    $$
    \begin{align}
    J(\theta) &= \mathbb{E}_{s\sim d^{\pi_\theta}, a\sim\pi_\theta(\cdot\vert s)}[Q^{\pi}(s,a)] \\
    \text{s.t.} &\ D(\pi_\theta(\cdot\vert s), \hat{\pi}_\beta(\cdot\vert s)) \le \epsilon
    \end{align}
    $$
    • 其中D表示某个距离评估指标,一般常用KL散度
    • 需要直接评估行为策略,如果行为策略评估不准确,这种方式效果会比较差
  • 隐式策略约束方法 :不显示的估计行为策略 \(\pi_\beta\),只依赖于样本(行为策略 \(\pi_\beta\) 收集来的样本),通过修改目标函数来隐式的约束策略 \(\pi_\theta\),实现不评估行为策略 \(\pi_\beta\) 的同时对策略 \(\pi_\theta\) 施加约束
    • 首先通过策略提升推导得到如下目标函数:
      $$
      \begin{align}
      \mathcal{L}(\pi,\lambda) &= \mathbb{E}_{s\sim d^{\pi_\beta}(\cdot)}[\mathbb{E}_{ a\sim\pi(\cdot\vert s)}[\hat{A}^{\pi}(s,a)] + \lambda (\epsilon - D_{\text{KL}}(\pi(\cdot\vert s), \hat{\pi}_\beta(\cdot\vert s)))]
      \end{align}
      $$
    • 然后进一步求解优化问题的解如下:
      $$ \pi^*(a|s) = \pi_\beta(a|s)exp(\frac{1}{\lambda}\hat{A}^{\pi}(s,a)) $$
    • 进一步地,最小化 \(\pi_\theta\) 和 \(\pi^*\) 的KL散度,可以最终推导得到如下的目标函数:
      $$ J(\theta) = \mathbb{E}_{(s,a) \sim \mathcal{D}} \log \pi_\theta(a|s)exp(\frac{1}{\lambda}\hat{A}^{\pi}(s,a)) $$
    • 以上方式相当于一种加权极大似然估计(Weighted Maximum Likelihood),根据以上公式,我们不再需要显示的学习行为策略 \(\pi_\beta\),可以直接从 \((s,a)\) 中学习到最优策略
  • 策略约束有两种形式,一种是Distribution Matching Constraints(也称为Distribution Constraints),一种是Support Matching Constraints(也称为Support Constraints)
    • Distribution Constraints:指限制 \(\pi_\theta\) 和 \(\pi_\beta\) 这两个策略的分布足够接近
    • Support Constraints:不要求 \(\pi_\theta\) 和 \(\pi_\beta\) 这两个策略的分布足够接近,只需要限制从 \(\pi_\theta\) 采样的动作在 \(\pi_\beta\) 采样动作的支持集里面即可(支持集即 \(\pi_\beta(a|s)\) 分配了正概率的所有动作 \(a\) 的集合)

Importance Sampling

  • 通过重要性采样实现
    $$
    \mathbb{E}_{\tau\sim \pi_\beta}\Big[ w_{0:H}\sum_{t=0}^H\nabla_\theta\gamma^t \log \pi_\theta(a_t|s_t)\hat{Q}^{\pi}(s_t,a_t) \Big]
    $$
    • 其中 \(w_{0:H}\) 是重要性权重 \(w_t = \frac{\pi(a_t|s_t)}{\hat{\pi}_\beta(a_t|s_t)}\) 的乘积

Regularization

  • 基本思路是在优化目标上增加一个正则项,分为策略正则(Policy Regularization)和值正则(Values Regularization)
  • 策略正则(Policy Regularization)
    $$ J(\theta) = \mathbb{E}_{(s,a) \sim \mathcal{D}} [\hat{Q}^{\pi_\theta}(s_t,a_t)] + \mathcal{R}(\theta) $$
  • 值正则(Values Regularization)
    $$ J(\phi) = \mathbb{E}_{(s,a,s’) \sim \mathcal{D}}\Big[(r(s,a) + \gamma \mathbb{E}_{a’\sim\pi_{\text{off}}(\cdot|s)}[Q_\phi^\pi(s’,a’)] - Q_\phi^\pi(s,a))^2\Big] + \mathcal{R}(\phi) $$

Uncertainty Estimation

  • 基本思路是在保守(Conservative)RL和原始(Native)RL上做一些Trade off,基于我们对泛化能力的信任程度不同,可以选择不同程度的保守策略,对策略做不同程度的放松;这里的不确定性评估可以是对策略、值和模型的评估
    $$ J(\theta) = \mathbb{E}_{(s,a) \sim \mathcal{D}}\Big[ \mathbb{E}_{Q^\pi\sim\mathcal{P}_{\mathcal{D}}(\cdot)} [Q^\pi(s,a)] - \alpha U_{\mathcal{P}_{\mathcal{D}}} (\mathcal{P}_{\mathcal{D}}(\cdot)) \Big] $$
    • \(\mathcal{P}_{\mathcal{D}}(Q^\pi)\) 表示在数据集 \(\mathcal{D}\) 中,Q函数的分布
    • 其中 \(U_{\mathcal{P}_{\mathcal{D}}}(\cdot)\) 是对 \(\mathcal{P}_{\mathcal{D}}\) 的不确定性评估(这里的 \(\mathcal{P}_{\mathcal{D}}\) 是下角标)
      • 理解:在对数据集 \(\mathcal{D}\) 上对某个变量进行不确定性评估,这个变量也是与数据集相关的,所以 \(U_{\mathcal{P}_{\mathcal{D}}} (\mathcal{P}_{\mathcal{D}}(\cdot))\) 中数据集出现了两次

Model-based

  • 首先通过标准的监督学习回归方法构建模型预测状态转移概率 \(T_{\psi_T}(s_{t+1}|s_t,a_t)\) 和奖励函数 \(r_{\psi_r}(s_t,a_t)\)
  • 使用学习到的状态转移概率和奖励函数模型作为真实环境的一个代理,与策略交互
  • 在数据覆盖面广(探索足够充分)时,Model-based方法效果比较容易学习
  • Model-based方法也可以用到Online RL场景,在Offline RL场景中,由于无法与环境交互,所以无法修正一些错误的预估值,一种解决这个问题的方法是采用保守策略,类似于Uncertainty Estimation思想对奖励函数进行修正
    $$ \tilde{r}_{\psi_r}(s,a) = r_{\psi_r}(s,a) + \lambda U_r(s,a) $$
    • \(U_r(s,a)\) 表示不确定度,在数据集中出现过的 \((s,a)\),对应的不确定性较小,没有出现过的不确定性较大

One-step

  • 多步策略评估和策略提升出现OOD问题的原因是需要对策略 \(\pi_{\text{off}}\) 做策略评估,在选择动作评估目标Q值时,不可避免的容易出现 \(\pi_\beta\) 没有见过的动作
  • One-step方法的思想:先对策略 \(\pi_\beta\) 做策略评估得到准确的 \(Q^{\pi_\beta}(s,a)\),此时不会出现OOD,因为 \(a’\) 都是来自 \(\pi_\beta\) 的;接着从 \(Q^{\pi_\beta}(s,a)\) 中进行一步策略提取,找到最优策略
  • 在One-step的方法下,不需要担心OOD问题,因为我们从不需要访问OOD的状态动作对

Imitation Learning

  • 模仿学习是一类学习方法,这类方法的思路是通过模仿行为策略的行为来实现策略学习
  • 离线强化学习中的模仿学习常见的有行为克隆(Behavior Clone,BC)及其改进版本
  • BC方法要求行为策略是专家策略,BC的基本目标如下:
    $$ J(\theta) D(\pi_\theta(\cdot\vert s), \hat{\pi}_\beta(\cdot\vert s)) $$
    • \(D(\cdot,\cdot)\) 是f-divergence,可以是交叉熵等
    • 其他说明:行为策略本身比较优质时BC能学到较好的效果
  • 改进版本不要求行为策略是专家策略,常常通过丢弃劣质动作或者对高收益动作进行加权实现
    • 动作挑选 :通过Q值或者一些启发式方法识别劣质和优质的动作
    • 动作加权 :设计特殊的目标函数,让策略决策到高收益动作的概率更高,比如AWR的优化目标
    • 条件策略(Conditional Policy) :学习一个条件生成网络和条件策略网络,条件生成网络可在给定轨迹 \(\tau\) 下生成条件,条件策略网络会根据给定条件和状态来生成策略,该策略的目标就是使得决策能够最终生成给定轨迹 \(\tau\) ?【TODO:待补充】

Trajectory optimization

  • 直接建模状态动作的联合分布,即轨迹分布,然后在这些分布里面规划出优质的轨迹,轨迹里面就包含了动作
  • 给定任意的 \(s_0\),先试探性规划多步,找到最优轨迹,然后选择最优轨迹上的动作决策一步即可(注意,为了避免误差累计,一次规划仅进行一次决策)

Off-policy Evaluation (OPE)

  • 目标:给定待评估策略 \(\pi\) 和评估数据集 \(\mathcal{D}_e\),希望定义一个可以评估效果的OPE目标 \(\hat{J}(\pi)\)
  • 方法包含Model-based,Importance Sampling和Fit Q Evaluation
  • 为什么需要OPE?
    • 现实世界中,许多领域里面,直接使用真实环境预测去交互以评估策略效果是危险且成本高昂(包括时间成本和资源成本)的,而OPE提供了一个不需要与环境交互就可以评估策略效果的方案(虽然有时候不是很准确)
    • OPE可以用作超参数的选择

Model-based Approach

  • 类似Model-based强化学习方法,通过标准的监督学习回归方法构建模型预测状态转移概率 \(T_{\psi_T}(s_{t+1}|s_t,a_t)\) 和奖励函数 \(r_{\psi_r}(s_t,a_t)\)
  • 评估结果为:
    $$\hat{J}(\pi) = \mathbb{E}_{\tau\sim p_{\phi_T}(\cdot)}\Big[ \sum_{t=0}^H\gamma^t r_{\psi_r}(s_t,a_t) \Big]$$
    • 其中 \(p_{\phi_T}(\cdot)\) 表示按照策略 \(\pi\) 在环境 \(T_{\psi_T}(s_{t+1}|s_t,a_t)\) 中采样得到的trajectory分布

Importance Sampling (IS)

  • 利用重要性采样的性质完成评估
    $$\hat{J}(\pi) = \mathbb{E}_{\tau\sim p_{\hat{\pi}_\beta(\cdot)}}\Big[ w_{0:H}\sum_{t=0}^H\gamma^t r(s_t,a_t) \Big]$$
    • 其中 \( w_{i:j}\) 是重要性采样的权重乘积
      $$ w_{i:j} = \frac{\prod_{t=i}^j\pi(a_t|s_t)}{\prod_{t=i}^j\hat{\pi}_\beta(a_t|s_t)} $$
    • 其中每一项 \(w_t = \frac{\pi(a_t|s_t)}{\hat{\pi}_\beta(a_t|s_t)}\),实际上 \( w_{i:j}\) 也可以写为:
      $$ w_{i:j} = \prod_{t=i}^j w_t = \prod_{t=i}^j\frac{\pi(a_t|s_t)}{\hat{\pi}_\beta(a_t|s_t)} $$

Fit Q Evaluation (FQE)

  • 先使用策略评估方法学习一个Q值 \(Q_\phi^\pi\) (最小化贝尔曼误差即可),然后在数据集上评估该Q值的累计值
    $$ \hat{J}(\pi) = \mathbb{E}_{(s,a)\sim \mathcal{D}_e}[Q_\phi^\pi(s,a)] $$

离线强化学习方法补充

AWR(Advantage-Weighted Regression)

  • 参考链接:ADVANTAGE-WEIGHTED REGRESSION: SIMPLE AND SCALABLE OFF-POLICY REINFORCEMENT LEARNING, UC Berkeley, arXiv 2019
  • AWR 训练流程

BCQ

CQL

IQL

BRAC(Behavior-Regularized Actor-Critic)

  • 参考链接:(BRAC)Behavior Regularized Offline Reinforcement Learning, arXiv 2019, CMU & Google
  • BRAC 训练流程

XQL(Extreme Q-Learning)

  • 参考链接:Extreme Q-Learning: MaxEnt RL without entropy, ICLR 2023, Google
  • 也称为 \(\mathcal{X}\)-QL
  • XQL 训练流程

SQL(Sparse Q-Learning)

  • 参考链接:[待确认]
  • 参考链接:Sparse Q-learning with Mirror Descent, University of Massachusetts, arXiv 2012
  • 参考链接:SPARSE Q-LEARNING: OFFLINE REINFORCEMENT LEARNING WITH IMPLICIT VALUE REGULARIZATION, Offline RL Workshop 2023

RWR(Reward-Weighted Regression)

  • 参考链接:待确认

EDAC(Ensemble Diversity Actor-Critic)

  • 参考链接:待确认

AWAC(Advantage-Weighted Actor-Critic)

  • 参考链接:AWAC: Accelerating Online Reinforcement Learning with Offline Datasets, UC Berkeley, arXiv 2020
  • AWAC 整体方案介绍(Offline + Online 的训练方式)
  • AWAC 训练流程

Cal-QL(Calibrated Q-Learning)

  • 参考链接:Cal-QL: Calibrated Offline RL Pre-Training for Efficient Online Fine-Tuning, UC Berkely & Stanford University, NeurIPS 2023
  • Cal-QL 训练流程

Python——Ray-多节点集群启动


整体说明

  • Ray 集群的启动有多种方法,本文简单总结这些方法
  • 核心概念补充:
    • 头节点(Head Node) :集群的主节点,负责管理整个集群的资源、任务调度和元数据存储
    • 工作节点(Worker Node) :通过连接头节点加入集群,提供计算资源(CPU/GPU/内存等)

配置文件启动

  • 设置配置文件 cluster.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    cluster_name: my-ray-cluster
    max_workers: 2

    # 头节点配置
    head_node:
    InstanceType: m5.large
    ImageId: ami-0abcdef1234567890 # 选择包含Ray的镜像

    # 工作节点配置
    worker_nodes:
    InstanceType: m5.xlarge

    # 启动命令(可选,自定义节点初始化)
    setup_commands:
    - pip install pandas # 安装依赖
  • 主节点启动 ray up cluster.yaml

  • 其他节点加入集群 ray attach cluster.yaml

  • 停止容器 ray down cluster.yaml


Kubernetes 部署

  • 待补充

Python 程序内启动

  • 主节点使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import ray

    # 启动本地集群(自动创建头节点和工作进程)
    ray.init(
    num_cpus=4, # 模拟4核CPU
    num_gpus=1, # 模拟1块GPU
    port=6380, # 手动指定通信端口
    dashboard_host="0.0.0.0" # 允许外部访问Dashboard
    )

    # 验证集群
    print("集群节点数:", len(ray.nodes())) # 输出1(单节点模拟)

    # 关闭集群
    ray.shutdown()
  • 其他节点加入使用

    1
    2
    3
    4
    5
    6
    # 头节点启动Ray
    import ray
    ray.init()

    # 工作节点连接到头节点
    ray.init(address='头节点地址:6380')

使用 ray start 命令启动

  • ray start 是启动 Ray 集群节点的常用命令
  • 可分别分别使用 ray start 用于初始化头节点(Head Node)和工作节点(Worker Node)

启动头节点

  • 头节点是集群的入口,必须先启动

  • 基本命令格式:

    1
    ray start --head [其他可选参数]
  • 关键参数包括:

    参数 说明 示例
    --head 声明当前节点为头节点(必选) -
    --port 指定 Ray 内部通信端口(默认 6379,若被占用会自动切换) --port=6380
    --dashboard-host 允许外部访问 Ray dashboard 的主机地址(默认仅本地访问) --dashboard-host=0.0.0.0
    --dashboard-port Dashboard 端口(默认 8265) --dashboard-port=8266
    --num-cpus 手动指定该节点可用的 CPU 核心数(默认自动检测) --num-cpus=16
    --num-gpus 手动指定该节点可用的 GPU 数量(默认自动检测) --num-gpus=2
    --memory 限制节点可用内存(单位:字节,如 1000000000 表示 1GB) --memory=8000000000
    --object-store-memory 对象存储的内存上限(默认总内存的 30%) --object-store-memory=2000000000
    --block 启动后阻塞终端(不后台运行,便于调试) -
    --log-dir 指定日志目录(默认 ~/raylogs) --log-dir=/path/to/logs
  • 示例:启动一个允许外部访问 Dashboard、指定 CPU/GPU 资源的头节点:

    1
    2
    3
    4
    5
    6
    ray start --head \
    --port=6379 \
    --dashboard-host=0.0.0.0 \
    --dashboard-port=8265 \
    --num-cpus=12 \
    --num-gpus=1

启动工作节点

  • 工作节点需通过头节点的地址加入集群,命令格式:

    1
    ray start --address=<IP>:<port> [其他可选参数]
  • 关键参数说明:

    • --address:头节点的地址(必填,格式为 <IP>:<port>,即头节点启动时输出的地址)
    • 其他参数(如 --num-cpus、--num-gpus 等)与头节点相同,用于限制工作节点的资源
  • 示例:连接到 IP 为 192.168.1.100、端口为 6379 的头节点,同时指定工作节点的资源:

    1
    2
    3
    ray start --address='192.168.1.100:6379' \
    --num-cpus=8 \
    --num-gpus=0

使用 ray 命令验证集群状态

  • 可在任意节点执行下面脚本查看节点列表

    1
    ray status
    • 输出会显示集群中的所有节点及资源使用情况
  • 通过头节点的 http://<头节点IP>:8265(或其他指定端口) 查看集群监控、任务状态等

使用 ray 命令停止集群

  • 停止单个节点(包括工作节点或头节点):

    1
    ray stop
  • 特别注意:若头节点停止,整个集群会自动解散

附录:ray start 命令的其他高级配置

  • 可通过 --runtime-env 指定环境配置文件(如依赖安装、环境变量等):

    1
    ray start --head --runtime-env=runtime_env.yaml
  • 可通过 --redis-password 设置密码,防止未授权节点加入:

    1
    2
    3
    4
    5
    # 头节点
    ray start --head --redis-password='mysecret'

    # 工作节点
    ray start --address=<IP:port> --redis-password='mysecret'
  • 可通过 --log-dir 指定日志目录(默认 ~/raylogs):

    1
    ray start --head --log-dir=/path/to/logs

附录:通过 ray job submit 向已经启动的 Ray 集群提交任务

  • ray job submit 命令用于将作业提交到 Ray 集群

基本语法

  • 用法说明:

    1
    ray job submit [options] -- <entrypoint> [<entrypoint_args>]
    • [options] 是命令的可选参数
    • -- 之后的 <entrypoint> 是要执行的入口点脚本或命令
      • 可以是 -- python my_script.py 或 bash my_shell.sh
    • <entrypoint_args> 是传递给 <entrypoint> 的参数

常用参数

  • --address:指定 Ray 集群的地址,通常是集群头节点的地址和端口,如http://127.0.0.1:8265
  • --working-dir:指定作业的工作目录
    • 该目录下的文件会被同步到集群节点上,默认为当前目录
    • 启动脚本会有一些文件依赖,这里上传所有文件可以保证本地能访问的文件,集群也能访问
  • --runtime-env:用于指定作业的运行时环境,可以是一个 JSON 格式的字符串或 YAML 文件路径
    • 例如,可以通过该参数指定需要安装的 Python 包,如--runtime-env='{"pip": ["requests"]}'
  • --no-wait:提交作业后不等待作业完成,立即返回
    • 如果不指定该参数,命令会等待作业完成,并输出作业的日志和结果
  • --submission-id:指定作业的提交 ID
    • 如果不指定,Ray 会自动生成一个唯一的 ID

用法示例

  • 假设要提交一个 Python 脚本 my_script.py 到 Ray 集群,指定集群地址为 http://127.0.0.1:8265,工作目录为当前目录:

    1
    RAY_ADDRESS='http://127.0.0.1:8265' ray job submit --working-dir. -- python my_script.py
  • 假设提交一个 Python 脚本 train.py,并传递参数 --epochs 10 --batch-size 32,同时指定运行时环境需要安装 torch 和 numpy 包:

    1
    ray job submit --address=http://your-ray-cluster-address:8265 --runtime-env='{"pip": ["torch", "numpy"]}' -- python train.py --epochs 10 --batch-size 32

其他高级配置

  • py_modules :指定需要导入的自定义 Python 模块路径,支持将本地模块添加到 Python 路径(sys.path),示例如下:

    1
    runtime_env={"py_modules": ["./my_utils"]}  # 同步 my_utils 模块并添加到路径
  • env :指定预定义的环境名称(如 Ray 集群中已配置的共享环境),避免重复配置,示例如下:

    1
    runtime_env={"env": "shared-training-env"}  # 使用集群中预定义的环境
  • 特别说明:runtime_env 还支持其他非官方扩展,比如 verl 库中就为英伟达显卡配置了 nsight 工具的参数传入

    1
    2
    ./verl/trainer/main_ppo.py
    runner = TaskRunner.options(runtime_env={"nsight": nsight_options}).remote()

NLP——LLM内存优化技术总结

本文主要介绍内存优化相关技术


LLM内存优化技术总结

  • LLM(大型语言模型)在内存优化方面采用了多种技术,常见的方法包括

梯度检查点(Gradient Checkpointing)

  • 在前向传播时只保存部分激活值,其余的在反向传播时重新计算,主要用在预训练阶段
  • 特点:显著减少内存占用,但会增加计算量

混合精度训练(Mixed Precision Training)

  • 使用16位浮点数(FP16)代替32位浮点数(FP32)进行计算和存储,通过自动混合精度(AMP)工具实现,关键部分(如梯度更新)仍使用FP32以保证数值稳定性
  • 特点:减少内存使用并提升计算速度,但对显卡有要求,适用于支持FP16的硬件(如NVIDIA Tensor Core GPU)

模型并行(Model Parallelism)

  • 将模型的不同层分配到多个设备上,减少单个设备的内存负担
  • 特点:支持更大模型的训练,但增加了通信开销

数据并行(Data Parallelism)

  • 将数据批次分配到多个设备上,每个设备拥有完整的模型副本
  • 特点:通过增加设备数量来分摊内存压力

梯度累积(Gradient Accumulation)

  • 在多个小批次上累积梯度后再更新模型参数,多次累计梯度后一次更新参数,能够用小内存实现大梯度更新参数
  • 特点:减少单次内存需求,支持更大的批次训练

参数卸载(Parameter Offloading)

  • 将部分模型参数存储在CPU或磁盘上,需要时再加载到GPU,可通过框架(如DeepSpeed的ZeRO-Offload)自动管理参数的加载和卸载
  • 特点:减少GPU内存占用,但可能增加I/O开销

稀疏注意力机制(Sparse Attention)

  • 只计算输入序列中部分位置的注意力权重,通过设计稀疏模式(如局部窗口、随机采样)减少计算量
    • 稀疏注意力机制(Sparse Attention)是一种优化 Transformer 模型中注意力计算的技术,旨在减少计算复杂度和内存占用。它通过限制每个输入位置只与部分其他位置进行注意力计算,而不是与所有位置进行全连接计算,从而实现高效的计算和内存管理
  • 特点:降低内存和计算复杂度、支持更长序列等,对效果是有损的

量化(Quantization)

  • 将模型参数从高精度(如FP32)转换为低精度(如INT8),比如 QLoRA 等可以做到更低的量化,大幅降低模型微调和推理的内存
  • 特点:减少内存占用和计算量,对效果可能有损

知识蒸馏(Knowledge Distillation)

  • 用大模型训练小模型,使其性能接近大模型,小模型模仿大模型的输出
  • 特点:减少内存和计算资源需求,可加速模型的部署和推理

内存高效优化器(Memory-Efficient Optimizers)

  • 使用如Adafactor等优化器,减少存储优化状态的内存
    • AdaFactor是一种优化算法,旨在减少在训练深度学习模型时的内存占用,同时保持或提高模型性能。它是由Google的研究人员提出的一种自适应学习率优化器,其特点是显存成本(Memory Cost)是次线性的(Sublinear),意味着随着参数数量的增长,所需的额外内存不会线性增长
  • 特点:降低训练时的内存使用

分层训练(Layer-wise Training)

  • 逐层训练模型,每次只加载当前层的参数和梯度
  • 特点:减少内存需求,但可能影响模型性能

内存池(Memory Pooling)

  • 预先分配并复用内存块,减少频繁分配和释放的开销
  • 特点:提高内存使用效率

模型剪枝(Model Pruning)

  • 通过删减网络结构,移除不重要的神经元或连接,实现模型压缩
  • 特点:减少模型大小和内存占用,但这种方式不常用

低秩分解(Low-Rank Factorization)

  • 将大矩阵分解为多个小矩阵,常见的方式就是LoRA相关的技术,也可以和量化结合,如QLoRA等
  • 特点:减少内存和计算需求,适用于各种微调场景

vLLM推理框架

  • vLLM 是一个专注于高效推理的框架,要用于推理阶段 ,通过 PagedAttention、连续批处理、量化等技术优化内存和计算效率,显著提升吞吐量和响应速度
  • PagedAttention
    • 这是 vLLM 的核心技术,灵感来源于操作系统的虚拟内存分页机制。它通过分页管理注意力机制中的键值(KV)缓存,显著减少了内存浪费,并支持动态调整缓存大小,从而提高了吞吐量和内存利用率
  • 连续批处理(Continuous Batching)
    • vLLM 支持将多个请求批量处理,通过共享计算资源(如 KV 缓存)来减少重复计算,从而提高吞吐量
  • 量化技术
    • 支持多种量化方法(如 GPTQ、AWQ、FP8 KV Cache 等),通过降低模型参数的精度来减少内存占用和计算开销
  • 张量并行(Tensor Parallelism)
    • 支持将模型分布到多个 GPU 上运行,通过并行计算加速推理过程
  • 推测解码(Speculative Decoding) ,也称为 投机采样
    • 使用较小的模型预测词元,再用大模型验证结果,从而加速文本生成
  • Flash Attention
    • 优化 Transformer 模型的注意力计算,减少计算复杂度和内存占用
  • OpenAI 兼容 API
    • 提供与 OpenAI API 兼容的接口,便于集成到现有应用中
  • 多 LoRA 支持
    • 支持多 LoRA(低秩适应)模型,允许在同一框架下运行多个微调模型

ZeRO显存优化技术

  • ZeRO(Zero Redundancy Optimizer)是一种用于训练阶段的显存优化技术,主要用于训练阶段,通过分片存储、通信优化和混合精度训练等技术减少显存占用,支持更大规模的模型训练
  • ZERO 技术最初是微软在 2020 年的论文 ZeRO: Memory Optimization Towards Training Trillion Parameter Models 中被提出的,详细阐述了 ZERO 的三个阶段(ZERO-1、ZERO-2、ZERO-3)及其内存优化原理
  • ZERO 技术也是 DeepSpeed 框架的核心创新之一(注:DeepSpeed 是微软开发的一个用于大规模深度学习训练的优化库)
  • 分片存储(Sharding)
    • ZeRO 将模型参数、梯度和优化器状态分片存储到多个 GPU 上,从而减少单个 GPU 的内存占用。分为三个阶段:
      • ZeRO Stage 1(ZeRO-1) :仅分片优化器状态
      • ZeRO Stage 2(ZeRO-2) :分片优化器状态和梯度
      • ZeRO Stage 3(ZeRO-3) :分片优化器状态、梯度和模型参数
  • 通信优化
    • ZeRO 通过优化 GPU 间的通信(如 All-Reduce 和 Reduce-Scatter 等GPU通信操作),减少分布式训练中的通信开销
  • 混合精度训练
    • 支持 FP16 和 FP8 等低精度训练,减少显存占用并加速计算
  • 重计算(Gradient Checkpointing)
    • 在前向传播时只保存部分激活值,反向传播时重新计算其余部分,从而减少显存占用
  • 负载均衡
    • 在 MoE(Mixture of Experts)模型中,通过优化路由策略和负载分配,避免专家模型之间的负载不均衡
  • 后来微软 DeepSpeed 团队继续对 ZERO 技术进行扩展,退出了 ZeRO-Offload 和 ZeRO-Infinity 等高级技术:
    • ZeRO-Offload 可以将已划分的优化器状态和梯度卸载到 CPU 内存中
    • ZeRO-Infinity 是 ZeRO-3 的扩展,它可以利用 CPU 和 NVMe 内存来进一步扩展 GPU 的内存,支持训练更大型的模型

附录:大模型推理中的模型量化技术总结

  • 大模型推理中,模型量化旨在减少模型的存储和计算需求,同时尽量保持模型的性能

GPTQ(Gradient-based Post-training Quantization)

  • TLDR:基于梯度的后训练量化方法
  • 基本原理:
    • 在模型训练完成后,对模型权重进行量化
    • 通过优化目标函数来最小化量化误差,利用梯度调整量化时的权重误差,使量化后模型与未量化模型的表现尽可能接近
    • 采用误差反馈机制,将量化误差传播到后续层进行补偿,减少累积误差对模型输出的影响
  • 特点:
    • 适用于 8-bit 或更低的量化需求,尤其对大语言模型量化效果好
    • 不需要额外的训练数据,精度损失相对较小,特别适合复杂模型
    • 针对 GPU 使用进行了优化,在 GPU 推理时性能较好,能将权重动态去量化为 float16,提高性能的同时保持低内存占用

AWQ(Activation-aware Quantization)

  • TLDR:关注激活值的量化方法,量化过程中考虑激活值分布对模型性能的影响
  • 基本原理:
    • 分析激活值的分布特性,对激活值进行适应性处理
    • 采用非均匀量化,针对不同的激活值范围选择不同的量化尺度
  • 特点:
    • 精度较高,通过对激活值分布的考虑,能更好地保留模型的性能
    • 计算复杂度较大,因为需要分析激活值分布并进行非均匀量化操作

GGUF(Generalized Global Uniform Quantization Framework)

  • TLDR:一种通用的全局统一量化框架,用于处理大规模神经网络
  • 基本原理:
    • 通常采用全局统一量化策略,假设模型的所有层或某一类参数具有相似的分布,对整个模型的权重或激活值采用相同的量化参数
    • 采用均匀量化,将所有数值线性地映射到一个均匀的范围,并引入缩放因子,在推理阶段重定标量化后的数值,避免数值溢出或精度过低
  • 特点:
    • 简单高效,适用于资源受限的部署场景,如普通 CPU 环境
    • 兼容 Windows 和 Linux 操作系统
    • 提供从 2-bit 到 8-bit 的多级量化选项
    • 由于采用统一量化策略,可能导致某些模型层的精度损失

DL——Teacher-Forcing方法

本文主要介绍Transformer和Attention相关内容


整体总结

  • 教师强制(Teacher Forcing) 是一种在训练序列生成模型(包括循环神经网络 RNN、长短期记忆网络 LSTM 等)时使用的方法
  • 其核心思想是在训练过程中强制模型使用真实的目标序列作为输入 ,而非模型自身的预测结果,从而解决序列生成任务中可能出现的误差累积问题
  • 大模型的 SFT 方法就是一种 Teacher Forcing 方法,属于一种 Token-level 的行为克隆

Teacher Forcing 的基本原理

  • 在序列生成任务(如机器翻译、文本生成、语音识别等)中,模型需要根据历史输入和已生成的序列来预测下一个输出
  • 传统训练方式下,若直接使用模型前一步的预测结果作为下一步的输入,一旦某一步预测错误,后续预测可能会因误差累积而“偏离轨道”,导致训练不稳定
  • 教师强制的做法 :在每一步训练中,强制使用真实的目标序列(而非模型上一步的预测值)作为下一步的输入
    • 例如:在机器翻译中,当生成第二个词时,不使用模型预测的第一个词,而是直接使用参考译文中的第一个词,以此类推

Teacher Forcing 的具体流程(以LSTM为例)

  • 假设我们有一个序列生成任务,目标序列为 \( y_1, y_2, y_3, \dots, y_T \),模型输入为 \( x_1, x_2, \dots, x_T \),则训练过程如下:
    • 第一步 :输入 \( x_1 \),模型预测 \( \hat{y}_1 \),与真实值 \( y_1 \) 计算损失并更新参数
    • 第二步 :不使用 \( \hat{y}_1 \),而是将真实值 \( y_1 \) 作为输入,结合 \( x_2 \),模型预测 \( \hat{y}_2 \),计算损失并更新参数
    • 后续步骤 :重复上述过程,每一步都用真实的 \( y_{t-1} \) 作为当前步的部分输入,直至生成 \( \hat{y}_T \)。

Teacher Forcing 的优缺点分析

优点

  • 训练更稳定 :避免因早期预测错误导致的误差累积,模型更容易收敛
  • 加速收敛 :真实目标序列提供了更准确的监督信号,减少了训练迭代次数
  • 降低训练难度 :尤其适合复杂序列任务(如长文本生成),避免模型“发散”

缺点

  • 训练与推理偏差 :推理时(如实际生成文本)无法获取真实目标序列,需依赖模型自身预测,可能导致“暴露偏差(Exposure Bias)”(即训练时的输入分布与推理时不一致)
  • 缺乏抗噪能力 :模型可能过度依赖真实标签,对预测误差的鲁棒性较差

其他相关训练方法的对比

  • 教师强制 :始终使用真实标签作为输入
  • 为解决教师强制的“暴露偏差”问题,有人提出了 Scheduled Sampling(计划采样) 方法:
    • Scheduled Sampling :在训练初期以高概率使用真实标签,随着训练推进,逐渐增加使用模型预测值的概率,使模型逐步适应推理时的输入分布
    • Scheduled Sampling通过平衡“教师指导”和“自主预测”,减少训练与推理的差异,提升模型泛化能力

代码示例

  • PyTorch实现简单教师强制训练
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import torch
    import torch.nn as nn
    import torch.optim as optim

    class LSTMModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
    super(LSTMModel, self).__init__()
    self.lstm = nn.LSTM(input_size, hidden_size)
    self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x, hidden):
    lstm_out, hidden = self.lstm(x, hidden)
    output = self.fc(lstm_out)
    return output, hidden

    def train_with_teacher_forcing(model, input_seq, target_seq, criterion, optimizer):
    model.train()
    hidden = model.init_hidden()
    optimizer.zero_grad()
    loss = 0

    for t in range(target_seq.size(0)):
    output, hidden = model(input_seq[t].unsqueeze(0), hidden)
    input_seq[t+1] = target_seq[t] # 下一时间步的输入使用真实目标值(Teacher Forcing 方法的核心代码)
    loss += criterion(output, target_seq[t].unsqueeze(0))

    loss.backward()
    optimizer.step()
    return loss.item()

DL——TensorBoard的使用


整体说明

  • TensorBoard 是 TensorFlow 提供的可视化工具,能帮助理解、调试和优化深度学习模型
  • 安装 TensorBoard
    1
    pip install tensorboard

启动 TensorBoard

  • 可以使用命令行工具执行下面的命令从一个指定目录启动 TensorBoard:

    1
    tensorboard --logdir=path/to/logs --port=6006 --host=0.0.0.0
  • 参数解释:

    • --logdir:这个参数用于指定 TensorFlow 事件文件所在的目录
      • TensorBoard 会对该目录进行监控,一旦有新的事件文件生成,它就会实时更新可视化内容
      • 可以只指定一个目录,也可以通过逗号分隔的方式指定多个目录,或者使用通配符来匹配多个目录
      • 一个目录下可以有多个子目录,TensorBoard 会同时显示,可通过页面选择勾选目标子文件夹
    • --port:此参数用于设置 TensorBoard 服务监听的端口,默认使用 6006 端口
      • 如果你想在同一台机器上同时运行多个 TensorBoard 实例,可以为它们指定不同的端口后分别启动
    • --host:通过这个参数可以设置 TensorBoard 服务监听的 IP 地址
      • 默认是 localhost(即 127.0.0.1),此时仅能在本机上访问
      • 若你想让其他机器能够访问当前机器上的 TensorBoard,可将其设置为 0.0.0.0

TensorBoard 展示细节

  • TensorBoard 启动之后,通过浏览器即可访问
  • 最常用的 TensorBoard 页面包含 Scalars、Graphs、Histograms 等,点击这些选项卡可以查看不同类型的可视化数据

TensorBoard 文件的生成

PyTorch TensorBoard 示例

  • 使用 PyTorch 生成 TensorBoard 文件的示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import torch
    from torch.utils.tensorboard import SummaryWriter

    # 创建 SummaryWriter 对象,指定日志保存目录
    writer = SummaryWriter('runs/pytorch_demo')

    # 模拟训练过程
    for epoch in range(100):
    # 模拟损失值(通常是训练过程中计算得到的)
    loss = 0.9 ** epoch

    # 记录损失值到 TensorBoard
    writer.add_scalar('Loss/train', loss, epoch)

    # 模拟模型权重
    weights = torch.randn(10) * (0.95 ** epoch)

    # 记录直方图到 TensorBoard
    writer.add_histogram('Weights', weights, epoch)

    # 关闭 writer
    writer.close()

PyTorch TensorBoard 示例

  • 使用 TensorFlow 生成 TensorBoard 文件的示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import tensorflow as tf
    import numpy as np

    # 创建 SummaryWriter 对象,指定日志保存目录
    writer = tf.summary.create_file_writer('runs/tensorflow_demo')

    # 模拟训练过程
    for epoch in range(100):
    # 模拟损失值
    loss = 0.9 ** epoch

    # 记录损失值到 TensorBoard
    with writer.as_default():
    tf.summary.scalar('Loss/train', loss, step=epoch)

    # 模拟模型权重
    weights = np.random.randn(10) * (0.95 ** epoch)

    # 记录直方图到 TensorBoard
    with writer.as_default():
    tf.summary.histogram('Weights', weights, step=epoch)

一些说明

  • TensorBoard 可以同时显示多个项目的数据,如上述示例就在同一个目录 runs 下分别创建了 PyTorch 和 TensorFlow 的文件夹
  • 训练过程中数据不会自动刷新,可以随时刷新浏览器查看实时更新的数据

Python——Ray-使用笔记


远程调用时传入的函数指针必须是远程函数

  • 在 Ray 中不支持直接传入 local 函数指针作为远程函数的执行对象,需通过 Ray 装饰器(@ray.remote)将函数注册为远程可执行,再通过 函数名.remote() 调用(本质是基于函数标识而非指针传递)
  • 总结:
    • 不推荐将普通函数作为参数传递给 Ray 远程函数
    • 推荐使用 @ray.remote 装饰器或在远程函数内部定义逻辑
    • 注意:一些代码在单机环境下可能碰巧能运行,但不具有可移植性和可靠性(这一点需要注意 Ray 本地调试通过可能也无法分布式运行)

错误示例(未注册本地函数)

  • 若 add 未被 @ray.remote 注册,它只是一个本地函数 ,无法在 Ray 分布式环境中执行,直接传递给远程函数(如 execute_func)会报错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import ray

    ray.init(ignore_reinit_error=True)

    # 未注册的本地函数
    def add(a, b):
    return a + b

    # 已注册的远程函数
    @ray.remote
    def execute_func(func, x, y):
    # 此处调用本地函数会失败,因为 func 在远程节点无定义
    # # 远程节点的工作进程无法导入本地主模块的 add_local 函数,也无法序列化传递普通函数,可能会直接抛出 SerializationError
    # # 单进程/单节点下调用指针函数可以执行,但是分布式情况下,local_func 无法被序列化,会出错
    return func(x, y) # 报错:NameError,PicklingError 或 SerializationError

    # 调用会抛出异常
    try:
    result = ray.get(execute_func.remote(add, 4, 6))
    except Exception as e:
    print("错误:", e) # 提示无法序列化或找不到函数

    ray.shutdown()
  • 核心原因:Ray 远程函数执行依赖序列化传输和集群节点间代码同步

    • 未注册的本地函数无法被序列化为集群可识别的任务,且远程节点没有该函数的定义,会导致执行失败

正确示例(远程函数调用)

  • Ray 的远程函数依赖集群调度,通过 @ray.remote 显式注册后使用远程调用函数调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import ray

    ray.init(ignore_reinit_error=True)

    # 定义远程函数(会注册到 Ray 集群)
    @ray.remote
    def add(a, b):
    return a + b

    # 远程函数,可接收其他远程函数的调用结果
    @ray.remote
    def execute_func(func, x, y):
    # 这里 func 是远程函数标识,通过 .remote() 触发执行
    result = ray.get(func.remote(x, y)) # 使用远程调用的方式调用函数指针,实现调用远程函数,正确!
    # result = func(x, y) # remote 函数无法被直接调用,错误!
    # result = add(x,y) # remote 函数无法被直接调用,错误!
    # result = add_local(x, y) # add_local 当做 local 函数调用(注意:不再是指针传入),正确!
    return result

    # # 不使用 remote 直接调用 远程函数,错误
    # result1 = add(2, 3)

    # 使用remote直接调用远程函数,正确
    result1 = ray.get(add.remote(2, 3))
    print("直接调用结果:", result1) # 输出:5

    # 间接通过另一个远程函数调用(模拟"传递函数逻辑")
    result2 = ray.get(execute_func.remote(add, 2, 3))
    print("间接调用结果:", result2) # 输出:10

    ray.shutdown()
  • Ray 的远程函数依赖集群调度,需通过 @ray.remote 显式注册,无法像本地代码那样传递函数指针(内存地址在分布式环境中无效)

  • 若需在远程函数中复用其他函数逻辑,直接传递已注册的远程函数名(如示例中的 add),再通过 func.remote() 调用即可

Python——Ray-option函数讲解


整体说明

  • 在 Ray 框架中,.option() 是用于配置 Actor 或远程函数(Task) 运行时属性的方法,其参数主要围绕资源分配、调度策略、容错机制等核心功能
  • 注意事项
    • 所有参数均需符合 Ray 框架的预定义类型,传入未支持的参数会抛出错误
    • 不同 Ray 版本可能新增或调整参数,建议结合官方文档(对应版本)查阅细节
    • 这些参数仅用于配置运行时属性,自定义业务参数需通过 Actor 初始化或 Task 函数参数传递(见前文说明)

Actor 配置示例(Task 类似)

  • Actor 使用 .option() 函数的示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @ray.remote
    class MyActor:
    pass

    # 配置 Actor 资源、名称和重启策略
    actor = MyActor.options(
    num_cpus=1,
    num_gpus=0.5,
    name="my_actor",
    max_restarts=2,
    runtime_env={"env_vars": {"LOG_LEVEL": "INFO"}}
    ).remote()

通用核心参数(Actor 和 Task 均支持)

  • num_cpus 参数(类型:int 或 float)
    • 指定运行该 Actor/Task 所需的 CPU 核心数(支持小数,如 0.5 表示半核)
    • 示例:MyActor.options(num_cpus=2).remote()
  • num_gpus 参数(类型:int 或 float)
    • 指定所需的 GPU 数量(需集群实际有 GPU 资源)
    • 示例:my_task.options(num_gpus=1).remote()
  • resources 参数(类型:dict(键为资源名称,值为数量))
    • 指定自定义资源需求(如特定硬件、加速器等)
    • 示例:options(resources={"custom_accelerator": 1})
  • runtime_env 参数(类型:dict)
    • 配置运行环境(如依赖库、环境变量、工作目录等),确保 Actor/Task 在一致的环境中运行
    • 示例:options(runtime_env={"pip": ["numpy==1.21.0"]})
  • name 参数(类型:str)
    • 为 Actor/Task 指定名称,用于日志追踪或通过名称查找 Actor(仅 Actor 有效)
    • 示例:MyActor.options(name="worker-1").remote()

Actor 专属参数(仅 Actor 支持)

  • max_restarts 参数(类型:int)
    • 指定 Actor 崩溃后的最大重启次数(默认 -1 表示无限重启,0 表示不重启)
    • 示例:options(max_restarts=3)
  • max_task_retries 参数(类型:int)
    • 指定 Actor 处理单个任务时的最大重试次数(任务失败后重试)
    • 示例:options(max_task_retries=2)
  • lifetime 参数(类型:str(可选值:"detached" 或 "non_detached"))
    • 设置 Actor 生命周期。"detached" 表示 Actor 可脱离创建它的进程独立存在(进程退出后不销毁)
    • 示例:options(lifetime="detached")
  • placement_group 参数(类型:PlacementGroup 实例)
    • 将 Actor 绑定到特定的 放置组(Placement Group),优化资源 locality(本地性)

Task 专属参数(仅远程函数支持)

  • retry_exceptions 参数(类型:bool 或 tuple)
    • 指定 Task 失败时是否重试,或仅对特定异常重试
    • 示例:options(retry_exceptions=(ConnectionError,))
  • num_returns 参数(类型:int)
    • 指定 Task 返回值的数量(默认 1,用于多返回值场景)
    • 示例:@ray.remote(num_returns=2) def f(): return 1, 2

其他实用参数

  • priority 参数(类型:int)
    • 设置 Task/Actor 任务的调度优先级(数值越高越优先,仅部分调度器支持)
  • memory 参数(类型:int)
    • 指定所需的内存量(字节),超过会被终止(需集群启用内存限制)
  • object_store_memory 参数(类型:int)
    • 指定 Task 可使用的对象存储内存量(字节)

附录:runtime_env 参数的详细说明

  • 在 Ray 框架中,runtime_env 是 option() 方法中用于配置 运行时环境 的核心参数
  • runtime_env 参数的作用是确保远程任务(Task)或 Actor 在分布式集群中运行时,拥有一致的依赖环境、配置和资源,解决“本地能跑,集群跑不通”的环境一致性问题
  • runtime_env 接收一个字典作为参数,支持多种环境配置项,覆盖依赖管理、环境变量、文件同步等核心场景:
  • 工作原理:当通过 option(runtime_env=...) 配置环境后,Ray 会在任务/Actor 启动前执行以下操作:
    • 1)在 提交任务的节点 上收集 runtime_env 定义的依赖、文件和配置
    • 2)将这些资源同步到 集群中的目标节点(通过 Ray 的对象存储或分布式文件系统)
    • 3)在目标节点上自动创建隔离的运行环境(如虚拟环境、Conda 环境),安装依赖并注入环境变量
    • 4)任务/Actor 在该隔离环境中启动,确保环境一致性

更多讨论

  • 在分布式计算中,集群节点的环境可能存在差异(如依赖库版本、环境变量、工作目录等)
  • runtime_env 通过预先定义环境配置,让 Ray 自动在所有执行任务的节点上同步这些配置,确保任务/Actor 在 完全一致的环境 中运行,避免因环境差异导致的错误(如“ModuleNotFoundError”“版本不兼容”等)
  • 适用场景
    • 确保所有 worker 节点使用相同版本的框架(如 PyTorch、TensorFlow)和依赖库
    • 同步本地自定义模块或配置文件到集群,避免手动在每个节点部署代码
    • 不同任务/Actor 可使用独立的依赖环境,避免版本冲突
  • 其他问题
    • 初次加载的性能开销:首次使用 runtime_env 时,同步依赖和文件可能需要时间(尤其是大文件或复杂依赖),后续任务会复用缓存
    • 确保输入路径可读:working_dir、wheel 等路径需确保提交节点和集群节点均可访问(本地路径需为集群共享存储路径,如 NFS)
    • 至少要提前安装 conda 等包:部分配置(如 conda)需集群节点预先安装 Conda,否则会失效

依赖库管理(确保三方库版本一致)

  • pip :指定需要安装的 Python 依赖包及版本,支持通过列表或 requirements.txt 路径配置,示例如下:

    1
    2
    3
    4
    5
    # 直接指定依赖
    runtime_env={"pip": ["numpy==1.24.3", "pandas==2.0.3"]}

    # 通过 requirements.txt 配置
    runtime_env={"pip": "requirements.txt"}
  • conda :指定 Conda 环境配置,支持通过 environment.yml 路径或字典定义环境,示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 通过 environment.yml 配置
    runtime_env={"conda": "environment.yml"}

    # 直接定义 Conda 环境
    runtime_env={
    "conda": {
    "dependencies": ["python=3.9", "numpy=1.24.3"]
    }
    }
  • wheel :指定本地 Wheel 包路径,用于安装自定义或私有库(需确保集群节点可访问路径),示例如下:

    1
    runtime_env={"wheel": "./my_custom_lib-0.1.0-py3-none-any.whl"}

环境变量与配置注入

  • env_vars :定义任务/Actor 运行时的环境变量,键值对形式传递,示例如下:

    1
    2
    3
    4
    5
    6
    runtime_env={
    "env_vars": {
    "LOG_LEVEL": "INFO", # 日志级别
    "DATA_PATH": "/data/training" # 数据路径
    }
    }
  • config :传递自定义配置字典,可在任务/Actor 中通过 ray.get_runtime_context().runtime_env.get("config") 获取,用于业务参数传递,示例如下:

    1
    runtime_env={"config": {"batch_size": 32, "epochs": 10}}

文件与目录同步(确保资源可访问)

  • working_dir :指定工作目录,Ray 会将该目录下的所有文件同步到执行任务的节点,确保代码、配置文件等资源可访问。支持本地路径或 Git 仓库 URL,示例如下:

    1
    2
    3
    4
    5
    # 同步本地目录
    runtime_env={"working_dir": "./my_project"}

    # 同步 Git 仓库(支持分支/标签)
    runtime_env={"working_dir": "https://github.com/my_repo.git#branch=main"}
  • excludes :配合 working_dir 使用,指定同步时需要排除的文件/目录(如日志、缓存文件),避免冗余同步,示例如下:

    1
    2
    3
    4
    runtime_env={
    "working_dir": "./my_project",
    "excludes": ["*.log", "venv/", "data/*"] # 排除日志、虚拟环境和数据目录
    }

其他高级配置

  • py_modules :指定需要导入的自定义 Python 模块路径,支持将本地模块添加到 Python 路径(sys.path),示例如下:

    1
    runtime_env={"py_modules": ["./my_utils"]}  # 同步 my_utils 模块并添加到路径
  • env :指定预定义的环境名称(如 Ray 集群中已配置的共享环境),避免重复配置,示例如下:

    1
    runtime_env={"env": "shared-training-env"}  # 使用集群中预定义的环境

附录:option 中使用 runtime_env 和 启动参数传入 yaml 文件的区别

  • 在 Ray 中,可以使用 ray job submit 命令提交任务到已经启动的 Ray 集群中
  • ray job submit 命令的 --runtime-env 参数也可以通过传入 yaml 文件(或 JSON 格式的字符串)指定 runtime-env 参数
  • 至此,我们有了两种指定方式:
    • 在代码中通过 option 函数(如 @ray.remote 的 runtime_env 参数)指定运行时环境
    • 在 ray job submit 命令中通过 --runtime-env 参数传入 yaml 文件指定运行时环境
  • 两者的生效级别不同:
    • --runtime-env=./runtime_env.yaml 是 全局级别的配置 ,会为整个 Ray Job 中的所有任务(包括所有 Actor、任务函数)设置默认的运行时环境
      • 适用于需要为整个作业统一配置环境的场景(如统一的工作目录、环境变量等)
    • 代码中 option 函数指定(如 @ray.remote(runtime_env=...))是局部级别的配置 ,仅对当前修饰的 Actor 或任务生效
      • 适用于为特定任务/Actor 单独设置差异化环境的场景(如某个任务需要额外的依赖包,而其他任务不需要)
  • 两者的优先级不同:
    • 局部配置(代码中 option 函数)的优先级高于全局配置(命令行 --runtime-env)
    • 当两者配置不冲突时,会进行合并(例如全局配置了 env_vars,局部配置了 pip,最终环境会同时包含这两者)
    • 当两者配置冲突时(如同一环境变量在两处被设置为不同值),局部配置会覆盖全局配置

RL——AlphaGo系列算法


AlphaGo整体说明

  • AlphaGo是强化学习的阶段性集大成者,其核心思想值得细细推敲

AlphaGo棋局介绍

  • AlphaGo 的目标是解决围棋对弈问题 ,即在标准的 19×19 围棋棋盘上(共381维),通过观察棋盘情况选择最优的落子动作,击败对手
  • 输入 :当前棋盘状态(包括棋子分布和历史信息)
  • 输出 :下一步落子的最优位置(或概率分布)
  • 挑战 :
    • 围棋的状态空间复杂度高达 \(3^{361}\)(约\(10^{172}\)量级),无法暴力搜索
    • 围棋的决策步长约100+步,决策需要结合长期策略收益
    • 围棋的评估函数难以设计(难以量化当前局面是否有“局面优势”)

ALphaGo问题建模

  • 状态空间:\(19 \times 19 \times N\),这里的N在不同版本不一样,比如17=16+1时,16是用于记录最近 8 步的状态(相当于记录了窗口),1则表示该哪一方下棋,其他还有一些需要理解围棋规则才能解释
  • 动作空间:离散动作空间,362个动作可选(361位置 + 1 Pass)
  • 奖励函数:对局结束时,胜利方奖励+1,失败方-1,平局为0(围棋通常无平局)

AlphaGo 训练过程(分三个阶段)

第一阶段:行为克隆(监督学习模仿人类专家)

  • 基于 KGS 围棋平台上的 3000 万局人类对弈棋谱,学习人类专家的落子的模式
  • 通过监督学习训练一个策略网络(Policy Network) ,输入棋盘状态,输出人类选手的落子概率分布
    • 网络结构:13 层卷积神经网络(CNN)
    • 准确率:57%(预测人类下一步动作)

第二阶段:强化学习(自我对弈优化,策略梯度优化)

  • 通过策略梯度法,进一步优化策略网络,超越人类水平
    • 自我对弈 :使用初始策略网络生成大量自我对局数据
    • 策略梯度 :通过胜负结果优化策略网络(即强化学习策略网络),使其更倾向于获胜的走法
    • 回报函数 :对局结束时获胜方获得 +1 奖励,失败方获得 -1

第三阶段:价值网络学习(局面评估)

  • 价值网络学习阶段的目标是训练一个价值网络(Value Network) ,预测当前局面的胜率(替代蒙特卡洛rollout的耗时评估)
  • 通过自我对弈生成 3000 万组棋盘状态及最终胜负结果,学习一个标量值(-1 到 +1),表示当前玩家获胜的概率

AlphaGo 决策过程

  • AlphaGo 的实时决策基于蒙特卡洛树搜索(MCTS) ,结合神经网络的输出:
  • 选择(Selection) :从根节点(当前棋盘状态)出发,通过树策略(如UCT算法)选择子节点,平衡探索与利用,使用策略网络优先选择高概率的落子分支
  • 扩展(Expansion) :当遇到未探索的节点时,用策略网络生成可能的落子概率,扩展树结构
  • Evaluation 对价值网络评估胜率,和策略网络模拟rollout到终局得到的奖励两者做加权结合
  • 回溯(Backup) :将叶子节点的评估结果反向传播更新路径上的节点统计量(访问次数、平均胜率)
  • 最终决策(Decision) :搜索结束后,选择访问次数最多的节点对应的落子动作

AlphaGo 相关思考

  • 以模仿学习作为强化学习策略的冷启动是个不错的想法
  • 使用MCTS来决策有助于提升模型决策能力,在MCTS下,随着围棋的进行,搜索空间变小,AlphaGo相对人类的优势会越来越明显

附录:AlphaGo 的迭代版本

版本 发版时间 训练数据 核心算法 计算需求 棋力提升
AlphaGo Fan 2015年10月 人类棋谱(监督学习) SL + MCTS 中等(分布式计算) 首次击败职业棋手(樊麾)
AlphaGo Lee 2016年3月 人类棋谱 + 自我对弈 SL + RL + MCTS + 价值网络 高(1202 CPU + 176 GPU) 超越人类顶尖(李世石)
AlphaGo Master 2017年1月(Master)
2017年5月(正式对战柯洁)
纯自我对弈 纯RL + 深度网络 低(单机) 完胜人类第一柯洁
AlphaGo Zero 2017年10月 从零开始自我对弈 纯RL + ResNet + MCTS 极低(4 TPU) 100:0击败AlphaGo Lee
AlphaZero 2017年12月 多棋类自我对弈 通用RL + 高效搜索 低(单机) 8小时超越AlphaGo Zero

RL——BCQ

  • 参考链接:【笔记】BCQ详解
    • 原作者的PPT
  • 参考链接:BCQ姊妹篇:Discrete BCQ - Metaqiang的文章 - 知乎
  • 参考链接:【代码速读】(RL)1.BCQ - 一条的文章 - 知乎

BCQ 整体介绍

  • BCQ(Batch-Constrained deep Q-learning)分为连续版本(Off-Policy Deep Reinforcement Learning without Exploration,2019年8月)和离散版本(Benchmarking Batch Deep Reinforcement Learning Algorithms,2019年10月)两篇文章,一作是同一个作者
  • 外推误差(Extrapolation Error)的定义:off-policy值学习中,当前策略真实状态动作访问分布和数据集中的状态动作分布不匹配导致的一种误差

    Extrapolation error is an error in off-policy value learning which is introduced by the mismatch between the dataset and true state-action visitation of the current policy

  • 背景:off-policy的策略理论上可以从任意行为策略采样的数据中学习最优策略,但是直接将off-policy策略应用到Offline RL(也称为Batch RL)场景中可能面临,Absent Data(状态动作对缺失),Training Mismatch(训练预测分布不一致),Model Bias(随机MDP的状态转移概率有偏差)等问题
  • BCQ的基本思想:采取保守策略,让学到的策略对应的状态动作访问空间尽量只在出现过的数据集上,或者相近的数据上
    • 基本方法:主要通过限制 \(Q(s’,\pi(s’))\) 中的 \(\pi(s’)\) 不要偏离数据集太多来实现

BCQ 连续版本

关键实验

  • 实验设置:

    • 第一个实验(Final Buffer),使用DDPG算法在线训练一个智能体,将智能体训练过程中与环境交互的所有数据保存下来,利用这些数据训练另一个离线DDPG智能体
    • 第二个实验(Concurrent),使用DDPG算法在线训练一个智能体,训练时每次从经验回放池中采样,并用相同的数据同步训练离线DDPG智能体,甚至保持训练时使用的数据和数据顺序都完全相同
    • 第三个实验(Imitation),使用DDPG算法在线训练一个智能体,将该智能体作为专家,与环境交互采集大量数据,利用这些数据训练另一个离线DDPG智能体
  • 实验结果:

    • 第三个实验中使用的样本最好,但是训练得到的离线智能体效果最差,原因分析主要是外推误差导致
    • 三个实验的离线DDPG智能体都有不同情况的Q值高估问题,其中第三个实验的Q值高估问题最为严重(注意图2中看起来高估问题大于图1中,其实不是,是因为图二的量纲较小导致的)

理论推导

  • 对于给定的真实MDP和数据集 \(\mathcal{B}\),定义外推误差
    $$\epsilon_\text{MDP}(s,a) = Q^\pi(s,a) - Q_{\mathcal{B}}^\pi(s,a)$$
  • 则有外推误差可推导得到如下结论:

训练流程

  • 整体流程概览:

  • 训练流程解释:

    • 训练时,使用4个Q网络(其中两个是Target Q网络),1个策略网络和扰动网络
    • 两个Q网络的用途是在计算Q值目标时做他们最大最小值的凸组合(实际上就是最大最小值的加权平均),类似Twin Q中取两个Q的最小值的方法, \(y\) 值计算方法(流程中 \(\color{red}{\text{公式(13)}}\) )
      $$ y = r + \gamma \max_{a_i}\Big[ \lambda \min_{j=1,2}Q_{\theta_j}(s’,a_i) + (1-\lambda)\max_{j=1,2}Q_{\theta_j}(s’,a_i) \Big] $$

Serving步骤

  • 给定一个状态 \(s\)
  • \(\{ a_i \sim G_w(s) \}_{i=1}^n\):通过conditional VAE网络 \(G_w(s)\) 采样 \(n\) 个动作
  • \(\xi_{\phi}(s,a_i,\Phi)\):将这些状态和动作经过扰动网络,扰动网络输出是在 \([-\Phi,\Phi]\) 内的,得到的扰动值
  • 将扰动添加到原始动作上,再将动作经过Q网络,选取能使Q value最大的动作
  • 最终总结如下:
    $$ \pi(s) = \mathop{\arg\max}_{a_i + \xi_{\phi}(s,a_i,\Phi)} Q_\theta(s, a_i + \xi_{\phi}(s,a_i,\Phi)), \quad with \quad \{ a_i \sim G_w(s) \}_{i=1}^n $$
  • 训练流程解释:
    • 相对普通的DQN,主要改进点在于学习Q的目标值选择时动作受到限制,动作与行为策略(离线数据集)的动作差异不能太大
    • 学习Q值时,使用的是Huber Loss
      $$
      l_{\mathcal{k}}(\delta) =
      \begin{cases}
      \ 0.5\delta^2& \text{if}\ \delta \le \mathcal{k}\\
      \mathcal{k}(|\delta| - 0.5\mathcal{k})& \text{otherwise.}
      \end{cases}
      $$

BCQ 离散版本

训练流程

  • 整体流程概览:

Serving 步骤

  • 按照如下策略决策:
    $$ \pi(s) = \mathop{\arg\max}_{a\vert\frac{G_w(a|s)}{\max_\hat{a} G_w(\hat{a}|s)} \gt \tau} Q_\theta(s,a) $$

RL——CQL

  • 参考链接:
    • 原始论文:NIPS 2020, Conservative q-learning for offline reinforcement learning
    • 【论文分享】Conservative Q-Learning for Offline Reinforcement Learning
    • Conservative Q-Learning for Offline Reinforcement Learning:手打公式
    • 离线强化学习系列3(算法篇): 值函数约束-CQL算法详解与实现:手打公式
    • github.com/aviralkumar2907/CQL:CQL实现源码

CQL 在解决什么问题?

  • 分布偏移(distribution shift) :分布偏移主要是模型训练和预测时的分布存在差异,即训练数据集中的数据分布(训练)与实际决策策略下环境反馈的数据分布(预测)之间的差异。
    • 这种差异可能导致学习到的策略在实际应用中表现不佳,因为该策略是在一个与实际环境不完全相同的数据分布上学习得到的
    • 离线强化学习中这种问题会放大,特别地,会导致OOD问题
  • OOD(Out-of-distribution) :OOD通常指在训练集中没有被观察到过的样本(状态或状态-动作对),实际上,出现频率极低的样本也可以算作一定程度上的OOD样本
    • OOD问题是由于分布偏移导致的,理论上,没有分布偏移问题就不存在OOD问题 ,因为模型不会遇到这些未观察过的样本
  • OOD 问题容易导致值高估(Overestimation of values)问题 :强化学习的迭代公式一般都是找时的动作最大的动作,这本身导致的容易值高估的现象

CQL 相关推导

一些定义

  • 数据集 \(\mathcal{D} \sim d^{\pi_{\beta}}(\mathbf{s})\pi_{\beta}(\mathbf{a} \mid \mathbf{s})\),即数据是行为策略 \(\pi_\beta\) 与环境交互来得到的
  • 对于任意 \((s_0, a_0) \in \mathcal{D}\),有经验行为策略(empirical behavior policy)为:
    $$ \hat{\pi}_\beta(a_0\vert s_0) = \frac{\sum_{(s,a) \in \mathcal{D}}\mathbf{1}(s=s_0,a=a_0)}{\sum_{s\in \mathcal{D}}\mathbf{1}(s=s_0)} $$

回顾贝尔曼算子

  • 一般的贝尔曼算子(Bellman Operator),写作 \(\mathcal{B}^\pi\),重复对 \(Q(s,a)\) 使用 \(\mathcal{B}^\pi\),持续迭代可收敛到策略 \(\pi\) 对应的Q值 \(Q^{\pi}(s,a)\) 值:
    $$
    \mathcal{B}^\pi Q = r(s, a) + \gamma \mathbb{E}_{s^\prime \sim p(s^\prime \vert s, a), a^\prime \sim \pi(a^\prime\vert s^\prime)}[Q (s^\prime, a^\prime)]
    $$
  • Q-Learning的迭代公式,相当于策略是找Q值最优的那个动作,通过对Q重复使用如下贝尔曼最优算子(Bellman Optimality Operator),写作 \(\mathcal{B}^{\pi^*}\) 或者 \(\mathcal{B}^*\):
    $$ \mathcal{B}^{*} Q(\mathbf{s}, \mathbf{a})=r(\mathbf{s}, \mathbf{a})+\gamma \mathbb{E}_{\mathbf{s}^{\prime} \sim P\left(\mathbf{s}^{\prime} \mid \mathbf{s}, \mathbf{a}\right)}\left[\max _{\mathbf{a}^{\prime}} Q\left(\mathbf{s}^{\prime}, \mathbf{a}^{\prime}\right)\right] $$

Offline RL 中的贝尔曼算子

  • 在 Offline RL 中,我们定义新的贝尔曼算子 \(\hat{\mathcal{B}}^\pi\),即更新只在固定数据集上进行,即对于给定的数据集 \(\mathcal{D} = { (s,a,r,s’)} \sim \pi_\beta\), \(\hat{\mathcal{B}}^\pi\) 的更新都发生在数据集上
  • 对于给定数据集 \(\mathcal{D} \sim \pi_\beta\) 上的贝尔曼算子 \(\hat{\mathcal{B}}^\pi\),我们定义 \(\hat{\mathcal{B}}^\pi\) 如下:
    $$
    \hat{\mathcal{B}}^\pi \hat{Q} = r(s, a) + \gamma \mathbb{E}_{s^\prime \sim \mathcal{D}, a^\prime \sim \pi(a^\prime\vert s^\prime)}[\hat{Q} (s^\prime, a^\prime)]
    $$

如果在 Offline RL 中使用常规AC方法会发生什么?

  • Actor-Critic的策略迭代定义为(这种限定数据集的做法是被迫的,因为没法与环境交互):
    $$
    \begin{align}
    \hat{Q}^{k+1} &\leftarrow \mathop{\arg\min}_{Q} \mathbb{E}_{s,a,s^\prime \sim \mathcal{D}}\left[ \left( r(s,a) + \gamma\mathbb{E}_{a^\prime \sim \hat{\pi}^{k}(a^\prime| s^\prime)}[\hat{Q}^k(s^\prime, a^\prime)] - Q(s, a) \right)^2 \right] \ &\text{(policy evaluation)}\\
    \hat{\pi}^{k+1} &\leftarrow \mathop{\arg\max}_{\pi} \mathbb{E}_{s\sim \mathcal{D}, a\sim \pi(a\vert s)}\left[ \hat{Q}^{k+1} (s,a) \right] \ &\text{(policy improvement)}
    \end{align}
    $$
    • 以上公式来自论文,原始论文中使用的是 \(a \sim \pi^k(a|s)\),这里应该改成 \(a \sim \pi(a|s)\) 更合适,因为argmax的目标参数是 \(\pi\),所以我改成了 \(a \sim \pi(a|s)\) 表示找到最优的策略 \(\pi\),使得按照这个策略决策(采样或者选择动作)得到的期望收益Q值是最大的
    • 其实AC方法中,包含了DQN的迭代思路,策略迭代部分, \(a’\) 始终取上一轮Q值最大的动作即可
    • policy evaluation实际上就是在重复对Q使用贝尔曼算子,只是使用最小化均方误差的形式去实现了
  • 问题:Offline RL场景中直接使用上面的策略迭代会面临分布偏移问题 ,从而导致高估:
    • 理解:1) 目标Q值的计算应该使用当前策略 \(\pi^k\),但是数据集中只有从数据集 \(\mathcal{D}\) 中采样到的样本,对应策略 \(\pi_\beta\),极端情况下,策略 \(\pi^k\) 采样到的动作 \(a’ \sim \pi^k\) 可能从未在数据集中出现过,此时模型是无法准确评估 \(Q(s’,a’)\) 的;2)常规的迭代方法中,一般都包含着 \(a’ = \mathop{\arg\max}_a Q(s,a)\) 或者隐式的包含了 \(Q(s, a) = r + \max_{a} Q(s^\prime, a)\) 这样的思想,此时预估值 \(Q(s’,a’)\) 低估不会出现问题,但是 \(Q(s’,a’)\) 一旦高估,该动作就会被选中作为目标值;3)Offline RL场景中没有机会引入新样本来重新修正 \(Q(s’,a’)\) 的高估
    • 在Online RL的场景中,一般不存在该问题,因为一个动作被错误的高估以后,往往会在策略跟环境的交互中选中该动作,从而使得该动作被修正
  • 总结一下:对于策略 \(\pi\),我们的真实Q值是 \(Q^\pi\),在固定的数据集下学到的是 \(\hat{Q}^\pi\),但该值一般往往会高估,我们的目标是让 \(\hat{Q}^\pi\) 尽量接近真实值 \(Q^\pi\),那么面临的往往是高估这个问题,CQL算法的核心思想就是解决这个问题

改进一:打压未知动作的Q值

  • 对于任意的未知策略 \(\mu\),在行为策略 \(\pi_\beta\) 交互收集到的数据集 \(\mathcal{D}\) 中进行训练,其状态动作对访问分布为 \(\mu(s, a)=d^{\pi_\beta}(s) \mu(a \mid s)\),我们的目标通过最小化未知策略采样到的动作对应的Q值,来实现Q值的保守学习:
    $$\hat{Q}^{k+1} \leftarrow \arg \min_{Q} \color{red}{\alpha} \mathbb{E}_{\mathbf{s} \sim \mathcal{D}, \mathbf{a} \sim \mu(\mathbf{a} \mid \mathbf{s})}[Q(\mathbf{s}, \mathbf{a})]+\frac{1}{2} \mathbb{E}_{\mathbf{s}, \mathbf{a}, \mathbf{s’} \sim \mathcal{D}}\left[\left(Q(\mathbf{s}, \mathbf{a})-\hat{\mathcal{B}}^{\pi} \hat{Q}^{k}(\mathbf{s}, \mathbf{a})\right)^{2}\right]$$

  • 使用 \(\mathbf{s}, \mathbf{a}, \mathbf{s’} \sim \mathcal{D}\) 的原因是因为想强调 \(\hat{\mathcal{B}}^{\pi}\) 使用的 \(s’\) 不是按照环境的状态转移概率算的,而是直接使用的数据集中的内容

  • 上面的迭代公式学到的是 \(\hat{Q}^\pi\) (其中 \(\hat{Q}^\pi := \lim_{k\rightarrow \infty}\hat{Q}^k\))

  • 为了方便表达,一些论文或博客会使用 \(\mathcal{L}_{Bellman}(Q) = \frac{1}{2} \mathbb{E}_{\mathbf{s}, \mathbf{a} \sim \mathcal{D}}\left[\left(Q(\mathbf{s}, \mathbf{a})-\hat{\mathcal{B}}^{\pi} \hat{Q}^{k}(\mathbf{s}, \mathbf{a})\right)^{2}\right]\) 来替换等号后面的式子

  • 这里论文中给出了证明(Theorem 3.1):

    • 上式说明:
      • 当 \(supp\ \mu \subset supp\ \pi\) ( \(supp\ \mu\) 表示支持集),且 \(\alpha\) 足够大时, \(\forall \ s\in\mathcal{D},a\in \mathcal{A}\),均有 \(\hat{Q}^\pi(s,a) \le Q^\pi(s,a)\) 成立。即对于任意的分布 \(\mu\),只要我们这里 \(\alpha\) 取得足够大,总能学到一个比真实值 \(Q^\pi(s,a)\) 小的Q值
      • 当 \(supp\ \mu \subset supp\ \pi\),且 \(\hat{B^\pi} = \mathcal{B}^\pi, \ \alpha > 0\) 时,对 \(\forall \ s\in\mathcal{D},a\in \mathcal{A}\),均有 \(\hat{Q}^\pi(s,a) \le Q^\pi(s,a)\) 成立。即如果我们的数据集可以反映真实的数据分布,那么数据偏移就不存在了,我们直接令 \(\alpha=0\),退化到常规的贝尔曼算子对应的损失函数即可(注:此时的数据集 \(\mathcal{D}\) 是从当前策略采样的,故 \(\alpha=0\) 后的式子就是常规贝尔曼算子对应的损失函数)
    • 在概率论和机器学习中,当我们说两个离散概率分布 \(\mu(a|s)\) 和 \(\pi(a|s)\) 满足 \(supp\ \mu \subset supp\ \pi\),这意味着 \(\mu(a|s)\) 的支持集(即 \(\mu(a|s)\) 分配了正概率的所有动作 \(a\) 的集合)是 \(\pi(a|s)\) 支持集的子集。换句话说,对于所有 \(\mu(a|s)\) 给予正概率的动作 \(a\), \(\pi(a|s)\) 也必须给予正概率。简而言之,如果某个动作在 \(\mu(a|s)\) 下是可能发生的(即它有非零的概率),那么在 \(\pi(a|s)\) 下这个动作也是可能发生的。但是, \(\pi(a|s)\) 可能包括一些 \(\mu(a|s)\) 不考虑的动作,这些动作在 \(\pi(a|s)\) 中有正概率但在 \(\mu(a|s)\) 中没有或者为零概率

改进二:打压补偿

  • 改进一学到了 \(Q^\pi(s,a)\) 的逐点下界 \(\forall \ s\in\mathcal{D},a\in \mathcal{A}\),均有 \(\hat{Q}^\pi(s,a) \le Q^\pi(s,a)\),但直观上看,打压过于严格了,甚至行为策略采样到的状态动作对都会打压(其实这些地方我们能估准的),为了缓解这个问题,我们对改进一的打压做一些补偿:
    $$\hat{Q}^{k+1} = \mathop{\arg\min}_Q \color{red}{\alpha} \left(\mathbb{E}_{s\sim \mathcal{D}, a\sim \mu(a\vert s)}[Q(s, a)] - \color{red} { \mathbb{E}_{s\sim \mathcal{D}, a\sim\hat{\pi}_\beta(a\vert s)}[Q(s, a)] } \right) + \frac{1}{2} \mathbb{E}_{\mathbf{s}, \mathbf{a}, \mathbf{s’} \sim \mathcal{D}}\left[\left(Q(\mathbf{s}, \mathbf{a})-\hat{\mathcal{B}}^{\pi} \hat{Q}^{k}(\mathbf{s}, \mathbf{a})\right)^{2}\right]$$

  • 这里论文中给出了证明(Theorem 3.2):

    • 上式说明:
      • 当 \(\mu=\pi\) (注意,改进一种不需要这个约束),且 \(\alpha\) 较大时,有 \(\forall \ s\in\mathcal{D}\),均有 \(\hat{V}^\pi(s) \le V^\pi(s)\)
      • 当 \(\mu=\pi\),且 \(\hat{B^\pi} = \mathcal{B}^\pi, \ \alpha > 0\) 时,有 \(\forall \ s\in\mathcal{D}\),均有 \(\hat{V}^\pi(s) \le V^\pi(s)\)
    • 此时虽然不能再保证学到了 \(Q^\pi(s,a)\) 的逐点下界: \(\forall \ s\in\mathcal{D},a\in \mathcal{A}\),均有 \(\hat{Q}^\pi(s,a) \le Q^\pi(s,a)\)
    • 但可以保证学到了 \(Q^\pi(s,a)\) 的期望下界: \(\mathbb{E}_{\pi(a|s)}[\hat{Q}^\pi(s,a)] \le V^\pi(s) = \mathbb{E}_{\pi(a|s)}[Q^\pi(s,a)]\)

改进三:CQL(\(\mathcal{R}\))

  • 一个遗留问题:论文中提到改进二需要进一步改进,但为什么不能直接用策略二,令 \(\mu=\pi\),然后直接使用改进二的公式更新?原始论文关于改进二的缺点描述(原始论文中对这里的描述不够具体,缺乏说服力)
    • 改进二已经说明了在 \(\mu=\pi\) 时,可以学到一个合适的下界,但是由于改进二中需要对策略 \(\mu\) ( \(\mu=\pi\) )进行采样,即每次迭代Q值时都需要上一轮的策略 \(\pi\) 来采样,这样的话,智能交替进行策略评估和策略提升,且策略评估需要迭代足够长的步骤才能收敛,所以非常耗时(问题,常规的AC不都是这么实现的吗?其实也没有问题吧)
    • 补充:CQL原始论文的描述截图
  • 接下来,我们先假定论文中提到的改进二中的公式确实存在缺点,需要改进,那么可以做如下改进
  • 我们可以进一步地优化,考虑到 \(\pi\) 是使得Q值最大的策略,所以我们使用使得Q值最大的 \(\mu\) 去拟合,改进二的公式可以优化为下面这样:
    $$\hat{Q}^{k+1} = \min_Q \max_\mu \color{red}{\alpha} (\mathbb{E}_{s\sim \mathcal{D}, a\sim \color{red}{\mu(a\vert s)} }[Q(s, a)] - \mathbb{E}_{s\sim \mathcal{D}, a\sim\hat{\pi}_\beta(a\vert s)}[Q(s, a)]) + \frac{1}{2} \mathbb{E}_{\mathbf{s}, \mathbf{a}, \mathbf{s’} \sim \mathcal{D}}\left[\left(Q(\mathbf{s}, \mathbf{a})-\hat{\mathcal{B}}^{\pi} \hat{Q}^{k}(\mathbf{s}, \mathbf{a})\right)^{2}\right] + \color{red}{\mathcal{R}(\mu)} $$
    • 为了防止寻找Q值最大化的 \(\mu\) 时出现过拟合,我们增加正则项 \(\mathcal{R}(\mu)\),一般取 \(\mathcal{R}(\mu) = - D_{KL} (\mu| \rho)\),其中 \(\rho\) 是一个已知分布

改进四:CQL(\(\mathcal{\rho}\))

  • 在CQL(\(\mathcal{R}\))中,求解 \(\mu\) 相当于要求解下面的优化问题
    $$
    \begin{align}
    \max_{\mu} \mathbb{E}_{a \sim \mu(a|s)}[Q(s,a)]&\color{red}{-}D_{\mathrm{KL}}(\mu | \rho) \\
    \text { s.t. } \quad \sum_{a} \mu(a|s)&=1 \\
    \mu(a|s) &\geq 0, \ \forall \mathbf{a} .
    \end{align}
    $$
    • 注意:论文附录中错误地将 \(-D_{\mathrm{KL}}(\mu | \rho)\) 写成了 \(+D_{\mathrm{KL}}(\mu | \rho)\) (因为上述公式的本意是最小化KL散度),需要修正过来
  • 上述优化问题的最优解是:
    $$
    \mu^{*}(a|s)=\frac{1}{Z} \rho(a|s) \exp (Q(s,a))
    $$
    • 其中 \(Z=\sum_a \rho(a\vert s)\cdot \exp(Q(s, a))\), \(Z\) 也被称为归一化因子(normalizing factor)
    • 以上优化问题求解的详细证明与AWR论文相似。更一般地,将以上变量 \(a\) 替换成 \(x\),即 \(Q(s,a)\) 替换成 \(f(x)\) 均可成立
  • 最终我们有CQL(\(\mathcal{\rho}\))的表达形式:
    $$
    \hat{Q}^{k+1} = \min_Q \color{red}{\alpha} \mathbb{E}_{s\sim d^{\pi_\beta}(s)} \left[ \mathbb{E}_{a\sim \rho(a\vert s)} \left[ Q(s, a) \cfrac{\exp(Q(s, a))}{Z} \right ] - \mathbb{E}_{a \sim \hat{\pi}_\beta(a\vert s)}[Q(s, a)] \right] + \frac{1}{2} \mathbb{E}_{\mathbf{s}, \mathbf{a}, \mathbf{s’} \sim \mathcal{D}}\left[\left(Q(\mathbf{s}, \mathbf{a})-\hat{\mathcal{B}}^{\pi} \hat{Q}^{k}(\mathbf{s}, \mathbf{a})\right)^{2}\right]
    $$
    • 原始论文中使用 \(s\sim d^{\pi_\beta}(s)\),实际上与 \(s \sim \mathcal{D}\) 等价,其他形式都是使用 \(s \sim \mathcal{D}\)
    • 论文中,实验时取 \(\rho(a \vert s) = \hat{\pi}^{k-1}(a \vert s)\),实验表格中的CQL(\(\mathcal{\rho}\))就是这个含义

改进五:CQL(\(\mathcal{H}\))

  • 当CQL(\(\mathcal{\rho}\))中的 \(\rho\) 是均匀分布时,有最优解:
    $$
    \begin{align}
    \mu^*(a\vert s) &= \cfrac{\rho(a\vert s)\cdot \exp(Q(s, a))}{\sum_a \rho(a\vert s)\cdot \exp(Q(s, a))} \\
    &= \cfrac{\rho(a\vert s)\cdot \exp(Q(s, a))}{\rho(a\vert s) \sum_a \exp(Q(s, a))} \\
    &= \cfrac{\exp(Q(s, a))}{\sum_a \exp(Q(s, a))}
    \end{align}
    $$
  • 将 \(\mu^*(a\vert s) = \cfrac{\exp(Q(s, a))}{\sum_a \exp(Q(s, a))} \) 带入CQL(\(\mathcal{R}\)),可得CQL(\(\mathcal{H}\)):
    $$
    \hat{Q}^{k+1} = \min_Q \color{red}{\alpha} \mathbb{E}_{s\sim\mathcal{D}} \left[ \log \sum_a \exp(Q(s, a)) - \mathbb{E}_{a \sim \hat{\pi}_\beta(a\vert s)}[Q(s, a)] \right] + \frac{1}{2} \mathbb{E}_{\mathbf{s}, \mathbf{a}, \mathbf{s’} \sim \mathcal{D}}\left[\left(Q(\mathbf{s}, \mathbf{a})-\hat{\mathcal{B}}^{\pi} \hat{Q}^{k}(\mathbf{s}, \mathbf{a})\right)^{2}\right]
    $$
    • 论文中,取 \(\rho(a \vert s)\) 是均匀分布(即 \(\rho(a|s)=\text{Unif}(a)\))时,相当于最大化策略熵,故称此时的更新方式为CQL(\(\mathcal{H}\))

CQL 伪代码

  • 伪代码流程如下,其中CQL(\(\mathcal{R}\))可以是CQL(\(\mathcal{H}\)),也可以是CQL(\(\mathcal{\rho}\)):

  • 如果是Q-learning模式:仅更新Q值即可,最后定义 \(\mu(s) = \mathop{\arg\max}_a Q(s,a)\) 作为最终的策略

  • 如果是Actor-Critic模式:需要使用SAC的训练方式额外训练actor

实践说明

  • \(\alpha\) 的选择:
  • \(\alpha\) 可以变成可学习的值?
  • \(\log\sum_a \exp(Q(s,a))\) 的计算:
  • CQL(\(\mathcal{H}\))和CQL(\(\mathcal{\rho}\))谁更好?
    • 一般来说 CQL(\(\mathcal{H}\))优于CQL(\(\mathcal{\rho}\)),当动作空间特别大时logsumexp预测方差变得很大,此时使用CQL(\(\mathcal{\rho}\))效果更好
  • 一些超参数的设置:

一些说明和思考

  • CQL原始论文中符号使用有点混乱比如CQL(\(\mathcal{R}\)),CQL(\(\mathcal{H}\))和CQL(\(\mathcal{\rho}\))三者的定义不清晰,特别是CQL(\(\mathcal{\rho}\))在正文中没有得到明确的定义,附录里面才有定义,而伪代码中强调的公式4:CQL(\(\mathcal{R}\)),实际上公式4是CQL(\(\mathcal{H}\)),这里我们特别对三个方法的定义进行辨析:

    • CQL(\(\mathcal{R}\)):CQL原始形式
    • CQL(\(\mathcal{\rho}\)):CQL变体,对任意 \(\rho\) 均可使用这个表述,包含了后面的CQL(\(\mathcal{H}\)),论文中实验时使用的是 \(\rho(a \vert s) = \hat{\pi}^{k-1}(a \vert s)\)
    • CQL(\(\mathcal{H}\)):CQL变体,当CQL(\(\mathcal{\rho}\))中 \(\rho\) 取均匀分布时的更新形式
  • 原始论文中 \(s\sim d^{\pi_\beta}(s)\) 和 \(s \sim \mathcal{D}\) 混用,比较公式时容易对不齐,实际上两者是等价的

  • 从推导可以看出,数据越充足,需要的 \(\alpha\) 就越小

  • 问题:为什么直接更新改进二中的更新公式不可以?为什么训练耗时长?普通的AC不都是这么实现的吗?

    • 普通AC确实是这样实现的,但是在Offline RL场景,不希望迭代效率过慢?
  • 问题:为什么使用均匀分布以后,可以推导出CQL(\(\mathcal{H}\))的形式?

    • 详请见附录推导
  • CQL算法得到的策略一定很优秀吗?答案是不会比行为策略差太多

    • 具体来说(Theorem 3.6):CQL算法得到的策略 \(\pi^*(a|s)\) 是一个策略 \(\hat{\pi}_\beta\) 的 \(\zeta\) -safe policy improvement,即有 \(1-\zeta\) 的概率可以保证 \(J(\pi^*, M) \ge J(\hat{\pi}_\beta, M) - \zeta\)

附录:证明约束优化问题

  • 目标是求解下面的约束问题
    $$
    \begin{align}
    \max {\mu} \mathbb{E}_{\mathbf{x} \sim \mu(\mathbf{x})}[f(\mathbf{x})]&-D_{\mathrm{KL}}(\mu | \rho) \\
    \text { s.t. } \quad \sum_{\mathbf{x}} \mu(\mathbf{x})&=1\\
    \mu(\mathbf{x}) &\geq 0, \ \forall \mathbf{x}.
    \end{align}
    $$
  • 需要证明上述式子的最优解为:
    $$\mu^{*}(\mathbf{x})=\frac{1}{Z} \rho(\mathbf{x}) \exp (f(\mathbf{x}))$$
    • 其中 \(Z = \sum_{\mathbf{x}} \rho(\mathbf{x}) \exp (f(\mathbf{x}))\)
  • 证明前,我们先将上述问题修改成更一般的形式(更一般的形式更常用一些),在更一般的形式下, \(D_{\mathrm{KL}}(\mu | \rho)\) 经常会加上温度系数 \(\alpha\) 或出现在约束中:
    $$
    \begin{align}
    \max_{\mu} \mathbb{E}_{\mathbf{x} \sim \mu(\mathbf{x})}[&f(\mathbf{x})] \\
    \text { s.t. } \quad D_{\mathrm{KL}}(\mu | \rho) &\le \epsilon \\
    \quad \sum_{\mathbf{x}} \mu(\mathbf{x})&=1\\
    \mu(\mathbf{x}) &\geq 0, \ \forall \mathbf{x}.
    \end{align}
    $$
  • 求解上面的问题可以先转换成构造拉格朗日函数
    • 原始问题变形
      $$
      \begin{align}
      \min_{\mu} - \mathbb{E}_{\mathbf{x} \sim \mu(\mathbf{x})}[&f(\mathbf{x})] \\
      \text { s.t. } \quad D_{\mathrm{KL}}(\mu | \rho) - \epsilon &\le 0 \\
      \quad \sum_{\mathbf{x}} \mu(\mathbf{x}) - 1 &= 0 \\
      \mu(\mathbf{x}) &\geq 0, \ \forall \mathbf{x}.
      \end{align}
      $$
    • 拉格朗日函数如下
      $$
      \begin{align}
      L(\mu,\alpha,\beta) &= -\mathbb{E}_{\mathbf{x} \sim \mu(\mathbf{x})}[f(\mathbf{x})] + \alpha(D_{\mathrm{KL}}(\mu | \rho)-\epsilon) + \beta(\sum_{\mathbf{x}} \mu(\mathbf{x})-1) \\
      L(\mu,\alpha,\beta) &= -\sum_x \mu(\mathbf{x})f(\mathbf{x}) + \alpha( \sum_x \mu(\mathbf{x}) \log\frac{\mu(\mathbf{x})}{\rho(\mathbf{x})} -\epsilon) + \beta(\sum_{\mathbf{x}} \mu(\mathbf{x})-1) \\
      L(\mu,\alpha,\beta) &= -\sum_x \mu(\mathbf{x})f(\mathbf{x}) + \alpha( \sum_x \mu(\mathbf{x}) \log \mu(\mathbf{x}) - \sum_x \mu(\mathbf{x}) \log \rho(\mathbf{x}) -\epsilon) + \beta(\sum_{\mathbf{x}} \mu(\mathbf{x})-1) \\
      \end{align}
      $$
  • 对上式微分并令微分结果等于0有:
    $$
    \begin{align}
    \frac{\partial L(\mu,\alpha,\beta)}{\partial \mu(x)} &= -\sum_x f(\mathbf{x}) + \alpha \sum_x ( \log \mu(\mathbf{x}) + 1) - \alpha \sum_x \log \rho(\mathbf{x}) + \sum_x \beta \\
    &= \sum_x (- f(\mathbf{x}) + \alpha \log \mu(\mathbf{x}) + 1 - \alpha \log \rho(\mathbf{x}) + \beta) \\
    &= 0
    \end{align}
    $$
    • 上式中求导使用到了 \(\frac{\partial \log \mu(x)}{\partial \mu(x)} = \frac{1}{\mu(x)}\)
  • 进一步可以得到
    $$
    - f(\mathbf{x}) + \alpha \log \mu(\mathbf{x}) + 1 - \alpha \log \rho(\mathbf{x}) + \beta = 0
    $$
  • 即
    $$
    \begin{align}
    \alpha \log \mu(\mathbf{x}) &= f(\mathbf{x}) + \alpha \log \rho(\mathbf{x}) - (\beta + 1) \\
    \log \mu(\mathbf{x}) &= \frac{1}{\alpha}f(\mathbf{x}) + \log \rho(\mathbf{x}) + \frac{- (\beta + 1)}{\alpha} \\
    \mu(\mathbf{x}) &= exp(\frac{1}{\alpha}f(\mathbf{x}) + \log \rho(\mathbf{x}) + \frac{- (\beta + 1)}{\alpha}) \\
    \mu(\mathbf{x}) &= exp(\frac{1}{\alpha}f(\mathbf{x})) \cdot exp(\log \rho(\mathbf{x})) \cdot exp(\frac{- (\beta + 1)}{\alpha}) \\
    \mu(\mathbf{x}) &= \rho(\mathbf{x})exp(\frac{1}{\alpha}f(\mathbf{x})) \cdot exp(\frac{- (\beta + 1)}{\alpha}) \\
    \end{align}
    $$
  • 由于 \(exp(\frac{- (\beta + 1)}{\alpha})\) 包含拉格朗日乘子,是未知的,所以我们进一步化简,尝试将这部分替换为已知式子,对上述结果最后一步两边同时积分有
    $$
    \begin{align}
    \sum_x \mu(\mathbf{x}) &= \sum_x (\rho(\mathbf{x})exp(\frac{1}{\alpha}f(\mathbf{x})) \cdot exp(\frac{- (\beta + 1)}{\alpha})) \\
    1 &= exp(\frac{- (\beta + 1)}{\alpha}) \sum_x (\rho(\mathbf{x})exp(\frac{1}{\alpha}f(\mathbf{x}))) \\
    exp(\frac{- (\beta + 1)}{\alpha}) &= \frac{1}{\sum_x (\rho(\mathbf{x})exp(\frac{1}{\alpha}f(\mathbf{x})))} \\
    \end{align}
    $$
  • 将 \(exp(\frac{- (\beta + 1)}{\alpha}) = \frac{1}{\sum_x (\rho(\mathbf{x})exp(\frac{1}{\alpha}f(\mathbf{x})))}\) 的结果带入 \(\mu(\mathbf{x}) = \rho(\mathbf{x})exp(\frac{1}{\alpha}f(\mathbf{x})) \cdot exp(\frac{- (\beta + 1)}{\alpha})\) 可以解的最终解:
    $$\mu^{*}(\mathbf{x})=\frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x}))$$
    • 其中 \(Z = \sum_{\mathbf{x}} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x}))\)
  • 回到最初的问题:我们令 \(\alpha=1\) 即可退回到原始问题

附录:最优策略形式的使用方式

  • 从上面的证明,我们已经得到了最优策略的一般形式 \(\mu^{*}(\mathbf{x})=\frac{\rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x}))}{\sum_{\mathbf{x}} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x}))} \),但这个形式难以直接使用
    • 难以直接使用的原因(个人理解):
      • 离线强化场景中 \(\rho\) 未知,无法直接使用;
      • 在线强化学习场景中,难以用于状态维度高或动作空间大(包括连续状态或动作)的场景。状态和动作空间有限时, \(\rho\) 是上一布步的策略或者历史混合策略 \(f(\mathbf{x})\) 一般是 \(A^\rho(s,a)\),理论上如果按照on-policy更新或者记录policy和样本以后按照off-policy更新均可,但是实际更新时,这种非参数化的策略,不同状态的策略是隔离的(没有状态泛化能力),针对每个状态都要更行才能做到该状态上的策略迭代 \(\mu^{*}(\mathbf{a|s})=\frac{\rho(\mathbf{a|s}) \exp (\frac{1}{\alpha}f(\mathbf{a|s}))}{\sum_{\mathbf{a}} \rho(\mathbf{a|s}) \exp (\frac{1}{\alpha}f(\mathbf{a|s}))} \),相当于每次迭代需要收集大量的样本才能足够更新各个状态上的效果,否则在下一轮中遇到其他状态时,这里相当于没有被更新
  • 一般来说,可以用神经网络去表示策略,实际上还可以进一步推导,不同的算法,目标函数不同,推导得到的结果也不同
  • 对于CQL来说,由于目标是两步min max,需要进一步将最优解带入原始目标得到最终的目标
  • 对于AWR,AWAC和IQL(复用了AWR方法)等论文,这里会直接使用一个神经网络去拟合策略,并尝试求这个策略的参数更新公式
    $$
    \begin{align}
    \theta^* &= \mathop{\arg\min}_{\theta} D_{\mathrm{KL}}(\mu^*(\mathbf{x}) | \pi_\theta(\mathbf{x})) \\
    &= \mathop{\arg\min}_{\theta} D_{\mathrm{KL}}\Big( \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) | \pi_\theta(\mathbf{x})\Big) \\
    &= \mathop{\arg\min}_{\theta} \sum_{\mathbf{x}} \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) \log \frac{ \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x}))} {\pi_\theta(\mathbf{x})} \\
    &= \mathop{\arg\min}_{\theta} \sum_{\mathbf{x}} \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) \log \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) - \sum_{\mathbf{x}} \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) \log \pi_\theta(\mathbf{x}) \\
    &= \mathop{\arg\min}_{\theta} - \sum_{\mathbf{x}} \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) \log \pi_\theta(\mathbf{x}) \\
    &= \mathop{\arg\max}_{\theta} \sum_{\mathbf{x}} \frac{1}{Z} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) \log \pi_\theta(\mathbf{x}) \\
    &= \mathop{\arg\max}_{\theta} \sum_{\mathbf{x}} \rho(\mathbf{x}) \exp (\frac{1}{\alpha}f(\mathbf{x})) \log \pi_\theta(\mathbf{x}) \quad \quad \text{Z与策略参数\theta无关,可以消掉} \\
    &= \mathop{\arg\max}_{\theta} \mathbb{E}_{\mathbf{x} \sim \rho(\mathbf{x})} \Big[\exp (\frac{1}{\alpha}f(\mathbf{x})) \log \pi_\theta(\mathbf{x}) \Big] \\
    \end{align}
    $$
  • 此时,最优解求解目标变成了从已知策略 \(\rho(\mathbf{x} )\) 中采样,并最大化 \(\exp (\frac{1}{\alpha}f(\mathbf{x})) \log \pi_\theta(\mathbf{x})\) 即可,此时的目标是可以直接对策略求梯度的,非常容易迭代

附录:证明 logsumexp 公式

  • 参考:CQL算法logsumexp公式推导
  • 当CQL(\(\mathcal{\rho}\))中的 \(\rho\) 是均匀分布时,有最优解:
    $$
    \begin{align}
    \mu^*(a\vert s) &= \cfrac{\rho(a\vert s)\cdot \exp(Q(s, a))}{\sum_a \rho(a\vert s)\cdot \exp(Q(s, a))} \\
    &= \cfrac{\rho(a\vert s)\cdot \exp(Q(s, a))}{\rho(a\vert s) \sum_a \exp(Q(s, a))} \\
    &= \cfrac{\exp(Q(s, a))}{\sum_a \exp(Q(s, a))}
    \end{align}
    $$
  • 带入CQL(\(\mathcal{R}\))形式有
    $$
    \begin{align}
    \mathbb{E}_{a \sim \mu^*} [Q(s, a)] - D_{\mathrm{KL}}(\mu^* | \rho) &= \mathbb{E}_{a \sim \mu^*} \left[ \exp(Q(s, a)) - \log(\mu^*) + \log(\rho)\right]\\
    &= \mathbb{E}_{a \sim \mu^*} \left[Q(s, a) - \log(\cfrac{\exp(Q(s, a))}{\sum_a \exp(Q(s, a))} ) +\log(\rho)\right]\\
    &= \mathbb{E}_{a \sim \mu^*} \left[Q(s, a) - \log(\exp(Q(s, a))) + \log(\sum_a \exp(Q(s, a))) +\log(\rho)\right]\\
    &= \mathbb{E}_{a \sim \mu^*} \left[\log(\sum_a \exp(Q(s, a))) + \log(\rho)\right]\\
    \end{align}
    $$
  • 显然 \(\log(\sum_a \exp(Q(s, a))) + \log(\rho)\) 此时与策略 \(\mu^*\) 无关,期望可以消掉,同时 \(\rho\) 是均匀分布时,有 \(\rho(a|s) = \frac{1}{|A|}\), \(\log(\rho) = -\log(|A|)\),于是有:
    $$
    \begin{align}
    \mathbb{E}_{a \sim \mu^*} [Q(s, a)] - D_{\mathrm{KL}}(\mu^* | \rho) &= \log \sum_a \exp(Q(s, a)) -\log(|A|)
    \end{align}
    $$
    • 注意,这里需要的是一个 \(\max_Q f(Q)\) 的形式,而 \(-\log(|A|)\) 这个值是个常数,与优化目标Q无关,可以消去
  • 补充问题:为什么 CQL(\(\mathcal{\rho}\)) 中可以直接消掉 \(\mathcal{R}(\mu)\),但是 CQL(\(\mathcal{H}\)) 中不行?
    • 理论上,CQL(\(\mathcal{\rho}\))中不能直接消掉 \(\mathcal{R}(\mu)\),CQL(\(\mathcal{H}\))中推导才是对的,论文中没有提这一点,实际上,把 \(\mathcal{R}(\mu)\) 从目标挪到约束上 \(\mathcal{R}(\mu) \le \epsilon\),最终推导的结果可以没有 \(\mathcal{R}(\mu)\),指数权重上会有个温度系数(因为新增约束引入的拉格朗日乘子)
1…151617…61
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

608 posts
49 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4