使用DQN进行价格管理

您所在的位置:网站首页 huber影响函数 使用DQN进行价格管理

使用DQN进行价格管理

2023-02-27 11:33| 来源: 网络整理| 查看: 265

文章目录 前言一、不同的价格响应二、利用DQN优化定价策略1.定义环境2.DQN算法概述3.Algorithm: Deep Q Network (DQN) 总结强化学习-定价、决策 参考论文及源码

前言

供应链和价格管理是企业运营中最早采用数据科学和组合优化方法的领域,并且在使用这些技术方面有着悠久的历史,并取得了巨大的成功。虽然有广泛的传统优化方法可用于库存和价格管理应用,但深度强化学习定价有潜力大幅提高这些和其他类型的企业运营的优化能力。

本次博客中,我将会和大家一起探讨强化学习在价格管理中的应用,分享内容主要分三部分

首先我会通过一个简单的例子来说明传统价格优化问题的解法,以及优化的复杂性接着我会定义一个简单的价格管理环境,开发强化学习Optimizer,并评估其在价格管理 or 收益管理场景中的效果总结与展望,以及简单介绍另一篇进阶版强化学习在天猫动态定价中的应用 一、不同的价格响应

零售或制造业环境中的传统价格优化过程通常是使用某种需求模型对不同定价场景进行假设分析。在许多情况下,需求模型的开发具有挑战性,因为它必须正确地捕获影响需求的广泛因素和变量,包括常规价格、折扣、营销活动、季节性、竞争对手的价格、跨产品蚕食和光环效应。

然而,一旦建立了需求模型,定价决策的优化过程就相对简单了,线性或整数规划等标准技术通常就足够了。 例如,某生鲜电商在夏季季节开始时采购了一种季节性产品:西瓜,且必须在季节结束时将其销售一空。假如运营从离散集合中选择价格 [ 1 , 2 , . . , 98 , 99 ] [1,2,..,98,99] [1,2,..,98,99],并且可以进行频繁的变价,我们可以提出以下的优化问题: max ⁡ ∑ t ∑ j p j ⋅ d ( t , j ) ⋅ x t j s u b j e c t t o ∑ j x t j = 1 , f o r a l l t ∑ t ∑ j d ( t , j ) ⋅ x t j = c x t j ∈ 0 , 1 \max \sum_{t} \sum_{j}p_{j} \cdot d(t,j) \cdot x_{tj} \\ subject \quad to \quad \sum_{j}x_{tj} = 1, for \quad all \quad t \\ \sum_{t}\sum_{j} d(t,j) \cdot x_{tj} = c \\ x_{tj} \in 0,1 maxt∑​j∑​pj​⋅d(t,j)⋅xtj​subjecttoj∑​xtj​=1,foralltt∑​j∑​d(t,j)⋅xtj​=cxtj​∈0,1

其中 t t t代表时间间隔内迭代的时间戳, j j j代表有效价格水平上的索引, p j p_j pj​代表索引为 j j j的价格, d ( t , j ) d(t,j) d(t,j)代表在时间 t t t给定价格索引 j j j情况下的需求, c c c代表季节初的库存水平,并且 x t j x_{tj} xtj​是一个二进制的虚拟变量,如果价格索引被分配给时间间隔t那么 x t j x_{tj} xtj​就等于1,否则为0。第一个约束确保每个时间间隔只有一个有效价格,第二个约束确保总需求等于可用库存水平,这是个整数规划问题,可以用传统的优化lib来求解。

上面的模型【公式】非常灵活,因为它允许任意形状(线性、恒定弹性等)的价格需求函数和任意季节模式。它还可以直接扩展,以支持多个产品的联合价格优化。然而,上述模型假设时间间隔之间没有依赖关系。

