医学领域的文献库中蕴含了丰富的疾病诊断和治疗信息,如何高效地从海量文献中提取关键信息,进行疾病诊断和治疗推荐,对于临床医生和研究人员具有重要意义。
机器通过对论文摘要等信息的理解,判断该论文是否属于医学领域的文献。
任务示例:
输入:
论文信息,格式如下:
Inflammatory Breast Cancer: What to Know About This Unique, Aggressive
Breast Cancer.,[Arjun Menta, Tamer M Fouad, Anthony Lucci, Huong Le-Petross, Michael
C Stauder, Wendy A Woodward, Naoto T Ueno, Bora Lim],Inflammatory breast cancer (IBC) is a rare form of breast cancer that
accounts for only 2% to 4% of all breast cancer cases. Despite its low
incidence, IBC contributes to 7% to 10% of breast cancer caused
mortality. Despite ongoing international efforts to formulate better
diagnosis, treatment, and research, the survival of patients with IBC
has not been significantly improved, and there are no therapeutic
agents that specifically target IBC to date. The authors present a
comprehensive overview that aims to assess the present and new
management strategies of IBC.,Breast changes; Clinical trials; Inflammatory breast cancer;
Trimodality care.
输出:
是(1)
训练集与测试集数据为CSV格式文件,各字段分别是标题、作者、摘要、关键词。
下载链接
本次竞赛的评价标准采用F1_score,分数越高,效果越好。
解题思路
文献领域分类
针对文本分类任务,可以提供两种实践思路,一种是使用传统的特征提取方法(如TF-IDF/BOW)结合机器学习模型,另一种是使用预训练的BERT模型进行建模。
[CLS]就是classification的意思,可以理解为用于下游的分类任务。
主要用于以下两种任务:
单文本分类任务:对于文本分类任务,BERT模型在文本前插入一个[CLS]符号,并将该符号对应的输出向量作为整篇文本的语义表示,用于文本分类,如下图所示。可以理解为:与文本中已有的其它字/词相比,这个无明显语义信息的符号会更“公平”地融合文本中各个字/词的语义信息。
在模型设计中思路就体现为我们取出文本数据经过向量化后的[CLS]向量,然后经过二分类预测层得到最终的结果
outputs = self.bert(**src).last_hidden_state[:, 0, :]
self.predictor(outputs)
self.predictor = nn.Sequential(nn.Linear(768, 256),nn.ReLU(),nn.Linear(256, 1),nn.Sigmoid())
提交链接
实现代码
# 导入pandas用于读取表格数据
import pandas as pd# 导入BOW(词袋模型),可以选择将CountVectorizer替换为TfidfVectorizer(TF-IDF(词频-逆文档频率)),注意上下文要同时修改,亲测后者效果更佳
from sklearn. import CountVectorizer# 导入LogisticRegression回归模型
from sklearn.linear_model import LogisticRegression# 过滤警告消息
from warnings import simplefilter
ptions import ConvergenceWarning
simplefilter("ignore", category=ConvergenceWarning)# 读取数据集
train = pd.read_csv('/home/aistudio/data/data231041/train.csv')
train['title'] = train['title'].fillna('')
train['abstract'] = train['abstract'].fillna('')test = pd.read_csv('/home/aistudio/data/data231041/testB.csv')
test['title'] = test['title'].fillna('')
test['abstract'] = test['abstract'].fillna('')# 提取文本特征,生成训练集与测试集
train['text'] = train['title'].fillna('') + ' ' + train['author'].fillna('') + ' ' + train['abstract'].fillna('')+ ' ' + train['Keywords'].fillna('')
test['text'] = test['title'].fillna('') + ' ' + test['author'].fillna('') + ' ' + test['abstract'].fillna('')vector = CountVectorizer().fit(train['text'])
train_vector = ansform(train['text'])
test_vector = ansform(test['text'])# 引入模型
model = LogisticRegression()# 开始训练,这里可以考虑修改默认的batch_size与epoch来取得更好的效果
model.fit(train_vector, train['label'])# 利用模型对测试集label标签进行预测
test['label'] = model.predict(test_vector)
test['Keywords'] = test['title'].fillna('')
test[['uuid','Keywords','label']].to_csv('submit_task1.csv', index=None)
提交结果
0.67116
实现代码
#import 相关库
#导入前置依赖
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
from pathlib import Pathbatch_size = 16
# 文本的最大长度
text_max_length = 128
# 总训练的epochs数,我只是随便定义了个数
epochs = 30
# 学习率
lr = 3e-5
# 取多少训练集的数据作为验证集
validation_ratio = 0.1device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# print(device)
log_per_step = 50dataset_dir = Path("./data")
os.makedirs(dataset_dir) if not ists(dataset_dir) else ''model_dir = Path("./model/bert_checkpoints")# 如果模型目录不存在,则创建一个
os.makedirs(model_dir) if not ists(model_dir) else ''pd_train_data = pd.read_csv('./data/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('./data/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('') + ' ' + pd_train_data['author'].fillna('') + ' ' + pd_train_data['abstract'].fillna('')+ ' ' + pd_train_data['Keywords'].fillna('')
test_data['text'] = test_data['title'].fillna('') + ' ' + test_data['author'].fillna('') + ' ' + test_data['abstract'].fillna('')+ ' ' + pd_train_data['Keywords'].fillna('')
# test_data['Keywords'] = test_data['title'].fillna('')
# test_data['text'] = test_data['title'].fillna('') + ' ' + test_data['author'].fillna('') + ' ' + test_data['abstract'].fillna('')# 从训练集中随机采样测试集
validation_data = pd_train_data.sample(frac=validation_ratio)
train_data = pd_train_data[~pd_train_data.index.isin(validation_data.index)]# 构建Dataset
class MyDataset(Dataset):def __init__(self, mode='train'):super(MyDataset, self).__init__()de = mode# 拿到对应的数据if mode == 'train':self.dataset = train_dataelif mode == 'validation':self.dataset = validation_dataelif mode == 'test':# 如果是测试模式,则返回内容和uuid。拿uuid做target主要是方便后面写入结果。self.dataset = test_dataelse:raise Exception("Unknown mode {}".format(mode))def __getitem__(self, index):# 取第index条data = self.dataset.iloc[index]# 取其内容text = data['text']# 根据状态返回内容de == 'test':# 如果是test,将uuid做为targetlabel = data['uuid']else:label = data['label']# 返回内容和labelreturn text, labeldef __len__(self):return len(self.dataset)train_dataset = MyDataset('train')
validation_dataset = MyDataset('validation')# print(train_dataset.__getitem__(0))#获取Bert预训练模型
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")#接着构造我们的Dataloader。
#我们需要定义一下collate_fn,在其中完成对句子进行编码、填充、组装batch等动作:
def collate_fn(batch):"""将一个batch的文本句子转成tensor,并组成batch。:param batch: 一个batch的句子,例如: [('推文', target), ('推文', target), ...]:return: 处理后的结果,例如:src: {'input_ids': tensor([[ 101, ..., 102, 0, 0, ...], ...]), 'attention_mask': tensor([[1, ..., 1, 0, ...], ...])}target:[1, 1, 0, ...]"""text, label = zip(*batch)text, label = list(text), list(label)# src是要送给bert的,所以不需要特殊处理,直接用tokenizer的结果即可# padding='max_length' 不够长度的进行填充# truncation=True 长度过长的进行裁剪src = tokenizer(text, padding='max_length', max_length=text_max_length, return_tensors='pt', truncation=True)return src, torch.LongTensor(label)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)inputs, targets = next(iter(train_loader))
# print("inputs:", inputs)
# print("targets:", targets)#定义预测模型,该模型由bert模型加上最后的预测层组成
class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()# 加载bert模型self.bert = BertModel.from_pretrained('bert-base-uncased', mirror='tuna')# 最后的预测层self.predictor = nn.Sequential(nn.Linear(768, 256),nn.ReLU(),nn.Linear(256, 1),nn.Sigmoid())def forward(self, src):""":param src: 分词后的推文数据"""# 将src直接序列解包传入bert,因为bert和tokenizer是一套的,所以可以这么做。# 得到encoder的输出,用最前面[CLS]的输出作为最终线性层的输入outputs = self.bert(**src).last_hidden_state[:, 0, :]# 使用线性层来做最终的预测return self.predictor(outputs)model = MyModel()
model = (device)#定义出损失函数和优化器。这里使用Binary Cross Entropy:
criteria = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=lr)# 由于inputs是字典类型的,定义一个辅助函数帮助to(device)
def to_device(dict_tensors):result_tensors = {}for key, value in dict_tensors.items():result_tensors[key] = (device)return result_tensors#定义一个验证方法,获取到验证集的精准率和loss
def validate():model.eval()total_loss = 0.total_correct = 0for inputs, targets in validation_loader:inputs, targets = to_device(inputs), (device)outputs = model(inputs)loss = criteria(outputs.view(-1), targets.float())total_loss += float(loss)correct_num = (((outputs >= 0.5).float() * 1).flatten() == targets).sum()total_correct += correct_numreturn total_correct / len(validation_dataset), total_loss / len(validation_dataset)# 首先将模型调成训练模式
ain()# 清空一下cuda缓存
if torch.cuda.is_available():pty_cache()# 定义几个变量,帮助打印loss
total_loss = 0.
# 记录步数
step = 0# 记录在验证集上最好的准确率
best_accuracy = 0# 开始训练
for epoch in range(epochs):ain()for i, (inputs, targets) in enumerate(train_loader):# 从batch中拿到训练数据inputs, targets = to_device(inputs), (device)# 传入模型进行前向传递outputs = model(inputs)# 计算损失loss = criteria(outputs.view(-1), targets.float())loss.backward()optimizer.step()_grad()total_loss += float(loss)step += 1if step % log_per_step == 0:print("Epoch {}/{}, Step: {}/{}, total loss:{:.4f}".format(epoch+1, epochs, i, len(train_loader), total_loss))total_loss = 0del inputs, targets# 一个epoch后,使用过验证集进行验证accuracy, validation_loss = validate()print("Epoch {}, accuracy: {:.4f}, validation loss: {:.4f}".format(epoch+1, accuracy, validation_loss))torch.save(model, model_dir / f"model_{epoch}.pt")# 保存最好的模型if accuracy > best_accuracy:torch.save(model, model_dir / f"model_best.pt")best_accuracy = accuracy#加载最好的模型,然后进行测试集的预测
model = torch.load(model_dir / f"model_best.pt")
model = model.eval()test_dataset = MyDataset('test')
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, collate_fn=collate_fn)results = []
for inputs, ids in test_loader:outputs = (device))outputs = (outputs >= 0.5).int().flatten().tolist()ids = list()results = results + [(id, result) for result, id in zip(outputs, ids)]test_label = [pair[1] for pair in results]
test_data['label'] = test_label
test_data['Keywords'] = test_data['title'].fillna('')
test_data[['uuid', 'Keywords', 'label']].to_csv('submit_task1.csv', index=None)
提交结果
0.99701
可以发现,Bert预训练模型具备非常强大的文本理解能力。后续可以研究一下如果通过微调提高实验效果。
本文发布于:2024-01-28 03:54:12,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17063852564589.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |