论文主要内容详解

第3节 Preliminaries 初步介绍

  • 将GUI交互形式化为目标条件POMDP:指令空间为自然语言ℓ,环境含潜在状态S、动作A、观测O、转移T、奖励R与折扣γ。每步观测通常是截图ot;由于部分可观测,策略以指令ℓ、历史ht与当前观测ot为条件输出动作at,即πθ(at∣ℓ,ht,ot)。奖励多为稀疏的成功奖励:达成目标为1,否则为0;目标是最大化期望回报,γ=1时等价于最大化成功率。
  • 高层任务 vs 低层任务:低层任务可由单个原子操作完成;高层任务需要跨多屏、多步的交互轨迹。论文将grounding视为低层决策特例:在给定(ℓ,ot)时预测应交互的坐标;本文重点在高层导航任务
  • GUI后训练两类主流范式及其问题背景
    • 监督微调SFT:对每步输出“推理trace ct + 可执行动作at”的联合序列做NLL最小化。
    • RLVR式逐步RL:用规则/验证器对“动作类型、文本值、坐标”等打分,采用GRPO目标更新策略;但GUI多步导航存在误差累积导致分布漂移部分可验证性(同一状态多种动作都可能正确,但离线只验证单个示范动作),会让逐步奖励变得含糊并引入偏置。

第4节 Reasoning Data Curation 数据(GUI-Libra-81K)

4.1 数据来源与清洗起点

  • 聚合多源公开轨迹数据,覆盖Web与移动端,包括GUI-Odyssey、AMEX、AndroidControl、AITZ、AITW、GUIAct、MM-Mind2Web等;并额外加入GUIAct中文子集以增强多语与网站多样性。
  • 初始清洗:去除不完整轨迹、过短/过长轨迹(<3步或>50步)、以及无法映射到统一动作空间的复合动作;得到约19K轨迹、170K步。

4.2 统一结构化格式

  • 每条样本由输入输出构成:
    • 输入:系统提示(枚举可用动作)、用户指令、历史动作、当前截图;
    • 输出:<think>...</think>中的推理 + <answer>...</answer>中的结构化动作JSON。
  • 动作JSON包含:action type、action description、value、point_2d,并可选action target(用于描述目标UI元素、便于一致性检查与过滤)。动作类型覆盖Web与移动常见的13类(如Click/Write/Swipe/Scroll/Wait/Terminate等)。

4.3 动作对齐的推理增强(augmentation)

  • 指出现有数据推理往往短且噪声大;作者用更“GUI特化”的提示词生成更长、更结构化的推理,并比较不同生成模型后选择GPT-4.1以获得更丰富可见推理、且更贴近动作对齐。
  • 生成时不强制生成器完全复述标注动作,而是将标注动作作为参考,允许在有理由时选择不同动作;但坐标初始仍复用原数据,因此可能出现“推理/target与坐标不一致”等错配,需后续过滤。

4.4 SFT数据过滤(得到81K)

两步自动过滤以提升“推理-动作-坐标”一致性与数据质量:

  1. 动作重预测一致性过滤:对每个输入做多次随机预测,统计与标注匹配频率;低于阈值(0.3)的样本丢弃,用于剔除不确定/低质量步骤。
  2. bounding box一致性验证:用大模型根据action target预测目标框,只有当原point_2d落在框内才保留;既去除坐标错误,也减少推理-动作错配,并为后续RL提供框监督。
  • 过滤后得到81K SFT steps,来自9K轨迹;其中Web占比约14.3%,动作分布以Click约60%为主,少数动作稀缺(如LongPress/Select),也成为后续引入RL的动机之一。

4.5 RL训练数据过滤(得到40K)

  • 为RL构建更均衡的离线步数据:缓解
    • early-step bias(大量轨迹共享相似的初始屏幕/动作),
    • domain imbalance(移动端轨迹占比过高)。
  • 方法:下采样早期步骤与移动域轨迹,得到更平衡的40K步RL子集。

第5节 GUI-Libra 训练(SFT与RL)

5.1 关键观察:长CoT的SFT会伤害grounding

  • 系统性实验显示:在“先推理再落点”的结构下,输出越长,grounding准确率越容易下降;对比多种SFT变体发现主要问题来自“过长推理序列”而非简单的“是否有推理”。

5.2 Action-aware SFT(ASFT):兼顾推理与可执行性

  • 混合监督:同时使用
    1. reasoning-then-action样本(带<think>),
    2. direct-action样本(去掉推理,仅保留<answer>),以获得“可推理”与“可直接出动作”两种监督信号。
  • token级重加权目标:将输出拆为推理ct、动作token at、grounding相关token gt,并对动作/grounding给予更高权重(αa、αg),降低推理token对loss的主导,从而缓解“推理-落点”干扰。其目标函数为:

LASFT(θ)=−E(xt,ct,at,gt)∼Dmixlog⁡πθ(ct∣xt)+αalog⁡πθ(at∣xt,ct)+αglog⁡πθ(gt∣xt,ct,at)∣ct∣+αa∣at∣+αg∣gt∣ L_{\mathrm{ASFT}}(\theta)=- \mathbb{E}_{(x_t,c_t,a_t,g_t)\sim D_{\mathrm{mix}}} \frac{ \log \pi_\theta(c_t\mid x_t)+ \alpha_a \log \pi_\theta(a_t\mid x_t,c_t)+ \alpha_g \log \pi_\theta(g_t\mid x_t,c_t,a_t) }{|c_t|+\alpha_a|a_t|+\alpha_g|g_t|} LASFT(θ)=E(xt,ct,at,gt)Dmixct+αaat+αggtlogπθ(ctxt)+αalogπθ(atxt,ct)+αglogπθ(gtxt,ct,at)

  • 直观效果:通过“混合数据 + 动作/落点token加权”,在保留推理能力的同时尽量维持grounding与动作预测质量。作者默认示例权重为αa=2、αg=4。

5.3 部分可验证奖励下的保守RL(Conservative RL)

  • 问题:部分可验证性:离线逐步验证只“承认”示范动作,其他同样可行的动作会被当成失败,从而产生偏置梯度与不稳定训练,并削弱离线指标对在线成功率的预测性。
  • RL算法与关键正则:采用GRPO进行逐步RL,并强调GUI场景中需要适度KL正则形成“信任域”以抑制策略漂移、缓解奖励歧义与离线-在线失配;这与很多RLVR工作常取β=0的做法不同。
  • 奖励实现:每步rollout输出结构化动作,奖励为格式奖励与准确性奖励加权和:

r~(s,a)=wfmtrfmt+(1−wfmt)racc,wfmt=0.1 \tilde r(s,a)=w_{\mathrm{fmt}}r_{\mathrm{fmt}}+(1-w_{\mathrm{fmt}})r_{\mathrm{acc}},\quad w_{\mathrm{fmt}}=0.1 r~(s,a)=wfmtrfmt+(1wfmt)racc,wfmt=0.1

其中racc=ract⋅rval⋅rgr_{\mathrm{acc}}=r_{\mathrm{act}}\cdot r_{\mathrm{val}}\cdot r_gracc=ractrvalrg,分别检验动作类型、文本值F1、以及point是否落入目标框。

  • 进一步稳定化:成功自适应负梯度缩放SNGS:针对“未命中不一定错”的负样本更新进行下调,以降低有效但未被离线信用的动作带来的错误惩罚,提升稳定性与离线到在线可预测性。
  • 两阶段总览:Stage1用ASFT获得动作对齐推理并缓解grounding退化;Stage2用“KL正则GRPO + SNGS”的保守RL在部分可验证反馈下提升决策并增强离线-在线一致性。

还有些内容没搞明白

先把“在线vs离线”讲清楚

  • 离线offline:训练和评估都只基于一份固定数据集DμD_\muDμ。对每个状态sss数据集只给了一个“示范动作”a~(s)\tilde a(s)a~(s),于是离线常用指标是“我有没有匹配示范动作”

Moff(π)≜Es∼dμ[π(a~(s)∣s)] M_{\text{off}}(\pi)\triangleq \mathbb{E}_{s\sim d_\mu}\big[\pi(\tilde a(s)\mid s)\big] Moff(π)Esdμ[π(a~(s)s)]

  • 在线online:模型真的在环境里一步步执行,走出由自己策略决定的轨迹分布{dπ,t}\{d_{\pi,t}\}{dπ,t},最后看整条任务是否完成

