【论文学习】GUI-Libra: Training Native GUI Agents to Reason and Act with Action-aware Supervision and Part
论文主要内容详解
第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)
两步自动过滤以提升“推理-动作-坐标”一致性与数据质量:
- 动作重预测一致性过滤:对每个输入做多次随机预测,统计与标注匹配频率;低于阈值(0.3)的样本丢弃,用于剔除不确定/低质量步骤。
- 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):兼顾推理与可执行性
- 混合监督:同时使用
- reasoning-then-action样本(带
<think>), - direct-action样本(去掉推理,仅保留
<answer>),以获得“可推理”与“可直接出动作”两种监督信号。
- reasoning-then-action样本(带
- 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)∼Dmix∣ct∣+αa∣at∣+αg∣gt∣logπθ(ct∣xt)+αalogπθ(at∣xt,ct)+αglogπθ(gt∣xt,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+(1−wfmt)racc,wfmt=0.1
其中racc=ract⋅rval⋅rgr_{\mathrm{acc}}=r_{\mathrm{act}}\cdot r_{\mathrm{val}}\cdot r_gracc=ract⋅rval⋅rg,分别检验动作类型、文本值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(π)≜Es∼dμ[π(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~=1⇒a一定是有效动作KaTeX parse error: Undefined control sequence: \* at position 8: a\in A^\̲*̲(s)
- 但r~=0⇏a\tilde r=0\not\Rightarrow ar~=0⇒a无效:你可能选了另一个同样能推进任务的动作,只是数据集没给你“记功”
所以:正反馈可靠,负反馈含糊。
为什么要“保守RL”conservative RL:离线到在线可预测性
论文给了一个下界,说明在线成功率J(π)J(\pi)J(π)受两件事控制:
- 分布漂移distribution shift:策略π\piπ诱导的状态分布偏离离线数据分布,系数用占用分布不匹配
C(π)≜maxt∈[H]sups: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)
- “有效但未被记功”的概率质量:在一个状态里,除了示范动作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+(1−wfmt)racc,wfmt=0.1
准确分分解为
racc=ract⋅rval⋅rg r_{\text{acc}}=r_{\text{act}}\cdot r_{\text{val}}\cdot r_g racc=ract⋅rval⋅rg
其中 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=1∑Gr~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,Ak≥0Ak<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)
关键细节说明
-
KL散度计算:
采用torch.distributions.kl.kl_divergence计算在线策略与离线专家策略的KL散度,约束方向为KL[online || expert](保证在线策略不偏离离线专家策略过远)。 -
信任域约束实现:
通过torch.max(kl_loss - kl_target, 0)实现“软约束”——仅当KL散度超过目标阈值时,才施加惩罚,避免过度约束导致策略无法探索。 -
动态KL系数调整:
训练中监控KL散度,动态调整kl_coeff:KL过大时增大惩罚,KL过小时减小惩罚,平衡“探索”与“约束”。 -
离线到在线迁移:
核心是固定离线专家策略(actor_expert),在线训练时通过KL约束限制actor_online的漂移,同时融合在线交互数据(示例中简化为离线数据,实际需加入在线采样数据)。
适配GUI-Libra的扩展建议
- 多模态数据融合:GUI-Libra针对GUI交互场景,需适配离散/连续混合动作空间(如点击、滑动),可修改Actor网络输出离散分布(如Categorical)+ 连续分布。
- 离线数据重加权:GUI-Libra通常对离线数据的分布偏移做加权,可在损失计算中加入数据重要性权重。
- KL约束的自适应调整:结合在线环境的反馈(如任务成功率),动态调整
kl_target,提升可预测性。
以上代码为核心框架,需根据GUI-Libra的具体场景(如GUI交互的状态/动作定义、离线数据集格式)进行适配和扩展。
更多推荐

所有评论(0)