EE-想说爱你不容易
数据挖掘很多人认为explore机制在冷启动中会非常有效,但是工业界很少看到explore用到广告系统的介绍和传闻。当然也许是因为场景限制,即一个广告系统只有一次冷启动,所以没有太大的工业应用价值。但是,我最近参与的广告系统正好会遇到非常多的冷启动问题,即我们的广告系统会不断的有新的广告类型和形式出现,因此有很多冷启动场景,按照道理explore在初期会有比较好的效果,但是在我们最近对广告系统的实验中,发现想象很美好,现实很骨感。同时,我们也很惊讶的发现,LR比你想象的更强大,更稳健,果然是simple but not simpler。
根据我们的实验结果,总结了explore机制的几个鸡肋原因:
通常情况下,我们认为ee有两个地方可能会出效果
1.冷启动:系统刚开始启动的时候,LR数据不够。
在我们的广告系统中,这个假设并不对。
(1)广告库的主体变化不大(与新闻推荐,物料每天都变化相比),探测空间没那么大
(2)即使初期数据不够,LR有一定的overfitting也没关系,因为在广告系统中,总展现一个广告没问题,并且是好的(持续带来好的pay),其实在LR中加入自解释特征其实给这些好的广告是有策略倾斜的,在广告系统,这个策略倾斜是好,对于新闻推荐是不好的,因为没有人愿意看老新闻,但是好的老新闻在LR系统中会给比较好的点击率,此时新的新闻出现的机会会少(尽管比历史上证明差的新闻出现概率高,但仍然不够)
(3)实际数据也标明,即使一天的数据,也能训练一个auc还可以的LR,这时的LR具有这样的性质:展现充分的广告预估ctr基本为后验平均ctr(通过自解释特征),展现不充分的广告通过泛化的特征有一个天然的explore(即肯定比线上已知表现差的广告展现概率高,比线上已知表现好的广告展现概率低),和ee相比,它只是不具备展现次数越少,被explore出来的概率越大的这种性质罢了。
其次,ee算法存在本身的局限性:
不管e&e用的什么算法,核心都是有个均值,然后有个上下届估计(或者分布估计),E&E认为这个均值是确定的,不会随着其他环境变化而变化。而对于广告系统,即使我们选取query-广告这么细的粒度,都会发现不是定值,还会受到地域,位次等众多因素的影响,如果我们把这些环境都考虑进去,会使得单个item的粒度太小,数据积累慢,收敛慢,而如果我们不考虑这些因素,item的粒度太大,受环境因素影响,p不再是定值,后面的算法也就不会有好的效果。如果考虑LINUCB对环境的因素做线性建模,但是这样和LR就没太大差异了。
2.线上已经有LR了,但是LR可能会因为bias,有些新广告永远出不来。
(1)这种钻空子的行为在线上比较难收到预期的效果,从原理上说,除非有大量好的广告在泛化特征上打分很低,导致不能展现出来。如果不做任何分析,直接上explore,有点撞大运的感觉了(很难保证能explore出好的)。但是如果仔细分析,并发现这些case,似乎从中找到规律加入合适的泛化特征效率更高。
(2)LR对样本的学习效率远高于E&E,因为E&E是对单样本做的学习,样本与样本之间没有关联,而LR是对样本的特征做的学习,一个样本会同时带来多个特征的更新
在我们实际的实验中,2已经被充分证明,撞大运很难,而1从前期分析来看,希望不是很大,我们似乎只能寄希望E&E+LR在探测样本上会被LR效率更高,从而收敛比单纯LR快了。实验马上会进行,后续会再补充实验的效果。
PPT(请翻墙!!)
R代码
library(ggplot2)
#######################################
# multi-armed bandit problem
# try number
#######################################
N = 10000
# ad info
ctr <- c(0.3, 0.1, 0.2, 0.5)
#########################
# experiment for e-greedy
#########################
result1=eps_greedy(N, ctr, 0.01)
result2=eps_greedy(N, ctr, 0.1)
result3=eps_greedy(N, ctr, 0.2)
best_index<-data.frame("algo1"=result1$best_ratio,
"algo2"=result2$best_ratio,
"algo3"=result3$best_ratio)
lost_com<-data.frame("algo1"=result1$regret,
"algo2"=result2$regret,
"algo3"=result3$regret)
ggplot(data=lost_com, aes(x=c(1:N))) +
geom_point(aes(y=algo1), colour="red") +
geom_point(aes(y=algo2), colour="blue") +
geom_point(aes(y=algo3), colour="black")
ggplot(data=best_index, aes(x=c(1:N))) +
geom_line(aes(y=algo1), colour="red") +
geom_line(aes(y=algo2), colour="blue") +
geom_line(aes(y=algo3), colour="black")
#########################
# experiment for UCB
#########################
result=UCB(N, ctr)
best_index<-data.frame("algo"=result$best_ratio)
ggplot(data=best_index, aes(x=c(1:N))) +
geom_point(aes(y=algo))
lost_com<-data.frame("algo"=result1$regret)
lost_com$com <- log(c(1:N))
ggplot(data=lost_com, aes(x=c(1:N))) +
geom_line(aes(y=algo)) +geom_line(aes(y=38*com))
clk_shw = data.frame(result$clk_shw)
ggplot(data=clk_shw, aes(x=c(1:N))) +
geom_line(aes(y=X5), colour="red") +
geom_line(aes(y=X6), colour="blue") +
geom_line(aes(y=X7), colour="black") +
geom_line(aes(y=X8), colour="green")
interval_plot(as.vector(clk_shw[100,], mode = "numeric"))
interval_plot(as.vector(clk_shw[5000,], mode = "numeric"))
interval_plot(as.vector(clk_shw[7500,], mode = "numeric"))
interval_plot(as.vector(clk_shw[10000,], mode = "numeric"))
interval_plot<-function(s)
{
cnt = length(s)/2
data = data.frame("x"=rep(0, cnt*3), "y"=rep(0, cnt*3))
for (i in 1:cnt)
{
clk = s[i]
shw = s[i+cnt]
data[3*i-2,1] = i
data[3*i-1,1] = i
data[3*i,1] = i
data[3*i-2,2] = clk/shw
data[3*i-1,2] = clk/shw + sqrt(2 * log(sum(shw))/shw)
data[3*i,2] = clk/shw - sqrt(2 * log(sum(shw))/shw)
}
ggplot(data = data, aes(x=x, y=y)) + geom_point() + ylim(-0.5,1)
}
#########################
# experiment for thompson sampling
#########################
result1=thompson(N, ctr, alpha=1, beta=10)
result2=thompson(N, ctr, alpha=10, beta=100)
result3=thompson(N, ctr, alpha=100, beta=10000)
result1=thompson(N, ctr, alpha=1, beta=10)
result2=thompson(N, ctr, alpha=1, beta=10)
result3=thompson(N, ctr, alpha=1, beta=10)
best_index<-data.frame("algo1"=result1$best_ratio,
"algo2"=result2$best_ratio,
"algo3"=result3$best_ratio)
lost_com<-data.frame("algo1"=result1$regret,
"algo2"=result2$regret,
"algo3"=result3$regret)
ggplot(data=lost_com, aes(x=c(1:N))) +
geom_point(aes(y=algo1), colour="red") +
geom_point(aes(y=algo2), colour="blue") +
geom_point(aes(y=algo3), colour="black")
ggplot(data=best_index, aes(x=c(1:N))) +
geom_line(aes(y=algo1), colour="red") +
geom_line(aes(y=algo2), colour="blue") +
geom_line(aes(y=algo3), colour="black")
#########################
# experiment for the compare of 3 methods
#########################
result1=eps_greedy(N, ctr, 0.1)
result2=UCB(N, ctr)
result3=thompson(N, ctr)
best_index<-data.frame("algo1"=result1$best_ratio,
"algo2"=result2$best_ratio,
"algo3"=result3$best_ratio)
lost_com<-data.frame("algo1"=result1$regret,
"algo2"=result2$regret,
"algo3"=result3$regret)
ggplot(data=lost_com, aes(x=c(1:N))) +
geom_point(aes(y=algo1), colour="red") +
geom_point(aes(y=algo2), colour="blue") +
geom_point(aes(y=algo3), colour="black")
ggplot(data=best_index, aes(x=c(1:N))) +
geom_line(aes(y=algo1), colour="red") +
geom_line(aes(y=algo2), colour="blue") +
geom_line(aes(y=algo3), colour="black")
###############################################
# get best_chose_ratio according to best_index
###############################################
get_best_ratio <- function(ctr, best_index)
{
best = which(ctr == max(ctr))[1]
N = length(best_index)
best_index_ratio = rep(0, N)
s=0
for (i in 1:N)
{
if (best_index[i] == best)
{
s = s + 1
}
best_index_ratio[i] = s/i
}
best_index_ratio
}
########################################################
# Algo1:
# e-greedy
########################################################
eps_greedy<-function(N, ctr, e=0.1){
index <- c(1:length(ctr))
best = 1
lost = rep(0, N)
clk <- rep(0, length(ctr))
shw <- rep(1, length(ctr))
best_index <- rep(0, N)
for(i in 1:N){
prob = runif(1)
# explore
if (prob < e)
{
try_index = sample(index[-best],1)
}
else
{
try_index = best
}
best_index[i] = try_index
shw[try_index] = shw[try_index] + 1
x = runif(1)
if (x <= ctr[try_index])
{
clk[try_index] = clk[try_index] + 1
}
lost[i] = max(ctr) * i - sum(clk)
# renew best index
best = which(clk/shw == max(clk/shw))[1]
}
best_ratio = get_best_ratio(ctr, best_index)
list("best_ratio"=best_ratio, "regret"=lost)
}
########################################################
# Algo2:
# ucb
########################################################
UCB<-function(N, ctr){
index <- c(1:length(ctr))
best = 1
lost = rep(0, N)
clk <- rep(0, length(ctr))
shw <- rep(1, length(ctr))
best_index <- rep(0, N)
clk_shw <-matrix(data=NA, nrow = N, ncol = length(ctr) * 2)
for(i in 1:N)
{
prior = clk/shw + sqrt(2 * log(sum(shw))/shw)
clk_shw[i, 1:4] = clk
clk_shw[i, 5:8] = shw
best = which(prior == max(prior))[1]
best_index[i] = best
shw[best] = shw[best]+1
x = runif(1)
if (x <= ctr[best])
{
clk[best] = clk[best] + 1
}
lost[i] = max(ctr) * i - sum(clk)
}
best_ratio = get_best_ratio(ctr, best_index)
list("best_ratio"=best_ratio, "regret"=lost, "clk_shw"=clk_shw)
}
########################################################
# Algo3:
# Thompson Sampling
########################################################
thompson<-function(N, ctr, alpha = 1, beta = 1){
index <- c(1:length(ctr))
best = 1
lost = rep(0, N)
clk <- rep(0, length(ctr))
shw <- rep(1, length(ctr))
best_index <- rep(0, N)
for(i in 1:N)
{
theta = rep(0, length(index))
for(j in index)
{
theta[j] = rbeta(1, clk[j] + alpha, shw[j] - clk[j] + beta)
}
best = which(theta == max(theta))[1]
best_index[i] = best
shw[best] = shw[best]+1
x = runif(1)
if (x <= ctr[best])
{
clk[best] = clk[best] + 1
}
lost[i] = max(ctr) * i - sum(clk)
}
best_ratio = get_best_ratio(ctr, best_index)
list("best_ratio"=best_ratio, "regret"=lost)
}
Tags: