1:数据处理,数据分割。2:模型构建。包括各个网络的搭建损失函数的设计等。3:构建trainer,包括模型训练fit,和模型评估evaluate。4:数据预测
1:数据处理,数据分割
根据需要的数据集合inter,link,kg数据。进行remap。数据进行分割操作包括以8:1:1的方式分割成train,valid,test。并且根据模型的不同计算neg_item。kg_neg_item。最后返回三个dataloader。
2:KGAT模型构建
网络层的设计,Loss设计,以及Aggarater设计,A的初始化,Cal_Loss,Cal_kg_loss,Updata_A,predict。
3:构建trainer,包括,构建优化器,fit函数,和evaluate函数。
1:数据处理
拼接路径
kg_path = os.path.join(dataset_path, f’{token}.kg’)
‘/Users/apple/Desktop/Code/Recommendation_System/rec/config/…/dataset_example/ml-100k/ml-100k.kg’
这个为拼接起来的路径加文件。
with open(kg_path, ‘r’) as f:
head = f.readline()[:-1]
‘head_id:token\trelation_id:token\ttail_id:token’
for field_type in head.split(field_separator):
field, ftype = field_type.split(‘:’)
columns.append(field)
usecols.append(field_type)
dtype[field_type] = np.float64 if ftype == FeatureType.FLOAT else str
columns->[‘head_id’, ‘relation_id’, ‘tail_id’]
usecols->[‘head_id:token’, ‘relation_id:token’, ‘tail_id:token’]
dtype->{‘head_id:token’:, ‘relation_id:token’: , ‘tail_id:token’: }
之后用df = pd.read_csv(filepath, delimiter=self.config[‘field_separator’], usecols=usecols, dtype=dtype)读取数据
读取的kg数据
同样的方式读取link数据
以同样的方式读取interaction数据
kg数据和interaction数据存储在pandas.DataFrame框架中。
对于读取的link数据,以字典的形式存储。字典的长度为1598。
self.inter_feat 存储的是interaction的数据
然后进行最关键的remap操作。
通过getattr(self, f’{source}_feat’)获取kg_feat。
entity_list = feat[ent_field].values获取entity_list。
if entity_id in self.entity2item:我们就讲entity_list[i] =self.entity2item[entity_id] 这样我们 通过Link文件,将在item里面的商品序号映射到entity实体中。
最终kg_feat映射成下面的样子。存储在pandas.DataFrame框架中。
然后通过pd.factorize(tokens)函数将,UserID重新映射。
将拼起来的item(1000000)和Kg的tail和head进行cat起来,统一的做映射。然后再对relation_id做映射。这样就将kg_feat和inter_feat做了统一的map。
创建ckg_graph。
src = torch.cat([user, item, head_entity])在head_entity上加上user偏移量944。
创建关系
torch.full((2 * ui_rel_num,), ui_rel_id, dtype=kg_rel.dtype);
然后将关系cat起来
导入dgl。来存储图
graph = dgl.graph((src, tgt))
graph.edata[self.relation_field] = edge来存储图的关系。
最后return graph就行。
2:模型构建
构建KGAT模型。
首先初始化def init(), 2.1加载数据集合, 2.2加载参数, 2.3初始化A_in ,2.4设计网络和损失函数。
2.1加载数据集合
self.ckg = dataset.ckg_graph(form=‘dgl’, value_field=‘relation_id’),引入Graph。
根据torch.LongTensor(dataset.ckg_graph(form=‘coo’,value_field=‘relation_id’).row).to(self.device)获取self.all_hs,self.all_ts,self.all_rs。
根据self.n_users 和 self.n_entities,创建 torch.Size([self.n_users + self.n_entities, self.n_users + self.n_entities])。矩阵self.matrix_size。
2.2加载参数
config[‘embedding_size’],包括embedding_size,mess_dropout等。
2.3初始化A_in
初始化注意力矩阵,通过协同知识图谱。
导入 dgl,创建adj_list = []存储邻接矩阵。
按照边的关系抽取出来所有的节点
edge_idxs = self.ckg.filter_edges(lambda edge: edge.data[‘relation_id’] == rel_type);
首先抽取relation_id=1的所有节点
根据edge_idxs创建子矩阵
sub_graph = dgl.edge_subgraph(self.ckg, edge_idxs, ).adjacency_matrix在原矩阵的基础上构建关系为1的子图。sub_graph就是创建的邻接矩阵。
rowsum = np.array(sub_graph.sum(1))
d_inv = np.power(rowsum, -1).flatten() 为D的-1次方。里面有值为负无穷。
我们将负无穷的结果替换成 d_inv[np.isinf(d_inv)] = 0.。
d_mat_inv = sp.diags(d_inv). #归一化的度矩阵的负一次方
norm_adj = d_mat_inv.dot(sub_graph).tocoo()然后那归一化的度矩阵的负一次方乘邻接矩阵。这个操作相当于进行了一次卷积操作。
然后对其他的relation关系都进行同样的操作。求出norm_adj。并且都进行append到adj_list 里面,然后求sum()转成sum(adj_list).tocoo() 稀疏存储的方式。
indices = torch.LongTensor([final_adj_matrix.row, final_adj_matrix.col])#获取两节点之间对应的连接。 values = torch.FloatTensor(final_adj_matrix.data)#获取两节点之间对应的关系的值新的relation。然后将adj_matrix_tensor = torch.sparse.FloatTensor(indices, values, self.matrix_size)进行存储。
2.4设计网络和损失函数。
用nn.Embedding(),分别获取self.n_users_embedding, self.entity_embedding,self.relation_embedding,以及self.trans_w nn.Embedding(self.n_relations, self.embedding_size * self.kg_embedding_size)。
创建self.aggregator_layers=nn.ModuleList()模块,在这个模块中append进来Aggregator(),这个Aggregator(input_dim, output_dim, self.mess_dropout, self.aggregator_type);
损失函数为
self.mf_loss = BPRLoss()
self.reg_loss = EmbLoss()
经过GNN Aggregator layer我们可以得到嵌入了协同知识图谱的ego_embeddings。这个ego_embedding包含了user和entity。
首先拿到norm_matrix[35657,35657]和ego_embeddings [35657,64]进行矩阵相乘
side_embeddings[35657,64] = torch.sparse.mm(norm_matrix, ego_embeddings)。
然后卷积的方式采用’bi’方式。
将ego_embeddings和side_embeddings相加放入nn.Linear中再放入self.activation中sum_embeddings。
将torch.mul(ego_embeddings, side_embeddings)相乘放入nn.Linear中再放入self.activation中bi_embeddings。
将两种方式获取的embedding相加进行dropout求的ego_embeddings。
GNN有三种方式去抽取特征。
第一:aggregator_type == ‘gcn’
将ego_embeddings和side_embeddings相加放入nn.Linear中再放入self.activation中
第二:aggregator_type == ‘graphsage’
[ego_embeddings, side_embeddings] 进行cat起来。变成128维,然后放入linear特征抽取器中,输出为64维。
第三种就是上面的 aggregator_type==“bi”。
3:构建trainer
根据model_type和model_name利用getattr()获取Trainer类。
Trainer类里面主要包括 fit(), evaluate()模块。
Trainer是一个类,主要包括__init__,fit和evaluate模块。
初始化包括初始化learning_rate,saved_model_file = os.path.join(self.checkpoint_dir, saved_model_file),和初始化self.optimizer
self.optimizer = self._build_optimizer()构建优化器optim.Adam, optim.SGD, optim. Adagrad等。
对于fit过程,首先我们传入train_data格式为DataLoader,我们利用tqdm()函数进行解析,解析完的数据变成interaction。 第一步梯度截断self.optimizer.zero_grad(),将数据传入损失函数losses = loss_func(interaction)计算出损失,第二步 loss.backward(),第三步self.optimizer.step()。
每一次训练结束后存储模型参数。torch.save(state, self.saved_model_file)。
对于evalute过程,调用模型checkpoint = torch.load(checkpoint_file)调用模型。传入valit_data 格式为DataLoader,我们利用tqdm()函数进行解析,解析完的数据变成interaction为只有userID,并且还包括每个ID购买过的item的编号即history_index。
我们对一个批次的userID进行全预测,即算出来
u_embeddings = self.restore_user_e[user]
i_embeddings = self.restore_entity_e[:self.n_items]
scores = torch.matmul(u_embeddings, i_embeddings.transpose(0, 1))
算出来用户对所有1683个商品的评分。
每个用户在训练集中购买的item至为负无穷。使其不再出现在推荐中,然后
然后根据每个商品的history_index将其至为负无穷 -np.inf。然后获取每个用户的topk_idx = torch.topk(scores_matrix, max(self.topk), dim=-1) # n_users x k->2*10个。然后添加一个值,每个用户的推荐列表变成了11个值。
这样我们就算出944个用户所对应的推荐列表。根据评价列表和eval_data放入evaluate中去计算评价指标。
对于推荐的商品我们需要看是否推荐正确,如果推荐的商品索引>(item总数-每个用户在测试集中购买的商品数)则为true,表示该商品在测试集合中真实被用户购买。
根据这个数据集合。计算评估指标。