Clicks can be Cheating: Counterfactual Recommendation for Mitigating Clickbait Issue -- SIGIR‘21

Clicks can be Cheating: Counterfactual Recommendation for Mitigating Clickbait Issue – Clickbait SIGIR ’21


前言

标题与内容不符,俗称标题党,这个问题广泛存在于推荐系统中。它不仅会伤害用户的推荐体验,还会在严重的情况下影响整个推荐生态,使推荐系统被低质量的内容所占据。本文从因果推理的角度出发,使用反事实(counterfactual)的方法缓解了标题党问题。


提示:以下内容来自原文翻译和个人理解,仅供参考,详情请查看原论文。

一、作者

注:他们都与南洋理工的Next++实验室有关

二、核心

因果图

使用因果图对标题党问题建模,将item特征分为曝光特征e和内容特征t,在反事实世界(图c)中,消除曝光特征e对Y的影响(有向边消失了),从而达到解决标题党(clickbait issue)问题的目的。

三、贡献

  • 1、本文强调了仅使用点击数据和使用因果图对解决标题党(clickbait issue)问题的重要性。
  • 2、提出了一个反事实推荐框架,该框架可应用于任何以项目特征作为输入的推荐模型。
  • 3、在MMGCN模型上进行了实现,并在tiktok和adressa数据集上进行了实验,证明了方法的有效性。

四、问题描述

标题党问题是存在的
从图a中我们可以看到用户点击了item2,在浏览完内容后却表达了对item2的dislike。这说明item2存在标题与事实不符的问题。图b也说明了点击行为与喜欢行为在item1与item2之间的差距。图c展示了不存在标题党问题的情况下推荐系统应做出的推荐。

五、公式

(1)训练过程

loss函数
D是历史数据,Sθ(u,i)是评分函数。

(2)Clickbait Issue

clickbait issue
item i的曝光特征对评分函数的影响比item j的大

(3)因果图

预备知识
TE
TE(total effect)
NDE
NDE(natural direct effect)
TIE
TIE(total indirect effect)

这些都是因果推理[1][2]里的术语,后文会使用到

(4)推荐系统的数学表示

在这里插入图片描述
在这里插入图片描述

(5)消除标题党问题(Mitigating Clickbait Issue)

这里是重点。根据以上的知识和构建的因果图,我们可以推导以下公式。
在这里插入图片描述
其中:
i=fI(E=e*,T=t*)
fY(U=u,I=i*,E=e)表示曝光特征E从e*变为e
CR 反事实推荐
省略部分简单公式,这是最核心的公式。
在这里我们可以看到反事实处理核心是减法,这里减掉曝光特征对推荐的影响。那么如何表示计算出或者说表示曝光特征对评分的影响呢?

融合策略
作者使用了融合策略[3](fusion strategy),其中Y是评分,σ是非线性函数,作者使用的是sigmoid。后面我们会讲到代码。

六、实验

(1) 数据集

dataset
tiktok 短视频数据集
adressa 新闻数据集

  • 原始数据集很难弄到,且不好处理 -_-!
  • 由于adressa的item数量少,实验跑起来比tiktok的快很多。

使用这两个数据集有两大原因

  1. 短视频和新闻是标题党问题的重灾区。
  2. 这些数据中含有视频,音频和文字。非常适合用MMGCN训练。

(2) 实验结果

实验结果
NT:普通训练,不适用其他strategy
CFT:仅使用content特征训练
IPW:使用因果推断的方法除曝光偏差
CT:不仅有点击行为,还有like行为,才认为是正样本的训练方法
NR:对负样本重新加权 Negative feedback Re-weighting
RR:重排序 re-rank (RR)
CR:Conterfactual Recommendation 反事实推荐
从表格中可以看到CR方法在precision,recall,ndcg三个指标上的结果都是最好的,证明了反事实方法的有效性!

(3)深度分析 In-depth Analysis

作者对因果图,融合策略以及数据集的处理做了很多分析,还有数据可视化,详情看论文(笔者没细看)
激活函数
值得一提的是,作者在使用融合策略时,对比了不同的函数。

七、代码

源代码已在github上开源

(1)数据文件

数据描述

  • adj.npy:是用户的交互记录,使用的是邻接矩阵的形式
  • feat_a,t,v.npy:分别是处理好的音频,文本,视频数据特征
  • test.full.npy:有喜好行为的数据post_feature_like数据
  • user_item_dict.npy:只在训练集中出现的点击数据

(2) 模型

首先实现了GCN

class GCN(torch.nn.Module):
	def __init__(self, edge_index, batch_size, num_user, num_item, dim_feat, dim_id, aggr_mode, concate, num_layer,
				 has_id, dim_latent=None):
		super(GCN, self).__init__()
		self.batch_size = batch_size
		self.num_user = num_user
		self.num_item = num_item
		self.dim_id = dim_id
		self.dim_feat = dim_feat  # features.size(1)
		self.dim_latent = dim_latent
		self.edge_index = edge_index
		self.aggr_mode = aggr_mode
		self.concate = concate
		self.num_layer = num_layer
		self.has_id = has_id

在GCN的基础上实现MMGCN,其实就是用三个模态(a,v,t)的数据训练出三个GCN

