Transformer与离线强化学习在广告自动出价中的实践:GAVE框架解析

📅 2026/8/12 17:07:02
Transformer与离线强化学习在广告自动出价中的实践:GAVE框架解析
1. 项目概述当自动出价遇上Transformer在信息流广告这个没有硝烟的战场上每一次广告展示机会的竞价都是一场毫秒级的决策博弈。广告主们既希望用有限的预算触达尽可能多的目标用户又担心预算被无效流量快速消耗。传统的自动出价策略无论是基于规则的还是简单的机器学习模型在面对海量、高维、动态变化的竞价环境时常常显得力不从心。它们要么过于依赖人工经验调参要么难以捕捉长期的价值回报序列导致预算分配效率低下。快手作为国内领先的短视频平台其广告竞价环境尤为复杂。用户兴趣瞬息万变广告库存类型多样如发现页、同城页、直播等这使得构建一个既能精准评估流量长期价值又能实时做出高效出价决策的智能系统成为一项极具挑战性的任务。近期快手团队在KDD 2023上发表的论文《GAVE: A General Framework for Automated Bidding via Value Estimation》提出了一种全新的思路将近年来在序列建模领域大放异彩的Transformer架构与离线强化学习Offline RL相结合为自动出价问题提供了一个通用且强大的解决方案框架。简单来说GAVE的核心思想是将自动出价决策过程建模为一个序列决策问题并利用Transformer强大的序列建模能力从海量的历史竞价日志即离线数据中学习如何做出最优的出价决策以最大化广告主的长期回报如总转化数。这篇论文不仅是将Decision Transformer这类前沿算法成功应用于工业级广告系统的典范其提出的通用价值估计框架也为许多类似的序列决策问题如库存管理、动态定价提供了新的解决路径。接下来我将结合论文内容和个人在广告算法领域的实践经验为你深入拆解GAVE的架构设计、核心原理与实现细节。2. GAVE框架的整体设计与核心思路拆解2.1 自动出价问题的本质一个序列决策难题要理解GAVE的价值首先要明白自动出价到底在解决什么问题。我们抛开复杂的商业术语用一个简单的类比来说明假设你是一个手握100元预算的采购员需要在一天内逛一个巨大的集市流量市场集市里的摊位广告展示机会不断出现每个摊位上的商品用户流量质量参差不齐价格竞价成本也实时变动。你的目标是用这100元买到总价值最高的商品组合。这里的关键难点在于即时反馈与长期回报的冲突你每次出价购买一个商品立刻会付出成本但这个商品带来的真实价值比如用户最终是否购买可能要很久之后才能知道。你不能只挑眼前看起来便宜的商品买因为可能错过后面那些“贵但价值更高”的机会。预算的全局约束你的100元预算是一个硬约束。如果在集市刚开始就把钱花在了低价值商品上后面遇到高价值商品时就会“弹尽粮绝”。反之如果过于保守可能直到集市结束钱还没花完同样造成浪费。环境的复杂性与不确定性集市的人流流量质量、商品价格竞价环境都在动态变化没有固定的规律可循。传统的解决方案如基于PID控制器的策略或使用逻辑回归等模型预测点击率CTR后简单出价往往只能处理上述一两个问题。例如PID控制器擅长控制预算花费速度但对流量价值的判断能力弱而单纯依赖CTR模型出价则容易陷入“短视”忽视预算约束和长期回报。2.2 GAVE的破局思路价值估计与序列建模GAVE论文的标题直指核心——通过价值估计实现自动出价。它认为自动出价智能体的核心能力应该是准确估计“在当前状态下采取某个出价动作后所能获得的长期累积回报即价值”。一旦能准确估计这个价值决策就变得简单了在预算和策略约束下选择能带来最高价值的出价动作。那么如何从数据中学习这种复杂的价值估计函数呢GAVE的创新在于引入了两个关键武器离线强化学习Offline RL与需要与环境实时交互、成本高昂且风险大的在线强化学习不同离线RL直接从历史记录即离线数据集中学习策略。这完美契合了广告系统的需求——我们拥有海量的历史竞价日志记录了在以往各种策略下系统状态、出价动作、即时成本、以及最终是否产生转化等数据。利用这些数据我们可以安全、高效地训练一个超级智能体。Transformer架构自动出价决策是一个典型的序列决策过程。一次广告活动从开始到结束可以看作是由“状态-动作-奖励”组成的轨迹。Transformer凭借其强大的自注意力Self-Attention机制能够捕捉序列中任意两个元素之间的长距离依赖关系。这意味着模型在决定当前时刻的出价时可以“回忆”并综合考虑很久之前的花费情况、获得的回报以及当时的市场状态从而做出更全局、更明智的决策。GAVE框架巧妙地将两者结合。它采用Decision Transformer的范式但针对广告出价的特性进行了关键改造。Decision Transformer原本的输入是“回报-状态-动作”序列目标是预测下一个动作。而在GAVE中其核心模型被训练来预测累积的未来回报即价值。给定一段历史状态和动作序列以及一个目标回报如剩余预算对应的期望转化数模型可以评估在当前状态下后续采取一系列动作能达成该目标的可能性从而指导出价。2.3 框架总览与工作流程GAVE的整体框架包含离线训练和在线服务两个主要部分。离线训练阶段数据准备收集历史竞价日志构建轨迹数据集。每条轨迹对应一次广告活动从开始到结束的完整序列包含每个时间步的状态如剩余预算、时间进度、历史花费、市场特征、动作出价价格、即时奖励如是否有点击/转化等信息。模型训练使用Transformer架构构建价值估计模型。输入是状态序列和动作序列模型被训练来预测从当前时刻到轨迹结束的累积回报即状态-动作对的价值。这里的一个关键技巧是引入了“目标回报”作为模型的额外输入使模型能够学习在不同目标下的条件价值函数。策略提取训练好的价值模型本身就是一个策略。在线使用时给定当前状态和一个目标如“用剩余预算获取尽可能多的转化”模型可以评估不同出价动作对应的预期价值然后选择价值最高的动作执行。在线服务阶段当一个广告请求到来时系统收集当前状态信息如广告活动实时数据。将状态序列和目标回报输入到已训练好的GAVE模型中。模型对多个候选出价动作进行价值评估。系统选择预估价值最高的出价动作参与实时竞价。这个流程听起来清晰但其中充满了工程与算法上的挑战。例如如何定义“状态”“动作”空间是连续的吗Transformer模型如何设计才能兼顾效率与效果目标回报如何设定接下来我们将深入核心细节。3. 核心细节解析与实操要点3.1 状态、动作与奖励的设计业务逻辑的数学表达将业务问题形式化为强化学习问题第一步也是最重要的一步就是定义状态、动作和奖励。设计的好坏直接决定了模型能否学到有效的策略。状态设计 GAVE中的状态需要全面刻画竞价环境的动态以及广告活动的进程。论文中可能包含以下几类特征在实际应用中需要根据业务数据丰富度进行增补活动级状态剩余预算、已花费金额、剩余时间或时间进度、历史累积转化数、平均转化成本CPA等。这些是决定出价激进与否的核心宏观指标。请求级上下文当前流量的用户特征画像、兴趣标签、历史行为、上下文特征时间、地理位置、网络环境、广告位特征等。这些决定了当前流量的即时价值。市场状态近期市场竞争激烈程度如平均成交价分布、同行业广告主出价水平等。这部分数据获取较难但对模型理解环境动态至关重要。历史序列信息过去一段时间如最近10个竞价请求的状态、动作、奖励摘要。这部分信息会被Transformer的序列建模能力直接处理。实操心得状态特征并非越多越好。需要警惕特征稀疏性和共线性问题。对于类别型特征如城市、广告位必须做好嵌入Embedding和分桶。对于数值型特征如预算建议进行标准化或分桶处理使其分布更稳定便于模型学习。一个常见的技巧是加入“比例特征”如“花费预算比”、“时间消耗比”这些比例特征对模型判断当前阶段非常有效。动作空间 动作即出价。理论上出价是一个连续值但实践中通常将其离散化到一个合理的范围内以降低学习难度和在线推理的复杂度。例如可以设定一个基础出价然后让模型学习一个乘数因子动作空间就是这个乘数因子的几个离散档位如0.5x, 0.8x, 1.0x, 1.2x, 1.5x。奖励设计 奖励函数是指引模型学习的“指挥棒”。在自动出价场景中最直接的奖励是广告主关心的业务指标如转化安装、下单等。可以设定一次转化奖励为1。同时为了引导模型更好地管理预算可以在花费超出预算时给予大的负奖励或者在时间结束时未花完预算时给予小的负奖励惩罚浪费。奖励的设计需要谨慎不合理的奖励会导致模型学到奇怪的行为例如为了获得转化奖励而在初期疯狂高价抢量迅速耗尽预算。3.2 Transformer模型的关键改造适配序列决策GAVE的核心模型是一个基于Transformer Decoder的架构。为什么是Decoder而不是完整的Encoder-Decoder因为在序列决策预测中我们通常是以自回归的方式生成动作序列这更接近语言模型中生成下一个词的模式。其关键改造点包括输入序列构造输入不再是单纯的单词ID而是由状态、动作、奖励或目标回报拼接而成的token。论文中可能采用了一种“回报-状态-动作”的排列方式。例如一个时间步的输入可以是[剩余预算, 时间进度, 用户特征..., 动作(出价), 即时奖励]的嵌入向量。整个输入序列就是一次广告活动轨迹的拼接。目标回报条件化这是GAVE区别于原始Decision Transformer的一个重要创新。在训练时除了历史轨迹模型还会接收一个“目标回报”作为条件输入通常放在序列开头。这个目标回报可以是剩余预算对应的期望转化数或者是广告主设定的目标CPA。模型被训练去预测未来累积回报而这个预测会以目标回报为条件。这使得模型学会了“按需调整策略”的能力——给定不同的目标它能给出不同的最优出价序列。因果注意力掩码为了保证自回归生成的性质必须使用因果注意力掩码Causal Attention Mask。这意味着在预测第t个时间步的token时模型只能看到第1到第t-1个时间步的信息而不能“偷看”未来的信息。这符合在线决策时我们无法预知未来的实际情况。输出与损失函数模型的输出是对下一个token可能是动作也可能是价值的预测。在GAVE的价值估计框架下一个主要的训练目标可能是预测累积回报价值。损失函数通常采用均方误差MSE或平滑L1损失来最小化价值预测的误差。3.3 离线强化学习的关键处理分布偏移离线RL最大的挑战是分布偏移。我们训练所用的数据是由历史上的某个或多个旧策略可能是人工规则、旧版模型产生的。这些旧策略的动作分布与我们现在要学习的新策略的动作分布可能存在巨大差异。如果模型在训练时只见过状态s下旧策略采取的动作a那么当新策略在状态s下想采取一个数据中很少见的动作a‘时模型对这个s, a‘的价值估计可能会非常不准确甚至是荒谬的这被称为“外推误差”。GAVE框架需要通过算法设计来缓解这个问题。论文中可能借鉴或采用了以下一些离线RL的经典思路保守性正则化在训练目标中加入一个正则化项惩罚模型对那些数据分布之外的状态-动作对做出过于乐观的价值估计。这迫使模型的学习更加“保守”避免提出数据中未经验证的激进策略。行为克隆约束约束新学习到的策略不要偏离数据中的旧策略行为策略太远。这可以通过在策略网络中增加一个与行为策略输出动作的KL散度惩罚来实现。不确定性估计让模型除了预测价值还预测价值的不确定性如方差。在线决策时可以倾向于选择那些价值估计高且不确定性低的动作避开高不确定性的区域。注意事项离线RL的训练非常敏感。需要仔细监控训练过程中策略在验证集同样是离线数据上的表现。一个重要的评估方法是计算学得策略的预计回报并与数据中旧策略的实际回报进行比较。如果学得策略的预计回报远高于旧策略但它的动作分布与旧策略差异很大这很可能是一个危险的信号表明模型出现了严重的过估计和外推误差。此时需要调整保守性正则化的强度。4. 实操过程与核心环节实现4.1 数据管道与轨迹构建实现GAVE的第一步也是工作量最大的一步是构建高质量的训练数据集。原始日志解析从数据仓库中提取指定时间段内、特定广告活动的所有竞价请求日志。每条日志应包含请求ID、活动ID、时间戳、用户上下文特征、参与竞价的广告列表、最终胜出广告及其出价和扣费如果是自己的广告、以及后续产生的用户行为点击、转化等。轨迹切片将一个广告活动从开始到结束或从开始到某个时间点的所有请求日志按时间顺序排列构成一条轨迹。需要确保轨迹的完整性。对于超长活动可以考虑按天或按预算消耗阶段进行切片。状态特征工程对每条请求计算其状态特征。这包括静态特征活动总预算、目标CPA等在轨迹内不变。动态累积特征到当前请求为止的已花费、已获得转化数、平均CPM/CPC等。这些需要实时滚动计算。实时上下文特征直接从日志中提取的用户、环境特征。序列特征将最近N个请求的状态、动作、奖励进行聚合如均值、标准差或直接作为序列输入模型。动作与奖励标注动作即本公司在这次竞价中的出价。如果未参与竞价或未胜出则需要根据业务逻辑定义一个“虚拟出价”或视为特殊动作。即时奖励通常一次请求的即时奖励为0无转化或1有转化。这里有一个关键点转化可能延迟发生。需要使用归因窗口如点击后7天内内的数据来回填转化标签确保奖励的准确性。数据集划分与清洗按活动ID或时间划分训练集、验证集和测试集。清洗掉异常轨迹如预算为0、数据严重缺失、或短时间内花费异常高的活动。一个简化的轨迹数据表示如下表所示轨迹ID时间步剩余预算时间进度用户兴趣分...出价动作即时奖励转化Camp_001110000.000.85...2.50Camp_0012997.50.010.42...1.80Camp_0013995.70.020.91...3.01........................Camp_001T5.20.990.67...1.504.2 模型构建与训练我们使用PyTorch框架来示意GAVE核心模型的构建。以下是一个高度简化的代码骨架聚焦于关键结构。import torch import torch.nn as nn import torch.nn.functional as F from torch.nn import TransformerDecoder, TransformerDecoderLayer class GAVETransformer(nn.Module): def __init__(self, state_dim, action_dim, hidden_dim, nhead, num_layers, max_seq_len): super().__init__() self.state_dim state_dim self.action_dim action_dim self.hidden_dim hidden_dim self.max_seq_len max_seq_len # 嵌入层分别处理状态、动作、目标回报 self.state_embed nn.Linear(state_dim, hidden_dim) self.action_embed nn.Linear(action_dim, hidden_dim) self.return_embed nn.Linear(1, hidden_dim) # 目标回报是标量 self.timestep_embed nn.Embedding(max_seq_len, hidden_dim) # 时间步位置编码 # Transformer Decoder层 decoder_layer TransformerDecoderLayer(d_modelhidden_dim, nheadnhead, dim_feedforwardhidden_dim*4, batch_firstTrue) self.transformer TransformerDecoder(decoder_layer, num_layersnum_layers) # 输出头预测状态-动作对的价值 (Q值) self.value_head nn.Linear(hidden_dim, 1) def forward(self, states, actions, target_returns, timesteps): states: [Batch, Seq_len, State_dim] actions: [Batch, Seq_len, Action_dim] target_returns: [Batch, 1] # 每个序列一个目标回报 timesteps: [Batch, Seq_len] # 每个位置的时间步索引 batch_size, seq_len states.shape[:2] # 1. 嵌入 state_emb self.state_embed(states) # [B, L, H] action_emb self.action_embed(actions) # [B, L, H] # 将目标回报扩展并嵌入然后加到序列每个位置上作为条件 return_emb self.return_embed(target_returns.unsqueeze(1)) # [B, 1, H] return_emb return_emb.expand(-1, seq_len, -1) # [B, L, H] timestep_emb self.timestep_embed(timesteps) # [B, L, H] # 2. 构造输入序列这里采用 [状态, 动作] 交错的方式也可以尝试其他排列 # 为简化我们将状态和动作嵌入相加再加上回报和时间步嵌入 token_emb state_emb action_emb return_emb timestep_emb # [B, L, H] # 3. 因果注意力掩码防止看到未来信息 causal_mask torch.triu(torch.ones(seq_len, seq_len) * float(-inf), diagonal1).to(states.device) # 4. 通过Transformer # 在Decoder-only架构中tgt 和 memory 都是我们的输入序列 transformer_out self.transformer(tgttoken_emb, memorytoken_emb, tgt_maskcausal_mask, memory_maskcausal_mask) # [B, L, H] # 5. 预测价值 values self.value_head(transformer_out) # [B, L, 1] return values.squeeze(-1) # [B, L] # 训练循环示意 model GAVETransformer(...) optimizer torch.optim.Adam(model.parameters(), lr1e-4) for epoch in range(num_epochs): for batch in dataloader: states, actions, rewards_to_go, timesteps batch # rewards_to_go: 从当前时刻到结束的累积回报 # 目标回报可以设定为轨迹初始的回报期望或根据剩余预算动态计算 target_returns ... # [B, 1] # 前向传播预测每个状态-动作对的价值 predicted_values model(states, actions, target_returns, timesteps) # 计算损失让预测价值接近真实的“未来累积回报” # 注意这里需要对齐。对于序列中第t个位置应预测从t到结束的回报。 # 假设 rewards_to_go 已经是对齐好的 [B, L] 张量 loss F.mse_loss(predicted_values, rewards_to_go) optimizer.zero_grad() loss.backward() optimizer.step()训练要点回报归一化累积回报的数值可能很大且不稳定建议对rewards_to_go进行归一化如减去均值除以标准差训练后再反归一化。目标回报的设定在训练时target_returns可以取每条轨迹初始的期望回报如总预算/目标CPA。在线推理时这个目标可以是动态的比如剩余预算 / 历史平均CPA。批次构建由于轨迹长度不一需要做padding和masking。确保注意力掩码和损失计算时忽略padding部分。4.3 在线推理与策略执行模型训练好后在线服务的关键是高效地进行价值评估。状态实时更新维护一个在线状态管理器为每个活跃的广告活动实时更新其状态特征剩余预算、花费速度、时间进度等。动作候选集生成对于一次竞价请求根据当前状态和业务规则生成一组候选出价动作如基础出价的0.5, 0.8, 1.0, 1.2, 1.5倍。价值评估构建输入序列将历史最近的K个状态-动作对从状态管理器中获取作为历史序列。对于每个候选动作将其与当前状态拼接形成一个“假设的”下一个时间步的输入。将整个序列历史序列 假设步与目标回报一起输入GAVE模型。模型输出序列中每个位置的价值预测。我们取最后一个位置即假设步的预测价值作为执行该候选动作的预期长期回报。动作选择选择预估价值最高的候选动作作为最终出价。有时为了探索或平滑可以加入一些随机性如ε-greedy或选择Top-K价值动作的概率分布。实操心得在线推理的延迟是必须考虑的核心工程指标。Transformer的解码过程是逐位进行的但我们的候选动作评估是并行的。一种优化方法是将多个候选动作与同一段历史状态序列批量组合一次性输入模型进行预测这可以充分利用GPU的并行计算能力显著降低延迟。此外历史序列长度K需要权衡太长会增加计算量太短可能信息不足需要通过实验确定一个平衡点。5. 常见问题与排查技巧实录在实际实现和调优GAVE这类基于Transformer的离线RL出价模型时会遇到许多典型问题。以下是一些常见坑点及排查思路。5.1 模型训练不稳定或发散现象训练损失剧烈震荡不收敛甚至变成NaN。可能原因与排查梯度爆炸Transformer模型层数深容易梯度爆炸。解决使用梯度裁剪torch.nn.utils.clip_grad_norm_并尝试更小的学习率。回报数值过大/不稳定累积回报可能达到几千上万导致学习困难。解决对回报进行归一化。可以按批次归一化或者使用整个训练集的统计量进行归一化。数据中存在异常轨迹某些活动数据异常如被攻击的虚假流量。解决加强数据清洗过滤掉花费速度、转化率等指标极端异常的轨迹。离线RL的分布偏移导致外推误差模型对未见过的状态动作对做出了极端错误的价值估计。解决引入保守性正则化。在损失函数中加入一个惩罚项例如鼓励模型对训练数据分布内的s,a做出准确预测同时对分布外的s,a的价值预测向某个先验值如最小值靠拢。5.2 模型在线表现不佳出价策略不如旧版现象离线评估在历史数据上模拟指标很好但上线A/B测试后关键指标如转化量、CPA反而下降。可能原因与排查离线评估指标不可靠单纯用模型预测价值在历史数据上的拟合误差来评估是不够的。需要使用离线策略评估方法如重要性采样Importance Sampling或双重稳健估计Doubly Robust Estimation来更准确地估计新策略在历史数据上的期望回报并与旧策略对比。在线环境分布偏移训练数据是过去一段时间的数据而在线环境已经发生了变化如市场竞争格局、用户行为。解决定期用最新数据更新模型增量训练。可以考虑在线学习或模仿学习来快速适应微小变化。模型过于保守由于离线RL的保守性约束过强模型只敢采取与历史数据非常相似的动作缺乏探索性无法发现更优策略。解决调整保守性正则化的强度。可以尝试在验证集上寻找一个平衡点使得离线评估的新策略回报显著高于旧策略同时其动作分布又不会偏离太远。状态特征在线获取不一致离线训练时使用的某些特征在线推理时无法实时获取或计算逻辑不一致。解决建立严格的特征一致性校验管道确保线上线下特征计算代码和口径完全一致。5.3 在线推理延迟过高现象模型预测耗时超过竞价系统允许的时限通常要求在10毫秒内。可能原因与排查模型过大或序列过长Transformer的计算复杂度与序列长度的平方成正比。解决精简模型减少层数、隐藏层维度限制历史序列长度K。可以使用知识蒸馏训练一个小模型。未充分利用硬件并行如4.3节所述应批量评估候选动作。解决将当前请求下所有候选动作的推理合并到一个批次中大幅减少GPU kernel启动开销。预处理/后处理耗时特征工程、数据组装等步骤在CPU上完成成为瓶颈。解决优化特征计算逻辑将能提前计算的特征缓存起来。考虑使用TensorRT等工具对模型进行推理优化和部署。5.4 出价策略出现“极端”行为现象模型在某些情况下突然出价极高或极低不符合业务常识。可能原因与排查目标回报设定不合理在线推理时传入的target_returns计算有误导致模型为了达成不可能的目标而采取极端动作。解决检查目标回报的计算逻辑确保其与当前状态如剩余预算、剩余时间是匹配的、合理的。状态特征存在缺失或异常值某个重要特征在线推理时出现缺失或超出训练时的范围导致模型进入未知领域产生怪异输出。解决完善特征监控和兜底逻辑。对于缺失特征使用均值或默认值填充。对于异常值进行截断处理。价值估计模型在某些区域过拟合模型对训练数据中某些稀疏区域学到了错误规律。解决增加数据多样性或者在损失函数中加入对价值预测平滑性的正则化。将上述问题与解决方案汇总便于快速查阅问题大类具体现象可能原因排查与解决思路训练问题损失震荡/发散梯度爆炸、回报未归一化、数据异常梯度裁剪、回报归一化、严格数据清洗训练问题离线评估好在线效果差离线评估不准、环境偏移、策略过保守使用离线策略评估方法、定期更新模型、调整正则化强度性能问题在线推理延迟高模型复杂、序列长、未批量推理精简模型、限制序列长度、批量评估候选动作策略问题出价行为极端目标回报不合理、特征异常、模型过拟合检查目标计算逻辑、监控并处理特征异常、增加数据/正则化实现GAVE这样的系统是一个算法与工程深度结合的挑战。它要求团队不仅要对强化学习、Transformer理论有深刻理解还要具备强大的大数据处理、模型训练与部署、以及在线系统架构能力。从论文到落地中间有很长的路要走但一旦走通其带来的广告投放效率提升将是显著的。我个人在实践中的体会是先从一个小流量、预算充足的广告场景开始试点用最简单的模型结构如1层Transformer和特征集跑通全链路验证框架的可行性然后再逐步迭代优化模型和特征是控制风险、稳步推进的有效策略。