seq2seq召回(u2i)

概述:利用用户点击历史,来给用户进行u2i的召回。

样本格式:按照item点击顺序组装 user1:[item1,item2,item4..];   user2:[item2,item9,item10...]

参数说明:

seq_length:序列最大长度,70。

batch_size:198。

Seq2seq模型流程:

  • 第一步:输入n条样本序列sequence 为[198, 70]
  • 第二步:对序列进行itemEmbedding:item_embeddings [198, 70, 64]
  • 第三步:根据padding的个数进行posiitonEmbedding:position_embeddings[198, 70, 64]
  • 第四步:合并item_embeddings+position_embeddings:得到sequence_emb[198, 70, 64]
  • 第五步:sequence_emb经过两层Encoder(SelfAttention),最后一个encoder输出得到seq_out[198, 70, 64]
  • 第六步:随机生成neg_label,合并正负样本矩阵得到label[197, 70](padding位置随机取负样本,未padding取正样本)
  • 第七步:求每个位置的相似度:对label使用outputs_embedding进行向量化ids_emb,seq_out与ids_emb相乘(同shape)
  • 第八步:计算loss(详见下)

Self-Attention的实现:

  1. 对sequence_emb使用K、Q、V进行变化,分别得到mixed_key_layer、mixed_query_layer、mixed_value_layer 均为 [198, 70, 64]
  2. 把每个64维向量切成head_num个小向量,平均分成每个head。例如head=2,则query_layer [198, 2, 70, 32]
  3. Q*K得到attention_scores。 query_layer([198, 2, 70, 32])*key_layer_transpose([198, 2, 32, 70])=attention_scores([198, 2, 70, 70]) attention_scores记录的是当前位置与其他位置的相关权重。
  4. attention_scores缩小:attention_scores = attention_scores / math.sqrt(self.attention_head_size)
  5. 剔除穿越问题 attention_scores([198, 2, 70, 70]) = attention_scores([198, 2, 70, 70])+ attention_mask([198, 1, 70, 70])
  6. 对attention_scores进行softmax得到attention_probs([198, 2, 70, 70])。#sattention_probs记录是记录当前位置与其他位置相似性的权重。
  7. 对attention_probs点乘value_layer,再合并成64维。得到context_layer([198, 70, 64])
  8. 再过一层Dense,得到hidden_states([198, 70, 64])#hidden_states后可以追加dropout
  9. 残差处理:hidden_state+input_tensor。
  10. 过LayerNorm。

说明:self.attention_head_size = int(args.hidden_size / args.num_attention_heads) # 为了使每个头能够处理相等大小的隐藏状态,需要将隐藏状态大小平均分配给每个头。

