【NLP实践学习阶段二】基于论文摘要的文本分类与关键词抽取挑战赛

基于论文摘要的文本分类与关键词抽取挑战赛

赛事背景

赛题地址:https://challenge.xfyun.cn/topic/info?type=abstract-of-the-paper&option=ssgy&ch=ZuoaKcY

医学领域的文献库中蕴含了丰富的疾病诊断和治疗信息,如何高效地从海量文献中提取关键信息,进行疾病诊断和治疗推荐,对于临床医生和研究人员具有重要意义。

赛事任务

本任务分为两个子任务:

  1. 机器通过对论文摘要等信息的理解,判断该论文是否属于医学领域的文献。
  2. 提取出该论文关键词。

任务一:使用预训练的BERT模型解决文本二分类问题

#导入前置依赖
import os
import pandas as pd
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
# 用于加载bert模型的分词器
from transformers import AutoTokenizer
# 用于加载bert模型
from transformers import BertModel, BertTokenizer
from pathlib import Path
batch_size = 16
# 文本的最大长度
# text_max_length = 128
text_max_length = 256
# 总训练的epochs数,我只是随便定义了个数
epochs = 100
# 学习率
lr = 3e-5
# 取多少训练集的数据作为验证集
validation_ratio = 0.1
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# 每多少步,打印一次loss
log_per_step = 50

# # 数据集所在位置
# dataset_dir = Path("./基于论文摘要的文本分类与关键词抽取挑战赛公开数据")
# os.makedirs(dataset_dir) if not os.path.exists(dataset_dir) else ''

# 模型存储路径
model_dir = Path("./model/bert_checkpoints")
# 如果模型目录不存在,则创建一个
os.makedirs(model_dir) if not os.path.exists(model_dir) else ''

print("Device:", device)
# 读取数据集,进行数据处理
pd_train_data = pd.read_csv('./train.csv')
pd_train_data['title'] = pd_train_data['title'].fillna('')
pd_train_data['abstract'] = pd_train_data['abstract'].fillna('')

test_data = pd.read_csv('./testB.csv')
test_data['title'] = test_data['title'].fillna('')
test_data['abstract'] = test_data['abstract'].fillna('')
pd_train_data['text'] = pd_train_data['title'].fillna('') + '[SEP]' +  pd_train_data['author'].fillna('') + '[SEP]' + pd_train_data['abstract'].fillna('')
test_data['text'] = test_data['title'].fillna('') + '[SEP]' +  test_data['author'].fillna('') + '[SEP]' + test_data['abstract'].fillna('')
for idx, (X_train_i,X_test_i) in enumerate(kf.split(pd_train_data)):
    train_data = pd_train_data.iloc[X_train_i]
    validation_data = pd_train_data.iloc[X_test_i]
    train_dataset = MyDataset('train')
    validation_dataset = MyDataset('validation')
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, collate_fn=collate_fn)
    validation_loader = DataLoader(validation_dataset, batch_size=batch_size, shuffle=False, collate_fn=collate_fn)

    # model = Bert_model()
    model = MyModel()
    model = model.to(device)
    #定义出损失函数和优化器。这里使用Binary Cross Entropy:
    criteria = nn.BCELoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    # 首先将模型调成训练模式
    model.train()

    # 清空一下cuda缓存
    if torch.cuda.is_available():
        torch.cuda.empty_cache()

    # 定义几个变量,帮助打印loss
    total_loss = 0.
    # 记录步数
    step = 0

    # 记录在验证集上最好的准确率
    best_accuracy = 0

    # 开始训练
    for epoch in range(epochs):
        model.train()
        for i, (inputs, targets) in enumerate(train_loader):
            # 从batch中拿到训练数据
            inputs, targets = to_device(inputs), targets.to(device)
            # 传入模型进行前向传递
            outputs = model(inputs)
            # 计算损失
            loss = criteria(outputs.view(-1), targets.float())
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()

            total_loss += float(loss)
            step += 1

            if step % log_per_step == 0:
                print("Epoch {}/{}, Step: {}/{}, total loss:{:.4f}".format(epoch+1, epochs, i, len(train_loader), total_loss))
                total_loss = 0

            del inputs, targets

        # 一个epoch后,使用过验证集进行验证
        accuracy, validation_loss = validate()
        print("Epoch {}, accuracy: {:.4f}, validation loss: {:.4f}".format(epoch+1, accuracy, validation_loss))
        ppp = str(idx)+'my_model_best.pt'
        # 保存最好的模型
        if accuracy > best_accuracy:
            torch.save(model, model_dir / ppp)
            best_accuracy = accuracy
test_label_all_ = []
for i in range(4):
    p = str(i)+'my_model_best.pt'
    model = torch.load(model_dir / p)
    model = model.eval()
    results = []
    for inputs, ids in test_loader:
        outputs = model(inputs.to(device))
        outputs = (outputs >= 0.5).int().flatten().tolist()
        ids = ids.tolist()
        results = results + [(id, result) for result, id in zip(outputs, ids)]
    test_label = [pair[1] for pair in results]
    test_label_all_.append(test_label)
test_data['label'] = np.array(test_label_all_+test_label_all).sum(axis=0)>=5
test_data[['uuid', 'label']].to_csv('submit_task8.csv', index=None)
test_label_all = []
for i in range(5):
    p = str(i)+'Bert_model_best.pt'
    model = torch.load(model_dir / p)
    model = model.eval()
    results = []
    for inputs, ids in test_loader:
        outputs = model(inputs.to(device))
        outputs = (outputs >= 0.5).int().flatten().tolist()
        ids = ids.tolist()
        results = results + [(id, result) for result, id in zip(outputs, ids)]
    test_label = [pair[1] for pair in results]
    test_label_all.append(test_label)

任务二:使用预训练的BERT模型解决关键词提取

# 导入pandas用于读取表格数据
import pandas as pd

# 导入BOW(词袋模型),可以选择将CountVectorizer替换为TfidfVectorizer(TF-IDF(词频-逆文档频率)),注意上下文要同时修改,亲测后者效果更佳
from sklearn.feature_extraction.text import TfidfVectorizer
# 导入Bert模型
from sentence_transformers import SentenceTransformer

# 导入计算相似度前置库,为了计算候选者和文档之间的相似度,我们将使用向量之间的余弦相似度,因为它在高维度下表现得相当好。
from sklearn.metrics.pairwise import cosine_similarity

# 过滤警告消息
from warnings import simplefilter
from sklearn.exceptions import ConvergenceWarning
simplefilter("ignore", category=ConvergenceWarning)

# 读取数据集
test = pd.read_csv('./testB.csv')
test['title'] = test['title'].fillna('')
test['abstract'] = test['abstract'].fillna('')

test['text'] = test['title'].fillna('') + ' ' +test['abstract'].fillna('')

这里使用distiluse-base-multilingual-cased

model = SentenceTransformer(r'xlm-r-distilroberta-base-paraphrase-v1')

提取关键词:获取文本内容的embedding,同与文本标题的embedding进行比较,文章的关键词往往与标题内容有很强的相似性,使用余弦相似度进行度量,选取topk个作为关键词。

test_words = []
for row in tqdm(test.iterrows()):
    # 读取第每一行数据的标题与摘要并提取关键词
    # 修改n_gram_range来改变结果候选词的词长大小。例如,如果我们将它设置为(3,3),那么产生的候选词将是包含3个关键词的短语。
    n_gram_range = (1,3)
    # 这里我们使用TF-IDF算法来获取候选关键词 
    count = TfidfVectorizer(ngram_range=n_gram_range, stop_words='english').fit([row[1].text])
    candidates = count.get_feature_names_out()
    # 将文本标题以及候选关键词/关键短语转换为数值型数据(numerical data)。我们使用BERT来实现这一目的
    title_embedding = model.encode([row[1].title])
    
    candidate_embeddings = model.encode(candidates)
    
    # 通过修改这个参数来更改关键词数量
    top_n = 40
    # 利用文章标题进一步提取关键词
    distances = cosine_similarity(title_embedding, candidate_embeddings)
    keywords = [candidates[index] for index in distances.argsort()[0][-top_n:]]
    
    if len( keywords) == 0:
         keywords = ['A', 'B']
    test_words.append('; '.join( keywords))
    

与第一个任务的结果合并,提取结果。

sub = pd.read_csv('submit_task8.csv')
sub['Keywords'] = test_words
sub[['uuid', 'Keywords', 'label']].to_csv('submit_task8.csv', index=None)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值