推荐算法支配着半个互联网世界的运转,加快了信息和价值的流通速度。
然而,面对同样海量且更讳莫如深的学术信息,算法能否判断,一篇冷门角落里的论文,比一篇在自媒体上刷屏的论文更适合你读。
今年的“未来杯 AI 挑战赛” 赛题——预测一篇论文对哪些学者具有吸引力,便与这个挑战息息相关。
日前,由 “未来杯 AI 挑战赛” 发起,智谱・AI 与 AI TIME 联合承办的 “未来杯 - 智谱人工智能科技探索赛” 正式启动(大赛官网:https://ai.futurelab.tv/contest_detail/21)。
该挑战赛总奖金池 100000 元,使用 AMiner 数据提供的数据集,其中包括 6 万篇学术文献信息(含标题、摘要、关键词等),以及 50 万名 “公开专家” 数据集,含 50 万的学者画像信息(包括姓名、研究兴趣、学术指标信息、论文等)。
为此,比赛主办人员、智谱・AI 算法工程师杨珍针对本次大赛baseline进行详细剖析,点击文末 “阅读原文” 即可收看回放。
本文整理出如下 4 个部分baseline干货和具体代码,帮助广大选手进一步理解和参与比赛。
1、赛题解析
2、OAG-BERT介绍
3、数据处理
4、基于OAG-BERT的专家推荐
一、赛题解析及简介
首先是赛题解析。
比赛采用的是AMiner提供的数据集,主要包含了三个部分,第一个部分是论文和专家的数据集,选手需要利用此数据集进行offline的模型训练。
此外,本次比赛还提供了论文的属性数据和专家的属性数据,其中包括了6万篇的学术文献信息,包含了标题、摘要、关键词等信息,以及50万名的公开专家数据,即学者画像信息,包括姓名、研究兴趣、学术指标信息和论文等等。
选手需要做的是,根据这些数据库中的信息,去搭建模型来预测对文章感兴趣的学者。也就是说,通过模型可以预测该论文会吸引到哪些学者的阅读的兴趣。
其实,本次比赛的任务是一个预测问题,只要能够分别学习到论文和专家的embedding,也就是嵌入表示之后,计算论文和专家的相似度,就可以进行专家推荐了。
二、OAG-BERT介绍
简单介绍一下OAG-BERT。这是清华大学唐杰老师课题组做的一项工作, OAG-BERT的训练语料库为Open Academic Graph,考虑title、abstract、venue等异构实体信息。
关于OAG-BERT的详细内容可以阅读我们arXiv上的paper:https://arxiv.org/abs/2103.02410。
同时,我们也给出OAG-BERT的GitHub的代码链接:https://github.com/THUDM/cogdl/tree/master/cogdl/oag。
三、数据处理
在数据处理部分,使用的是AMiner数据开放计划的公开论文和公开专家数据。
首先是论文专家数据集,有论文的id,以及这篇论文评审专家的id集合,如上图所示, id代表的是论文, experts代表的是不同的专家。
第二部分是论文和专家的属性信息。
在此说明一下论文的属性信息,论文的属性信息有包括title、abstract、中文 key words、year等等。如下表所示,对于每一个字段都有一个详细的描述。
有了这些id、title、abstract中文之后,用这些属性信息作为feature的话,肯定是要进行一定的选择,下文会介绍如何处理。
第三个部分是专家的属性信息,包括专家的id、中文name、interest、target、学术指标以及他所发表的一些论文等。
我们采用的这些数据中,最重要的就是论文专家数据,因为这个数据集是训练的主要数据。
至于论文的属性信息,包括专家的属性信息,可以作为feature去用。
接下来来看数据集的划分,也就是对于paper-expert pairs的划分原则是什么呢?
我们需要构建的是paper和experts的pairs,希望借助这样的一个样本对来分别学习到paper embedding和expert embedding,然后根据竞赛的要求,进行top-K召回,为每一篇paper召回500个相关的ex。
因此,划分的原则是,对每一篇paper,与该paper交互的所有的experts,随机选择一个作为test,再选择一个作为valid,其余的experts作为train。这里,当该paper交互的experts少于3人时,我们将该paper和其交互的experts全部作为train data。
这是我们对于训练集的一个构造处理的方式,还有一种方式是可以随机采样80%作训练集,20%作为测试集,其中把这个80%训练集再划分出来,10%作为它的验证集都可以。
但是在做baseline的时候,采取的是一篇作test,再选择一篇作validation,其余都作train。
介绍完划分原则之后,来对paper-expert pairs进行预处理。
对于paper而言,给定的数据集中,每一篇paper会包含多种属性,包括id、 title、 abstract、 key words等等中英文版本都会有,我们选择title 、abstract和key words英文版这三个属性来作为paper的feature,进行预处理。
对这些feature进行处理,肯定需要NLP领域的一些知识。
这里我们选择的是OAG-BERT来将paper的这些属性作为feature输入。
值得注意的是,输入到OAG-BERT的是一段sequence,而这里的keywords是list,我们需要将其处理成sequence,具体的做法是将keywords逐次相连成一个sequence,每个keyword之间用空格隔开。
title和abstract不用多做处理。它们可以直接输入到OAG-BERT。
继续看一下关于expert,即关于专家信息的处理。
在给定的数据集中,每个专家也会有多种的属性来,包括id、 interest等等,需要注意的一点是,对于experts而言的话,它有它本身的属性,但也有从侧面来反映expert feature信息的东西,比如发表论文的信息。
为了简单起见,我们选择了两个部分,第一个部分把 interest作为它的feature来用。interest作为每个expert研究兴趣的话,能代表专家的一部分学者画像。若是interests为空,选择tags为研究兴趣,或者tags也为空,则interests为空。
除此之外,pub_info也是表从侧面反映了学者画像。
pub_info中包含了该expert发表的论文,这里我们同样选择title、abstract及keywords作为feature。
基本的数据处理之后,下面来了解基于OAG-BERT的专家推荐。
四、基于OAG-BERT的专家推荐
我们选择OAG-BERT作为学习embedding的模型。
OAG-BERT以每篇paper的title、keywords、abstract作为输入,来得到每一篇paper的embedding表示。这里,需要先得到每篇paper的title、keywords、abstract等的token表示,而非简单的seq句子。具体会在在batch数据的得到中详细讲述。
对于batch数据,需要得到batch数据,进行OAG-BERT的finetune训练。
值得注意的是,给定的数据集中只包含有paper-expert正的交互项,没有负样本,这里我们对每一个正paper-expert选择negs_num个负样本,也就是负采样。
负采样具体的操作是,将该paper没有交互过的所有experts作为负样本的候选集合,然后随机采样negs_num个。这样可以得到batch的数据,包括正、负样本。注意,这里得到的训练样本集合,以单个为例的话就是[一篇paper, 一个正expert,negs_num个负experts]。这样就构成了一个训练样本。
得到了batch数据之后,OAG-BERT和BERT是类似的,输入的并不是sequence本身,而是token化的一个东西,接下来就要对batch数据进行token化处理。
借助OAG-BERT的build_inputs()函数得到OAG-BERT的inputs——token化的inputs。
这里的input tokens包括input_ids、input_masks、token_type_ids、masked_lm_labels、position_ids、position_ids_second、masked_positions、num_spans。详细操作可以看github上OAG-BERT详细使用说明。
之后,就可以把它输到OAG-BERT中。
第一步,获得paper embedding。
我们的OAG-BERT的输入需要input_ids、input_masks、token_type_ids、position_ids、position_ids_second作为输入,输出paper的embedding表示。
然后,获得expert embedding。
当得到expert的tokens后,可以将其输入到OAG-BERT,得到expert的embedding。
值得注意的是,这里expert的embedding由两部分组成:expert的interests的embedding,expert的pub_info的embedding。
结合interests embedding和pub_info embedding,这里,简单使用torch.mean()来进行merge这两种embedding来得到最终的expert embedding。参赛选手可以对此处进行改进。可以去想一想,怎么样得到更好的expert embedding。
之后,还要获得最终的paper embedding & expert embedding,此时要加入MLP去非线性变换了OAG-BERT得到的embedding,以便更好地finetune。
获得完 paper和expert的embedding之后,进入训练环节。此时需要定义loss,在这里我们采用的是 infoNCE 这么一个loss,具体的loss形式如下所示:
最后一个部分是Test/Valid,在valid/test上进行验证/测试。
valid可以用来调整模型的超参,test来测试模型的泛化能力。这些都是offline的测试。最终,保存效果最好的模型参数,再进行online的valid和test。
这里用mrr来作为评价指标,当然,各位选手可以选择给定的评价指标。
注意,由于test时,为每篇paper做全局召回,耗时太大,我们采用的是随机sample 100个负样本,与test/valid中的正例进行rank。
为了进一步节省test的时间,采用了两种test方式:每个batch共享100个negs;整个test共享100个negs。
接下来,先简单介绍一baseline文件(“数据实战派”后台回复“OAG-BERT”获取下载链接)。
如上图所示,其中,cogdl——这是唐杰老师团队开发的一个工具包,包含了OAG-BERT模型。
主要的代码则分别是:finetune.py--finetune的代码,包括训练和test/valid;finetune_config.py--配置代码,包括batch_size等;modeling.py-- loss及MLP;load_data.py--data处理以及token化。
首先是finetune.py代码,分为两个部分先进行load model & data,再进行define loss。
if __name__ == "__main__":
# create model
tokenizer, model = oagbert("./saved/oagbert-v2")
model = model.cuda()
projection = MLP(config["output_dim"]).cuda()
# load data
data_loader = data_loader(model)
# paper-experts
train_data, valid_data, test_data = data_loader.train_data, data_loader.valid_data, data_loader.test_data
print("train_data_length", len(train_data))
print("valid_data_length", len(valid_data))
print("test_data_length", len(test_data))
paper_infos = data_loader.paper_infos
experts_infos = data_loader.experts_infos
print("experts_infos.length", len(experts_infos.keys()))
train_batches = data_loader.get_batch(train_data, batch_size)
valid_batches = data_loader.get_batch(valid_data, 10*batch_size)
test_batches = data_loader.get_batch(test_data, 10*batch_size)
print("train_batches", len(train_batches))
print("valid_batches", len(valid_batches))
print("test_batches", len(test_batches))
# infoNCE loss
criterion = infoNCE().cuda()
optimizer = torch.optim.Adam([{'params':model.parameters()},{'params': projection.parameters()}], lr=config["learning_rate"])
model.train()
projection.train()
best_mrr = -1
patience = 0
接下来是详细的训练代码:
# finetuning
for epoch in range(epochs):
random.shuffle(train_batches)
batch_loss = []
batch_num = 0
for batch in train_batches:
batch_num += 1
# get anchor pos neg
anchor, pos, neg = data_loader.generate_batch_data(paper_infos, experts_infos, batch, config["neg_num"])
anchor_tokens = data_loader.get_batch_tokens(anchor, "anchor")
pos_tokens = data_loader.get_batch_tokens(pos, "pos")
neg_tokens = data_loader.get_batch_tokens(neg, "neg")
anchor_emb = get_batch_embed(anchor_tokens, "anchor")
pos_emb = get_batch_embed(pos_tokens, "pos")
neg_emb = get_batch_embed(neg_tokens, "neg")
# add MLP
# infoNCE loss
loss = criterion(projection(anchor_emb), projection(pos_emb), projection(neg_emb))
print("loss...", loss.item())
# compute gradient and do Adam step
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch_num > 1 and batch_num % valid_step == 0:
print("evalute.......")
t_valid = time()
mrr = evaluate(model, valid_batches, data_loader, paper_infos, experts_infos)
print("time for valid....", time() - t_valid)
print("Epoch:{} batch:{} loss:{} mrr:{}".format(epoch, batch_num, loss.item(), mrr))
if mrr > best_mrr:
best_mrr = mrr
# save model
torch.save(model.state_dict(), output_dir + "oagbert")
print("Best Epoch:{} batch:{} loss:{} mrr:{}".format(epoch, batch_num, loss.item(), mrr))
else:
patience += 1
if patience > config["patience"]:
print("Best Epoch:{} batch:{} loss:{} mrr:{}".format(epoch, batch_num, loss.item(), mrr))
model.train()
projection.train()
以上便是整个finetune的大体代码。
接下来,get embeddings的代码如下:
def get_batch_embed(batch_tokens, flag):
# get embed
batch_embed = []
for tokens in batch_tokens:
for p_id, token in tokens.items():
if flag == "anchor":
input_ids, input_masks, token_type_ids, masked_lm_labels, position_ids, position_ids_second, maksed_positions, num_spans = token
_, pooled_output = model.bert.forward(
input_ids=torch.LongTensor(input_ids).unsqueeze(0).cuda(),
token_type_ids=torch.LongTensor(token_type_ids).unsqueeze(0).cuda(),
attention_mask=torch.LongTensor(input_masks).unsqueeze(0).cuda(),
output_all_encoded_layers=False,
checkpoint_activations=False,
position_ids=torch.LongTensor(position_ids).unsqueeze(0).cuda(),
position_ids_second=torch.LongTensor(position_ids_second).unsqueeze(0).cuda())
batch_embed.append(pooled_output)
else:
# experts embedding
# interests process
interests = token["interests"]
#print("interests", interests)
input_ids, input_masks, token_type_ids, masked_lm_labels, position_ids, position_ids_second, maksed_positions, num_spans = interests
if input_ids != []:
_, pooled_output_expert_interests = model.bert.forward(
input_ids=torch.LongTensor(input_ids).unsqueeze(0).cuda(),
token_type_ids=torch.LongTensor(token_type_ids).unsqueeze(0).cuda(),
attention_mask=torch.LongTensor(input_masks).unsqueeze(0).cuda(),
output_all_encoded_layers=False,
checkpoint_activations=False,
position_ids=torch.LongTensor(position_ids).unsqueeze(0).cuda(),
position_ids_second=torch.LongTensor(position_ids_second).unsqueeze(0).cuda())
else:
pooled_output_expert_interests = None
# pub_info of experts process
pub_info = token["pub_info"]
pub_info_embed = []
for pid, p_token in pub_info.items():
input_ids, input_masks, token_type_ids, masked_lm_labels, position_ids, position_ids_second, maksed_positions, num_spans = p_token
if input_ids != []:
_, pooled_output_expert_pub = model.bert.forward(
input_ids=torch.LongTensor(input_ids).unsqueeze(0).cuda(),
token_type_ids=torch.LongTensor(token_type_ids).unsqueeze(0).cuda(),
attention_mask=torch.LongTensor(input_masks).unsqueeze(0).cuda(),
output_all_encoded_layers=False,
checkpoint_activations=False,
position_ids=torch.LongTensor(position_ids).unsqueeze(0).cuda(),
position_ids_second=torch.LongTensor(position_ids_second).unsqueeze(0).cuda())
pub_info_embed.append(pooled_output_expert_pub)
# merge interests_embed and pub_info_embed
# here, we use torch.mean() for merge
# check...
if pooled_output_expert_interests == None:
if len(pub_info_embed) != 0:
pooled_output_expert_cat = torch.cat(pub_info_embed)
else:
pooled_output_expert_cat = None
else:
if len(pub_info_embed) != 0:
pooled_output_expert_cat = torch.cat((pooled_output_expert_interests, torch.cat(pub_info_embed)), 0)
else:
pooled_output_expert_cat = pooled_output_expert_interests
pooled_output_expert_final = torch.mean(pooled_output_expert_cat, 0).view(1, config['output_dim'])
batch_embed.append(pooled_output_expert_final)
return torch.cat(batch_embed, 0)
test/valid的部分如下:
def evaluate(model, valid_batches, data_loader, paper_infos, experts_infos):
model.eval()
mrr = 0.0
total_count = 0
with torch.no_grad():
"""
整个test公用一个negs
"""
negs = data_loader.generate_negs_data(paper_infos, experts_infos, config["Negs"])
neg_tokens = data_loader.get_batch_tokens(negs, "neg")
neg_emb_candidates = get_batch_embed(neg_tokens, "neg")
"""
share batch negs
"""
for batch in valid_batches:
anchor, pos, _ = data_loader.generate_batch_data_test(paper_infos, experts_infos, batch, config["Negs"])
# use too much time
anchor_tokens = data_loader.get_batch_tokens(anchor, "anchor")
pos_tokens = data_loader.get_batch_tokens(pos, "pos")
# use too much time
anchor_emb = get_batch_embed(anchor_tokens, "anchor")
pos_emb = get_batch_embed(pos_tokens, "pos")
neg_emb = neg_emb_candidates.repeat(len(batch), 1)
# batch share negs
#for batch in valid_batches:
# anchor, pos, negs = data_loader.generate_batch_data_test(paper_infos, experts_infos, batch, config["Negs"])
# # use too much time
# tt = time()
# anchor_tokens = data_loader.get_batch_tokens(anchor, "anchor")
# print("time...", time() - tt)
# tt = time()
# pos_tokens = data_loader.get_batch_tokens(pos, "pos")
# print("time...", time() - tt)
# tt = time()
# neg_tokens = data_loader.get_batch_tokens(negs, "neg")
# print("time...", time() - tt)
# # use too much time
# tt = time()
# anchor_emb = get_batch_embed(anchor_tokens, "anchor")
# print("time...", time() - tt)
# tt = time()
# pos_emb = get_batch_embed(pos_tokens, "pos")
# print("time...", time() - tt)
# tt = time()
# neg_emb_candidates = get_batch_embed(neg_tokens, "neg")
# print("time...", time() - tt)
# neg_emb = neg_emb_candidates.repeat(len(batch), 1)
# anchor & pos_embed
anchor_emb = F.normalize(anchor_emb.view(-1, 1, dim), p=2, dim=2)
pos_emb = F.normalize(pos_emb.view(-1, 1, dim), p=2, dim=2)
neg_emb = F.normalize(neg_emb.view(-1, config["Negs"], dim), p=2, dim=2)
pos_score = torch.bmm(anchor_emb, pos_emb.transpose(1, 2)) # B*1*1
neg_score = torch.bmm(anchor_emb, neg_emb.transpose(1, 2)) # B*1*Negs
# logits:B*(1+Negs)
logits = torch.cat([pos_score, neg_score], dim=2).squeeze()
logits = logits.cpu().numpy()
for i in range(batch_size):
total_count += 1
logits_single = logits[i]
rank = np.argsort(-logits_single)
true_index = np.where(rank==0)[0][0]
mrr += np.divide(1.0, true_index+1)
mrr /= total_count
return mrr
介绍完finetune.py的代码,来看finetune_config.py,即配置代码,包括batch_size下图显示的部分等。
config = {
"train_ratio": 0.8,
"valid_ratio": 0.1,
"batch_size": 2,
"epochs": 10,
"output_dim": 768,
"neg_num": 2,
"learning_rate": 2e-5,
"valid_step": 5000,
"Negs": 100,
"patience": 5,
}
然后是modeling.py,包括 loss及MLP,如下图所示:
# infoNCE loss
class infoNCE(nn.Module):
def __init__(self):
super(infoNCE, self).__init__()
self.T = 0.07
self.cross_entropy = nn.CrossEntropyLoss().cuda()
def forward(self, query, pos, neg):
query = F.normalize(query.view(batch_size, 1, dim), p=2, dim=2)
pos = F.normalize(pos.view(batch_size, 1, dim), p=2, dim=2)
neg = F.normalize(neg.view(batch_size, K, dim), p=2, dim=2)
pos_score = torch.bmm(query, pos.transpose(1, 2)) # B*1*1
neg_score = torch.bmm(query, neg.transpose(1, 2)) # B*1*K
# logits:B*(K+1)
logits = torch.cat([pos_score, neg_score], dim=2).squeeze()
logits /= self.T
labels = torch.zeros(logits.shape[0], dtype=torch.long).cuda()
info_loss = self.cross_entropy(logits, labels)
return info_loss
class MLP(nn.Module):
def __init__(self, in_dim):
super(MLP, self).__init__()
self.projection = nn.Sequential(
nn.Linear(in_dim, in_dim),
nn.ReLU(),
nn.Linear(in_dim, in_dim)
)
def forward(self, x):
x = self.projection(x)
return x
第四个部分是load_data.py,包括get paper-expert pairs、划分数据集等。
get train, valid, test data form training_set
"""
with open(self.data_dir+'training_set/results.json', 'r') as f:
self.raw_data_dict = json.load(f)
# train_data, valid_data, test_data
self.train_data_dict, self.valid_data_dict, self.test_data_dict = self.get_examples(self.raw_data_dict)
print("Train:{} Valid:{} Test:{}".format(len(self.train_data_dict.keys()), len(self.valid_data_dict.keys()), len(self.test_data_dict.keys())))
self.train_data = self.get_data_pairs(self.train_data_dict)
self.valid_data = self.get_data_pairs(self.valid_data_dict)
self.test_data = self.get_data_pairs(self.test_data_dict)
在get pairs之后,如下是get paper infos、get expert infos的代码部分:
def load_data(self):
"""
get paper info
"""
with open(self.data_dir+'training_set/publications.json', 'r') as f:
self.raw_paper_infos = json.load(f)
print("self.raw_paper_infos", len(self.raw_paper_infos))
self.paper_infos = self.get_papers(self.raw_paper_infos)
print("self.paper_infos", len(self.paper_infos.keys()))
"""
get experts info
"""
with open(self.data_dir+'experts/experts0.json', 'r') as f:
self.raw_experts_0 = json.load(f)
print("self.raw_experts_0", len(self.raw_experts_0))
self.experts_infos_0 = self.get_experts(self.raw_experts_0)
print("self.experts_infos_0", len(list(self.experts_infos_0.keys())))
with open(self.data_dir+'experts/experts1.json', 'r') as f:
self.raw_experts_1 = json.load(f)
print("self.raw_experts_1", len(self.raw_experts_1))
self.experts_infos_1 = self.get_experts(self.raw_experts_1)
print("self.experts_infos_1", len(list(self.experts_infos_1.keys())))
with open(self.data_dir+'experts/experts2.json', 'r') as f:
self.raw_experts_2 = json.load(f)
print("self.raw_experts_2", len(self.raw_experts_2))
self.experts_infos_2 = self.get_experts(self.raw_experts_2)
print("self.experts_infos_2", len(list(self.experts_infos_2.keys())))
with open(self.data_dir+'experts/experts3.json', 'r') as f:
self.raw_experts_3 = json.load(f)
print("self.raw_experts_3", len(self.raw_experts_3))
self.experts_infos_3 = self.get_experts(self.raw_experts_3)
print("self.experts_infos_3", len(list(self.experts_infos_3.keys())))
with open(self.data_dir+'experts/experts4.json', 'r') as f:
self.raw_experts_4 = json.load(f)
print("self.raw_experts_4", len(self.raw_experts_4))
self.experts_infos_4 = self.get_experts(self.raw_experts_4)
print("self.experts_infos_4", len(list(self.experts_infos_4.keys())))
self.experts_infos = {**self.experts_infos_0, **self.experts_infos_1, **self.experts_infos_2, **self.experts_infos_3, **self.experts_infos_4}
print("self.experts_infos", len(list(self.experts_infos.keys())))
load_data.py还包括get batch data 。
def generate_batch_data_test(self, paper_infos, experts_infos, batch, neg_num):
batch_infos_anchor = []
batch_infos_pos = []
batch_infos_neg = []
# generate anchor, pos, neg
batch_experts = []
for pid, eid in batch:
p_infos = paper_infos[pid]
e_infos = experts_infos[eid]
# build anchor, pos, neg
# anchor
anchor_infos = {}
if pid in anchor_infos:
print("repeat pid....")
else:
anchor_infos[pid] = p_infos
batch_infos_anchor.append(anchor_infos)
# pos
pos_infos = {}
if pid in pos_infos:
print("repeat pid in pos_infos...")
else:
pos_infos[pid] = e_infos
batch_infos_pos.append(pos_infos)
batch_experts.extend(self.train_data_dict[pid])
# batch share negs
# random.sample K negs
#print("batch_experts", batch_experts)
negs_id = random.sample(list(set(experts_infos.keys()) - set(batch_experts)), neg_num)
#print("negs_id", negs_id)
negs_infos = {}
for neg in negs_id:
if neg in negs_infos:
print("repeat negs in negs_info....")
else:
negs_infos[neg] = experts_infos[neg]
if experts_infos[neg]["interests"] == "" and experts_infos[neg]["pub_info"] == {}:
print("experts_empty", experts_infos[neg])
batch_infos_neg.append(negs_infos)
return batch_infos_anchor, batch_infos_pos, batch_infos_neg
得到sequence后,下一步是token化:
def get_batch_tokens(self, infos, flag):
batch_tokens = []
for info in infos:
tokens_dict = {}
for p_id, p_info in info.items():
# get tokens
tokens = self.build_bert_inputs(p_info, flag)
#print("tokens...", tokens)
if p_id in tokens_dict:
print("repeat p_id.....")
else:
tokens_dict[p_id] = tokens
batch_tokens.append(tokens_dict)
return batch_tokens
def build_bert_inputs(self, p_info, flag):
if flag == "anchor":
# title & abstract & keywords
if p_info.get("abstract", None) != None:
abstract = p_info["abstract"]
else:
abstract = ""
if p_info.get("title", None) != None:
title = p_info["title"]
else:
title = ""
if p_info.get("keywords", None) != None:
keywords = p_info["keywords"]
else:
keywords = ""
return self.oagbert.build_inputs(title=title, abstract=abstract, concepts=keywords)
elif flag == "pos" or flag == "neg":
# experts
if p_info.get("interests", None) != None:
interests = p_info["interests"]
else:
interests = ""
e_tokens = self.oagbert.build_inputs(title=interests, abstract="")
# prcoess experts'pub infos
expert_pub_infos = p_info["pub_info"]
pub_tokens = {}
for pid, expert_pub_info in expert_pub_infos.items():
title = expert_pub_info['title']
abstract = expert_pub_info['abstract']
keywords_list = expert_pub_info['keywords']
keywords = ""
if len(keywords_list) != 0:
for word in keywords_list:
if word == keywords_list[0]:
keywords = word
else:
keywords = keywords + ' ' + word
p_tokens = self.oagbert.build_inputs(title=title, abstract=abstract, concepts=keywords)
if pid in pub_tokens:
print("repeat pid....")
else:
pub_tokens[pid] = p_tokens
tokens = {"interests": e_tokens, "pub_info": pub_tokens}
return tokens
else:
raise Exception("undefine flag")
return
以上便是完整的代码解读。目前比赛仍在招募选手中,添加大赛小助手 —— 微信号:futurelab001,发送数字 “1” 加入【2021 未来杯 - 智谱人工智能科技探索大赛报名交流群】,群内可以进行参赛问题解答、组队邀约、选手交流等。