J(π)≜Pr⁡τ∼π,P(success(τ)=1) J(\pi)\triangleq \Pr_{\tau\sim \pi,P}(\text{success}(\tau)=1) J(π)τπ,PPr(success(τ)=1)

GUI多步任务里,离线“一步匹配”不一定能预测在线“整任务成功”,因为你在线会走到和离线数据分布不同的状态,并且一步里可能有多种正确动作但离线只“承认”一种。


“部分可验证奖励”到底是什么

论文把离线验证器产生的逐步奖励定义为

r~(s,a)≜1{a=a~(s)} \tilde r(s,a)\triangleq \mathbf{1}\{a=\tilde a(s)\} r~(s,a)1{a=a~(s)}

它叫部分可验证partially verifiable,因为:

  • r~=1⇒a\tilde r=1\Rightarrow ar~=1a一定是有效动作KaTeX parse error: Undefined control sequence: \* at position 8: a\in A^\̲*̲(s)
  • r~=0⇏a\tilde r=0\not\Rightarrow ar~=0a无效:你可能选了另一个同样能推进任务的动作,只是数据集没给你“记功”

所以:正反馈可靠,负反馈含糊


为什么要“保守RL”conservative RL:离线到在线可预测性

论文给了一个下界,说明在线成功率J(π)J(\pi)J(π)受两件事控制:

  1. 分布漂移distribution shift:策略π\piπ诱导的状态分布偏离离线数据分布,系数用占用分布不匹配

C(π)≜max⁡t∈[H]sup⁡s:dμ(s)>0dπ,t(s)dμ(s) C(\pi)\triangleq \max_{t\in[H]}\sup_{s:d_\mu(s)>0}\frac{d_{\pi,t}(s)}{d_\mu(s)} C(π)t[H]maxs:dμ(s)>0supdμ(s)dπ,t(s)

  1. “有效但未被记功”的概率质量:在一个状态里,除了示范动作a~(s)\tilde a(s)a~(s)外的其它有效动作也算对,但离线匹配看不到。定义

KaTeX parse error: Undefined control sequence: \* at position 32: …riangleq \pi(A^\̲*̲(s)\setminus\{\…

于是得到(简化理解即可):如果你走偏了很多状态C(π)C(\pi)C(π)大,或者你把概率从示范动作挪到了“其它有效动作”导致离线分数变但真实能力没变(ηˉπ\bar\eta_\piηˉπ不稳定),那离线分数就很难对应在线成功。

“保守RL”的核心思想:别让策略更新太激进,尽量在一个“离线指标仍然靠谱”的区域里优化。


KL正则与“信任域”trust region 是什么用

1) KL正则是什么

在GRPO目标里加一项,把新策略πθ\pi_\thetaπθ拉回参考策略πref\pi_{\text{ref}}πref(通常是SFT初始化):

−β⋅KL(πθ(⋅∣x) ∥ πref(⋅∣x)) -\beta\cdot \mathrm{KL}\big(\pi_\theta(\cdot\mid x)\,\|\,\pi_{\text{ref}}(\cdot\mid x)\big) βKL(πθ(x)πref(x))

直觉:别一步更新把策略改得面目全非

2) 为什么叫信任域trust region

论文用“每个状态的KL不超过ε\varepsilonε”来表达“更新幅度受限”。在这个约束下有两个关键效果:

  • 限制状态分布漂移:占用分布不匹配C(π)C(\pi)C(π)不会比参考策略大太多(式8)
  • 限制偏离示范动作太多:如果参考策略本来就很偏向示范动作(demo-concentrated),那新策略跑到“非示范动作”的概率也被上界住(式9)

所以KL信任域同时在压两件事:少走偏的状态+少把概率质量从示范动作大幅挪走,从而让离线匹配分数MoffM_{\text{off}}Moff更能预测在线成功JJJ。经验上也观察到:没有KL会出现“训练reward上升但在线变差”的现象。


文本值F1 value reward 是什么

在他们的逐步奖励实现里,最终奖励是格式分+准确分加权:

r~(s,a)=wfmtrfmt+(1−wfmt)racc,wfmt=0.1 \tilde r(s,a)=w_{\text{fmt}}r_{\text{fmt}}+(1-w_{\text{fmt}})r_{\text{acc}},\quad w_{\text{fmt}}=0.1 r~(s,a)=wfmtrfmt+(1wfmt)racc,wfmt=0.1

准确分分解为

racc=ract⋅rval⋅rg r_{\text{acc}}=r_{\text{act}}\cdot r_{\text{val}}\cdot r_g racc=ractrvalrg

其中 rvalr_{\text{val}}rval词级别word-level F1比较你输出的文本值vvv和示范文本KaTeX parse error: Undefined control sequence: \* at position 3: v^\̲*̲:如果KaTeX parse error: Undefined control sequence: \* at position 8: F1(v,v^\̲*̲)>0.5,就给1,否则0。

  • 这里的F1就是信息检索里常见的:
    • precision = 你输出的词里有多少是对方也有的
    • recall = 对方的词你覆盖了多少
    • F1 = 二者调和平均
      它的用处:输入文本时允许轻微措辞差异,别因为不完全一致就全判错。

SNGS 是什么:为什么要“缩放负梯度”

问题:在部分可验证里,r~=0\tilde r=0r~=0很含糊——可能真错,也可能是“对但没记功”。如果把所有r~=0\tilde r=0r~=0都当强负反馈,会把策略逼着过拟合示范者那一个动作,更新会偏、会激进。

SNGS(success-adaptive negative gradient scaling)的做法:

  • GRPO对同一状态采样一组GGG个动作,看有多少个刚好命中示范动作,定义组成功率

p^g(s)=1G∑k=1Gr~k \hat p_g(s)=\frac{1}{G}\sum_{k=1}^G \tilde r_k p^g(s)=G1k=1Gr~k

  • 用它构造一个缩放系数

λg(s)=min⁡(λ0+κp^g(s),1) \lambda_g(s)=\min(\lambda_0+\kappa \hat p_g(s),1) λg(s)=min(λ0+κp^g(s),1)

  • 只缩放负优势项(也就是负向更新),正向更新保留:

A~k={Ak,Ak≥0λg(s) Ak,Ak<0 \tilde A_k= \begin{cases} A_k,& A_k\ge 0\\ \lambda_g(s)\,A_k,& A_k<0 \end{cases} A~k={Ak,λg(s)Ak,Ak0Ak<0

直觉:当你在这个状态上“命中示范动作”的概率还不高时,很多没命中的样本可能其实也合理,因此负更新要更保守;等模型已经很常命中示范动作时,没命中就更可能是真错,就逐步恢复正常强度的负更新。


用一句话串起来

  • 由于GUI逐步奖励“只认示范动作”,非命中不一定错,导致离线分数不稳且与在线成功脱钩;
  • GUI-Libra用KL信任域约束策略漂移来提升离线到在线的可预测性,并用SNGS降低“含糊负样本”带来的过度惩罚和偏置更新;奖励里用文本值F1坐标落框等规则验证来构造可自动打分的逐步信号。

代码实现

GUI-Libra中KL信任域约束策略漂移的强化训练实现思路

GUI-Libra结合KL散度(Kullback-Leibler Divergence)信任域约束来限制策略漂移,核心是在离线强化学习(RL)向在线阶段迁移时,通过约束新策略与离线专家策略的KL散度,保证策略更新的稳定性和可预测性。以下是核心代码实现框架(基于PyTorch),涵盖KL约束计算、损失函数构建、策略更新等关键模块。

核心代码实现

1. 环境与基础组件定义
import torch
import torch.nn as nn
import torch.optim as optim
import torch.distributions as dist
import numpy as np

# 策略网络(Actor):输出动作分布的参数(连续动作空间为例)
class Actor(nn.Module):
    def __init__(self, state_dim, action_dim, hidden_dim=256):
        super(Actor, self).__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, action_dim)  # 输出动作均值(方差单独初始化)
        )
        self.log_std = nn.Parameter(torch.zeros(action_dim))  # 动作方差的对数

    def forward(self, state):
        mean = self.net(state)
        std = torch.exp(self.log_std)
        return dist.Normal(mean, std)  # 返回正态分布(连续动作)