SelfAttention-Mask的实现:

  • 创建两种mask:
    • subsequent_mask:([1, 1, 70, 70])信息是否可用矩阵。其实就是一个序列长度n*n 的方形矩阵。右上角部分是0,左下角和对角线是1(1代表当前信息可用。例如(20,2)值为1,即在进行20次输出时,第2个token的影响信息是可以使用的。(我们希望每个位置的注意力权重只受到当前位置及之前位置的影响,而不受到之后位置的影响)
    • padding_mask:([198, 70])序列中有padding的值为0,没有padding的值为1。
  • 合并mask:attent_mask = padding_mask * subsequent_mask (只有一个为0,则值为0,[199, 1, 70, 70] *[1,1,70,70]得到[199, 1, 70, 70])
  • 转成权重:attention_mask_score = (1.0 - attention_mask) * -10000.0 (值为1起始分为0,值为0起始分为-10000)
  • 应用:与attention_scores相加
attention_scores = tf.matmul(query_layer, tf.transpose(key_layer, (0,1,3,2)) )

attention_scores = attention_scores / self.norm

attention_scores = attention_scores + attention_mask_score ##

attention_probs = tf.nn.softmax(attention_scores)

模型的训练输入和输出

loss, logits, seq_embed = self.model.fit_city(input_ids, label, weights=hot_weight)
    • input_ids:(196, 70),也就是196条样本,每条样本是长度为70的序列,被点击序列。
    • label:(196, 70),196条样本,每条样本有最多有70个label(实际长最多69个)。
    • hot_weight:其实就是input_id中每个节点(元素)对应的权重值(小于0)。(item节点的度越大,被抽样的概率越大)

损失函数

总结:正样本就是当前序列的预测拟合当前位置的label,负样本为当前位置的其他样本的label(batch内负采样)。

  • weights = tf.convert_to_tensor(weights) * attention_mask # attention_mask是padding矩阵,最终weights有padding位置值为0,其他为小于0的一个权重值。
  • loss计算流程总结:
  • seq_out:(200,70,64) 代表200个序列,在不同位置都预测了一个输出,他们在每一个位置都对应一个label。所以label也是(200,70,64)。
  • pos_logits:是序列中某个位置的预测和label之间的logit,也就是对角线上的相似度。
pos_logits, logits = self.bert_logits(sequence_output, label, weights)

#计算正样本和全部样本的损失当做loss

loss = self.softmax(pos_logits, logits, tf.transpose(attention_mask)) #attention_mask 是是否padding的矩阵。



"""

bert_logits 函数的作用是计算序列输出与目标序列的对应位置的相似度得分。



softmax:

正样本:预测当前位置的label的logit。

负样本:把原本的概率相乘转换为概率相加。???

"""

def bert_logits(self, seq_out, ids, weights=None): # seq_out:(200,70,64) ids:(200,70)

ids_emb = self.outputs_embedding(ids, 'sample') # ids_emb:(200,70,64)

seq_out, pos_emb = seq_out, ids_emb

# 输出向量与真实向量,在每个位置(1~70)上的相似度。第2、3个维度表示seq_out、pos_emb中的样本序列长度。

logits = tf.einsum('bld,mld->lbm', seq_out, pos_emb) # logits:(70,200,200)

# weight (200,70)表示为每个位置上的初始权重值,padding的位置为0,无padding的值为一个负数。

if weights is not None:

logits = logits - tf.transpose(tf.expand_dims(weights, 1), (2, 1, 0))

pos_logits = tf.linalg.diag_part(logits) # 提取矩阵对角线上的元素 (70,200,200)-> (70,200)

return pos_logits, logits





def softmax(self, pos_logits, logits, mask=None, weight=1.0): # pos_logits: (70,200) 不同位置下,n样本的相似度。

if mask is None:

mask = tf.ones_like(pos_logits)

loss = -tf.reduce_sum((pos_logits * mask * weight))

loss += tf.reduce_sum((tf.math.log(tf.reduce_sum(tf.exp(logits), axis=-1)) * mask * weight))

return loss / tf.reduce_sum(mask)



说明:计算softmax时 把当前样本 当前位置的预测与当前样本当前位置的label计算得到pos_logits当做正样本收益,

把当前样本 当前位置的预测与其他样本的的当前位置的label计算得到叫负收益neg_logits(负收益需要e次方,在加和)

mask:是由padding的矩阵进行transpose得到,即从“n个样本,每个位置是否mask”变成了“n个位置,每个样本是否mask“

Predict流程:

  • 获取user信息和user点击序列
  • 执行模型call函数,得到每个user最后一个位置的向量表征;item来自与outputs_embedding。
  • 把两种向量concat,保存在本地npz。

Recall流程:

  • 读取主动方被动方向量:
  • 被动方(geek)建索引:faiss.IndexFlatIP(self.geek_embedding.shape[1]).add(self.geek_embedding)。
  • 查询每个user相似的topK,并剔除已点击的item:index.search(self.boss_embedding[i:i + 1],int(self.top_n * 24 + 3 * len(visited))

其他:

  • Seq2seq模型有3个Embedding层:item_embeddings(输入id),outputs_embedding(预测label的Embedding),position_embeddings(位置编码)
# csr的adj.indptr[i] - adj.indptr[i+1]表示提取当前行中所有非零元素的列索引,减去user_id,即得编码后的item_id

visited = set([self.datasets.adj.indices[j] - self.datasets.n_user for j in range(self.datasets.adj.indptr[i], self.datasets.adj.indptr[i + 1])])

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值