PID控制器原理及在预算平滑中的应用

342 阅读12分钟

计算广告中,广告主在投放广告时一般会在广告的推广层级(例如计划)上设置天级预算,广告投放系统需要保证广告主的计划在一天中的实际消耗不超过预算,另外,为了让广告主的计划中的广告在每天的各个时段尽可能触达更多的用户,广告投放系统一般也会支持预算平滑(Budget Pacing)功能,使得广告主的计划的预算不会在一天中的初始若干个时段消耗完,还是按照一定的机制将预算分配到一天的各个时段中进行广告投放。目前,已有多篇经典论文介绍了预算平滑的解决方案。

PID控制器是自动控制领域的经典控制算法,在工业控制中有广泛的应用,其引入反馈机制,将被控制变量的预期值和实际值的偏差作为反馈信号输入控制器,调节控制变量,而调节后的控制变量进一步输入执行结构,从而对被控制变量产生影响,使得被控制变量的实际值趋近至预期值。目前,PID控制器也广泛应用于计算广告中,预算平滑是其应用场景之一。

本文将分别介绍预算平滑、PID控制器以及PID控制器在预算平滑中的应用。如有不足之处,请指正。

预算平滑

预算平滑领域有两篇比较经典的论文,分别是2014年LinkedIn发表的《Budget Pacing for Targeted Online Advertisements at LinkedIn》和2015年Yahoo发表的《Smart Pacing for Effective Online Ad Campaign Optimization》。第一篇论文主要介绍了一种基于曝光曲线的预算平滑算法,第二篇论文在前者基础上,进一步优化算法以保障广告主效果。

《Smart Pacing for Effective Online Ad Campaign Optimization》这篇论文的应用场景是基于广告位拍卖的广告交易。在这个场景下,DSP作为广告主的代理,管理大量的广告,这些广告既包括品牌广告,也包括效果广告,品牌广告的目标是在一定的预算约束下,将广告投放至尽可能多的受众,而效果广告的目标是在一定的预算约束下,将广告投放至高点击、高转化的人群,达成效果目标,比如平均点击成本需小于某个上限。在满足上述目标的同时,DSP还需设计一定的预算平滑机制,比较平滑地消耗预算,使得广告主的广告能够在每天的各个时段都能投放出来,而不是在每天的开始阶段能投尽投,导致预算过早被消耗完,而无法投放到当天其他时段。

较理想的预算平滑机制如图1所示,一种是平均平滑(Even Pacing),即将预算平均分配到每天各个时段,另一种是基于流量的平滑(Traffic Based Pacing),即根据每天各时段的流量多少对预算进行分配,流量多的时段分配的预算多,还有一种图中未列出的是基于效果的平滑(Performance Based Pacing),即根据每天各时段的效果优劣对预算进行分配,效果好的时段分配的预算多。

图1 较理想的预算平滑机制

论文指出其进行预算平滑的一个前提假设是流量远大于广告主的需求,即预算平滑不会出现因前期限制投放、后期流量不足导致预算剩余的情况。在这个前提假设下,预算平滑主要解决的问题将当天预算按一定机制分配到各个时间段,在每个时间段中,挑选流量进行投放,使得各个时段段的实际消耗逼近预期预算,并达成效果目标。

在广告投放流程中,DSP首先接收到流量侧的告请求,根据当前请求召回相关的广告,预估点击率,将广告及其出价返回给流量侧,而流量侧可能根据一定的拍卖机制决定该广告是否竞胜,广告竞胜的概率被称为竞胜率(Win Rate),若广告竞胜,其在流量侧被曝光、点击,并被计费,这些数据反馈至DSP后,DSP扣减预算,并统计相关效果指标。论文指出在上述流程中,有两种预算平滑方法,如图2所示:

  • Probabilistic throttling,通过设置参竞率(Pacing Rate)控制广告是否参竞,从而实现对流量的挑选,进而实现预算平滑;
  • Bid modification,通过调整出价控制广告是否竞胜,从而实现对流量的挑选,进而实现预算平滑。

图2 两种预算平滑方法

论文指出Bid modification存在以下不足:

  • Probabilistic throttling直接控制参竞率,而Bid modification通过调整出价间接控制竞胜率,而RTB下的竞价环境是不断变化的,竞胜率并不只受广告本身的出价影响,还会受其他参竞者的出价影响,从而无法通过调整出价精准控制竞胜率、进而实现预算平滑;
  • 流量侧一般都会设置保留价,如果Bid modification调整出价后,导致出价低于保留价,则会导致竞价失败,进而导致预算平滑失败;
  • Bid modification将出价计算和预算平滑耦合在一起,无法分别单独进行优化。

因此论文采用Probabilistic throttling这一方法,设计了Smart Pacing算法用于不同类型广告的预算平滑。关于Smart Pacing算法,读者可以进一步阅读笔者梳理的《预算平滑论文阅读笔记(2)》了解其细节,这里不再详述。这里主要引出预算平滑的两种基本方法——Probabilistic throttling和Bid modification。

PID控制器

PID控制器是自动控制领域的经典控制算法,在工业控制中有广泛的应用,其引入反馈机制,将被控制变量的预期值和实际值的偏差作为反馈信号输入控制器,调节控制变量,而调节后的控制变量进一步输入执行结构,从而对被控制变量产生影响,使得被控制变量的实际值趋近至预期值。

PID控制器问世至今已有近70年历史,它以其结构简单、稳定性好、工作可靠、调整方便而成为工业控制的主要技术之一。当被控对象的结构和参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数必须依靠经验和现场调试来确定,这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象,或不能通过有效的测量手段来获得系统参数时,最适合用PID控制技术。

PID控制,实际中也有PI和PD控制。PID控制器就是根据系统的误差,利用比例、积分、微分计算出控制量进行控制的。PID控制中的P即比例(Proportional)控制,I即积分(Integral)控制,D即微分(Derivative)控制:

  • 比例(P)控制:比例控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系。偏差一旦产生,控制器立即就发生作用即调节控制输出,使被控量朝着减小偏差的方向变化,偏差减小的速度取决于比例系数KPK_PKPK_P越大偏差减小的越快,但是很容易引起振荡,KPK_P减小,发生振荡的可能性减小但是调节速度变慢。但单纯的比例控制存在稳态误差不能消除的缺点。这里就需要积分控制。
  • 积分(I)控制:在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。实质就是对偏差累积进行控制,直至偏差为零。积分控制作用始终施加指向给定值的作用力,有利于消除静差,其效果不仅与偏差大小有关,而且还与偏差持续的时间有关。简单来说就是把偏差积累起来,一起算总帐。
  • 微分(D)控制:在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。

模拟PID控制器

图3 模拟PID控制系统原理框图

模拟PID控制系统原理框图如图3所示。令r(t)r(t)表示被控制变量的预期值随时间变化的函数,c(t)c(t)表示被控制变量的实际值随时间变化的函数,e(t)e(t)表示被控制变量的预期值和实际值的差值随时间变化的函数,即:

e(t)=r(t)c(t)(1)e(t)=r(t)-c(t) \tag{1}

PID控制器的输入是e(t)e(t),PID控制器的输出是u(t)u(t)u(t)u(t)表示控制变量的取值随时间变化的函数。u(t)u(t)再作为执行结构的输入,对执行结构和被控制变量施加影响,使得被控制量的实际值向着趋近预期值的方向调整。

PID控制器是一种线性调节器,它将被控制变量的预期值和实际值的差值的比例(P)、积分(I)、微分(D)通过线性组合构成控制变量u(t)u(t)u(t)u(t)可具体由下式表示:

u(t)=KP[e(t)+1TI0te(t)dt+TDde(t)dt](2)u(t)=K_P\left[e(t)+\frac{1}{T_I}\int_0^te(t)dt+T_D\frac{de(t)}{dt}\right] \tag{2}

其中,KPK_PTIT_ITDT_D均为常数项,而比例(P)、积分(I)、微分(D)三项的作用:

  • 比例,即时成比例地反应控制系统的偏差信号e(t)e(t),偏差一旦产生,调节器立即产生控制作用以减小偏差。
  • 积分,主要用于消除静差,提高系统的无差度。积分作用的强弱取决于积分时间常数TIT_ITIT_I越大,积分作用越弱,反之则越强。
  • 微分,能反应偏差信号的变化趋势(变化速率),并能在偏差信号的值变得太大之前,在系统中引入一个有效的早期修正信号,从而加快系统的动作速度,减小调节时间。

数字PID控制器

由于计算机的出现,计算机进入了控制领域,人们将模拟 PID 控制规律引入到计算机中来,对式2进行适当的变换,就可以用软件实现PID控制,即数字PID控制。

全量式PID控制器

令采样周期为TT,按此周期进行多次采样,对于第nn次采样,对被控制变量的预期值和实际值的偏差的比例、积分和微分进行离散化的计算,具体计算方式如下所示:

  • 比例,e(n)=r(n)c(n)e(n)=r(n)-c(n)
  • 微分,e(n)e(n1)T\frac{e(n)-e(n-1)}{T}
  • 积分,i=1ne(i)T\sum_{i=1}^n{e(i)T},即,Ti=0ne(i)T\sum_{i=0}^n{e(i)}

因此,数字PID控制器中,对于第nn次采样,控制变量u(n)u(n)可具体由下式表示:

u(n)=KP{e(n)+TTIi=0ne(i)+TDT[e(n)e(n1)]}+u0=uP(n)+uI(n)+uD(n)+u0(3)\begin{align} u(n)&=K_P\left\{e(n)+\frac{T}{T_I}\sum_{i=0}^n{e(i)}+\frac{T_D}{T}\left[e(n)-e(n-1)\right]\right\}+u_0 \\ &=u_P(n)+u_I(n)+u_D(n)+u_0 \end{align} \tag{3}

其中,u0u_0即控制变量的初始值,而uP(n)u_P(n)uI(n)u_I(n)uD(n)u_D(n)即:

  • uP(n)=KPe(n)u_P(n)=K_Pe(n),称为比例项;
  • uI(n)=KPTTIi=0ne(i)u_I(n)=K_P\frac{T}{T_I}\sum_{i=0}^n{e(i)},称为积分项;
  • uD(n)=KPTDT[e(n)e(n1)]u_D(n)=K_P\frac{T_D}{T}\left[e(n)-e(n-1)\right],称为微分项。

通过灵活组合uP(n)u_P(n)uI(n)u_I(n)uD(n)u_D(n)这三项,可以构建相应的控制器:

  • P控制,即只有比例项:
u(n)=uP(n)+u0(4)u(n)=u_P(n)+u_0 \tag{4}
  • PI控制,即只有比例项和积分项:
u(n)=uP(n)+uI(n)+u0(5)u(n)=u_P(n)+u_I(n)+u_0 \tag{5}
  • PD控制,即只有比例项和微分项:
u(n)=uP(n)+uD(n)+u0(6)u(n)=u_P(n)+u_D(n)+u_0 \tag{6}
  • PID控制,即同时有比例项、积分项和微分项:
u(n)=uP(n)+uI(n)+uD(n)+u0(7)u(n)=u_P(n)+u_I(n)+u_D(n)+u_0 \tag{7}

如果采样周期足够小,则式3的近似计算可以获得足够精确的结果,离散控制过程与连续过程十分接近。式3直接给出了控制变量的大小,因此被称为全量式或位置式PID控制器。

这种控制器的缺点是:由于全量输出,所以每次输出均与过去状态有关,计算时要对过去所有采样的偏差进行累加,计算量大。并且,因为控制器输出的控制变量对应的是执行机构的实际位置,如果控制器出现故障,输出的控制变量将大幅度变化,会引起执行机构的大幅度变化,有可能因此造成严重的生产事故,这在实际生产中是不允许的。

增量式PID控制器

为了解决全量式PID控制器存在的缺点,增量式PID控制器被提出。所谓增量式PID控制器是指数字控制器的输出只是控制变量的增量Δu\Delta u

增量式PID控制器的输出可以基于全量式PID控制器的输出推导得到。由式3可以得到全量式PID控制器在第 n1n-1个采样周期的输出为:

u(n1)=KP{e(n1)+TTIi=0n1e(i)+TDT[e(n1)e(n2)]}+u0(4)u(n-1)=K_P\left\{e(n-1)+\frac{T}{T_I}\sum_{i=0}^{n-1}{e(i)}+\frac{T_D}{T}\left[e(n-1)-e(n-2)\right]\right\}+u_0 \tag{4}

u(n)u(n)u(n1)u(n-1)的差值Δu\Delta u可由下式计算:

Δu=u(n)u(n1)=KP{[e(n)e(n1)]+TTIe(n)+TDT[e(n)2e(n1)+e(n2)]}=KP(1+TTI+TDT)e(n)KP(1+2TDT)e(n1)+KPTDTe(n2)(5)\begin{align} \Delta u&=u(n) - u(n - 1) \\ &=K_P\left\{[e(n)-e(n-1)]+\frac{T}{T_I}e(n)+\frac{T_D}{T}[e(n)-2e(n-1)+e(n-2)]\right\}\\ &=K_P(1+\frac{T}{T_I}+\frac{T_D}{T})e(n)-K_P(1+2\frac{T_D}{T})e(n-1)+K_P\frac{T_D}{T}e(n-2)\\ \end{align} \tag{5}

KI=KPTTIK_I=K_P\frac{T}{T_I}KD=KPTDTK_D=K_P\frac{T_D}{T},则Δu\Delta u可进一步表示为:

Δu=(KP+KI+KD)e(n)(KP+2KD)e(n1)+KDe(n2)(6)\Delta u = (K_P+K_I+K_D)e(n)-(K_P+2K_D)e(n-1)+K_De(n-2) \tag{6}

由式6可以看出,一旦确定KPK_PKIK_IKDK_D,只要使用最近三次测量的偏差值,就可以由6求出控制量变量的增量。增量式PID控制器与全量式PID控制器相比,计算量小的多,因此在实际中得到广泛的应用。得到控制变量的增量Δu\Delta u后,可进一步基于上一个周期的控制变量,得到当前周期的控制变量:

u(n)=u(n1)+Δu(7)u(n)=u(n-1)+\Delta u \tag{7}

实际生产中,控制变量一般有取值范围约束,若控制器输出的控制变量超出上下限,则取相应的上下限值:

u(n)={uminu(n)uminu(n)umin<u(n)<umaxumaxu(n)umax(8)u(n)=\begin{cases} u_{\text{min}} & u(n)\le u_{\text{min}} \\ u(n) & u_{\text{min}}\lt u(n) \lt u_{\text{max}} \\ u_{\text{max}} & u(n) \ge u_{\text{max}} \end{cases} \tag{8}

补充说明一下,上述当前周期的控制变量u(n)u(n)是在当前周期结束后,由控制器输出的更新值,其并不作用于当前周期的执行机构,而是作用于下一周期的执行机构。

参数整定

由式6可以看出,一旦确定KPK_PKIK_IKDK_D(即TTKPK_PTIT_ITDT_D),只要使用最近三次测量的偏差值,就可以由6求出控制量变量的增量。因此,数字PID控制器设计过程的一个重要环节就是参数整定,确定上述各个常量项的取值。

参数整定的方法包括理论计算和工程整定两类,理论计算法依赖被控对象准确的数学模型,一般较难做到,而工程整定法不依赖被控对象准确的数学模型,直接在控制系统中进行现场整定,简单易行。

常用的简易工程整定法包括扩充临界比例度法、归一参数整定法等。

扩充临界比例度法适用于有自平衡特性的被控对象,其步骤包括:

  • 选择采样周期为被控对象纯滞后时间的十分之一以下。
  • 去掉积分项和微分项,只保留比例项,逐渐增大比例系数KPK_P直至系统对阶跃输入的响应达到临界振荡状态(稳定边缘),记下此时的临界比例系数KKK_K及系统的临界振荡周期TKT_K
  • 选择控制度。控制度的计算公式如式9所示,即数字控制累积的偏差和模拟控制累积的偏差的比值,若控制度为1,则说明数字控制和模拟控制的效果一致,通常,当控制度为1.05时。就可以认为数字控制与模拟控制效果相当。
控制度=[0e2(t)dt]数字控制[0e2(t)dt]模拟控制(9)控制度=\frac{\left[\int_0^\infin{e^2(t)dt}\right]_{数字控制}}{\left[\int_0^\infin{e^2(t)dt}\right]_{模拟控制}} \tag{9}
  • 根据选定的控制度,查下表求得TTKPK_PTIT_ITDT_D的值。
控制度控制算法TS/TKT_S/T_KKP/KKK_P/K_KTI/TKT_I/T_KTD/TKT_D/T_K
1.05PI0.030.550.88-
PID0.0140.630.490.14
1.2PI0.050.490.91-
PID0.0430.470.470.16
1.5PI0.140.420.99-
PID0.090.340.430.2
2.0PI0.220.361.05-
PID0.160.270.400.22
常规控制器PI-0.570.83-
PID-0.70.50.13

归一参数整定法,直接令T=0.1TKT=0.1T_KTI=0.5TKT_I=0.5T_KTD=0.125TKT_D=0.125T_K,则式6可进一步简化为:

Δu=KP[2.45e(n)3.5e(n1)+1.25e(n2)](10)\Delta u=K_P[2.45e(n)-3.5e(n-1)+1.25e(n-2)] \tag{10}

式10只剩下KPK_P,可以对控制器进行实验,改变KPK_P,观察控制效果,直到满意为止,确定KPK_P的取值。

图4 各常量项的取值对控制效果的影响

图4动态地显示了各常量项的取值对控制效果的影响。图中横坐标表示时间,纵坐标表示被控制变量的取值,红色虚线表示被控制变量的预期值,固定为1,随时间不变,蓝色实线表示被控制变量的实际值。

初始时,只使用比例项,采用P控制,KIK_IKDK_D均固定为0,KPK_P从1增长到5,随着KPK_P的增大,过渡过程时间逐渐缩短,稳态误差逐渐减小,但振荡逐渐增大,系统稳定程度变差,且最终无法消除稳态误差。

然后固定KPK_P为5,引入积分项,采用PI控制,KIK_I从0增长到3,随着KIK_I的增大,稳态误差进一步逐渐减少,但振荡也进一步逐渐增大。

最后固定KIK_I为3,引入微分项,采用PID控制,KDK_D从0增长到3,随着KDK_D的增大,振荡逐渐减少,最终当KP=5K_P=5KI=3K_I=3KD=3K_D=3时,蓝色实线快速、稳定收敛至给定值。

基于PID控制器的预算平滑

《用PID进行预算控制》这篇文章中,作者使用增量式PID控制器实现预算平滑。本节笔者参考上述这篇文章,对基于PID控制器的预算平滑进行介绍。

预算平滑目标

广告投放一般设置天级预算,即对于一天中的广告投放进行预算平滑,使一天中各时段的消费满足一定要求,且各时段的消费之和不超过预算。

假设一天的流量随时间的变化满足双峰分布,若实现基于流量的预算平滑(Traffic Based Pacing),则广告在一天中各时段的消费也需满足双峰分布。

将采样周期设置为15分钟,即将一天划分为96个时段,各时段的消费分布具体采用两个正态分布叠加来表示,第一个正态分布为X1N(μ1,σ12)X_1\sim\mathcal{N}(\mu_1,\sigma_1^2),其中,μ1=20\mu_1=20σ1=15\sigma_1=15,第二个正态分布,X2N(μ2,σ22)X_2\sim\mathcal{N}(\mu_2,\sigma_2^2),其中,μ2=70\mu_2=70σ2=15\sigma_2=15。令每天广告投放的预算是100000元,则各时段的消费分布预期应如图5所示。

图5 各时段的消费分布预期

各时段消耗的预期值如下所示:

[ 546.72506907 596.22852449 647.33268225 699.70285423 752.95923313

806.68031794 860.4075841 913.65136822 965.89789658 1016.61734596

1065.27278583 1111.3298149 1154.26667338 1193.58458698 1228.81808109

1259.54499448 1285.39592175 1306.06282361 1321.30656282 1330.96315161

1334.94853133 1333.26174761 1325.98643029 1313.29053699 1295.42436941

1272.71692035 1245.57065495 1214.45487086 1179.89781587 1142.47776849

1102.81330515 1061.55298731 1019.36470292 976.92488971 934.90785399

893.97537871 854.76679065 817.8896299 783.91103676 753.34994403

726.6701373 704.27422328 686.49852828 673.60893442 665.79765257

663.18092528 665.79765257 673.60893442 686.49852828 704.27422328

726.6701373 753.34994403 783.91103676 817.8896299 854.76679065

893.97537871 934.90785399 976.92488971 1019.36470292 1061.55298731

1102.81330515 1142.47776849 1179.89781587 1214.45487086 1245.57065495

1272.71692035 1295.42436941 1313.29053699 1325.98643029 1333.26174761

1334.94853133 1330.96315161 1321.30656282 1306.06282361 1285.39592175

1259.54499448 1228.81808109 1193.58458698 1154.26667338 1111.3298149

1065.27278583 1016.61734596 965.89789658 913.65136822 860.4075841

806.68031794 752.95923313 699.70285423 647.33268225 596.22852449

546.72506907 499.1096958 453.62147904 410.45130711 369.74301653

331.59541837]

各时段消耗的预期值求和后为93482元,接近预算100000元,消耗的预期值的高峰在第20个和第70个时段。

实现上述双峰分布的代码如下所示:

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm

if __name__ == "__main__":
    # 产出从0到95,共96个整数,表示每天的96个时段,每个时段15分钟
    t = np.arange(0, 96, 1)
    # 令每天广告投放的预算是1000元,各个时段的消费分布和流量分布一致,满足双峰分布,用两个正态分布的叠加表示该双峰分布
    # 根据双峰分布计算得到各时段消耗的预期值
    r = 100000 * (0.5 * norm.pdf(t, loc=20, scale=15) + 0.5 * norm.pdf(t, loc=70, scale=15))
    print(r)
    plt.plot(t, r,  linewidth=3, color='r', marker='o',markerfacecolor='blue', markersize=4)
    plt.show()

通过调控出价实现预算平滑

第一节已介绍预算平滑有Probabilistic throttling和Bid modification两种解决思路。在《用PID进行预算控制》这篇文章中,作者采用Bid modification这一思路实现预算平滑。

广告投放系统中存在多个广告主的竞价博弈,各广告主的出价等投放设置不断变化,因此无法提前根据各时段消耗的预期值准确计算出各时段的出价。

广告主的竞价博弈一般满足单调分配(Monotone Allocation),即排序分随出价的变化满足单调性,从而使得广告拍卖机制满足博弈均衡的特性,出价越高,越有可能竞得广告曝光的机会,并相应产生越高的广告消耗。

因此可以采用PID控制器,将出价作为控制变量,将消耗作为被控制变量,根据各时段消耗的预期值和实际值的偏差,调整下一时段的出价,从而使得各时段消耗的实际值趋近预期值。

简单来说,若消耗的实际值低于预期值,则调高出价,在下一时段竞得更多广告曝光,若消耗的实际值高于预期值,则调低出价,在下一时段竞得更少广告曝光。

在下下时段,再根据各时段消耗的预期值和实际值的偏差,调整下下时段的出价,如此循环,直至一天的广告投放完成。

出价控制器的设计

对应至增量式PID控制器:

  • u(n)u(n)即某一时段结束时被控制器更新的出价(用于下一时段的广告投放);
  • r(n)r(n)即某一时段的消耗的预期值;
  • c(n)c(n)即某一时段的消耗的实际值;
  • e(n)=r(n)c(n)e(n)=r(n)-c(n)即某一时段的消耗的预期值的实际值的偏差

在某一时段结束、下一时段开始时,根据以下公式,由当前时段的偏差、上一时段的偏差、上上时段的偏差计算出价的修改值:

Δu=(KP+KI+KD)e(n)(KP+2KD)e(n1)+KDe(n2)(11)\Delta u = (K_P+K_I+K_D)e(n)-(K_P+2K_D)e(n-1)+K_De(n-2) \tag{11}

再根据出价的修改值更新出价,用于下一时段的广告投放:

u(n)=u(n1)+Δu(12)u(n)=u(n-1)+\Delta u \tag{12}

最后,再判断出价是否超过上下限:

u(n)={uminu(n)uminu(n)umin<u(n)<umaxumaxu(n)umax(13)u(n)=\begin{cases} u_{\text{min}} & u(n)\le u_{\text{min}} \\ u(n) & u_{\text{min}}\lt u(n) \lt u_{\text{max}} \\ u_{\text{max}} & u(n) \ge u_{\text{max}} \end{cases} \tag{13}

综上,增量式PID控制器的代码如下所示:

class Incr_PID_Controller:
    def __init__(self, K_P = 0, K_I = 0, K_D = 0, u = 0):
        self.K_P = K_P
        self.K_I = K_I
        self.K_D = K_D
        self.u = u
        self.e = 0
        self.e_1 = 0
        self.e_2 = 0

    def update(self, r, c):
        # 更新e(n-2)
        self.e_2 = self.e_1
        # 更新e(n-1)
        self.e_1 = self.e
        # 计算e(n)=r(n)-c(n)
        self.e = r - c
        # 计算u的修改
        u_diff = (self.K_P + self.K_I +  self.K_D) * self.e - (self.K_P + 2* self.K_D) * self.e_1 + self.K_D * self.e_2
        self.u = max(1, min(self.u + u_diff, 100))

    def get_u(self):
        return self.u

执行机构的模拟

广告投放系统中存在多个广告主的竞价博弈,各广告主的出价等投放设置不断变化。由于广告竞价环境的动态性,无法直接对某一时段出价和实际消耗的关系构建确定的函数,但为了简单模拟广告投放系统和竞价环境,使用下述函数描述某一时段出价和实际消耗的关系:

c=0.124×u2+0.876×u+ϵ(14)c=0.124\times u^2+ 0.876\times u+\epsilon \tag{14}

其中,uu表示某一时段的出价,cc表示某一时段的实际消耗,ϵ\epsilon表示噪声,其从标准正态分布N(0,1)\mathcal{N}(0,1)中进行随机采样。

出价uu的取值范围为从1到100之间的浮点数,且实际消耗cc基本满足随uu单调递增,若uu取值为1,则cc的值为1加上噪声,若uu取值为100,则cc的值为1327.6加上噪声,即cc的取值范围基本为从1到1328之间的浮点数。

# 模拟广告投放和竞价环境,根据某一时段的出价返回某一时段的消耗
def ad_env(u):
    return 0.124 * pow(u, 2) + 0.876 * u + np.random.normal(0, 1, 1)[0]

预算平滑的效果

初始化增量式PID控制器,其中,KP=0.01K_P=0.01KI=0.02K_I=0.02KD=0.01K_D=0.01,并设置出价初始值为70,对于一天中的各时段,使用控制器输出的出价进行广告投放,产出消耗实际值,计算和消耗预期值的偏差,通过偏差调控出价,代码如下所示:

if __name__ == "__main__":
    # 产出从0到95,共96个整数,表示每天的96个时段,每个时段15分钟
    t = np.arange(0, 96, 1)
    # 令每天广告投放的预算是1000元,各个时段的消费分布和流量分布一致,满足双峰分布,用两个正态分布的叠加表示该双峰分布
    # 根据双峰分布计算得到各时段消耗的预期值
    r = 100000 * (0.5 * norm.pdf(t, loc=20, scale=15) + 0.5 * norm.pdf(t, loc=70, scale=15))

    # print(r)
    # plt.plot(t, r,  linewidth=3, color='r', marker='o', markerfacecolor='blue', markersize=4)
    # plt.show()

    # 初始化控制器
    # K_P = 0.01
    # K_I = 0.02
    # K_D = 0.01
    # 出价初始为70
    pid_controller = Incr_PID_Controller(0.01, 0.02, 0.01, 70)

    c = []
    for n in t:
        # 从控制器得到出价
        u = pid_controller.get_u()
        # 使用出价在当前时段进行广告投放,产出消耗
        c_n = ad_env(u)
        c.append(c_n)
        # 根据消耗的实际值和预期值的偏差更新出价用于下一时段的出价
        pid_controller.update(r[n], c_n)
        u_1 = pid_controller.get_u()
        print('第[{:02d}]个时段,出价:{:.2f},实际消耗:{:.2f},预期消耗:{:.2f},更新后的出价:{:.2f},出价的修改值:{:.2f}'.format(n, u, c_n, r[n], u_1, u_1 - u))

    plt.plot(t, r, linewidth=3, color='r', marker='o', markerfacecolor='blue', markersize=4)
    plt.plot(c, linewidth=3, color='b', marker='o', markerfacecolor='red', markersize=4)
    plt.show()      

一天中的各时段的出价、消耗实际值、消耗预期值等如下所示:

第[00]个时段,出价:70.00,实际消耗:669.13,预期消耗:546.73,更新后的出价:65.10,出价的修改值:-4.90

第[01]个时段,出价:65.10,实际消耗:583.89,预期消耗:596.23,更新后的出价:69.27,出价的修改值:4.17

第[02]个时段,出价:69.27,实际消耗:654.53,预期消耗:647.33,更新后的出价:67.39,出价的修改值:-1.88

第[03]个时段,出价:67.39,实际消耗:623.61,预期消耗:699.70,更新后的出价:70.77,出价的修改值:3.38

第[04]个时段,出价:70.77,实际消耗:683.34,预期消耗:752.96,更新后的出价:71.20,出价的修改值:0.43

第[05]个时段,出价:71.20,实际消耗:689.71,预期消耗:806.68,更新后的出价:74.55,出价的修改值:3.35

第[06]个时段,出价:74.55,实际消耗:754.73,预期消耗:860.41,更新后的出价:75.97,出价的修改值:1.41

第[07]个时段,出价:75.97,实际消耗:782.54,预期消耗:913.65,更新后的出价:79.21,出价的修改值:3.24

第[08]个时段,出价:79.21,实际消耗:845.93,预期消耗:965.90,更新后的出价:81.13,出价的修改值:1.92

第[09]个时段,出价:81.13,实际消耗:887.50,预期消耗:1016.62,更新后的出价:84.01,出价的修改值:2.88

第[10]个时段,出价:84.01,实际消耗:949.81,预期消耗:1065.27,更新后的出价:85.95,出价的修改值:1.94

第[11]个时段,出价:85.95,实际消耗:990.14,预期消耗:1111.33,更新后的出价:88.63,出价的修改值:2.67

第[12]个时段,出价:88.63,实际消耗:1052.81,预期消耗:1154.27,更新后的出价:90.21,出价的修改值:1.58

第[13]个时段,出价:90.21,实际消耗:1089.46,预期消耗:1193.58,更新后的出价:92.54,出价的修改值:2.33

第[14]个时段,出价:92.54,实际消耗:1142.02,预期消耗:1228.82,更新后的出价:93.90,出价的修改值:1.36

第[15]个时段,出价:93.90,实际消耗:1175.06,预期消耗:1259.54,更新后的出价:95.72,出价的修改值:1.82

第[16]个时段,出价:95.72,实际消耗:1219.58,预期消耗:1285.40,更新后的出价:96.68,出价的修改值:0.97

第[17]个时段,出价:96.68,实际消耗:1246.06,预期消耗:1306.06,更新后的出价:97.95,出价的修改值:1.27

第[18]个时段,出价:97.95,实际消耗:1275.13,预期消耗:1321.31,更新后的出价:98.66,出价的修改值:0.71

第[19]个时段,出价:98.66,实际消耗:1293.37,预期消耗:1330.96,更新后的出价:99.38,出价的修改值:0.72

第[20]个时段,出价:99.38,实际消耗:1313.14,预期消耗:1334.95,更新后的出价:99.58,出价的修改值:0.21

第[21]个时段,出价:99.58,实际消耗:1318.28,预期消耗:1333.26,更新后的出价:99.91,出价的修改值:0.32

第[22]个时段,出价:99.91,实际消耗:1326.12,预期消耗:1325.99,更新后的出价:99.67,出价的修改值:-0.24

第[23]个时段,出价:99.67,实际消耗:1319.16,预期消耗:1313.29,更新后的出价:99.59,出价的修改值:-0.08

第[24]个时段,出价:99.59,实际消耗:1315.75,预期消耗:1295.42,更新后的出价:98.95,出价的修改值:-0.64

第[25]个时段,出价:98.95,实际消耗:1300.98,预期消耗:1272.72,更新后的出价:98.37,出价的修改值:-0.58

第[26]个时段,出价:98.37,实际消耗:1287.30,预期消耗:1245.57,更新后的出价:97.35,出价的修改值:-1.02

第[27]个时段,出价:97.35,实际消耗:1261.21,预期消耗:1214.45,更新后的出价:96.44,出价的修改值:-0.90

第[28]个时段,出价:96.44,实际消耗:1238.21,预期消耗:1179.90,更新后的出价:95.10,出价的修改值:-1.35

第[29]个时段,出价:95.10,实际消耗:1205.17,预期消耗:1142.48,更新后的出价:93.87,出价的修改值:-1.23

第[30]个时段,出价:93.87,实际消耗:1175.89,预期消耗:1102.81,更新后的出价:92.25,出价的修改值:-1.63

第[31]个时段,出价:92.25,实际消耗:1135.77,预期消耗:1061.55,更新后的出价:90.84,出价的修改值:-1.40

第[32]个时段,出价:90.84,实际消耗:1103.96,预期消耗:1019.36,更新后的出价:88.95,出价的修改值:-1.89

第[33]个时段,出价:88.95,实际消耗:1058.94,预期消耗:976.92,更新后的出价:87.47,出价的修改值:-1.48

第[34]个时段,出价:87.47,实际消耗:1025.03,预期消耗:934.91,更新后的出价:85.48,出价的修改值:-1.99

第[35]个时段,出价:85.48,实际消耗:981.35,预期消耗:893.98,更新后的出价:83.87,出价的修改值:-1.61

第[36]个时段,出价:83.87,实际消耗:944.32,预期消耗:854.77,更新后的出价:82.01,出价的修改值:-1.86

第[37]个时段,出价:82.01,实际消耗:904.55,预期消耗:817.89,更新后的出价:80.35,出价的修改值:-1.65

第[38]个时段,出价:80.35,实际消耗:872.35,预期消耗:783.91,更新后的出价:78.52,出价的修改值:-1.83

第[39]个时段,出价:78.52,实际消耗:832.80,预期消耗:753.35,更新后的出价:77.13,出价的修改值:-1.39

第[40]个时段,出价:77.13,实际消耗:804.97,预期消耗:726.67,更新后的出价:75.49,出价的修改值:-1.63

第[41]个时段,出价:75.49,实际消耗:774.05,预期消耗:704.27,更新后的出价:74.26,出价的修改值:-1.24

第[42]个时段,出价:74.26,实际消耗:747.63,预期消耗:686.50,更新后的出价:73.12,出价的修改值:-1.14

第[43]个时段,出价:73.12,实际消耗:727.98,预期消耗:673.61,更新后的出价:72.08,出价的修改值:-1.04

第[44]个时段,出价:72.08,实际消耗:707.80,预期消耗:665.80,更新后的出价:71.42,出价的修改值:-0.66

第[45]个时段,出价:71.42,实际消耗:695.75,预期消耗:663.18,更新后的出价:70.84,出价的修改值:-0.59

第[46]个时段,出价:70.84,实际消耗:683.42,预期消耗:665.80,更新后的出价:70.69,出价的修改值:-0.15

第[47]个时段,出价:70.69,实际消耗:682.21,预期消耗:673.61,更新后的出价:70.55,出价的修改值:-0.14

第[48]个时段,出价:70.55,实际消耗:679.49,预期消耗:686.50,更新后的出价:70.91,出价的修改值:0.36

第[49]个时段,出价:70.91,实际消耗:686.50,预期消耗:704.27,更新后的出价:71.33,出价的修改值:0.41

第[50]个时段,出价:71.33,实际消耗:693.20,预期消耗:726.67,更新后的出价:72.20,出价的修改值:0.88

第[51]个时段,出价:72.20,实际消耗:709.29,预期消耗:753.35,更新后的出价:73.14,出价的修改值:0.94

第[52]个时段,出价:73.14,实际消耗:728.00,预期消耗:783.91,更新后的出价:74.39,出价的修改值:1.25

第[53]个时段,出价:74.39,实际消耗:751.86,预期消耗:817.89,更新后的出价:75.79,出价的修改值:1.40

第[54]个时段,出价:75.79,实际消耗:777.61,预期消耗:854.77,更新后的出价:77.46,出价的修改值:1.66

第[55]个时段,出价:77.46,实际消耗:812.48,预期消耗:893.98,更新后的出价:79.06,出价的修改值:1.61

第[56]个时段,出价:79.06,实际消耗:843.72,预期消耗:934.91,更新后的出价:81.04,出价的修改值:1.97

第[57]个时段,出价:81.04,实际消耗:885.19,预期消耗:976.92,更新后的出价:82.78,出价的修改值:1.75

第[58]个时段,出价:82.78,实际消耗:920.99,预期消耗:1019.36,更新后的出价:84.88,出价的修改值:2.09

第[59]个时段,出价:84.88,实际消耗:967.31,预期消耗:1061.55,更新后的出价:86.61,出价的修改值:1.74

第[60]个时段,出价:86.61,实际消耗:1007.05,预期消耗:1102.81,更新后的出价:88.60,出价的修改值:1.99

第[61]个时段,出价:88.60,实际消耗:1050.71,预期消耗:1142.48,更新后的出价:90.34,出价的修改值:1.74

第[62]个时段,出价:90.34,实际消耗:1092.40,预期消耗:1179.90,更新后的出价:92.05,出价的修改值:1.70

第[63]个时段,出价:92.05,实际消耗:1131.07,预期消耗:1214.45,更新后的出价:93.67,出价的修改值:1.63

第[64]个时段,出价:93.67,实际消耗:1170.87,预期消耗:1245.57,更新后的出价:95.04,出价的修改值:1.36

第[65]个时段,出价:95.04,实际消耗:1203.55,预期消耗:1272.72,更新后的出价:96.40,出价的修改值:1.36

第[66]个时段,出价:96.40,实际消耗:1235.67,预期消耗:1295.42,更新后的出价:97.46,出价的修改值:1.06

第[67]个时段,出价:97.46,实际消耗:1262.74,预期消耗:1313.29,更新后的出价:98.38,出价的修改值:0.92

第[68]个时段,出价:98.38,实际消耗:1286.06,预期消耗:1325.99,更新后的出价:99.06,出价的修改值:0.68

第[69]个时段,出价:99.06,实际消耗:1303.90,预期消耗:1333.26,更新后的出价:99.54,出价的修改值:0.48

第[70]个时段,出价:99.54,实际消耗:1314.92,预期消耗:1334.95,更新后的出价:99.86,出价的修改值:0.32

第[71]个时段,出价:99.86,实际消耗:1324.76,预期消耗:1330.96,更新后的出价:99.80,出价的修改值:-0.06

第[72]个时段,出价:99.80,实际消耗:1322.71,预期消耗:1321.31,更新后的出价:99.76,出价的修改值:-0.04

第[73]个时段,出价:99.76,实际消耗:1323.92,预期消耗:1306.06,更新后的出价:99.15,出价的修改值:-0.61

第[74]个时段,出价:99.15,实际消耗:1307.11,预期消耗:1285.40,更新后的出价:98.80,出价的修改值:-0.35

第[75]个时段,出价:98.80,实际消耗:1297.00,预期消耗:1259.54,更新后的出价:97.77,出价的修改值:-1.03

第[76]个时段,出价:97.77,实际消耗:1270.99,预期消耗:1228.82,更新后的出价:96.99,出价的修改值:-0.78

第[77]个时段,出价:96.99,实际消耗:1253.21,预期消耗:1193.58,更新后的出价:95.50,出价的修改值:-1.49

第[78]个时段,出价:95.50,实际消耗:1214.85,预期消耗:1154.27,更新后的出价:94.44,出价的修改值:-1.06

第[79]个时段,出价:94.44,实际消耗:1188.05,预期消耗:1111.33,更新后的出价:92.60,出价的修改值:-1.85

第[80]个时段,出价:92.60,实际消耗:1144.71,预期消耗:1065.27,更新后的出价:91.11,出价的修改值:-1.48

第[81]个时段,出价:91.11,实际消耗:1109.76,预期消耗:1016.62,更新后的出价:89.00,出价的修改值:-2.11

第[82]个时段,出价:89.00,实际消耗:1058.68,预期消耗:965.90,更新后的出价:87.29,出价的修改值:-1.71

第[83]个时段,出价:87.29,实际消耗:1019.72,预期消耗:913.65,更新后的出价:84.90,出价的修改值:-2.39

第[84]个时段,出价:84.90,实际消耗:966.92,预期消耗:860.41,更新后的出价:82.90,出价的修改值:-2.01

第[85]个时段,出价:82.90,实际消耗:925.19,预期消耗:806.68,更新后的出价:80.29,出价的修改值:-2.61

第[86]个时段,出价:80.29,实际消耗:869.74,预期消耗:752.96,更新后的出价:78.11,出价的修改值:-2.18

第[87]个时段,出价:78.11,实际消耗:825.31,预期消耗:699.70,更新后的出价:75.40,出价的修改值:-2.71

第[88]个时段,出价:75.40,实际消耗:771.52,预期消耗:647.33,更新后的出价:73.04,出价的修改值:-2.37

第[89]个时段,出价:73.04,实际消耗:726.57,预期消耗:596.23,更新后的出价:70.29,出价的修改值:-2.74

第[90]个时段,出价:70.29,实际消耗:675.55,预期消耗:546.73,更新后的出价:67.81,出价的修改值:-2.48

第[91]个时段,出价:67.81,实际消耗:629.99,预期消耗:499.11,更新后的出价:65.13,出价的修改值:-2.67

第[92]个时段,出价:65.13,实际消耗:582.91,预期消耗:453.62,更新后的出价:62.60,出价的修改值:-2.53

第[93]个时段,出价:62.60,实际消耗:537.60,预期消耗:410.45,更新后的出价:60.08,出价的修改值:-2.52

第[94]个时段,出价:60.08,实际消耗:500.23,预期消耗:369.74,更新后的出价:57.39,出价的修改值:-2.70

第[95]个时段,出价:57.39,实际消耗:458.16,预期消耗:331.60,更新后的出价:54.97,出价的修改值:-2.42

各时段的消费预期值和实际值如图6所示,红色为消耗预期值,蓝色为消耗实际值,消耗实际值除开始若干个时段相对消耗预期值上下震荡外,其余时段均趋近于消耗预期值。

图6 各时段的消费预期值和实际值

进一步讨论

上述增量式PID控制器基于Bid modification这一思路,通过调控出价实现预算平滑,也可以基于Probabilistic throttling这一思路,通过调控参竞率或某一业务指标的阈值实现预算平滑。对于这两种调控方式,PID控制器的实现基本一致,只是控制变量和调控逻辑不同。

例如,某个广告投放只需要满足预算上限约束,则u(n)u(n)可以表示参竞率,若某个时间段广告超投,则PID控制器调低参竞率,打压下一时段的广告投放,若某个时间段广告投放不足,则PID控制器调高参竞率,放量下一时段的广告投放。

例如,某个广告投放除需满足预算上限约束外,还需优化最大化广告点击的目标。则u(n)u(n)可以表示广告预估点击率的阈值,若某个时间段广告超投,则PID控制器调高阈值,打压下一时段的广告投放,若某个时间段广告投放不足,则PID控制器调低阈值,放量下一时段的广告投放。

上面所讲的示例只是理想情况下基于PID控制器实现预算平滑,而在实际的广告投放场景中,还需要考虑各种细节问题。在《用PID进行预算控制》这篇文章中,作者分数据和策略两方面,介绍了实际用PID控制器进行预算平滑需要考虑的细节问题。

例如,在数据方面,由于PID控制器是基于预期值和实际值的偏差实时进行调控,因此,需要建设实时数据流,尽量减少数据延迟,从而提升调控效果。另外,不管基于Probabilistic throttling还是Bid modification,PID控制器均需进行初始化,设置某种业务指标的阈值或出价的初始值,若已有历史数据,则初始值可以参考历史数据,若没有历史数据,则初始值可以参考同行业或同转化类型的其他广告主的历史数据。

例如,在策略方面,由于各广告主设置不同的预算,因此被调控变量的实际值和预期值一般不会采用绝对值,而是采用实际值和预期值的相对值,例如将累计至当前时段的消费占预算的比例作为被调控变量、使用PID控制器进行调控,以实现预算平滑。

总结

本文介绍了PID控制器的原理和在计算广告预算平滑场景下的应用,其实在计算广告中还有很多场景应用了PID控制器。只要某对控制变量和被控制变量较难提前根据两者的关系函数,计算控制变量的值,使得被控制变量的值满足预期目标,则可以采用PID控制器对控制变量进行实时调控,使得被控制变量尽量趋近至预期目标。

另一种应用场景是成本保障。目前,广告投放一般采用oCPX方式,即广告主设置预期转化成本,而广告投放系统预估点击率和转化率,根据预估点击率和转化率,将转化成本转化为对应的展现或点击的计费,由于转化数据具有稀疏和延迟的特点,预估转化率和实际转化率存在一定的偏差,导致广告主的预期转化成本和实际转化成本存在一定的偏差,因此,可以采用PID控制器,将转化成本作为被控制变量,将计费的打折系数作为控制变量,通过调控计费的打折系数,当实际转化成本高于预期转化成本时,调低打折系数,后续适当少计费,当实际转化成本低于预期转化成本时,则调高打折系数,后续适当多计费,最终使得实际转化成本趋近至预期转化成本。

还有一种应用场景是自动出价。通过调控出价使得最终实际的业务指标趋近于预期值,《Bid Optimization by Multivariable Control in Display Advertising》这篇论文介绍了基于反馈控制的自动出价,读者可进一步阅读该论文了解细节。

参考文献