使用DQN进行价格管理
Posted 纯洁の小黄瓜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用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_jp_j \\cdot d(t,j) \\cdot x_tj \\\\ subject \\quad to \\quad \\sum_jx_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)⋅xtjsubjecttoj∑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−=xifx<0,and0otherwise
p t p_t pt是当前时间间隔内的价格, p t − 1 p_t-1 pt−1是前一个时间间隔内的价格,公式的前两部分代表一个截距 q 0 q_0 q0,斜率 k k k的线性需求模型。后两项模拟了在两个时间间隔内的价格变化的响应。参数 a a a, b b b分别代表价格上涨、下跌的敏感度。 s s s是一个冲击函数,可以用来指定价格和需求变化之间的非线性关系,在本demo中,我们用 s ( x ) = x s(x) = \\sqrtx s(x)=x。
为了可视化方便,我们先定义并实现一些可视化函数:
import math
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn-white')
import pandas as pd
from matplotlib import animation, rc
plt.rcParams.update('pdf.fonttype': 'truetype')
from qbstyles import mpl_style
mpl_style(dark=False)
# Visualization functions
from bokeh.io import show, output_notebook
from bokeh.palettes import PuBu4
from bokeh.plotting import figure
from bokeh.models import Label
output_notebook()
def plot_return_trace(returns, smoothing_window=10, range_std=2):
plt.figure(figsize=(16, 5))
plt.xlabel("Episode")
plt.ylabel("Return ($)")
returns_df = pd.Series(returns)
ma = returns_df.rolling(window=smoothing_window).mean()
mstd = returns_df.rolling(window=smoothing_window).std()
plt.plot(ma, c = 'blue', alpha = 1.00, linewidth = 1)
plt.fill_between(mstd.index, ma-range_std*mstd, ma+range_std*mstd, color='blue', alpha=0.2)
def plot_price_schedules(p_trace, sampling_ratio, last_highlights, fig_number=None):
plt.figure(fig_number);
plt.xlabel("Time step");
plt.ylabel("Price ($)");
plt.xticks(range(T))
plt.plot(range(T), np.array(p_trace[0:-1:sampling_ratio]).T, c = 'k', alpha = 0.05)
return plt.plot(range(T), np.array(p_trace[-(last_highlights+1):-1]).T, c = 'red', alpha = 0.5, linewidth=2)
def bullet_graph(data, labels=None, bar_label=None, axis_label=None,
size=(5, 3), palette=None, bar_color="black", label_color="gray"):
stack_data = np.stack(data[:,2])
cum_stack_data = np.cumsum(stack_data, axis=1)
h = np.max(cum_stack_data) / 20
fig, axarr = plt.subplots(len(data), figsize=size, sharex=True)
for idx, item in enumerate(data):
if len(data) > 1:
ax = axarr[idx]
ax.set_aspect('equal')
ax.set_yticklabels([item[0]])
ax.set_yticks([1])
ax.spines['bottom'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
prev_limit = 0
for idx2, lim in enumerate(cum_stack_data[idx]):
ax.barh([1], lim - prev_limit, left=prev_limit, height以上是关于使用DQN进行价格管理的主要内容,如果未能解决你的问题,请参考以下文章