Source code for recbole.model.sequential_recommender.shan
# -*- coding: utf-8 -*-# @Time : 2020/11/20 22:33# @Author : Shao Weiqi# @Reviewer : Lin Kun# @Email : shaoweiqi@ruc.edu.cnr"""SHAN################################################Reference: Ying, H et al. "Sequential Recommender System based on Hierarchical Attention Network."in IJCAI 2018"""importnumpyasnpimporttorchimporttorch.nnasnnfromtorch.nn.initimportnormal_,uniform_fromrecbole.model.abstract_recommenderimportSequentialRecommenderfromrecbole.model.lossimportBPRLoss
[docs]classSHAN(SequentialRecommender):r""" SHAN exploit the Hierarchical Attention Network to get the long-short term preference first get the long term purpose and then fuse the long-term with recent items to get long-short term purpose """def__init__(self,config,dataset):super(SHAN,self).__init__(config,dataset)# load the dataset informationself.n_users=dataset.num(self.USER_ID)self.device=config['device']# load the parameter informationself.embedding_size=config["embedding_size"]self.short_item_length=config["short_item_length"]# the length of the short session itemsassertself.short_item_length<=self.max_seq_length,"short_item_length can't longer than the max_seq_length"self.reg_weight=config["reg_weight"]# define layers and lossself.item_embedding=nn.Embedding(self.n_items,self.embedding_size,padding_idx=0)self.user_embedding=nn.Embedding(self.n_users,self.embedding_size)self.long_w=nn.Linear(self.embedding_size,self.embedding_size)self.long_b=nn.Parameter(uniform_(tensor=torch.zeros(self.embedding_size),a=-np.sqrt(3/self.embedding_size),b=np.sqrt(3/self.embedding_size)),requires_grad=True).to(self.device)self.long_short_w=nn.Linear(self.embedding_size,self.embedding_size)self.long_short_b=nn.Parameter(uniform_(tensor=torch.zeros(self.embedding_size),a=-np.sqrt(3/self.embedding_size),b=np.sqrt(3/self.embedding_size)),requires_grad=True).to(self.device)self.relu=nn.ReLU()self.loss_type=config['loss_type']ifself.loss_type=='BPR':self.loss_fct=BPRLoss()elifself.loss_type=='CE':self.loss_fct=nn.CrossEntropyLoss()else:raiseNotImplementedError("Make sure 'loss_type' in ['BPR', 'CE']!")# init the parameter of the modelself.apply(self.init_weights)
[docs]definverse_seq_item(self,seq_item,seq_item_len):""" inverse the seq_item, like this [1,2,3,0,0,0,0] -- after inverse -->> [0,0,0,0,1,2,3] """seq_item=seq_item.cpu().numpy()seq_item_len=seq_item_len.cpu().numpy()new_seq_item=[]foritems,lengthinzip(seq_item,seq_item_len):item=list(items[:length])zeros=list(items[length:])seqs=zeros+itemnew_seq_item.append(seqs)seq_item=torch.tensor(new_seq_item,dtype=torch.long,device=self.device)returnseq_item
[docs]deflong_and_short_term_attention_based_pooling_layer(self,long_short_item_embedding,user_embedding,mask=None):""" fusing the long term purpose with the short-term preference """long_short_item_embedding_value=long_short_item_embeddinglong_short_item_embedding=self.relu(self.long_short_w(long_short_item_embedding)+self.long_short_b)long_short_item_embedding=torch.matmul(long_short_item_embedding,user_embedding.unsqueeze(2)).squeeze(-1)# batch_size * seq_lenifmaskisnotNone:long_short_item_embedding.masked_fill_(mask,-1e9)long_short_item_embedding=nn.Softmax(dim=-1)(long_short_item_embedding)long_short_item_embedding=torch.mul(long_short_item_embedding_value,long_short_item_embedding.unsqueeze(2)).sum(dim=1)returnlong_short_item_embedding
[docs]deflong_term_attention_based_pooling_layer(self,seq_item_embedding,user_embedding,mask=None):""" get the long term purpose of user """seq_item_embedding_value=seq_item_embeddingseq_item_embedding=self.relu(self.long_w(seq_item_embedding)+self.long_b)user_item_embedding=torch.matmul(seq_item_embedding,user_embedding.unsqueeze(2)).squeeze(-1)# batch_size * seq_lenifmaskisnotNone:user_item_embedding.masked_fill_(mask,-1e9)user_item_embedding=nn.Softmax(dim=1)(user_item_embedding)user_item_embedding=torch.mul(seq_item_embedding_value,user_item_embedding.unsqueeze(2)).sum(dim=1,keepdim=True)# batch_size * 1 * embedding_sizereturnuser_item_embedding