简介
最近忙于工作,没怎么看新的论文,今天把之前写的一点记录分享一下~
本文主要介绍新浪微博机器学习团队发表在 RecSys19 上的一项工作:FiBiNET: Combining Feature Importance and Bilinear feature Interaction for Click-Through Rate Prediction
文章指出当前的许多通过特征组合进行 CTR 预估的工作主要使用特征向量的内积或哈达玛积来计算交叉特征,这种方法忽略了特征本身的重要程度。提出通过使用 Squeeze-Excitation network ( SENET ) 结构动态学习特征的重要性以及使用一个双线性函数来更好的建模交叉特征。
下面对该模型进行一个简单的介绍并提供核心代码实现以及运行 demo,细节问题请参阅论文。
模型结构
1. 整体结构
图中可以看到相比于我们熟悉的基于深度学习的 CTR 预估模型,主要增加了 SENET Layer 和 Bilinear-Interaction Layer 两个结构。下面就针对这两个结构进行简单的说明。
2. SENET Layer
SENET Layer 的主要作用是学习不同特征的一个重要程度,对重要特征加权,对蕴含信息量不多的特征进行削弱。对于该结构的更详细的介绍可以参考论文:Squeeze-and-Excitation Networks
该使用特征组的 embedding 向量作为输入,产生一个特征组权重向量
最后将原始特征组 embedding 向量 E 乘上 A 得到一组新的 embedding 向量:
具体来说,分为 3 个步骤:
① Squeeze
这一步主要是对每个特征组中的 embedding 向量进行汇总统计量的操作。文章使用了池化操作来对原始特征组 embedding 向量 E=[e1,…,ef] 进行压缩表示得到统计向量:
其中 zi 表示第 i 个特征的全局信息。zi 可以通过如下的平均池化的方式计算得到:
当然,也可以使用最大池化的方式,文章表示平均池化效果要好于最大池化。
② Excitation
这一步基于特征组的压缩统计量来学习特征组的重要性权重,文章使用两层的神经网络来学习。第一层为一个维度缩减层,第二层为维度提升层。形式化表示为:
其中 A∈Rf 是一个向量,σ1 和 σ2 是激活函数,需要学习的参数为:
r 为缩减比例参数。
③ Re-Weight
这一步利用 Excitation 操作得到的特征重要性权重来对原始的特征组 embedding 向量重新赋权, 新的 embedding 向量通过如下的方式计算得到:
其中,ai∈R,ei∈Rk,vi∈Rk
3. Bilinear-Interaction
传统的特征交叉方式广泛采用了内积 ( fm,ffm 等 ) 和哈达玛积 ( AFM,NFM 等 )。而这两种方式在稀疏数据上很难有效对特征交叉进行建模。文章提出结合内积和哈达玛积并引入一个额外的参数矩阵 W 来学习特征交叉,
交叉向量 pij 可以通过以下三种方式计算得到:
① Field-All Type
pij=vi⋅W⊙vi 这种情况下,所有特征组交叉时共享一个参数矩阵 W,额外参数量为 k×k
② Field-Each Type
pij=vi⋅Wi⊙vj 这种情况下,每个特征组 i 维护一个参数矩阵 wi,额外参数量为 (f−1)×k×k
③ Filed-Interaction Type
pij=vi⋅Wij⊙vj 每对交互特征 pij 都有一个参数矩阵 wij,额外参数量为:
最终,交叉层由原始的特征组 embedding 向量 E 以及 SENET 层输出的 embedding 向量 v 分别得到交叉向量 p=[p1,…,pi,…,pn] 和 q=[q1,…,qi,…,qn],其中 pi,qi∈Rk 为向量。
4. Combination Layer
组合层将交叉向量 p 和 q 进行拼接操作,得到结果向量:
若直接对 c 向量中的元素进行求和并使用一个 sigmoid 函数输出,则得到一个浅层的 CTR 预估模型,若将该向量输入深度神经网络,则得到一个深度 CTR 预估模型。
实验结果对比
文章在 criteo 和 avazu 两个公开数据集上进行了大量的对比实验,这里只贴出 FiBiNET 相比于其他模型的一个对比结果,其他实验细节请参阅论文~
1. 浅层 FiBiNET
2. 深层 FiBiNET
3. 核心代码
这边只简单贴一下运行部分的代码,预处理和构造参数的代码请参考 github.com/shenweichen/:
https://github.com/shenweichen/DeepCTR/blob/master/deepctr/layers/interaction.py
4. SENET Layer
Z = tf.reduce_mean(inputs,axis=-1,)
A_1 = tf.nn.relu(self.tensordot([Z,self.W_1]))
A_2 = tf.nn.relu(self.tensordot([A_1,self.W_2]))
V = tf.multiply(inputs,tf.expand_dims(A_2,axis=2))
复制代码
5. Bilinear Interaction Layer
if self.type == "all":
p = [tf.multiply(tf.tensordot(v_i,self.W,axes=(-1,0)),v_j) for v_i, v_j in itertools.combinations(inputs, 2)]
elif self.type == "each":
p = [tf.multiply(tf.tensordot(inputs[i],self.W_list[i],axes=(-1,0)),inputs[j]) for i, j in itertools.combinations(range(len(inputs)), 2)]
elif self.type =="interaction":
p = [tf.multiply(tf.tensordot(v[0],w,axes=(-1,0)),v[1]) for v,w in zip(itertools.combinations(inputs,2),self.W_list)]
复制代码
6. 运行用例
首先确保你的 python 版本为 2.7,3.4,3.5 或 3.6,然后pip install deepctr[cpu]
或者pip install deepctr[gpu]
, 再去下载一下 demo 数据:
https://github.com/shenweichen/DeepCTR/blob/master/examples/criteo_sample.txt
然后直接运行下面的代码吧!
import pandas as pd
from sklearn.metrics import log_loss, roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from deepctr.models import FiBiNET
from deepctr.inputs import SparseFeat, DenseFeat,get_fixlen_feature_names
if __name__ == "__main__":
data = pd.read_csv('./criteo_sample.txt')
sparse_features = ['C' + str(i) for i in range(1, 27)]
dense_features = ['I' + str(i) for i in range(1, 14)]
data[sparse_features] = data[sparse_features].fillna('-1', )
data[dense_features] = data[dense_features].fillna(0, )
target = ['label']
# 1.Label Encoding for sparse features,and do simple Transformation for dense features
for feat in sparse_features:
lbe = LabelEncoder()
data[feat] = lbe.fit_transform(data[feat])
mms = MinMaxScaler(feature_range=(0, 1))
data[dense_features] = mms.fit_transform(data[dense_features])
# 2.count #unique features for each sparse field,and record dense feature field name
fixlen_feature_columns = [SparseFeat(feat, data[feat].nunique())
for feat in sparse_features] + [DenseFeat(feat, 1,)
for feat in dense_features]
dnn_feature_columns = fixlen_feature_columns
linear_feature_columns = fixlen_feature_columns
fixlen_feature_names = get_fixlen_feature_names(linear_feature_columns + dnn_feature_columns)
# 3.generate input data for model
train, test = train_test_split(data, test_size=0.2)
train_model_input = [train[name] for name in fixlen_feature_names]
test_model_input = [test[name] for name in fixlen_feature_names]
# 4.Define Model,train,predict and evaluate
model = FiBiNET(linear_feature_columns, dnn_feature_columns, task='binary')
model.compile("adam", "binary_crossentropy",
metrics=['binary_crossentropy'], )
history = model.fit(train_model_input, train[target].values,
batch_size=256, epochs=10, verbose=2, validation_split=0.2, )
pred_ans = model.predict(test_model_input, batch_size=256)
print("test LogLoss", round(log_loss(test[target].values, pred_ans), 4))
print("test AUC", round(roc_auc_score(test[target].values, pred_ans), 4))
复制代码
参考文献
FiBiNET: Combining Feature Importance and Bilinear feature Interaction for Click-Through Rate Prediction
https://arxiv.org/pdf/1905.09433.pdf
Squeeze-and-Excitation Networks
http://openaccess.thecvf.com/content_cvpr_2018/papers/Hu_Squeeze-and-Excitation_Networks_CVPR_2018_paper.pdf
作者介绍:
沈伟臣,阿里巴巴算法工程师,硕士毕业于浙江大学计算机学院。对机器学习,强化学习技术及其在推荐系统领域内的应用具有浓厚兴趣。
本文来自 DataFun 社区
原文链接:
https://mp.weixin.qq.com/s/VGtfGPxu_unbge3JU0OL-A
评论