Coverage for hopwise/model/general_recommender/cdae.py: 77%

69 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-09-30 13:25 +0000

1# @Time : 2020/12/12 

2# @Author : Xingyu Pan 

3# @Email : panxy@ruc.edu.cn 

4 

5r"""CDAE 

6################################################ 

7Reference: 

8 Yao Wu et al., Collaborative denoising auto-encoders for top-n recommender systems. In WSDM 2016. 

9 

10Reference code: 

11 https://github.com/jasonyaw/CDAE 

12""" 

13 

14import torch 

15from torch import nn 

16 

17from hopwise.model.abstract_recommender import AutoEncoderMixin, GeneralRecommender 

18from hopwise.model.init import xavier_normal_initialization 

19from hopwise.utils import InputType 

20 

21 

22class CDAE(GeneralRecommender, AutoEncoderMixin): 

23 r"""Collaborative Denoising Auto-Encoder (CDAE) is a recommendation model 

24 for top-N recommendation that utilizes the idea of Denoising Auto-Encoders. 

25 We implement the the CDAE model with only user dataloader. 

26 """ 

27 

28 input_type = InputType.USERWISE 

29 

30 def __init__(self, config, dataset): 

31 super().__init__(config, dataset) 

32 

33 self.reg_weight_1 = config["reg_weight_1"] 

34 self.reg_weight_2 = config["reg_weight_2"] 

35 self.loss_type = config["loss_type"] 

36 self.hid_activation = config["hid_activation"] 

37 self.out_activation = config["out_activation"] 

38 self.embedding_size = config["embedding_size"] 

39 self.corruption_ratio = config["corruption_ratio"] 

40 

41 self.build_histroy_items(dataset) 

42 

43 if self.hid_activation == "sigmoid": 

44 self.h_act = nn.Sigmoid() 

45 elif self.hid_activation == "relu": 

46 self.h_act = nn.ReLU() 

47 elif self.hid_activation == "tanh": 

48 self.h_act = nn.Tanh() 

49 else: 

50 raise ValueError("Invalid hidden layer activation function") 

51 

52 if self.out_activation == "sigmoid": 

53 self.o_act = nn.Sigmoid() 

54 elif self.out_activation == "relu": 

55 self.o_act = nn.ReLU() 

56 else: 

57 raise ValueError("Invalid output layer activation function") 

58 

59 self.dropout = nn.Dropout(p=self.corruption_ratio) 

60 

61 self.h_user = nn.Embedding(self.n_users, self.embedding_size) 

62 self.h_item = nn.Linear(self.n_items, self.embedding_size) 

63 self.out_layer = nn.Linear(self.embedding_size, self.n_items) 

64 

65 # parameters initialization 

66 self.apply(xavier_normal_initialization) 

67 

68 def forward(self, x_items, x_users): 

69 h_i = self.dropout(x_items) 

70 h_i = self.h_item(h_i) 

71 h_u = self.h_user(x_users) 

72 h = torch.add(h_u, h_i) 

73 h = self.h_act(h) 

74 out = self.out_layer(h) 

75 return out 

76 

77 def calculate_loss(self, interaction): 

78 x_users = interaction[self.USER_ID] 

79 x_items = self.get_rating_matrix(x_users) 

80 predict = self.forward(x_items, x_users) 

81 

82 if self.loss_type == "MSE": 

83 predict = self.o_act(predict) 

84 loss_func = nn.MSELoss(reduction="sum") 

85 elif self.loss_type == "BCE": 

86 loss_func = nn.BCEWithLogitsLoss(reduction="sum") 

87 else: 

88 raise ValueError("Invalid loss_type, loss_type must in [MSE, BCE]") 

89 loss = loss_func(predict, x_items) 

90 # l1-regularization 

91 loss += self.reg_weight_1 * (self.h_user.weight.norm(p=1) + self.h_item.weight.norm(p=1)) 

92 # l2-regularization 

93 loss += self.reg_weight_2 * (self.h_user.weight.norm() + self.h_item.weight.norm()) 

94 

95 return loss 

96 

97 def predict(self, interaction): 

98 users = interaction[self.USER_ID] 

99 predict_items = interaction[self.ITEM_ID] 

100 

101 items = self.get_rating_matrix(users) 

102 scores = self.forward(items, users) 

103 scores = self.o_act(scores) 

104 return scores[[torch.arange(len(predict_items)).to(self.device), predict_items]] 

105 

106 def full_sort_predict(self, interaction): 

107 users = interaction[self.USER_ID] 

108 

109 items = self.get_rating_matrix(users) 

110 predict = self.forward(items, users) 

111 predict = self.o_act(predict) 

112 return predict.view(-1)