在廣告CTR預估的過程中,有一個十分重要的特徵,那就是廣告本身的CTR,從直觀上也能明白,對於一個廣告,在曝光次數充分的前提下,基於統計的CTR很大程度上反應了廣告本身的信息。
作爲一個特徵,很自然的,我們可以直接把它作爲向量的某一維的數值放入樣本,這樣是有一定的作用的,但作用還不夠:首先,對於點擊率差距較小的廣告,它無法捕捉到相似性;其次,對於點擊率差距較大的廣告,也很難捕捉到差異性。這是因爲一維數值特徵的表達力欠缺引起的,那麼,怎麼解決這個問題呢?
設想,假如我們能有一種手段,能對CTR進行分類,那麼,我們就可以藉助One Hot Encoding的方法對這個特徵進行處理,這樣處理之後的特徵往往對線性模型更加友好,那麼怎麼實現這件事呢,我們有以下幾種考慮:
1.直接人工分段,人爲的根據CTR的分佈,來分區間;
2.使用GBDT,K-means等分類算法作映射
可以看到上述兩種方法,都有一個明顯的缺陷:依賴於先前觀測到的數據的分佈,這也就意味着,在特徵轉化的過程中,需要維護額外的數據結構,有沒有什麼稍微更加簡單的方式能做這件事呢?
我們希望存在一個函數滿足:
最簡單的 ,其中N是我們預設的維度的數目,比如N=500,那麼CTR會被均勻的映射到0~499共500個維度上,這樣做有一定的效果,但是還不夠。
我們知道,CTR的分佈區間是在[0,1]上,但實際情況中卻往往不是這樣,實際的CTR往往非常的小,能夠超過10%點擊率的廣告幾乎是九牛一毛,所以,基於這樣的事實,我們是可以對CTR進行區間放縮的,定義一個 upperBound,一個lowerBound:
實踐中也證明,在做完這樣一個放縮之後,CTR的分佈變得更加的均勻,實際中也取得了更好的效果
那麼該怎麼來評估這件事情呢?
對於一個一般的Category類型的特徵,要衡量它的好壞,我們會選擇卡方檢驗或者使用信息增益,對於信息增益,我們知道一般有如下定義:
信息增益表徵了從總體中去掉該類特徵時,總體中丟失掉的信息量,也就客觀的反映了該特徵的重要性。
對於一份包含CTR與是否點擊(點擊爲1,否則爲0)的樣本,採用不同的CTR離散化方法,計算信息增益,信息增益大的,也就意味着這種方式可能比較有效。
計算信息增益的Python代碼如下:
import math
def entropy(p):
if p <= 0 or p >= 1:
return 0.0
return - p * math.log(p, 2) - (1 - p) * math.log(1 - p, 2)
def calc_gain(lables, indices):
max_index = max(indices)
index_map = [[0, 0] for x in range(max_index + 1)]
i = 0
for index in indices:
if lables[i] == 1:
index_map[index][0] += 1
else:
index_map[index][1] += 1
i += 1
index_map = filter(lambda t: t[0] > 0 or t[1] > 0, index_map)
# print "index map:", index_map
total_positive_count = 0.0
total_count = 0.0
for x in index_map:
total_positive_count += x[0]
total_count += (x[0] + x[1])
total_entropy = entropy(total_positive_count / total_count)
conditional_entropy = 0.0
for x in index_map:
index_count = float(x[0] + x[1])
conditional_entropy += (index_count / total_count) * entropy(x[0] / index_count)
return total_entropy - conditional_entropy
同時,對於離散化之後的Index在每個桶內的分佈,我們可以畫一個頻率分佈直方圖,直覺上,這個直方圖越平緩越好,而不是像CTR分佈一樣,大家都擠在一堆,彼此不可分。以下是一個示例:
直接將CTR乘上500後數值分佈情況,可以看到數據分佈非常極端
仔細觀察這個頻率分佈直方圖,再聯繫到我們想要達到的目的:想要原本密集的區間的值儘可能的分散,原本分散的區間的值儘量的聚合。在圖像處理領域,有一類算法天然是爲了這類目的存在的:對比度增強算法
常用的對比度增強算法有對數變換、指數變換(伽馬變換)、灰度拉昇等,至於直方圖均衡化甚至是小波變換等一些更加複雜的算法,由於它們均依賴於數據的先驗分佈,因此我們暫時不作考慮:
對數變換:
指數變換:
灰度拉伸:
我們嘗試着三種算法,其中指數變換的部分我們用開方實現,三種算法均事先作值域變換,默認與值域變換作對比,相關Python代碼如下:
def domain_transform(ctr, func):
a = 0.002
b = 0.3
ctr = max(a, ctr)
ctr = min(b, ctr)
ctr = (ctr - a) / (b - a)
fa, fb = func(0), func(1)
assert fa >= 0
return (func(ctr) - fa) / (fb - fa)
def map_func1(ctrs):
return [domain_transform(ctr, lambda x: x) for ctr in ctrs]
def map_func2(ctrs):
return [domain_transform(ctr, lambda x: math.log(1 + 100 * x)) for ctr in ctrs]
def map_func3(ctrs):
return [domain_transform(ctr, lambda x: math.sqrt(x)) for ctr in ctrs]
def map_func4(ctrs):
return [mat2gray(ctr) for ctr in ctrs]
def mat2gray(x):
return x / (x + 0.01)
我們得到了如下的結果:
各種變換後CTR的分佈及對應的信息增益
圖中的gain爲信息增益,cv爲下標的覆蓋率,顯然是越高越好。
實際工程中,採用對數變換,取得了不錯的效果。