關(guān)鍵詞
:Transformer
,BST
翰铡,推薦算法
內(nèi)容摘要
- 推薦算法中行為序列建模概述
- BST網(wǎng)絡(luò)結(jié)構(gòu)解析
- BST代碼實(shí)戰(zhàn),對(duì)比WDL,DIN的增益
推薦算法中行為序列建模概述
Embedding+MLP是推薦算法中使用深度學(xué)習(xí)的一般泛式承冰,即將用戶,商品食零,上下文等其他特征映射到低維稠密向量困乒,再拼接送入全連接層完成二分類(lèi)任務(wù),這種模型架構(gòu)忽略了用戶的行為序列贰谣,包括用戶在一段連續(xù)時(shí)間內(nèi)對(duì)商品的點(diǎn)擊收藏購(gòu)買(mǎi)等行為娜搂,而行為序列表達(dá)了用戶強(qiáng)烈的興趣信息可以輔助對(duì)用戶的下一個(gè)商品進(jìn)行預(yù)測(cè),以往有基于求和/均值池化的方法將用戶交互的商品id序列直接求和/平均吱抚,以及基于DIN Attention通過(guò)計(jì)算和候選商品的相似度對(duì)交互的商品id加權(quán)求和的方式百宇,這些方法丟棄了行為序列的順序信息,而隨著Transformer在自然語(yǔ)言處理上的成功和對(duì)上下文的強(qiáng)大表征能力秘豹,阿里巴巴團(tuán)隊(duì)將Transformer應(yīng)用在用戶行為序列的表征上携御,充分學(xué)習(xí)商品和商品之間的依賴(lài)關(guān)系,從而引出本文介紹的BST算法既绕。
BST網(wǎng)絡(luò)結(jié)構(gòu)解析
BST(Behavior Sequence Transformer)是阿里巴巴團(tuán)隊(duì)在2019年提出的推薦排序算法啄刹,網(wǎng)絡(luò)結(jié)構(gòu)如下
自下而上解析網(wǎng)絡(luò),底層有三塊輸入分別時(shí):
- Other Feature:包括user特征凄贩,item特征鸵膏,上下文特征,cross特征怎炊,其中cross特征為人工設(shè)計(jì)的交叉統(tǒng)計(jì)特征谭企,比如年齡和目標(biāo)商品的交互特征廓译,性別對(duì)目標(biāo)商品的交互特征等
- User Behavior Sequence:用戶行為序列特征,輸入1~N長(zhǎng)度的歷史交互商品id和位置信息债查,其中位置信息采用歷史商品交互時(shí)間和當(dāng)前推薦時(shí)點(diǎn)的時(shí)間差在分箱的embedding進(jìn)行表征
- Target Item:目標(biāo)候選商品特征非区,也包括商品embedding和位置編碼embedding
每塊的特征進(jìn)行拼接輸入上層網(wǎng)絡(luò),其中歷史行為序列和候選商品一齊進(jìn)入Transformer層盹廷,通過(guò)Transformer層得到每個(gè)商品的表征征绸,BST將待預(yù)測(cè)的 item 也加入Transformer來(lái)達(dá)到抽取行為序列中的商品與待推薦商品之間的相關(guān)性的目的。最終所有特征進(jìn)行拼接進(jìn)入一個(gè)三層的全連接俄占,通過(guò)sigmod進(jìn)行輸出預(yù)測(cè)結(jié)果管怠。 右上角是標(biāo)準(zhǔn)的Transformer的Encoder層,包括多頭self attention缸榄,殘差連接渤弛,layer norm等操作,在BST中同樣可以堆疊多層Encoder層甚带。
BST的網(wǎng)絡(luò)結(jié)構(gòu)比較清楚明了她肯,除此之外作者對(duì)一些細(xì)節(jié)進(jìn)行了補(bǔ)充描述
- 1.Transformer的目的是用來(lái)學(xué)習(xí)商品之間的依賴(lài)關(guān)系
- 2.序列中的商品特征僅使用商品id和商品類(lèi)別id已經(jīng)足夠好,增加其他維度表征序列成本太高
- 3.為什么使用時(shí)間相減表征位置編碼鹰贵,而不是使用Transformer的sin-cos晴氨,作者說(shuō)是在他們的數(shù)據(jù)上時(shí)間相減的效果好于sin-cos編碼
- 4.僅使用一層Transformer Encoder就夠了,推疊越多效果越差碉输,原因可能是在推薦場(chǎng)景下學(xué)習(xí)序列遠(yuǎn)遠(yuǎn)沒(méi)有機(jī)器翻譯中學(xué)習(xí)上下文那么復(fù)雜
BST代碼實(shí)戰(zhàn)籽前,對(duì)比WDL,DIN的增益
本例使用自有的業(yè)務(wù)數(shù)據(jù)進(jìn)行復(fù)現(xiàn)測(cè)試敷钾,特征如下
特征歸屬 | 特征 | 類(lèi)型 |
---|---|---|
user | 性別 | 離散 |
user | 年齡 | 分箱離散 |
user | 注冊(cè)年限 | 分箱離散 |
user | 渠道來(lái)源 | 離散 |
user | 會(huì)員等級(jí) | 離散 |
... | ... | ... |
item | 商品id | 離散 |
item | 大類(lèi)id | 離散 |
item | 中類(lèi)id | 離散 |
item | 小類(lèi)id | 離散 |
item | 產(chǎn)地id | 離散 |
item | 品牌id | 離散 |
... | ... | ... |
行為序列 | 商品id,行為日期 | 序列 |
使用均值池化序列的WDL模型
先建立WDL(wide deep learn)和DIN兩個(gè)模型進(jìn)行對(duì)比聚假,其中WDL加入了序列的平均池化,WDL核心代碼部分如下
history_seq_emb = tf.concat(
[self.history_seq_item_id_emb, self.history_seq_pty1_id_emb, self.history_seq_pty2_id_emb,
self.history_seq_pty3_id_emb, self.history_seq_brand_id_emb, self.history_seq_origin_id_emb], axis=2)
# 去除mask的均值 [None, seq_len] => [None, seq_len]
mask_weight = tf.expand_dims(self.mask / tf.reduce_sum(self.mask, axis=1, keepdims=True), axis=-1)
history_seq_emb = tf.reduce_sum(history_seq_emb * mask_weight, axis=1)
# [None, emb_size * 6]
target_item_emb = tf.concat(
[self.target_item_id_emb, self.target_item_pty1_id_emb, self.target_item_pty2_id_emb,
self.target_item_pty3_id_emb, self.target_item_brand_id_emb, self.target_item_origin_id_emb], axis=1)
# concat => user_emb_size + emb_size * 6 + emb_size * 6
input = tf.concat([self.user_feature_emb, history_seq_emb, target_item_emb], axis=1)
其中行為序列每個(gè)元素由6個(gè)embedding拼接而成闰非,采用帶有mask的求和平均膘格,最終將用戶特征,序列特征财松,目標(biāo)商品特征輸入給全連接瘪贱,最終驗(yàn)證集AUC維持在0.796±0.002
使用Attention加權(quán)序列的DIN模型
DIN采用DIN Attention采用序列的加權(quán)求和,辆毡,DIN的核心代碼如下
def din_attention(query, facts, mask=None, stag='null', mode='SUM', softmax_stag=1, time_major=False,
return_alphas=False):
queries = tf.tile(query, [1, tf.shape(facts)[1]])
queries = tf.reshape(queries, tf.shape(facts)) # [None, seq_len, emb_size * 6]
din_all = tf.concat([queries, facts, queries - facts, queries * facts], axis=-1)
d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att' + stag)
d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att' + stag)
d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att' + stag)
d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(facts)[1]])
scores = d_layer_3_all
if mask is not None:
mask = tf.equal(mask, tf.ones_like(mask))
key_masks = tf.expand_dims(mask, 1) # [B, 1, T]
paddings = tf.ones_like(scores) * (-2 ** 32 + 1)
scores = tf.where(key_masks, scores, paddings) # [B, 1, T]
if softmax_stag:
scores = tf.nn.softmax(scores) # [B, 1, T]
if mode == 'SUM':
output = tf.matmul(scores, facts) # [B, 1, H]
else:
scores = tf.reshape(scores, [-1, tf.shape(facts)[1]])
output = facts * tf.expand_dims(scores, -1)
output = tf.reshape(output, tf.shape(facts))
return output
with tf.name_scope('Attention_layer'):
attention_output = din_attention(target_item_emb, history_seq_emb, self.mask)
att_fea = tf.reduce_sum(attention_output, 1)
input = tf.concat([self.user_feature_emb, att_fea, target_item_emb], axis=1)
輸出和WDL類(lèi)似菜秦,只是行為序列變?yōu)榱俗⒁饬訖?quán)的結(jié)果,DIN的最終驗(yàn)證集AUC穩(wěn)定在0.801±0.001
BST模型
下面進(jìn)入進(jìn)入BST的復(fù)現(xiàn)舶掖,核心Encoder部分代碼如下
def multi_head_attention(self, input):
output_list = []
k_dim = self.token_emb_size * 7 // self.n_head # 28
for n in range(self.n_head):
k_out = tf.layers.dense(input, k_dim, activation=None) # [None, seq_len, 28]
q_out = tf.layers.dense(input, k_dim, activation=None) # [None, seq_len, 28]
v_out = tf.layers.dense(input, k_dim, activation=None) # [None, seq_len, 28]
# dot [None, seq_len, seq_len]
dot = tf.matmul(k_out, tf.transpose(q_out, [0, 2, 1])) / tf.sqrt(
tf.cast(tf.shape(k_out)[-1], dtype='float32'))
self.dot = dot
# mask [None, 1, seq]
mmask = tf.expand_dims((-1e+9) * (1 - self.mask), axis=1)
dot = dot + mmask
attn = tf.nn.dropout(tf.nn.softmax(dot), self.dropout)
# [None, seq_len, seq_len] => [None, seq_len, 24]
output = tf.matmul(attn, v_out)
output_list.append(output)
# [None, seq_len, 24] => [None, seq_len, 192]
outputs = tf.concat(output_list, axis=-1)
outputs = tf.layers.dense(outputs, self.token_emb_size * 7)
outputs = tf.nn.dropout(outputs, self.dropout)
return outputs
def encoder_block(self, input):
"""多頭 -> add&norm -> ff -> add&norm"""
attn_output = self.multi_head_attention(input)
attn_output = tf.contrib.layers.layer_norm(inputs=attn_output + input, begin_norm_axis=-1, begin_params_axis=-1)
pff = tf.layers.dense(attn_output, 512, activation=tf.nn.relu)
pff = tf.layers.dense(pff, self.token_emb_size * 7, activation=None)
pff = tf.nn.dropout(pff, self.dropout)
output = tf.contrib.layers.layer_norm(attn_output + pff) # [None, seq_len, 224]
return output
def build_hidden_layer(self):
# transformer的輸入
self.history_seq_emb = tf.concat(
[self.history_seq_item_id_emb, self.history_seq_pty1_id_emb, self.history_seq_pty2_id_emb,
self.history_seq_pty3_id_emb, self.history_seq_brand_id_emb, self.history_seq_origin_id_emb,
self.pos_id_emb], axis=2)
encoder_output = self.history_seq_emb
for i in range(self.n_block):
encoder_output = self.encoder_block(encoder_output)
output = tf.reshape(encoder_output, [-1, encoder_output.get_shape()[1] * encoder_output.get_shape()[2]])
output = tf.concat([self.user_feature_emb, output], axis=1)
return output
其中build_hidden_layer為構(gòu)建Transformer層的入口球昨,將所有商品的Transformer結(jié)果全部concat作為輸出,在和用戶特征進(jìn)行合并眨攘,輸入全連接層主慰。最終驗(yàn)證集AUC維持在0.798±0.004嚣州。
模型效果總結(jié)
從驗(yàn)證集AUC來(lái)看BST基本優(yōu)于均值池化這種base模型,但是不能明顯優(yōu)于DIN共螺,且預(yù)測(cè)耗時(shí)明顯高于DIN该肴,存在繼續(xù)優(yōu)化的必要。