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
« 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
5r"""CDAE
6################################################
7Reference:
8 Yao Wu et al., Collaborative denoising auto-encoders for top-n recommender systems. In WSDM 2016.
10Reference code:
11 https://github.com/jasonyaw/CDAE
12"""
14import torch
15from torch import nn
17from hopwise.model.abstract_recommender import AutoEncoderMixin, GeneralRecommender
18from hopwise.model.init import xavier_normal_initialization
19from hopwise.utils import InputType
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 """
28 input_type = InputType.USERWISE
30 def __init__(self, config, dataset):
31 super().__init__(config, dataset)
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"]
41 self.build_histroy_items(dataset)
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")
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")
59 self.dropout = nn.Dropout(p=self.corruption_ratio)
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)
65 # parameters initialization
66 self.apply(xavier_normal_initialization)
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
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)
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())
95 return loss
97 def predict(self, interaction):
98 users = interaction[self.USER_ID]
99 predict_items = interaction[self.ITEM_ID]
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]]
106 def full_sort_predict(self, interaction):
107 users = interaction[self.USER_ID]
109 items = self.get_rating_matrix(users)
110 predict = self.forward(items, users)
111 predict = self.o_act(predict)
112 return predict.view(-1)