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

1# @Time : 2020/8/28 14:32 

2# @Author : Yujie Lu 

3# @Email : yujielu1998@gmail.com 

4 

5# UPDATE 

6# @Time : 2020/10/2 

7# @Author : Yujie Lu 

8# @Email : yujielu1998@gmail.com 

9 

10r"""FPMC 

11################################################ 

12 

13Reference: 

14 Steffen Rendle et al. "Factorizing Personalized Markov Chains for Next-Basket Recommendation." in WWW 2010. 

15 

16""" 

17 

18import torch 

19from torch import nn 

20from torch.nn.init import xavier_normal_ 

21 

22from hopwise.model.abstract_recommender import SequentialRecommender 

23from hopwise.model.loss import BPRLoss 

24from hopwise.utils import InputType 

25 

26 

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. 

30 

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. 

35 

36 """ 

37 

38 input_type = InputType.PAIRWISE 

39 

40 def __init__(self, config, dataset): 

41 super().__init__(config, dataset) 

42 

43 # load parameters info 

44 self.embedding_size = config["embedding_size"] 

45 self.loss_type = config["loss_type"] 

46 

47 # load dataset info 

48 self.n_users = dataset.user_num 

49 

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) 

59 

60 if self.loss_type == "BPR": 

61 self.loss_fct = BPRLoss() 

62 else: 

63 raise NotImplementedError("Make sure 'loss_type' in ['BPR']!") 

64 

65 # parameters initialization 

66 self.apply(self._init_weights) 

67 

68 def _init_weights(self, module): 

69 if isinstance(module, nn.Embedding): 

70 xavier_normal_(module.weight.data) 

71 

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] 

76 

77 user_emb = self.UI_emb(user) 

78 user_emb = torch.unsqueeze(user_emb, dim=1) # [b,1,emb] 

79 

80 iu_emb = self.IU_emb(next_item) 

81 iu_emb = torch.unsqueeze(iu_emb, dim=1) # [b,n,emb] in here n = 1 

82 

83 il_emb = self.IL_emb(next_item) 

84 il_emb = torch.unsqueeze(il_emb, dim=1) # [b,n,emb] in here n = 1 

85 

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] 

93 

94 score = mf + fmc 

95 score = torch.squeeze(score) 

96 return score 

97 

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] 

104 

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 

109 

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 

117 

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] 

122 

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 

127 

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