Coverage for hopwise/model/sequential_recommender/fpmc.py: 99%
73 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 13:25 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 13:25 +0000
1# @Time : 2020/8/28 14:32
2# @Author : Yujie Lu
3# @Email : yujielu1998@gmail.com
5# UPDATE
6# @Time : 2020/10/2
7# @Author : Yujie Lu
8# @Email : yujielu1998@gmail.com
10r"""FPMC
11################################################
13Reference:
14 Steffen Rendle et al. "Factorizing Personalized Markov Chains for Next-Basket Recommendation." in WWW 2010.
16"""
18import torch
19from torch import nn
20from torch.nn.init import xavier_normal_
22from hopwise.model.abstract_recommender import SequentialRecommender
23from hopwise.model.loss import BPRLoss
24from hopwise.utils import InputType
27class FPMC(SequentialRecommender):
28 r"""The FPMC model is mainly used in the recommendation system to predict the possibility of
29 unknown items arousing user interest, and to discharge the item recommendation list.
31 Note:
32 In order that the generation method we used is common to other sequential models,
33 We set the size of the basket mentioned in the paper equal to 1.
34 For comparison with other models, the loss function used is BPR.
36 """
38 input_type = InputType.PAIRWISE
40 def __init__(self, config, dataset):
41 super().__init__(config, dataset)
43 # load parameters info
44 self.embedding_size = config["embedding_size"]
45 self.loss_type = config["loss_type"]
47 # load dataset info
48 self.n_users = dataset.user_num
50 # define layers and loss
51 # user embedding matrix
52 self.UI_emb = nn.Embedding(self.n_users, self.embedding_size)
53 # label embedding matrix
54 self.IU_emb = nn.Embedding(self.n_items, self.embedding_size)
55 # last click item embedding matrix
56 self.LI_emb = nn.Embedding(self.n_items, self.embedding_size, padding_idx=0)
57 # label embedding matrix
58 self.IL_emb = nn.Embedding(self.n_items, self.embedding_size)
60 if self.loss_type == "BPR":
61 self.loss_fct = BPRLoss()
62 else:
63 raise NotImplementedError("Make sure 'loss_type' in ['BPR']!")
65 # parameters initialization
66 self.apply(self._init_weights)
68 def _init_weights(self, module):
69 if isinstance(module, nn.Embedding):
70 xavier_normal_(module.weight.data)
72 def forward(self, user, item_seq, item_seq_len, next_item):
73 item_last_click_index = item_seq_len - 1
74 item_last_click = torch.gather(item_seq, dim=1, index=item_last_click_index.unsqueeze(1))
75 item_seq_emb = self.LI_emb(item_last_click) # [b,1,emb]
77 user_emb = self.UI_emb(user)
78 user_emb = torch.unsqueeze(user_emb, dim=1) # [b,1,emb]
80 iu_emb = self.IU_emb(next_item)
81 iu_emb = torch.unsqueeze(iu_emb, dim=1) # [b,n,emb] in here n = 1
83 il_emb = self.IL_emb(next_item)
84 il_emb = torch.unsqueeze(il_emb, dim=1) # [b,n,emb] in here n = 1
86 # This is the core part of the FPMC model,can be expressed by a combination of a MF and a FMC model
87 # MF
88 mf = torch.matmul(user_emb, iu_emb.permute(0, 2, 1))
89 mf = torch.squeeze(mf, dim=1) # [B,1]
90 # FMC
91 fmc = torch.matmul(il_emb, item_seq_emb.permute(0, 2, 1))
92 fmc = torch.squeeze(fmc, dim=1) # [B,1]
94 score = mf + fmc
95 score = torch.squeeze(score)
96 return score
98 def calculate_loss(self, interaction):
99 user = interaction[self.USER_ID]
100 item_seq = interaction[self.ITEM_SEQ]
101 item_seq_len = interaction[self.ITEM_SEQ_LEN]
102 pos_items = interaction[self.POS_ITEM_ID]
103 neg_items = interaction[self.NEG_ITEM_ID]
105 pos_score = self.forward(user, item_seq, item_seq_len, pos_items)
106 neg_score = self.forward(user, item_seq, item_seq_len, neg_items)
107 loss = self.loss_fct(pos_score, neg_score)
108 return loss
110 def predict(self, interaction):
111 user = interaction[self.USER_ID]
112 item_seq = interaction[self.ITEM_SEQ]
113 item_seq_len = interaction[self.ITEM_SEQ_LEN]
114 test_item = interaction[self.ITEM_ID]
115 score = self.forward(user, item_seq, item_seq_len, test_item) # [B]
116 return score
118 def full_sort_predict(self, interaction):
119 user = interaction[self.USER_ID]
120 item_seq = interaction[self.ITEM_SEQ]
121 item_seq_len = interaction[self.ITEM_SEQ_LEN]
123 user_emb = self.UI_emb(user)
124 all_iu_emb = self.IU_emb.weight
125 mf = torch.matmul(user_emb, all_iu_emb.transpose(0, 1))
126 all_il_emb = self.IL_emb.weight
128 item_last_click_index = item_seq_len - 1
129 item_last_click = torch.gather(item_seq, dim=1, index=item_last_click_index.unsqueeze(1))
130 item_seq_emb = self.LI_emb(item_last_click) # [b,1,emb]
131 fmc = torch.matmul(item_seq_emb, all_il_emb.transpose(0, 1))
132 fmc = torch.squeeze(fmc, dim=1)
133 score = mf + fmc
134 return score