class MMGCN(torch.nn.Module):
	def __init__(self, v_feat, a_feat, words_tensor, edge_index, batch_size, num_user, num_item, aggr_mode, concate,
				 num_layer, has_id, user_item_dict, dim_x, alpha):
		super(MMGCN, self).__init__()
		...
		self.a_gcn = GCN(self.edge_index, batch_size, num_user, num_item, self.a_feat.size(1), dim_x, self.aggr_mode,
						 self.concate, num_layer=num_layer, has_id=has_id, dim_latent=128)  # 256)
		self.v_gcn = GCN(self.edge_index, batch_size, num_user, num_item, self.v_feat.size(1), dim_x, self.aggr_mode,
						 self.concate, num_layer=num_layer, has_id=has_id, dim_latent=128)  # 256)
		self.t_gcn = GCN(self.edge_index, batch_size, num_user, num_item, 128, dim_x, self.aggr_mode, self.concate,
						 num_layer=num_layer, has_id=has_id, dim_latent=128)

forward函数

	def forward(self, user_nodes, pos_item_nodes, neg_item_nodes):
		v_rep = self.v_gcn(self.v_feat, self.id_embedding)
		a_rep = self.a_gcn(self.a_feat, self.id_embedding)
		self.t_feat = torch.tensor(
			scatter_('mean', self.word_embedding(self.words_tensor[1]), self.words_tensor[0])).cuda()
		t_rep = self.t_gcn(self.t_feat, self.id_embedding)

		# pre_interaction_score
		pre_representation = t_rep
		self.pre_embedding = pre_representation
		pre_user_tensor = pre_representation[user_nodes]
		pre_pos_item_tensor = pre_representation[pos_item_nodes]
		pre_neg_item_tensor = pre_representation[neg_item_nodes]
		pre_pos_scores = torch.sum(pre_user_tensor * pre_pos_item_tensor, dim=1)
		pre_neg_scores = torch.sum(pre_user_tensor * pre_neg_item_tensor, dim=1)

		# post_interaction_score
		post_representation = (v_rep + a_rep + t_rep) / 3
		self.post_embedding = post_representation
		post_user_tensor = post_representation[user_nodes]
		post_pos_item_tensor = post_representation[pos_item_nodes]
		post_neg_item_tensor = post_representation[neg_item_nodes]
		post_pos_scores = torch.sum(post_user_tensor * post_pos_item_tensor, dim=1)
		post_neg_scores = torch.sum(post_user_tensor * post_neg_item_tensor, dim=1)

		# fusion of pre_ and post_ interaction scores
		# # post*sigmoid(pre)
		pos_scores = post_pos_scores * torch.sigmoid(pre_pos_scores)
		neg_scores = post_neg_scores * torch.sigmoid(pre_neg_scores)

		return pos_scores, neg_scores, pre_pos_scores, pre_neg_scores

pre仅含有曝光特征,pos是三个特征取平均

	def loss(self, data):
		user, pos_items, neg_items = data
		pos_scores, neg_scores, pre_pos_scores, pre_neg_scores = self.forward(user.cuda(), pos_items.cuda(),
																			  neg_items.cuda())
		# BPR loss
		loss_value = -torch.sum(torch.log2(torch.sigmoid(pos_scores - neg_scores)))
		loss_value_pre = -torch.sum(torch.log2(torch.sigmoid(pre_pos_scores - pre_neg_scores)))

		return loss_value + self.alpha * loss_value_pre
  • 最终loss是由两部分组成的,代表多任务Multi-task training
  • 使用了BPR计算loss,让正样本得分与负样本差距最大
	def full_ranking(self, val_data, topk=[10, 20, 50, 100]):
		pre_user_tensor = self.pre_embedding[:self.num_user]
		pre_item_tensor = self.pre_embedding[self.num_user:]
		post_user_tensor = self.post_embedding[:self.num_user]
		post_item_tensor = self.post_embedding[self.num_user:]

		start_index = 0
		end_index = 3000

		all_index_of_rank_list = torch.LongTensor([])
		while end_index <= self.num_user and start_index < end_index:
			temp_pre_user_tensor = pre_user_tensor[start_index:end_index]
			temp_post_user_tensor = post_user_tensor[start_index:end_index]
			pre_score_matrix = torch.matmul(temp_pre_user_tensor, pre_item_tensor.t())
			post_score_matrix = torch.matmul(temp_post_user_tensor, post_item_tensor.t())

			# 重点关注的代码
			score_matrix = (post_score_matrix - torch.mean(post_score_matrix, 1, True)) * torch.sigmoid(
				pre_score_matrix)

在full_ranking中实现了上面所说的核心公式(5)。这里就是一种反事实手法

七、总结

因果图、多任务训练、反事实推理

参考

[1] Judea Pearl. 2001. Direct and indirect effects. In UAI. Morgan Kaufmann Publishers
Inc, 411–420.
[2] Judea Pearl. 2009. Causality. Cambridge university press.
[3] Yulei Niu, Kaihua Tang, Hanwang Zhang, Zhiwu Lu, Xian-Sheng Hua, and Ji-Rong Wen. 2020. Counterfactual VQA: A Cause-Effect Look at Language Bias. In
arXiv:2006.04315

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值