基本想法
首先说说DIN(Deep Interest Network)到底在干嘛,DIN主基本想法是:利用用户的历史行为序列(下单,点击等)提高推荐物品的点击率。
论文中有这样一幅图:
图中显示了一个女生的行为序列,被推荐物品是一个女大衣。传统的推荐物品的CTR计算方法是不会区别对待历史行为中的物品与被推荐物品的相关度,也就是下文提到的基础模型。DIN最大的特点是在计算推荐物品的CTR时,考虑历史序列中的物品对当前推荐物品的影响(图中的进度条),也就是Attention机制。这也是阿里将attention机制引入推荐模型的动机。
论文显示介绍了一个通用的基准模型,然后在加入attention机制,得到DIN模型
基准模型
基础模型共享一个类似的嵌入和多层感知机的范式,如下图所示。它由几个部分组成:
(1)Embedding layer:嵌入层,将输入(高维二进制向量)转换成低维稠密表示。
(2)Pooling layer and Concat layer:常见的池化操作有:sum pooling and average pooling,对嵌入向量列表应用求和/平均操作。因为用户的行为序列一般是不同的,因此可以对所有的Embedding结果进行sum pooling,得到一个固定大小的向量,作为全连接层的输入。
嵌入和池化层将原始的稀疏特征映射到多个固定的长度表示向量。然后将所有向量连接在一起获得实例的整体表示向量。
(3)MLP:多层感知机,完全连接层用于自动学习特征的组合。
(4)Loss:基本模型中使用的目标函数是负对数似然函数,定义如下:
L
=
−
1
N
∑
(
x
,
y
)
∈
S
(
y
l
o
g
p
(
x
)
+
(
1
−
y
)
l
o
g
(
1
−
p
(
x
)
)
)
L = - \frac{1}{N} \sum_{(x,y) \in S}(ylogp(x)+(1-y)log(1-p(x)))
L=−N1(x,y)∈S∑(ylogp(x)+(1−y)log(1−p(x)))
DIN模型
基本原理
与基本模型相比,DIN引入了一种新颖的Activation Unit,保留其他结构不变,与base模型基本相同。Activation Unit可以自适应地计算在给定广告A 的情况下用户表示向量
v
u
\mathbf v_u
vu
v
U
(
A
)
=
f
(
v
A
,
e
1
,
e
2
,
.
.
.
,
e
H
)
=
∑
j
=
1
H
a
(
e
j
,
v
A
)
e
j
=
∑
j
=
1
H
w
j
e
j
v_U(A) = f(v_A,e_1,e_2,...,e_H)=\sum_{j=1}^Ha(e_j,v_A)e_j=\sum_{j=1}^Hw_je_j
vU(A)=f(vA,e1,e2,...,eH)=j=1∑Ha(ej,vA)ej=j=1∑Hwjej
其中,
{
e
1
,
e
2
,
…
,
e
H
}
\{e_1,e_2,\dots,e_H\}
{e1,e2,…,eH} 是用户u的历史行为嵌入向量(历史序列长度为H),a(.)是Activation Unit的输出权重,此处的权重不需要进行softmax归一化,理由如下:
在t恤和手机两个候选广告中,t恤激活了大部分属于衣服的历史行为,衣服可能会比手机获得更大的权重值(更高的兴趣强度)。传统的注意力方法通过对a(·)的输出进行softmax归一化而失去了数值尺度上的分辨率。
v U v_U vU是代表用户行为的embedding向量, v A v_A vA 是候选广告商品的embedding向量, e i e_i ei是用户u的第i次行为的embedding向量,没有引入注意力机制的基准模型中, v U v_U vU是 e i e_i ei的加和,在DIN中,通过将 v A v_A vA与 e i e_i ei输入Activation Unit 输出可以衡量二者相关度的权重,然后对 e i e_i ei加权求和得到代表用户行为的embedding向量 v U v_U vU。
Activation Unit本质是求被推荐的广告与历史行为序列中的item的相似度。
这是这篇论文70%的价值所在。
训练技巧
这部分是论文的剩余30%价值主要包括:
(1)引入Adaptive的正则化方法:mini-batch aware
(2)用Dice方法替代经典的PReLU激活函数
(3)用GAUC这个离线metric替代AUC
mini-batch aware regularizer
为了避免过拟合,作者引入一种mini-batch aware regularizer:它只计算出现在每个小批中的稀疏特征参数的l2 范数,未出现的特征则不进行计算。
CTR网络大大部分参数来自于Embedding,假定嵌入向量的维度是D,特征空间的个数为K,则Embedding过程的参数 W ∈ R D × K W \in \mathcal R^{D\times K} W∈RD×K。
传统的L2正则化如下:
L
2
(
W
)
=
∣
∣
W
∣
∣
2
2
=
∑
j
=
1
K
∣
∣
w
j
∣
∣
2
2
=
∑
(
x
,
y
)
∈
S
∑
j
=
1
K
I
(
x
j
≠
0
)
n
j
∣
∣
w
j
∣
∣
2
2
L_2(W) = ||W||_2^2 = \sum_{j=1}^K ||w_j||^2_2 = \sum_{(x,y) \in S}\sum_{j=1}^K \frac{I(x_j \ne 0)}{n_j} ||w_j||_2^2
L2(W)=∣∣W∣∣22=j=1∑K∣∣wj∣∣22=(x,y)∈S∑j=1∑KnjI(xj=0)∣∣wj∣∣22
其中,
w
j
∈
R
D
\mathbf w_j \in \mathbf R^D
wj∈RD 是一个D维的向量(特征 j 嵌入到D维),
I
(
x
j
≠
0
)
I(x_j \ne 0)
I(xj=0)表示当前数据是否拥有特征
j
j
j;
n
j
n_j
nj 表示在所有数据中特征
j
j
j出现的次数。
注:在深度CTR领域,性别、学历等称为field,性别包含男、女两个取值,即:为两个不同的特征。
传统的L2正则化需要在每一个mini-batch的训练过程中更新所有的参数,这个过程计算量十分大。
在mini-batch aware regularizer中的L2正则化如下:
L
2
(
W
)
=
∑
j
=
1
K
∑
m
=
1
B
∑
(
x
,
y
)
∈
B
m
I
(
x
j
≠
0
)
n
j
∣
∣
w
j
∣
∣
2
2
L_2(W) = \sum_{j=1}^K \sum_{m=1}^B \sum_{(x,y) \in B_m}\frac{I(x_j \ne 0)}{n_j} ||w_j||_2^2
L2(W)=j=1∑Km=1∑B(x,y)∈Bm∑njI(xj=0)∣∣wj∣∣22
其中,B是数据集的mini-batches的个数,
B
m
B_m
Bm表示第m个mini-batch。
对上述公式继续简化:
L
2
(
W
)
≈
∑
j
=
1
K
∑
m
=
1
B
α
m
j
n
j
∣
∣
w
j
∣
∣
2
2
L_2(W) \approx \sum_{j=1}^K \sum_{m=1}^B \frac{\alpha_{mj}}{n_j}||w_j||_2^2
L2(W)≈j=1∑Km=1∑Bnjαmj∣∣wj∣∣22
其中,
α
m
j
\alpha_{mj}
αmj 表示特征 j 是否出现在 mini-batch 样本 B 中,若没有出现,
α
m
j
=
0
\alpha_{mj}=0
αmj=0,则对应参数不进行更新,极大减小计算量。
n
j
n_j
nj 表示样本 j 在 B 中的出现次数,
w
j
w_j
wj 则是第 j 个嵌入向量。整个公式的核心思想是出现的频率越大,正则化的强度越大。
所以,第
m
m
m个mini-batch对第
j
j
j个特征的嵌入向量
w
j
\mathbf w_j
wj的更新过程如下:
KaTeX parse error: Undefined control sequence: \part at position 92: …\in B_m} \frac{\̲p̲a̲r̲t̲ ̲L(p(x),y)}{\par…
Dice激活函数
PReLU 可以看作是 ReLU 的改版,计算方法为:
f
(
s
)
=
{
s
i
f
s
>
0
α
s
i
f
s
≤
0
=
p
(
s
)
⋅
s
+
(
1
−
p
(
s
)
)
⋅
α
s
\begin{aligned} f(s) & =\left\{ \begin{array}{l} s \;\;\qquad if\quad s>0 \\ \alpha s \qquad if\quad s \le 0 \end{array} \right. \\ & = p(s) \cdot s + (1-p(s))\cdot\alpha s \end{aligned}
f(s)={sifs>0αsifs≤0=p(s)⋅s+(1−p(s))⋅αs
无论是 ReLU 或者是 PReLU 突变点都是0。而论文认为突变点的选择应该依赖于数据,于是基于 PReLU 提出了 Dice 激活函数:
f
(
s
)
=
p
(
s
)
⋅
s
+
(
1
−
p
(
s
)
)
⋅
α
s
p
(
s
)
=
1
1
+
e
−
s
−
E
[
s
]
V
a
r
[
s
]
+
ϵ
f(s)=p(s) \cdot s+(1-p(s)) \cdot \alpha s\\ p(s)=\frac{1}{1+e^{-\frac{s-E[s]}{\sqrt{V a r[s]+\epsilon}}}}
f(s)=p(s)⋅s+(1−p(s))⋅αsp(s)=1+e−Var[s]+ϵs−E[s]1
其中
E
[
s
]
,
V
a
r
[
s
]
E[s], Var[s]
E[s],Var[s] 分别是每个 mini-batch 数据的均值与方差,
ϵ
\epsilon
ϵ 取
1
0
−
8
10^{-8}
10−8 。函数图像如下:
GAUC 评估指标
GAUC 是 AUC 的加权平均:
G
A
U
C
=
∑
i
=
1
n
w
i
×
A
U
C
i
∑
i
=
1
n
w
i
=
∑
i
=
1
n
i
m
p
i
×
A
U
C
i
∑
i
=
1
n
i
m
p
i
\mathrm{GAUC}=\frac{\sum_{i=1}^{n} w_{i} \times \mathrm{AUC}_{i}}{\sum_{i=1}^{n} w_{i}}=\frac{\sum_{i=1}^{n} \mathrm{imp}_{i} \times \mathrm{AUC}_{i}}{\sum_{i=1}^{n} \mathrm{imp}_{i}}
GAUC=∑i=1nwi∑i=1nwi×AUCi=∑i=1nimpi∑i=1nimpi×AUCi
其中:n 是用户的数量,
A
U
C
i
AUC_i
AUCi 表示用户
i
i
i 所有样本的 AUC,
i
m
p
i
imp_i
impi 是用户
i
i
i 所有样本的个数。AUC 是考虑所有样本的排名,而实际上,我们只要关注给每个用户推荐的广告的排序,因此GAUC更具有指导意义。
Deepctr实现
模型的代码实现较为复杂,具体细节可以参考我的github,以下是使用deepctr实现的DIN模型。
首先要安装deepctr模型,安装方式如下:
pip install deepctr[gpu]
使用包括两个部分
(1)处理数据
def get_xy_fd():
# 初始化虚拟数据
# 行为序列名称,一般包括item_id和item对应的cate_id
behavior_feature_list = ["item_id", "cate_id"]
# user_id
uid = np.array([0, 1, 2])
# user 性别特征
ugender = np.array([0, 1, 0])
# user 的评分特征
pay_score = np.array([0.1, 0.2, 0.3])
# 被推荐的物品的id及其所属类别
item_id = np.array([1, 2, 3]) # 0 is mask value
cate_id = np.array([1, 2, 2]) # 0 is mask value
# 用户的历史行为序列,假设长度为4,长度不足4的用0填充
hist_item_id = np.array([[1, 2, 3, 0], [3, 2, 1, 0], [1, 2, 0, 0]])
hist_cate_id = np.array([[1, 2, 2, 0], [2, 2, 1, 0], [1, 2, 0, 0]])
# 对特征进行嵌入
# SparseFeat(name, vocabulary_size, embedding_dim)
# eg 将3个user分别嵌入到10维
feature_columns = [SparseFeat('user',3,embedding_dim=10),
SparseFeat('gender', 2,embedding_dim=4),
SparseFeat('item_id', 3,embedding_dim=8),
SparseFeat('cate_id', 2,embedding_dim=4),
DenseFeat('pay_score', 1)]
# 处理历史序列数据
# VarLenSparseFeat(sparsefeat, maxlen, combiner, length_name, weight_name,weight_norm)
# vocabulary_size = 原始的行为种类个数+1,对于长度不足4的部分会用0来填充,因此 vocabulary_size 应该在原来的基础上 + 1(新增一种行为0)
feature_columns += [VarLenSparseFeat(SparseFeat('hist_item_id', vocabulary_size=3 + 1,embedding_dim=8,embedding_name='item_id'), maxlen=4),
VarLenSparseFeat(SparseFeat('hist_cate_id', 2 + 1,embedding_dim=4, embedding_name='cate_id'), maxlen=4)]
# 构造特征字典
feature_dict = {'user': uid, 'gender': ugender, 'item_id': item_id, 'cate_id': cate_id,
'hist_item_id': hist_item_id, 'hist_cate_id': hist_cate_id, 'pay_score': pay_score}
# 构造输入(x,y)
x = {name:feature_dict[name] for name in get_feature_names(feature_columns)}
y = np.array([1, 0, 1])
return x, y, feature_columns, behavior_feature_list
(2)训练模型
if __name__ == "__main__":
x, y, feature_columns, behavior_feature_list = get_xy_fd()
model = DIN(feature_columns, behavior_feature_list)
model.compile('adam', 'binary_crossentropy',
metrics=['binary_crossentropy'])
history = model.fit(x, y, verbose=1, epochs=10, validation_split=0.5)
总结思考:
Attention机制什么?(知识)
本质是加权求和,关键是“如何获得权重”,DIN通过计算被推荐商品与历史行为序列中的商品的关系来作为权重值,也就是论文中的Activation Unit部分。
参考: