Datawhale 组队学习Task4:教你读推荐论文-MIND

论文十问

Q1
论文试图解决什么问题?
先前的推荐系统中只把用户建模为一个嵌入表示,不能很好的反应用户的多种兴趣关系。

Q2
这是否是一个新的问题?
是一个新的问题

Q3
这篇文章要验证一个什么科学假设?
用户的兴趣是分散的,精细化建模每个用户的不同兴趣对推荐系统有帮助

Q4
有哪些相关研究?如何归类?谁是这一课题在领域内值得关注的研究员?
大领域属于序列推荐,小类为多兴趣推荐,Yitong Pang, Yiming Zhang

Q5
论文中提到的解决方案之关键是什么?
动态路由算法提取用户的兴趣,生成一组向量

Q6
论文中的实验是如何设计的?
线下:Train: Test = 19:1 评估HitRate,线上AB测试评估CTR

Q7
用于定量评估的数据集是什么?代码有没有开源?
Amazon Books 和Tmall,https://github.com/alibaba/easyrec

Q8
论文中的实验及结果有没有很好地支持需要验证的科学假设?
线上线下都是SOTA,而且建模的兴趣数量越多,性能越好。

Q9
这篇论文到底有什么贡献?
建模了用户的多种兴趣

Q10
下一步呢?有什么工作可以继续深入?
作者想加入更多信息:例如序列时间等进行建模,以及优化动态路由的初始化方案。此外感觉还可以优化兴趣的数量,每个用户拥有的兴趣数量可能不一样。

代码学习

胶囊网络学习
传统ANN处理标量,胶囊网络处理向量,对应论文中的动态路由模块。

    def forward(self, item_eb, mask):
        if self.bilinear_type == 0:  # MIND
            item_eb_hat = self.linear(item_eb)  # [b, s, h]
            item_eb_hat = paddle.repeat_interleave(item_eb_hat, self.interest_num, 2) # [b, s, h*in]
        elif self.bilinear_type == 1:
            item_eb_hat = self.linear(item_eb)
        else:  # ComiRec_DR
            u = paddle.unsqueeze(item_eb, 2)  # shape=(batch_size, maxlen, 1, embedding_dim)
            item_eb_hat = paddle.sum(self.w[:, :self.seq_len, :, :] * u,
                                    3)  # shape=(batch_size, maxlen, hidden_size*interest_num)

        item_eb_hat = paddle.reshape(item_eb_hat, (-1, self.seq_len, self.interest_num, self.hidden_size))
        item_eb_hat = paddle.transpose(item_eb_hat, perm=[0,2,1,3])
        # item_eb_hat = paddle.reshape(item_eb_hat, (-1, self.interest_num, self.seq_len, self.hidden_size))

        # [b, in, s, h]
        if self.stop_grad:  # 截断反向传播,item_emb_hat不计入梯度计算中
            item_eb_hat_iter = item_eb_hat.detach()
        else:
            item_eb_hat_iter = item_eb_hat

        # b的shape=(b, in, s)
        if self.bilinear_type > 0:  # b初始化为0(一般的胶囊网络算法)
            capsule_weight = paddle.zeros((item_eb_hat.shape[0], self.interest_num, self.seq_len))
        else:  # MIND使用高斯分布随机初始化b
            capsule_weight = paddle.randn((item_eb_hat.shape[0], self.interest_num, self.seq_len))

        for i in range(self.routing_times):  # 动态路由传播3次
            atten_mask = paddle.repeat_interleave(paddle.unsqueeze(mask, 1), self.interest_num, 1) # [b, in, s]
            paddings = paddle.zeros_like(atten_mask)

            # 计算c,进行mask,最后shape=[b, in, 1, s]
            capsule_softmax_weight = F.softmax(capsule_weight, axis=-1)
            capsule_softmax_weight = paddle.where(atten_mask==0, paddings, capsule_softmax_weight)  # mask
            capsule_softmax_weight = paddle.unsqueeze(capsule_softmax_weight, 2)

            if i < 2:
                # s=c*u_hat , (batch_size, interest_num, 1, seq_len) * (batch_size, interest_num, seq_len, hidden_size)
                interest_capsule = paddle.matmul(capsule_softmax_weight,
                                                item_eb_hat_iter)  # shape=(batch_size, interest_num, 1, hidden_size)
                cap_norm = paddle.sum(paddle.square(interest_capsule), -1, keepdim=True)  # shape=(batch_size, interest_num, 1, 1)
                scalar_factor = cap_norm / (1 + cap_norm) / paddle.sqrt(cap_norm + 1e-9)  # shape同上
                interest_capsule = scalar_factor * interest_capsule  # squash(s)->v,shape=(batch_size, interest_num, 1, hidden_size)

                # 更新b
                delta_weight = paddle.matmul(item_eb_hat_iter,  # shape=(batch_size, interest_num, seq_len, hidden_size)
                                            paddle.transpose(interest_capsule, perm=[0,1,3,2])
                                            # shape=(batch_size, interest_num, hidden_size, 1)
                                            )  # u_hat*v, shape=(batch_size, interest_num, seq_len, 1)
                delta_weight = paddle.reshape(delta_weight, (
                -1, self.interest_num, self.seq_len))  # shape=(batch_size, interest_num, seq_len)
                capsule_weight = capsule_weight + delta_weight  # 更新b
            else:
                interest_capsule = paddle.matmul(capsule_softmax_weight, item_eb_hat)
                cap_norm = paddle.sum(paddle.square(interest_capsule), -1, keepdim=True)
                scalar_factor = cap_norm / (1 + cap_norm) / paddle.sqrt(cap_norm + 1e-9)
                interest_capsule = scalar_factor * interest_capsule

        interest_capsule = paddle.reshape(interest_capsule, (-1, self.interest_num, self.hidden_size))

        if self.relu_layer:  # MIND模型使用book数据库时,使用relu_layer
            interest_capsule = self.relu(interest_capsule)

        return interest_capsule

多兴趣模型评估

ni = user_embs.shape[1]  # num_interest
            user_embs = np.reshape(user_embs,
                                   [-1, user_embs.shape[-1]])  # shape=(batch_size*num_interest, embedding_dim)
            user_embs = normalize(user_embs, norm='l2').astype('float32')
            D, I = gpu_index.search(user_embs, topN)  # Inner Product近邻搜索,D为distance,I是index
#             D,I = faiss.knn(user_embs, item_embs, topN,metric=faiss.METRIC_INNER_PRODUCT)
            for i, iid_list in enumerate(targets):  # 每个用户的label列表,此处item_id为一个二维list,验证和测试是多label的
                recall = 0
                dcg = 0.0
                item_list_set = []

                # 将num_interest个兴趣向量的所有topN近邻物品(num_interest*topN个物品)集合起来按照距离重新排序
                item_list = list(
                    zip(np.reshape(I[i * ni:(i + 1) * ni], -1), np.reshape(D[i * ni:(i + 1) * ni], -1)))
                item_list.sort(key=lambda x: x[1], reverse=True)  # 降序排序,内积越大,向量越近
                for j in range(len(item_list)):  # 按距离由近到远遍历推荐物品列表,最后选出最近的topN个物品作为最终的推荐物品
                    if item_list[j][0] not in item_list_set and item_list[j][0] != 0:
                        item_list_set.append(item_list[j][0])
                        if len(item_list_set) >= topN:
                            break
                test_gd[user_id] = iid_list
                preds[user_id] = item_list_set
                user_id +=1

参考资料

Datawhale推荐项目
MIND

你可能感兴趣的:(学习,深度学习,python)