Fake Data-Scientistの学習日記

似非データサイエンティストの学習記録です。

ベータ二項分布を用いたアイテム推薦におけるCTR推定(2)

最近、以下の書籍が出版されました。
レコメンドシステムの分析手法が書かれている和書はあまり出版されておらず*1、以前読んだことがあるとすれば、「集合知プログラミング*2」くらいでした。

教師あり学習の2値問題などは最近書籍で取り上げられることは多いのですが、この手の問題は教師あり学習手法だけでは不十分であり、ベイズモデリングやその他の知識や技術が求められることから、より専門性が高くなります。またマーケティング領域等における現実的な分析ニーズでは、2値問題を定義し、アプローチするケースが多い気がしますので*3、中々目に触れる機会がありません(私の努力不足でもありますが・・・)。


推薦システム: 統計的機械学習の理論と実践

推薦システム: 統計的機械学習の理論と実践


さて、前置きはここまでにして、上記書籍の中でStanを用いて解ける設問(3.6演習)がありますので、早速やっていきます。*4

アイテム1とアイテム2のCTRをそれぞれ p, p+\delta(>0)とした場合、アイテム1のCTRがアイテム2より優位となる事後確率を求める問題です。
上記のCTRはアイテム2の方が大きいですが、各アイテムの表示回数が小さいとき、アイテム1の事後確率が結果的に大きくなる可能性がある、ということが考えられます*5

題意より、ベータ二項分布を使用して、確率 p の差分 \delta と表示回数 n の倍率 k を以下のように動かし、上記ケースを見てみたいと思います。


\begin{equation}
\delta = \begin{cases}
   0 &  (アイテム1の場合) \\
   0.005, 0.0075, 0.01, 0.025,0.05 & (アイテム2の場合) \\
\end{cases}
\end{equation}
 \begin{equation}
 k = 1, 2, 3,  \cdots, 100
\end{equation}

ここで、ベータ二項分布のモデリングは以下記事に書いておりますので、割愛します。
http://blog.hatena.ne.jp/FAKED-SCIENTIST/faked-scientist.hatenablog.com/edit?entry=17391345971642131739


また今回は以下の二項分布に従うサンプルデータ(表示回数の)  X を生成し、数値シミュレートしました。
アイテム1のCTRは30%と設定しましたが、適宜変更して挙動を確かめてもいいかと思いますが、似たような振る舞いをすると思います。


\begin{eqnarray}
X &\sim& \text{Binomial} \hspace{1.0mm}(kn, p+\delta)   \\
p &=& 0.3 \\
n &=& 5, 10, 15, \cdots, 100
\end{eqnarray}

使用したStanコードです。

#stanコード:CTR_pred.stan
data{
  int<lower=0> n;
  int<lower=0> X[n];
  int<lower=0> N[n];
}

parameters{
  real<lower=0, upper=1> p;
  real<lower=0> alpha;
  real<lower=0> beta;
}

model{
  #事前分布
  p ~ beta(alpha, beta);
  #尤度
  X ~ binomial(N, p);
  #弱情報事前分布
  alpha ~ uniform(0,1);
  beta ~ uniform(0,1);
}

使用したRコードです。
前半はサンプルデータを生成し、それに対してMCMCでCTR ( p, p + \delta ) を推定するものです。後半は推定結果をプロットしてます。

library(rstan)
# Stanモデルのコンパイル
stanmodel <- stan_model(file='~/CTR_pred.stan')
#Set p値(事後分布のパラメータの真値)
p <- 0.3

# 差分δの値
delta <- c(0, 0.005, 0.0075, 0.01, 0.025,0.05);len.delta <- length(delta)

# 出力パラメータの設定
pars <- c("p")

# kの最大値を設定
K <- 100
#結果を格納するデータフレーム
res <- data.frame(matrix(NA, ncol=9, nrow=len.delta*K));
colnames(res) <- c('k', 'delta.num', 'p+delta', 'mean', '2.5%', '25%', '50%', '75%', '97.5%')
# カウンター変数
cnt <- 0

#########################
#サンプリング START
#########################
for(delta.cnt in 1:len.delta){
  for(k in 1:K){
    #表示回数の設定
    N <- seq(5, 100, by=5) * k; t_max <- length(N)
    
    # p + δの設定
    p2 <- p + delta[delta.cnt]

    # 二項分布に従うサンプルデータの生成
    X_t <- rep(NA, t_max)
    for(i in 1:t_max)
      X_t[i] <- rbinom(n=1, size=N[i], prob=p2 )
    
    # 読込データの設定
    data <- list(n=t_max, X=X_t, N=N)
    
    # サンプリング
    fit_stan <- sampling(
      stanmodel,
      data = data,
      par=pars,
      seed = 1234,
      chains=1, iter=10000, warmup=2500
    )
    
    # 結果格納
    cnt <- cnt + 1
    res[cnt, 1] <- k; res[cnt, 2] <- delta.cnt
    res[cnt, 3] <- p2; res[cnt, 4] <- mean(as.data.frame(fit_stan)$p)
    res[cnt, 5:9] <- quantile(as.data.frame(fit_stan)$p, probs = c(0.025, 0.25, 0.5, 0.75, 0.975))
  }
}
#########################
#サンプリング END
#########################

# 結果のプロット①
for(d in 1:len.delta){
  res.sub <- subset(res, delta.num==d)
  plot(res.sub$k, res.sub$mean, type="l", 
       xlim=c(0,K), ylim=c(0.28, 0.38), xlab="k", ylab="EAP of p+δ", col=d, lty=1, lwd=2)
  if(d < len.delta) par(new=T)
}
labels <- c("0.10", "0.105", "0.1075", "0.11", "0.125","0.15")
legend("topleft", legend = labels, col = 1:7,lty = 1, lwd=2)

# 結果のプロット②
xlims <- c(0, K); ylims<- c(0.28, 0.34)
mains <- c("δ = 0", "δ = 0.005", "δ = 0.0075", "δ = 0.01");
par(mfrow=c(2, 2)) 
for(d in 1:4){
  res.sub <- subset(res, delta.num==d)
  plot(x=res.sub$k, res.sub[,'50%'], type="l", 
       xlim=xlims, ylim=ylims, xlab="", ylab="", col=d, lty=1, lwd=2)
  par(new=T)
  plot(res.sub$k, res.sub[,'2.5%'], type="l", 
       xlim=xlims, ylim=ylims, xlab="", ylab="", col=d, lty=2, lwd=1)
  par(new=T)
  plot(res.sub$k, res.sub[,'97.5%'], type="l", 
       xlim=xlims, ylim=ylims, xlab="k", ylab="", col=d, lty=2, lwd=1,main=mains[d])
}


以下は、結果のプロット①に該当するグラフです。
横軸 k に対して、  \delta ごとのEAPを折れ線グラフでプロットしました。
ご覧頂く通り、  k が小さく、かつ  \delta = 0.01 まではアイテム1とアイテム2のEAPは大小関係が逆転するケースがあります。これは表示回数 kn が小さいとき、アイテム1・2のCTRは安定せず、それが逆転する事象を生起させている、と理解できます。
f:id:FAKED-SCIENTIST:20180509230219j:plain:w450

結果のプロット②では、2.5%~97.5%の信用区間 (Credit Interval) を \delta = 0, 0.005, 0.0075, 0.01 ごとに点線で表しました。実線はプロット①で示したEAPです。
表示回数の倍率 k が小さい場合は信用区間の幅が広くなり、k が大きくなるに従い、徐々に狭まることが分かります。つまり、k の大きさがCTRの安定化に寄与します。
f:id:FAKED-SCIENTIST:20180509232537j:plain

*1:洋書の状況は分かりません。

*2:www.oreilly.co.jp

*3:一部のデータサイエンティストや機械学習エンジニアには使う場面があるかと思いますが。

*4:あくまで個人の主観に基づくものですので、著者が意図しない解答になっている可能性があります。ご了承ください。

*5:本の同じ章にヒントは記述されてました