# 价值网络(Critic):评估状态价值
class Critic(nn.Module):
    def __init__(self, state_dim, hidden_dim=256):
        super(Critic, self).__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, 1)
        )

    def forward(self, state):
        return self.net(state)
2. KL信任域约束核心模块
class GUI_Libra_KL_Constrained:
    def __init__(self, state_dim, action_dim, lr_actor=3e-4, lr_critic=3e-4, 
                 kl_coeff=1.0, kl_target=0.01):
        # 初始化策略网络(在线策略)和离线专家策略(固定)
        self.actor_online = Actor(state_dim, action_dim)
        self.actor_expert = Actor(state_dim, action_dim)  # 离线预训练的专家策略
        self.critic = Critic(state_dim)
        
        # 优化器
        self.optim_actor = optim.Adam(self.actor_online.parameters(), lr=lr_actor)
        self.optim_critic = optim.Adam(self.critic.parameters(), lr=lr_critic)
        
        # KL约束超参数
        self.kl_coeff = kl_coeff  # KL惩罚系数
        self.kl_target = kl_target  # KL散度目标阈值(信任域)
        
        # 设备
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.actor_online.to(self.device)
        self.actor_expert.to(self.device)
        self.critic.to(self.device)

    # 计算两个策略之间的KL散度(逐样本,后求均值)
    def compute_kl_divergence(self, state):
        # 专家策略分布(固定,不计算梯度)
        dist_expert = self.actor_expert(state)
        # 在线策略分布
        dist_online = self.actor_online(state)
        
        # 计算KL[online || expert](约束在线策略不偏离专家策略过远)
        kl = dist.kl.kl_divergence(dist_online, dist_expert)
        return kl.mean()  # 批次均值

    # 策略损失计算(含KL约束)
    def compute_actor_loss(self, state, action, advantage):
        # 基础策略损失:最大化优势(对数概率)
        dist_online = self.actor_online(state)
        log_prob = dist_online.log_prob(action).sum(dim=-1)  # 连续动作求和
        policy_loss = -(log_prob * advantage).mean()
        
        # KL约束项
        kl_loss = self.compute_kl_divergence(state)
        # 总损失:策略损失 + KL惩罚(保证KL≤kl_target)
        total_loss = policy_loss + self.kl_coeff * torch.max(kl_loss - self.kl_target, torch.tensor(0.0).to(self.device))
        
        return total_loss, kl_loss

    # 价值网络损失(均方误差)
    def compute_critic_loss(self, state, target_value):
        value = self.critic(state)
        return nn.MSELoss()(value, target_value)

    # 单步训练
    def train_step(self, state_batch, action_batch, advantage_batch, target_value_batch):
        # 转换为tensor
        state = torch.FloatTensor(state_batch).to(self.device)
        action = torch.FloatTensor(action_batch).to(self.device)
        advantage = torch.FloatTensor(advantage_batch).to(self.device)
        target_value = torch.FloatTensor(target_value_batch).to(self.device)

        # 训练策略网络(含KL约束)
        self.optim_actor.zero_grad()
        actor_loss, kl_loss = self.compute_actor_loss(state, action, advantage)
        actor_loss.backward()
        self.optim_actor.step()

        # 训练价值网络
        self.optim_critic.zero_grad()
        critic_loss = self.compute_critic_loss(state, target_value)
        critic_loss.backward()
        self.optim_critic.step()

        # 返回损失和KL值(用于监控)
        return {
            "actor_loss": actor_loss.item(),
            "critic_loss": critic_loss.item(),
            "kl_divergence": kl_loss.item()
        }
3. 离线到在线迁移的训练流程
def offline_to_online_training(gui_libra, offline_dataset, online_env, epochs=100, batch_size=64):
    # 1. 离线阶段:加载预训练的专家策略(假设已完成离线训练)
    # (此处省略离线预训练代码,需先将actor_expert加载离线训练好的权重)
    # gui_libra.actor_expert.load_state_dict(torch.load("offline_expert_actor.pth"))
    
    # 2. 在线阶段:迭代训练(混合离线数据+在线交互数据)
    for epoch in range(epochs):
        # 采样批次数据(离线+在线混合,GUI-Libra的核心是融合离线数据约束)
        # 此处简化为从离线数据集采样,实际需加入在线交互数据
        idx = np.random.choice(len(offline_dataset["state"]), batch_size)
        state_batch = offline_dataset["state"][idx]
        action_batch = offline_dataset["action"][idx]
        advantage_batch = offline_dataset["advantage"][idx]  # 预计算的优势函数
        target_value_batch = offline_dataset["target_value"][idx]  # 预计算的目标价值

        # 训练步
        loss_info = gui_libra.train_step(state_batch, action_batch, advantage_batch, target_value_batch)

        # 监控KL散度,动态调整kl_coeff(可选,提升稳定性)
        if loss_info["kl_divergence"] > 2 * gui_libra.kl_target:
            gui_libra.kl_coeff *= 1.5  # KL过大,增大惩罚
        elif loss_info["kl_divergence"] < 0.5 * gui_libra.kl_target:
            gui_libra.kl_coeff *= 0.8  # KL过小,减小惩罚

        # 打印训练信息
        if (epoch + 1) % 10 == 0:
            print(f"Epoch {epoch+1}/{epochs} | Actor Loss: {loss_info['actor_loss']:.4f} | "
                  f"Critic Loss: {loss_info['critic_loss']:.4f} | KL: {loss_info['kl_divergence']:.4f}")

# 示例:初始化与训练
if __name__ == "__main__":
    # 超参数
    state_dim = 10  # 状态维度
    action_dim = 2  # 动作维度
    kl_coeff = 1.0
    kl_target = 0.01  # 信任域阈值(KL上限)

    # 初始化GUI-Libra
    gui_libra = GUI_Libra_KL_Constrained(state_dim, action_dim, kl_coeff=kl_coeff, kl_target=kl_target)

    # 模拟离线数据集(实际需替换为真实离线数据)
    offline_dataset = {
        "state": np.random.rand(1000, state_dim),
        "action": np.random.rand(1000, action_dim),
        "advantage": np.random.rand(1000),  # 需用GAE等方法计算
        "target_value": np.random.rand(1000, 1)  # 需用TD(λ)等方法计算
    }

    # 模拟在线环境(实际需替换为真实环境)
    online_env = None  # 示例占位

    # 离线到在线训练
    offline_to_online_training(gui_libra, offline_dataset, online_env, epochs=100, batch_size=64)

关键细节说明

  1. KL散度计算
    采用torch.distributions.kl.kl_divergence计算在线策略与离线专家策略的KL散度,约束方向为KL[online || expert](保证在线策略不偏离离线专家策略过远)。

  2. 信任域约束实现
    通过torch.max(kl_loss - kl_target, 0)实现“软约束”——仅当KL散度超过目标阈值时,才施加惩罚,避免过度约束导致策略无法探索。

  3. 动态KL系数调整
    训练中监控KL散度,动态调整kl_coeff:KL过大时增大惩罚,KL过小时减小惩罚,平衡“探索”与“约束”。

  4. 离线到在线迁移
    核心是固定离线专家策略(actor_expert),在线训练时通过KL约束限制actor_online的漂移,同时融合在线交互数据(示例中简化为离线数据,实际需加入在线采样数据)。

适配GUI-Libra的扩展建议

  1. 多模态数据融合:GUI-Libra针对GUI交互场景,需适配离散/连续混合动作空间(如点击、滑动),可修改Actor网络输出离散分布(如Categorical)+ 连续分布。
  2. 离线数据重加权:GUI-Libra通常对离线数据的分布偏移做加权,可在损失计算中加入数据重要性权重。
  3. KL约束的自适应调整:结合在线环境的反馈(如任务成功率),动态调整kl_target,提升可预测性。

以上代码为核心框架,需根据GUI-Libra的具体场景(如GUI交互的状态/动作定义、离线数据集格式)进行适配和扩展。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