我们在一些价格优化问题论文中[Online Network Revenue Management Using Thompson Sampling、Dynamic Learning and Pricing with Model Misspecification]往往会发现,论文通常假设价格需求响应函数是个简单的线性函数。然而在生鲜电商业务场景中,价格需求函数往往是非线性关系,同时,需求不仅取决于绝对价格水平,还可能受到近期价格变化幅度的影响——价格下降可能造成暂时的需求上涨,而价格上涨可能导致暂时的需求下降。价格变化的影响也可能是不对称的,因此价格上涨的影响比价格下跌的影响大得多或小得多。我们可以使用以下价格-需求函数对这些假设进行编码: d ( p t , p t − 1 ) = q 0 − k ⋅ p t − a ⋅ s ( ( p t − p t − 1 ) + ) + b ⋅ s ( ( p t − p t − 1 ) − ) d(p_t,p_{t-1}) = q_0 - k \cdot p_t - a \cdot s((p_t - p_{t-1})^+) + b \cdot s((p_t - p_{t-1})^-) d(pt​,pt−1​)=q0​−k⋅pt​−a⋅s((pt​−pt−1​)+)+b⋅s((pt​−pt−1​)−)

其中

x + = x i f x > 0 , a n d 0 o t h e r w i s e x − = x i f x < 0 , a n d 0 o t h e r w i s e x^+ = x \quad if \quad x > 0, \quad and \quad 0 \quad otherwise \\ x^- = x \quad if \quad x < 0, \quad and \quad 0 \quad otherwise x+=xifx>0,and0otherwisex−=xifxprice_opt_const}, 获得的最大利润为 {profits[p_idx]}')

最优价格为 40, 获得的最大利润为 38000.0

结合上面的曲线图及我们计算出的结果,我们在T个时间步中采用恒定价格的方式在这种环境下【需求不仅取决于绝对价格水平,还可能受到近期价格变化幅度的影响】不是最优的。因此我们可以通过贪婪优化来提高利润:首先在第一个时间步骤中找到最优价格,然后在固定第一个时间步骤的情况下优化第二个时间步骤,依此类推:

# 最优价格序列 def find_optimal_price_t(p_baseline, price_grid, t): p_grid = np.tile(p_baseline, (len(price_grid), 1)) p_grid[:, t] = price_grid profit_grid = np.array([ profit_response(p) for p in p_grid ]) return price_grid[ np.argmax(profit_grid) ] p_opt = np.repeat(price_opt_const, T) for t in range(T): price_t = find_optimal_price_t(p_opt, price_grid, t) p_opt[t] = price_t print(p_opt) print(f'获得的最佳利润为 is {profit_response(p_opt)}') plt.figure(figsize=(16, 5)) plt.xlabel("Time step") plt.ylabel("Price ($)") plt.xticks(np.arange(T)) plt.plot(np.arange(T), p_opt, c='red')

[99 59 48 99 59 48 99 59 48 99 59 48 99 59 48 99 59 48 41] 获得的最佳利润为 is 198145.7051278035 在这里插入图片描述

由上图可知,由于价格-需求函数内部的一个简单的时间依赖性决定了一个复杂的价格策略,包括价格上涨和折扣促销。 零售通常有两种定价策略,一种是基于促销的Hi-Lo的定价策略,另一种是稳定定价的EDLP策略。中国电商平台和多数业态目前是Hi-Lo的定价策略,刺激消费者到店或者刺激消费者流量。它可以被视为许多零售商、电商使用的Hi-Lo定价策略有效性的一个证明; 从上图我们看到改变常规价格和促销价格可以最大化商品利润。上面的例子也说明了价格管理和强化学习优化之间的关系。我们所定义的价格响应函数本质上是一个微分方程,其中利润不仅取决于当前的价格行为,还取决于价格的动态。可以预期,这种方程可以表现出非常复杂的行为,特别是在长时间间隔内,因此相应的控制策略也可以变得复杂。因此,这种策略的优化需要强大而灵活的方法,例如深度强化学习。

二、利用DQN优化定价策略

尽管我们上面实现的贪心算法为一个简单的微分价格响应函数生成了最优定价计划,但当我们添加更多的约束或相互依赖时,用线性或整数规划来求解就变得越来越具有挑战性。在这次demo中,我们从不同的角度来解决这个问题,并应用一个通用的深度Q网络(DQN)算法来学习最优价格控制策略。 我们在这个例子中使用原始的DQN,因为它是一个相当简单的起点,说明了现代强化学习的主要概念。在实际设置中,可能会使用原始DQN的最新修改或替代算法,RLib中也有更加鲁棒的开源算法可供参考。

1.定义环境

强化学习考虑的是一个智能体以离散时间步长与环境交互的设置,其目标是学习奖励最大化的行为策略。在每个时间步 t t t,在给定状态 s s s,代理根据策略 π ( s ) \pi(s) π(s)执行操作 a a a,同时获得了奖励 r r r,并进入了下一个状态 s ′ s^{'} s′。 我们在强化学习中重新定义了我们的价格环境:首先,我们在任何时间步 t t t对环境状态进行编码, s t s_t st​作为之前所有时间步长的价格向量,并与时间步本身的one-hot编码相连接: s t = ( p t − 1 , p t − 2 , . . . , p 0 , 0 , . . . ) ∣ ( 0 , . . . , 1 , . . . , 0 ) s_t = (p_{t-1}, p_{t-2}, ..., p_0, 0, ...) | (0, ..., 1,...,0) st​=(pt−1​,pt−2​,...,p0​,0,...)∣(0,...,1,...,0) 接下来, 每个时间步长内的动作 a a a只是有效价格水平数组中的一个索引。最后是奖励 r r r就是卖家的利润。我们的目标是找到一种基于当前状态规定定价行为的策略,以使销售季的总利润最大化。

2.DQN算法概述

在这里我们简要回顾下最原始的DQN算法

DQN算法的目标是在 T T T的时间步长中,学习一个动作策略 π \pi π,该策略能够最大化累计奖励总额(也被称为回报): R = ∑ t = 0 T γ t r t R = \sum_{t=0}^{T}\gamma^{t}r_{t} R=t=0∑T​γtrt​

我们定义一个函数 Q Q Q,它可以根据当前状态和下一个操作来估计预期收益,假设所有后续操作也将根据该策略执行: Q π ( s , a ) = E s , a [ R ] Q^{\pi}(s,a) = \mathbb{E}_{s,a}[R] Qπ(s,a)=Es,a​[R]

假如这个函数(称为 Q Q Q函数)是已知的,策略 π \pi π可以简单定义如下,以实现收益最大化: π ( s ) = arg max ⁡ a Q ( s , a ) \pi(s) = \argmax_{a} \quad Q(s,a) π(s)=aargmax​Q(s,a)

我们可以将上面的定义组合成以下的递归方程: Q π ( s , a ) = r + γ max ⁡ a ′ Q ( s ′ , a ′ ) Q^{\pi}(s,a) = r + \gamma \max_{a^{'}} Q(s^{'},a^{'}) Qπ(s,a)=r+γa′max​Q(s′,a′)

其中 s ′ s^{'} s′, a ′ a^{'} a′是下一个状态,以及在那种状态下采取的行动。如果我们使用近似函数来估计Q函数,那么可以通过这个方程两边的差值来近似度量这个近似函数的质量。

δ = Q π ( s , a ) − ( r + γ max ⁡ a ′ Q ( s ′ , a ′ ) ) \delta = Q^{\pi}(s,a) - (r + \gamma \max_{a^{'}} Q(s^{'},a^{'})) δ=Qπ(s,a)−(r+γa′max​Q(s′,a′))

这个值 δ \delta δ称为时间差分误差,DQN背后的主要思想是训练一个深度神经网络,使用时间差分误差作为损失函数来近似 Q Q Q函数。 原则上,训练过程可以很简单:

初始化网络。它的输入对应于状态表征,而输出是所有动作的 Q Q Q值向量。对每个时间步: 使用网络估计 Q Q Q值执行 Q Q Q值最大的action,并观察奖励reward计算误差 δ \delta δ使用随机梯度下降法更新网络参数,损失函数是由时间差误差推导出来的。

然而,这种简单的方法对于训练复杂的非线性逼近器(如深度神经网络)是不稳定的。DQN使用两种技术解决这个问题:

Replay buffer:上面描述的基本训练过程的一个问题是,序列化的观测对象通常是相关的,但是网络训练一般需要独立分布的样本。 DQN通过累计多个观察到的转换 ( s , a , r , s ′ ) (s,a,r,s^{'}) (s,a,r,s′)进入缓存,然后对这样的转换进行批量抽样以重新训练网络。缓存通常要足够大,以最小化样本之间的相关性。

Target networks:基本过程的第二个问题是,网络参数是根据使用同一网络产生的 Q Q Q值计算的损失函数更新的。换句话说,学习目标与我们试图学习的参数同时移动,使得优化过程不稳定。DQN通过维护两个网络实例缓解了这个问题。第一个实例用来执行操作,并按照上面描述的方法不断更新。第二个被称为目标网络,是第一个网络的滞后副本,专门用于计算损失函数(即目标)的 Q Q Q值。这种技巧有助于稳定学习过程。将基本学习过程与这两种思想相结合,得到DQN算法。

3.Algorithm: Deep Q Network (DQN) 参数及初始化 ϕ \phi ϕ – 策略网络 Q ϕ Q_{\phi} Qϕ​的参数 ϕ t a r g \phi_{targ} ϕtarg​ – 目标网络 Q ϕ t a r g Q_{\phi_{targ}} Qϕtarg​​ α \alpha α – 学习率 N N N – batch size T u T_u Tu​ – 目标更新频率初始化 ϕ t a r g = ϕ \phi_{targ} = \phi ϕtarg​=ϕ For t = 1 , . . . , M A X _ S T E P S t=1,\quad ...,\quad MAX\_STEPS t=1,...,MAX_STEPS do

基于 Q ϕ ( s t , a t ) Q_{\phi}(s_t, a_t) Qϕ​(st​,at​)选择action(价格)

执行该操作并保存转换 ( s t , a t , r t , s t ′ ) (s_t, a_t, r_t, s^{'}_t) (st​,at​,rt​,st′​)进缓存

更新策略网络

从buffer中抽样batch size为N的转换计算batch中每个样本的目标 Q Q Q值

y i = r i + γ max ⁡ a ′ Q ϕ t a r g ( s ′ , a ′ ) y_i = r_i + \gamma \max_{a^{'}}Q_{\phi_{targ}(s^{'},a^{'})} yi​=ri​+γa′max​Qϕtarg​(s′,a′)​ 其中, Q ( s , a ) = 0 Q(s,a)=0 Q(s,a)=0是整个过程的最近状态(初始条件)

计算loss: L ( ϕ ) = 1 N ∑ i ( y i − Q ϕ ( s i , a i ) ) 2 L(\phi) = \frac{1}{N}\sum_{i}(y_i-Q_{\phi}(s_i,a_i))^2 L(ϕ)=N1​i∑​(yi​−Qϕ​(si​,ai​))2更新网络参数: ϕ = ϕ − α ∇ ϕ L ( ϕ ) \phi = \phi - \alpha \nabla_{\phi}L(\phi) ϕ=ϕ−α∇ϕ​L(ϕ)

i f t m o d    T u = 0 t h e n if \quad t \mod T_u = 0 \quad then iftmodTu​=0then

更新目标网络: ϕ t a r g ← ϕ \phi_{targ} \gets \phi ϕtarg​←ϕ

我们需要解决的最后一个问题是如何根据网络估计的 Q Q Q值选择动作action。 从Thompson Sampling那篇paper中,我们知道总是以最大 Q Q Q值采取行动action的策略不会很好地work,因为学习过程不会充分地探索环境,因此我们选择随机化选择过程,来提高模型的exploration的能力。 更具体地说, ε \varepsilon ε-greedy策略以 1 − ε 1 - \varepsilon 1−ε 的概率选择最大Q值对应的action,其中随机化action的概率为 ε \varepsilon ε

同时我们还使用退火技术,从一个相对较大的 ε \varepsilon ε 值开始,随着训练阶段,逐步减少 ε \varepsilon ε 大小。

# 第三方库 import math import random import numpy as np from IPython.display import clear_output import matplotlib import matplotlib.pyplot as plt from collections import namedtuple from itertools import count import torch.nn.functional as F import torch import torch.nn as nn import torch.optim as optim device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

使用Pytorch实现DQN

第一步是实现一个内存缓冲区,用于保存观察到的转换,并在网络训练期间重现它们:

Transition = namedtuple('Transition', ('state', 'action', 'next_state', 'reward')) class ReplayMemory(object): def __init__(self, capacity): self.capacity = capacity self.memory = [] self.position = 0 def push(self, *args): # push new transition to the buffer if len(self.memory) eps_threshold: return np.argmax(q_values) else: return random.randrange(len(q_values))

实现中最复杂的部分是网络更新过程。它从缓冲区中采样一批非最终动作action,计算当前和下一个状态的q值,计算损失,并相应地更新网络:

GAMMA = 1.00 TARGET_UPDATE = 20 BATCH_SIZE = 512 def update_model(memory, policy_net, target_net): if len(memory)


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3