动态定价的强化学习
想象一位顾客打开一盒牛奶的产品页面。你有一次机会决定向他们收费多少——不是每个人看到的货架价格,而是为这个人量身定制的折扣,就在此刻。
提供太少的折扣,价格敏感的购物者会耸耸肩去别处买牛奶。你保留了从未发生的销售的利润。提供太大的折扣,你赢得了销售,但也教会了这位顾客牛奶"应该"比原价便宜。下周,他们会等待折扣。再过一周,他们会期待更深的折扣。你悄悄地训练了自己的顾客侵蚀你未来的利润。
这是个性化动态定价的核心陷阱。每个折扣同时是三个决定:
- 最大化转化 — 今天完成销售;
- 保护利润 — 不要给出超过必要的折扣;
- 不要毒害未来 — 保持顾客的价格期望健康。
残酷的是,你的反馈信号只告诉你今天的情况。你看到的是即时销售和即时利润。顾客价格锚点的缓慢侵蚀,悄悄转向竞争对手的顾客,因为你过度折扣和他们过度购买而变质的易腐库存——这些都不会出现在这笔交易的奖励中。
这正是强化学习应该擅长的问题类型:具有延迟、复杂后果的行动。所以我们提出了一个具体的问题:
RL代理能否仅从即时利润奖励中学习购物者的隐藏心理——价格锚点、消费节奏、对变质的恐惧——并击败精心手工调整的定价策略?
为了回答这个问题,我们构建了三样东西:一个模拟市场,一个由假但可信的购物者组成的群体,以及一个我们训练、打破并重建了十几次的RL代理。这篇文章就是那段旅程的故事——包括不起作用的部分,我们无法负担运行的一个实验,以及最让我们惊讶的结果。
1、市场
我们将环境建模为一个在线杂货店。目录包含25种产品,分布在五个类别中,特意选择使保质期和利润朝不同方向拉动。
产品按手工分为训练/验证/测试集,因此每个分割包含所有五个类别。代理在训练期间从未见过的产品上进行评估。
每次用户访问产品页面时,代理都会观察情况并选择折扣。它看到了什么?
从高层次来看:
- 用户的近期历史(他们上次访问的时间、接受的折扣、购买频率);
- 产品的属性(保质期、最大允许折扣)。
观察设计中有一个刻意的转折,这在后面变得重要——代理的评论者可以看到演员从未看到的隐藏信息。记住这一点;它在后面的章节中会得到回报(完整功能列表在附录A中)。
行动是折扣。在内部,代理从11个离散桶中选择——0%、10%、20%,最高到100%(设计上,100%代表产品的最大安全折扣,产生$0利润)——然后作为连续价格应用。那个"离散 vs 连续"的选择听起来像脚注。实际上,它被证明是整个项目中最重要的决定。
2、奖励函数
奖励有意设计得很简单:交易的归一化利润。
除以零售价格使数字在完全不同的产品之间具有可比性——4美元的一条面包和120美元的一罐鱼子酱不应该生活在完全不同的奖励尺度上(我们稍后会看到这种归一化是多么必要)。
重要! 奖励纯粹是即时的。没有保持顾客价格锚点健康的奖励。没有造成变质的惩罚。没有留存率或终身价值的术语。代理只被告知这笔交易赚取的利润。
使这个问题困难的一切——锚点侵蚀、叛变、变质——都不在奖励中。我们打的赌是,一个循环PPO代理,如果有足够的历史和正确的架构,将学会隐式地管理那些长期权衡,仅仅因为短视的贪婪会在几步之后受到惩罚。本文的其余部分实际上是这个赌注如何得到回报的故事。
3、构建人类:LLM代理
在训练定价代理之前,你需要有人来卖给他们。我们需要一个行为像人的购物者群体:他们讨厌被多收费,他们在划算的交易时囤货,如果买太多会让易腐品变质。
我们的第一个本能是明显的现代选择:使用LLM作为"人类代理"。它灵活,零样本,让你无需编写一行模拟逻辑就能测试行为假设。
所以我们先尝试了朴素版本——直接调用。将用户的情况打包到一个提示中(你的库存、价格、你的预算个性),然后问:"你买多少单位?"。
它一直失败。模型会正确注意到障碍——"这个价格很高","这会超过保质期"——然后说服自己无论如何都要购买。它以惊人的创造力为囤积创造了理由:
- 利用低库存作为以任何价格购买的借口;
- 引用维持"习惯"的需要;
- 合理化一笔糟糕的交易,因为它是"只是一点点"或"反正是便宜货"。
即使这种行为对某些人来说是天生的,为这个群体创建定价引擎也会太容易,不会为社区增加价值。
我们本可以通过少样本示例或硬阈值来缓解这个问题——但这只会教模型复制我们的示例,我们想要的有机、令人惊讶的行为会坍缩成过拟合。
解决方法是停止要求模型在单次调用中做出决定。我们将决策拆分为推拉链。
迫使模型在承诺之前阐述双方,解决了最严重的幻觉。此外,我们添加了一些支持技巧——例如,当供应只是中等偏低时隐藏确切的库存数字,这样模型就不能以"我快用完了"为懒惰借口。
最终,它起作用了。我们有了可信的购物者。只有一个问题,而且对我们的实际目标来说是致命的:它太慢太贵了。
单个用户访问成本约**$0.006**,使用gpt-5.4-nano在低推理设置下。RL训练需要数百万次访问——按照这个速率,一百万步大约花费**$1,200**(使用缓存),我们需要数百万步。本地开源模型规避了美元成本,但仍然比原型训练循环中的速度慢几个数量级。
LLM给了我们一个可信的购物者心理模型。我们只是负担不起针对它的训练成本。所以我们逆向工程了它!
4、构建人类:启发式双胞胎
4.1 简介
想法:构建一个数学模型来重现LLM的推拉心理,但运行速度快数千倍且不花钱。不是黑盒——而是一个透明的竞争力量系统,反映买家脑海中的拔河。
这些购物者的身份依据来自真实来源:NielsenIQ的全球消费者展望报告。行为特征及其流行度来自实际调查数据——例如,报告发现超过一半的消费者愿意为新鲜产品支付更多,因此我们一半的人口带有新鲜爱好者特征;大约30%在促销时囤货,所以30%是批量购买者,依此类推,在预算、计划和库存轴上(当然,这些是刻意的简化,不是学术人口模型——但它们使模拟锚定在真实的东西上)。
在我们的模拟中,每个购物者由七个行为特征组装而成—— bargain-hunter(讨价还价者)、premium-seeker(高端寻求者)、price-careless(价格无所谓者)、rational(理性者)、impulsive(冲动者)、bulk-buyer(批量购买者)和freshness-lover(新鲜爱好者)——每个都根据调查数据的流行度开启。他们沿着三个轴分布:一个人对价格的反应方式(bargain-hunter追逐每个折扣,premium-seeker对折扣产生怀疑,price-careless购物者几乎不看),他们的计划方式(rational vs impulsive),以及他们的囤货方式(bulk-buyer、freshness-lover)。一个用户可能有0个或多个特征。
此外,每个模拟购物者不仅仅是一袋特征。在他们的个性之上,每个用户还携带:
- 忠诚度得分,从
[0.2, 1.0]均匀抽取。它只做一件事:它设置price-insensitive(价格不敏感)购物者在任何给定访问中转向竞争对手的可能性。 - **个人访问节奏。**不是每个人都在相同的节奏上购物,因此每个用户获得自己的访问窗口——下限从2-14天抽取,上限增加另外2-14天的松弛——每次访问从该窗口新鲜采样。在整个人口中,访问间隔跨越2到28天。
- 每个产品状态——每个用户关心的四个随机值:他们的
interest-level(兴趣水平)(低/中/高),他们消费它的速度,他们当前在储藏室中有多少,以及他们的起始价格锚点。同一个人可以是忠实的牛奶饮用者,冰箱塞满了,也可以是随意的、没有锚点的咖啡购买者。
这种每个产品/每个用户的状态使群体真正异质:两个个性相同的购物者仍然可能要求完全不同的定价,仅仅因为他们的储藏室和消费率不同。
4.2 主逻辑
购买决策本身一次处理一个项目。对于添加到购物车的每个潜在单位,模型计算两个对立的力量:
- 推力 = 需求 + 贪婪 + 便利 — 购买的欲望;
- 拉力 = 风险 + 怨恨 — 购买的阻力。
如果推力(加上一点噪音)战胜拉力,物品进入购物车,我们评估下一个单位的相关性。当拉力获胜的那一刻,用户停止。这自然捕捉了递减收益:第一盒牛奶很容易合理化;第五盒必须克服更多积累的阻力。
这些力量内部的内容就是心理学所在:
- 需求随着用户的供应减少到下一次预期访问时增长;
- 贪婪是相对于他们期望支付的价格的好交易的刺激,不同个性反应不同。
bargain-hunter的喜悦随着折扣深度爆炸性增长;premium-seeker实际上对太深的折扣产生怀疑("它有什么问题?"); - 风险捆绑了花钱的摩擦,打开钱包的一次性障碍,以及购买会在吃完之前变质的易腐食品的真实恐惧;
- 怨恨是当价格高于你内部期望时的感受。用户会容忍在 vital、高兴趣的必需品(如水、面包)上被多收费,但如果价格上涨,他们很容易被冒犯并拒绝购买低兴趣的冲动物品。
其中两个值得强调,因为它们是RL代理必须学习的长期游戏的核心。
4.3 价格锚点
每个用户都携带一个产品应该成本的内部感觉——他们的价格锚点——它会随着时间漂移,就像人类记忆一样:
- 遗忘:在访问之间,锚点每天衰减约1%回到正常零售价格。一段时间不给顾客折扣,他们的期望会重置为正常。
- 学习:当他们看到新价格时,锚点向它移动——当价格低于他们的锚点时快速移动(我们喜欢便宜货并快速调整),当价格高于锚点时更顽固地移动(我们抵制价格上涨)。如果他们实际购买了,它比只看时移动更多。
这个单一机制就是为什么这个问题非同寻常。今天的大折扣实际上重写了顾客的期望,降低了你明天能赚取的利润。只优化今天利润的代理必须通过延迟反馈发现这个后果。
4.4 变质
在访问之间,模拟每天老化每个用户的库存:消耗从最旧批次开始消耗,保质期倒计时,任何变质或用完的都被移除。过度销售易腐品,顾客不会感谢你——他们被困在浪费中,这抑制了他们未来的购买。
4.5 销售损失
还有一个显式的销售损失机制,它沿着个性线分裂。price-insensitive(价格不敏感)购物者(price-careless和premium-seekers)偶尔会纯粹随机地转向竞争对手,根据他们的忠诚度缩放——他们需要产品,你没有捕获它。price-sensitive(价格敏感)购物者有机地叛变:他们未满足的日常消费累积为损失量——在合理定价下他们会购买的产品的确切数量。
以这种方式构建的回报是,因为每个购物者都是这些特征和力量的不同交叉点,群体的行为不像一个平均 blob。独特的行为集群自行出现。
5、指标和评估
在这样的市场中,单步奖励几乎告诉你 nothing —— 深度折扣在这笔交易中看起来很棒,却悄悄毁掉了接下来的十笔。因此,在比较代理之前,我们必须定义两件事:我们测量什么,以及测量多长时间。
每个代理在模拟的3年范围(1,095天)上评分,使用保留的测试集——500个用户和5个代理在训练期间从未见过的产品。三年很重要:它足够长,使得缓慢的动态——锚点侵蚀、变质、重复访问节奏——能够实际展开,并将短视策略与耐心策略区分开来。较短的运行会美化尚未为其造成的未来损害付出代价的贪婪代理。
决定排行榜的指标是每用户累积平均利润。这是业务底线,也是本文中每个"+13%"所指的:赚取的总利润,跨用户和时间平均。
其中m是每笔交易的利润。
除了标题数字外,我们还跟踪支持遥测以了解代理如何赚取——或失去——其利润:
- 每用户滚动平均奖励 — 原始RL步骤信号。用于观察训练健康状况,但过于短视无法排名策略。
- 交易数量和平均交易规模 — 行为指纹:代理是推动频繁的小折扣,还是等待并触发罕见的批量购买?
- 损失量 — 使贪婪代理诚实的指标。它计算我们未能捕获的需求,有两种风格:叛变量(
price-insensitive转向竞争对手的购物者)和赤字量(price-sensitive购物者因为我们的价格不断击败他们的期望而储藏室空了)。这是利润的关键平衡——一个策略可以发布健康的每笔交易利润,却悄悄流失量。
真正的目标在于这两者之间的权衡:最大化累积利润同时保持损失量低。单独一个数字都无法告诉你定价引擎是否好。
以下是我们一组基线的指标样本(StaticAgent=X意味着该代理始终从最大安全折扣中给出X*100%折扣)。
6、训练日记
这是通常没人向你展示的部分:不是精致的最终架构,而是导致它的失败序列。下面的每一步都是一个假设、一个实现和一个结果——失败教会我们的比胜利更多。
循环前的失败 #0。一个标准的连续PPO代理,通过线性网络接收用户最后50步的平均摘要,崩溃了——巨大的动作方差和一个实际上死亡的评论者(EV ~0.08)。原因是结构性的,不是调优问题:环境在连续步骤中通过不同用户,所以代理看到它在User A上的动作导致User B的下一个状态——一个破碎的MDP。修复rollout缓冲区以处理每个用户轨迹恢复了评论者,但线性网络仍然无法捕获长期历史。这迫使了结构性重写。
尝试 #1 — 通过循环PPO的记忆。 我们迁移到LSTM策略,并重写环境以按时间顺序从头到尾模拟单个用户-产品对。评论者保持敏锐(EV ~0.95)——但使用连续动作空间,业务表现仍然很差(−23.37% 相对于HeuristicAgent基线)。
尝试 #2 — 不对称演员-评论者。 我们让评论者看到11个秘密特征,而演员保持在5个公开特征上(特征集在后续尝试中增长——附录A列出了最终版本)。仍然是连续的,仍然低于基线(−12.89%)——但这是我们稍后回归的回报洞察的种子。
尝试 #3— 离散突破。 这是连续动作空间在这个市场中的核心缺陷:当代理在"0%保护锚点"和"50%触发批量购买"之间撕裂时,它会输出平均值——一个无用的25%,既不保护利润也不推动销量。最优动作是互斥的极端,平均它们是最糟糕的两全其美。切换到11个离散折扣桶迫使策略承诺分类选择。这是突破:它是第一个在测试集上击败手工调整启发式基线的代理(+4.20%)。
尝试 #4— 统计特征 + 诚实分布。 接下来,我们向演员提供每个用户的一些聚合统计信息(他们的历史平均折扣、转化模式),这样它就不必从原始历史中重建趋势。同时,我们切换到现实的客户分布(之前,我们悄悄过度加权了bargain-hunters来说服代理打折——我们不再需要的黑客)。结合起来,这将代理提升到**+11.08%** 超过启发式算法。
因为这两个变化同时落地,并且它们使早期检查点无效,我们无法完全分离多少功劳归于特征 vs 分布。
尝试 #5 — 用户重复。 我们设置user_repetitions=3,使代理连续几次与同一用户对战,以加强其利用该配置长期模式的能力。边际影响(+11.50% 在4M步骤)——它成为#7的不对称基线。
尝试 #6— 只是训练更久。 随着架构最终健全,我们怀疑4M步骤正在缩短学习曲线。加倍到8M步骤证实了这一点:性能攀升到**+13.00%**。
随着架构最终健全,剩下的工作是探索其边界——找到什么有帮助,什么没有,什么是承重的。
尝试 #7 — 公平评论者。此时,我们试验了不对称性真正给代理带来了多少价值。这个实验以**+3.5%**结束,并在下一节中更详细地描述。
尝试 #8 — 更明智的评论者。 既然不对称评论者显然在支撑团队,我们给它更多:在其秘密观察中添加一个显式的is_lost_sale标志,这样它就不必从库存和价格差异中推断叛变。结果令人失望——+11.97%,与可比模型相比没有真正提升。评论者显然已经在自己推断销售损失了。一个小结果,但对下一节有重要提示。
尝试 #9 — 更多计算(20M步骤)。 8M步骤效果如此之好,我们推到20M。它退化到**+9.02%** — 策略过拟合到特定训练轨迹的干净案例。超过最佳点,更多计算会主动伤害(当然,我们可以调整超参数使代理在更长训练中更稳健,但这不是我们研究的最终目标)。
尝试 #10 — 杀死奖励归一化。 一个消融研究,看看我们的每个产品归一化有多承重:我们移除了除以零售价格,信任框架自己的运行方差缩放来应对。它崩溃到**−7.88%** — 比愚蠢的静态基线更差。价格从几美元到一百多美元,高价值产品主导全局方差;廉价高销量产品的优势估计消失,策略停止优化更广泛的市场。静态、特定领域的归一化是强制性的,不是可选的!
尝试 #11 → #12 — 产品类别上下文。 最后,我们向演员提供关于它在销售什么的显式上下文——公开观察中的独热产品类别。在4M步骤时,它看起来像退化(+8.95%,#11):更丰富的输入需要更多时间来收敛。运行相同架构8M步骤(#12)证实了这一点——并产生了我们的全局最佳,+14.13%。与#6相同的教训,更尖锐:更丰富的输入只有在你支付训练来利用它们时才有回报。
其中两个尝试——特权评论者基线(#5)和其盲化的双胞胎(#7)——值得仔细研究,因为它们的差距是整个项目最令人惊讶的结果。那是下一节。
7、回报洞察:评论者的秘密知识
记住那个"转折"——评论者看到演员没有的信息?这是它赢得地位的地方,它产生了项目中最反直觉的结果。
在不对称演员-评论者设置中,演员(在生产中实际设置价格)只看到公开特征——你现实地观察到的关于客户的东西。但在训练期间,评论者(只估计状态有多好,在部署时被丢弃)被给予隐藏真相:客户的真实价格锚点、他们的真实库存、他们的心理特征。演员仍然必须盲目行动;评论者只是用完整知识来评分。
我们消融了它:一个模型使用特权评论者(#5),一个评论者被盲化到与演员相同的仅公开特征(#7)。差距很大——大约**+11.5%** 对 +3.5%。
反直觉的部分是为什么。你会期望盲化的评论者是更差的预测器……它不是!它的解释方差与特权评论者几乎相同,都坐在0.92-0.96范围内。两个评论者在预测平均回报方面都很出色。
差异不是预测准确性——而是梯度质量。特权评论者,因为它可以看到隐藏状态,可以精确地分配功劳:它知道提供深度折扣是聪明的, specifically 因为这个客户的库存刚刚归零,否则可以忘记。这种精确性产生了更尖锐、更可靠的学习信号,这反过来让演员学习真正有针对性的策略——在正确时刻精确折扣——而不是退回到安全的平均折扣率。
这个教训远远超出了定价:一个预测平均值很好的评论者仍然可能是一个糟糕的老师。你从评论者那里想要的不仅仅是均值上的准确性——而是区分两个表面上相似但需要相反动作的状态的能力。训练期间的特权信息正好为你购买了这一点,即使它几乎不移动准确性数字。
8、基准测试
这是完整的排行榜,每个代理作为相对于手工调整启发式算法(0%线)的相对利润进行测量。
首先,基线告诉我们关于市场本身的信息:
- 从不打折是灾难。
StaticAgent=0.0落在**−37.92%**。拒绝打折,price-sensitive客户 simply 泄漏到竞争对手。市场惩罚纯粹的贪婪。 - 随机打折更糟。
RandomAgent在**−48.66%** 确认了没有策略地扔钱会撕碎利润。 - 但愚蠢的恒定折扣令人震惊地好。
StaticAgent=0.5— 永远50%折扣,给每个人 — 只在**−4.65%**。这是一个令人谦卑的结果:一个零智能的策略捕获了足够的量来接近复杂的启发式算法。它是一个强大的局部最优,设定了合理的基线。启发式算法只能通过仔细管理锚点和时间来击败它。 - 而且基线本身并不天真。 HeuristicAgent 重建每个用户的隐藏锚点,根据他们的消费节奏缩放折扣,并使用稳健的UCB方法(附录B)选择深度——从而定义0%参考线。
其次,让我们重新审视我们的头条RL尝试:
- 早期的连续代理(
#1、#2)甚至低于愚蠢的基线,在**−23.37%** 和 −12.89% — 动作平均惩罚具体化。 - 离散突破(
#3)是第一个跨越线的,在**+4.20%**。 - 统计特征和现实分布(
#4)跳到**+11.08%**。 - 更多训练时间(
#6)达到**+13.00%**。 - 而全局最佳——产品类别上下文加上足够训练时间(
#12)——达到**+14.13%**。
仅以即时利润训练,RL代理学会了管理锚点侵蚀和消费时间,足以在从未见过的产品上以两位数的优势击败高度工程化的特定领域启发式算法。
8.1 但我们在训练/验证集上表现如何?
令人放心地好——我们经过十二次尝试建立的排名在未见过的产品上几乎完整保留:强大的代理在两个分割上都保持两位数,死胡同保持在水下,基线保持灾难性。顶部顺序略有重新洗牌,但那只是每个分割中少量产品的噪音,不是真正的退化。收益是可概括的。
关于统计有效性的说明:在五次评估运行中,启发式算法的跨运行方差约为0.5%,因此环境本身是稳定的,这些差距是真实的市场效应而不是评估噪音。
9、限制和下一步
几个诚实的警告,因为模拟只有在你愿意命名的假设下才值得信任:
- 我们的购物者是工程化的,不是拟合的。 行为参数基于消费者心理学调查数据,但它们是通过直觉调整的,而不是拟合到真实零售商的交易日志。目标是证明RL可以解决复杂的延迟奖励定价问题——而不是构建特定杂货链的数字孪生。
- 我们建模时消除了公平性问题。 超个性化定价带来真正的声誉风险。如果客户发现他们支付全价而朋友以50%折扣购买相同商品,打击可以抹去你获得的任何利润。我们的代理在社会真空中优化;生产系统不能。
- 静态宏观经济。 零售和批发价格固定。我们不建模通货膨胀、季节性需求波动或供应链冲击。
- 店内库存老化。 在我们的世界中,产品的保质期时钟只在客户购买后开始。我们忽略仓库端的批量处理——然而在实际零售中,清理货架上即将过期库存的压力是折扣策略的最大驱动力之一。
- 平坦成本。 我们假设每件商品的平坦批发成本。现实有规模经济(更高产量每单位更便宜)和新鲜度(更小、更频繁的补货使货架更新鲜并提高基线转化)。我们的平坦成本模型抽象了所有这些。
- 叛变是近似的,不是涌现的。 我们的"销售损失"机制使用调整的概率,而不是真正对我们的价格做出反应的竞争对手。
最后一点也是最令人兴奋的前进方向。自然的下一步是多代理市场:用实际竞争定价引擎取代脚本化的叛变概率,争夺同一客户的有限钱包。销售损失 then 会从真实竞争中有机涌现,而不是预设数字。除此之外,我们想要将购物者模型拟合到真实交易数据而不是调查直觉,并将演员的记忆从LSTM升级到Transformer,以便它可以从原始历史中更锐利地推断隐藏状态——库存、锚点。
8、底线
我们开始回答一个问题:RL代理能否仅从即时利润奖励中学习购物者的隐藏心理——锚点记忆、消费节奏、对变质的恐惧——并击败手工调整的专家?
在我们的模拟中,答案是肯定的。没有为锚点、留存率或变质进行显式奖励塑造,循环PPO代理学会了隐式管理所有这些,在从未见过的产品上以两位数的优势击败精心设计的启发式算法。在这个过程中,它教会了我们动作空间比算法更重要,奖励归一化是承重的,没有适当准备更多计算会主动伤害,以及——最令人惊讶的——你能给评论者的最有价值的东西不是准确性,而是它可以用来有效教学的特权知识。
原文链接: RL for dynamic pricing: How We Trained RL to Price Groceries Without Eroding Its Own Margins
汇智网翻译整理,转载请标明出处