CTR預估中的貝葉斯平滑方法(二)參數估計和代碼實現


1. 前言

前面博客介紹了CTR預估中的貝葉斯平滑方法的原理http://www.cnblogs.com/bentuwuying/p/6389222.html

這篇博客主要是介紹如何對貝葉斯平滑的參數進行估計,以及具體的代碼實現。

首先,我們回顧一下前文中介紹的似然函數,也就是我們需要進行最大化的目標函數:

下面我們就基於這個目標函數介紹怎樣估計參數。

 

2. 參數估計的幾種方法

1. 矩估計

矩估計在這里有點亂入的意思:),因為它其實不是用來最大化似然函數的,而是直接進行參數的近似估計。

矩估計的方法要追溯到19世紀的Karl Pearson,是基於一種簡單的 “替換” 思想建立起來的一種估計方法。 其基本思想是用樣本矩估計總體矩. 由大數定理,如果未知參數和總體的某個(些)矩有關系,我們可以很自然地來構造未知參數的估計。具體計算步驟如下:

1)根據給出的概率密度函數,計算總體的原點矩(如果只有一個參數只要計算一階原點矩,如果有兩個參數要計算一階和二階)。由於有參數這里得到的都是帶有參數的式子。比如,有兩個參數時,需要先計算出:期望  ; 方差  。在Beta分布中,可以計算得到,E(x) = α / (α+β),D(x) = αβ / (α+β)2(α+β+1)。

2)根據給出的樣本,按照計算樣本的原點矩。通常它的均值mean用  表示,方差var用  表示。(另外提一句,求時,通常用n-1為底。這樣是想讓結果跟接近總體的方差,又稱為無偏估計)

3)讓總體的原點矩與樣本的原點矩相等,解出參數。所得結果即為參數的矩估計值。這里有,mean = E(x) = α / (α+β),var = D(x) = αβ / (α+β)2(α+β+1)。於是乎,我們可以求得α,β:

α = [mean*(1-mean)/var - 1] * mean

β = [mean*(1-mean)/var - 1] * (1-mean)

 

2. Fixed-point iteration

首先構造出似然函數,然后利用Fixed-point iteration來求得似然函數的最大值。

1)首先給出參數的一個初始值(通常可以使用矩估計得到的結果作為初始值)。

2)在初始值處,構造似然函數的一個緊的下界函數。這個下界函數可以求得其最大值處的閉式解,將此解作為新的估計用於下一次迭代中。

3)不斷重復上述(2)的步驟,直至收斂。此時便可到達似然函數的stationary point。如果似然函數是convex的,那么此時就是唯一的最優解。

其實Fixed-point iteration的思想與EM類似。

首先給出兩個不等式關系:

1)

2)

由此可以得到對數似然函數的一個下界:

 

想要得到此下界函數的最大值,可以分別對α,β求偏導,並令之等於零,此時便得到α和β各自的迭代公式:

 由此,每次迭代,參數都會達到此次下界函數的最大值處,同時也就使得對應的似然函數值也相應地不斷增大,直至收斂到似然函數的最大值處。

 

3. EM

通過將概率參數作為隱含變量,任何估計概率參數的算法都可以使用EM進一步變成估計個數參數的算法。

(1)E-step:計算出隱含變量p在已觀測數據(觀測到的每個類別發生的次數,以及每個類別的超參數值的上一輪迭代的取值)下的后驗分布,便可以得到完全數據的對數似然函數的期望值。

(2)M-step:對E-step中的期望值求最大值,便可得到相應的超參數的本輪迭代的更新值。

(3)不斷重復地運行E-step和M-step,直至收斂。

回來我們這里的問題上,在有了觀測到的每個類別發生的次數,以及每個類別的超參數值的上一輪迭代的取值后,隱含變量p的后驗分布為:

而此時的完全數據的對數似然函數的期望為:

其中,

於是乎,我們可以對完全數據的對數似然函數的期望求最大值,從而得到α,β的更新值,有很多方法,直接求偏導,梯度下降,牛頓法等。

但是呢,此時我們並不需要非常精確地求得它的最大值,而是僅僅用牛頓法迭代一次。相比於精確地求得最大值,這種方法在每次迭代時只有一半的計算量,但是迭代次數會超過兩倍。

牛頓法的迭代可見:

 

3. 代碼實現

 1 #!/usr/bin/python
 2 # coding=utf-8
 3 
 4 import numpy
 5 import random
 6 import scipy.special as special
 7 import math
 8 from math import log
 9 
10 
11 class HyperParam(object):
12     def __init__(self, alpha, beta):
13         self.alpha = alpha
14         self.beta = beta
15 
16     def sample_from_beta(self, alpha, beta, num, imp_upperbound):
17         sample = numpy.random.beta(alpha, beta, num)
18         I = []
19         C = []
20         for click_ratio in sample:
21             imp = random.random() * imp_upperbound
22             #imp = imp_upperbound
23             click = imp * click_ratio
24             I.append(imp)
25             C.append(click)
26         return I, C
27 
28     def update_from_data_by_FPI(self, tries, success, iter_num, epsilon):
29         '''estimate alpha, beta using fixed point iteration'''
30         for i in range(iter_num):
31             new_alpha, new_beta = self.__fixed_point_iteration(tries, success, self.alpha, self.beta)
32             if abs(new_alpha-self.alpha)<epsilon and abs(new_beta-self.beta)<epsilon:
33                 break
34             self.alpha = new_alpha
35             self.beta = new_beta
36 
37     def __fixed_point_iteration(self, tries, success, alpha, beta):
38         '''fixed point iteration'''
39         sumfenzialpha = 0.0
40         sumfenzibeta = 0.0
41         sumfenmu = 0.0
42         for i in range(len(tries)):
43             sumfenzialpha += (special.digamma(success[i]+alpha) - special.digamma(alpha))
44             sumfenzibeta += (special.digamma(tries[i]-success[i]+beta) - special.digamma(beta))
45             sumfenmu += (special.digamma(tries[i]+alpha+beta) - special.digamma(alpha+beta))
46 
47         return alpha*(sumfenzialpha/sumfenmu), beta*(sumfenzibeta/sumfenmu)
48 
49     def update_from_data_by_moment(self, tries, success):
50         '''estimate alpha, beta using moment estimation'''
51         mean, var = self.__compute_moment(tries, success)
52         #print 'mean and variance: ', mean, var
53         #self.alpha = mean*(mean*(1-mean)/(var+0.000001)-1)
54         self.alpha = (mean+0.000001) * ((mean+0.000001) * (1.000001 - mean) / (var+0.000001) - 1)
55         #self.beta = (1-mean)*(mean*(1-mean)/(var+0.000001)-1)
56         self.beta = (1.000001 - mean) * ((mean+0.000001) * (1.000001 - mean) / (var+0.000001) - 1)
57 
58     def __compute_moment(self, tries, success):
59         '''moment estimation'''
60         ctr_list = []
61         var = 0.0
62         for i in range(len(tries)):
63             ctr_list.append(float(success[i])/tries[i])
64         mean = sum(ctr_list)/len(ctr_list)
65         for ctr in ctr_list:
66             var += pow(ctr-mean, 2)
67 
68         return mean, var/(len(ctr_list)-1)
69 
70 
71 
72 def test():
73     hyper = HyperParam(1, 1)
74     #--------sample training data--------
75     I, C = hyper.sample_from_beta(10, 1000, 10000, 1000)
76     print I, C
77 
78     #--------estimate parameter using fixed-point iteration--------
79     hyper.update_from_data_by_FPI(I, C, 1000, 0.00000001)
80     print hyper.alpha, hyper.beta
81 
82     #--------estimate parameter using moment estimation--------
83     hyper.update_from_data_by_moment(I, C)
84     print hyper.alpha, hyper.beta

 

4. 參考文獻

1. Click-Through Rate Estimation for Rare Events in Online Advertising

2. Estimating a Dirichlet distribution

 

版權聲明:

   本文由笨兔勿應所有,發布於http://www.cnblogs.com/bentuwuying。如果轉載,請注明出處,在未經作者同意下將本文用於商業用途,將追究其法律責任。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM