NLP比赛笔记(基于论文摘要的文本分类与关键词抽取挑战赛)

本文涉及的产品
NLP 自学习平台,3个模型定制额度 1个月
NLP自然语言处理_高级版,每接口累计50万次
NLP自然语言处理_基础版,每接口每天50万次
简介: NLP比赛笔记(基于论文摘要的文本分类与关键词抽取挑战赛)

任务一:

比赛链接:

2023 iFLYTEK A.I.开发者大赛-讯飞开放平台

任务描述与分析:

任务一为通过论文作者,标题和关键词确定文章类型,我们主要目标是应尽量突出那些有鲜明文章特色的词语,来确保分类尽可能准确。

baseline模型(基于BOW特征提取的方法)

# 导入pandas用于读取表格数据
import pandas as pd
# 导入BOW(词袋模型),可以选择将CountVectorizer替换为TfidfVectorizer(TF-IDF(词频-逆文档频率)),注意上下文要同时修改,亲测后者效果更佳
from sklearn.feature_extraction.text import CountVectorizer
# 导入LogisticRegression回归模型
from sklearn.linear_model import LogisticRegression
# 过滤警告消息
from warnings import simplefilter
from sklearn.exceptions 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 = vector.transform(train['text'])
test_vector = vector.transform(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)

基于TF-IDF特征提取的方法(0.67116→0.76324)

baseline模型基于BOW方法提取特征数据,简单来说BOW是一种统计某个词在文章中出现次数的方法,这样的缺陷是有些不是很重要的日常词所占的权重会很大,这样当然不利于我们的模型性能。

在浏览文档后,我决定使用基于TF-IDF的方法,TF-IDF计算权重的方法是通过文档频率和逆文档频率相乘得到的。

文档频率:某个词的文档频率为

逆文档频率则为

TF-IDF值则为文档频率乘以逆文档频率

这样每个词在每个文档中都会有一个TF-IDF值,由于像and这种常用词的逆文档频率会比其他特征词低,这样可以有效提高模型性能。

提交后分数上涨了10%

# 导入pandas用于读取表格数据
import pandas as pd
# 导入BOW(词袋模型),可以选择将CountVectorizer替换为TfidfVectorizer(TF-IDF(词频-逆文档频率)),注意上下文要同时修改,亲测后者效果更佳
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
# 导入LogisticRegression回归模型
from sklearn.linear_model import LogisticRegression
# 过滤警告消息
from warnings import simplefilter
from sklearn.exceptions import ConvergenceWarning
simplefilter("ignore", category=ConvergenceWarning)
# 读取数据集
train = pd.read_csv('data/train.csv')
stops =[i.strip() for i in open(r'data/stop.txt',encoding='utf-8').readlines()]
train['title'] = train['title'].fillna('')
train['abstract'] = train['abstract'].fillna('')
test = pd.read_csv('data/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 = TfidfVectorizer(stop_words=stops).fit(train['text'])
train_vector = vector.transform(train['text'])
test_vector = vector.transform(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)

使用bert预处理模型的方法(0.76324→0.99751)

bert模型是一种预训练+微调的语言模型,它有一些独到优势:

1.无需人工标注,这样可以节省人力,同时可以更好地让模型在大量数据上训练,再在下游针对具体的自然语言处理任务进行微调

2.Attention机制,使得模型更加注重于关键词语,为关键词语赋予更多权重,有效提高模型性能

3.新增两个预训练任务,MLM和NSP任务,为模型能更好地处理下游具体任务提供了保障

# 导入前置依赖
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 Path
batch_size = 16
# 文本的最大长度
text_max_length = 128
# 总训练的epochs数,我只是随便定义了个数
epochs = 50
# 学习率
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("data")
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('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('')
# 从训练集中随机采样测试集
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__()
        self.mode = mode
        # 拿到对应的数据
        if mode == 'train':
            self.dataset = train_data
        elif mode == 'validation':
            self.dataset = validation_data
        elif mode == 'test':
            # 如果是测试模式,则返回内容和uuid。拿uuid做target主要是方便后面写入结果。
            self.dataset = test_data
        else:
            raise Exception("Unknown mode {}".format(mode))
    def __getitem__(self, index):
        # 取第index条
        data = self.dataset.iloc[index]
        # 取其内容
        text = data['text']
        # 根据状态返回内容
        if self.mode == 'test':
            # 如果是test,将uuid做为target
            label = data['uuid']
        else:
            label = data['label']
        # 返回内容和label
        return text, label
    def __len__(self):
        return len(self.dataset)
train_dataset = MyDataset('train')
validation_dataset = MyDataset('validation')
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 = model.to(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] = value.to(device)
    return result_tensors
# 定义一个验证方法,获取到验证集的精准率和loss
def validate():
    model.eval()
    total_loss = 0.
    total_correct = 0
    for inputs, targets in validation_loader:
        inputs, targets = to_device(inputs), targets.to(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_num
    return total_correct / len(validation_dataset), total_loss / len(validation_dataset)
# 首先将模型调成训练模式
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))
    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 = 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_data['label'] = test_label
test_data['Keywords'] = test_data['title'].fillna('')
test_data[['uuid', 'Keywords', 'label']].to_csv('submit_task1.csv', index=None)

深度学习Topline(0.99751→1)

深度学习一个Topline模型的理论和bert模型相似,只是在bert模型的基础上有了些许调整

其大概步骤为

1.数据预处理,将文本数据转化为对应模型的数字序列,并生成掩码ID,最后将它们转化为torch张量,以便输入神经网络中进行训练

2.配置神经网络层和参数,进行训练

3.将训练结果整理保存

具体代码和教程请参考

大语言模型Topline(0.99751→1)

随着ChatGPT的出现,我们对大语言模型这个词已经不陌生了,单纯的大语言模型,我们可以简单理解为一个由庞大的语料库训练成的能理解人类语言基本模式的模型,但是要完成一些具体任务,我们还需要进行对应的操作:指令微调和RLHF(一个强化学习过程),指令微调可以让我们将大语言模型训练成更适合我们需要的样子,例如医疗,法律等等方面,而RLHF则让模型在使用过程中能够不断优化,具体的用大语言处理文本分类任务的教程请参考

常见问题与解决方法

bert模型本地无法运行

由于bert模型所需环境配置较高,可以租用算力来跑程序,可以进入autodl官网,注册后根据需要租用

进入控制台,打开JupyterLab

再在终端导入所需库后上传相关数据集就可以运行程序了

比赛感受

1.认识了很多大佬,能够互相学习

2.了解了NLP任务比赛流程

3.学习了一些处理NLP任务的常用模型和方法

4.获得了一些解决问题的新方法,如跑不动程序去租用算力(以前从来没遇到过跑不动程序的情况)

5.了解了大语言模型的工作流程,对学习路径有了更明确的规划

相关文章
|
3月前
|
自然语言处理 BI 数据处理
自然语言处理 Paddle NLP - 基于预训练模型完成实体关系抽取
自然语言处理 Paddle NLP - 基于预训练模型完成实体关系抽取
108 1
|
4月前
|
机器学习/深度学习 数据采集 自然语言处理
自然语言处理中的文本分类技术深度解析
【7月更文挑战第31天】文本分类作为自然语言处理领域的重要技术之一,正不断推动着智能信息处理的发展。随着深度学习技术的不断成熟和计算资源的日益丰富,我们有理由相信,未来的文本分类技术将更加智能化、高效化、普适化,为人类社会带来更加便捷、精准的信息服务。
|
3月前
|
机器学习/深度学习 数据采集 自然语言处理
【NLP-新闻文本分类】处理新闻文本分类所有开源解决方案汇总
汇总了多个用于新闻文本分类的开源解决方案,包括TextCNN、Bert、LSTM、CNN、Transformer以及多模型融合方法。
51 1
|
3月前
|
机器学习/深度学习 存储 自然语言处理
【NLP-新闻文本分类】3 Bert模型的对抗训练
详细介绍了使用BERT模型进行新闻文本分类的过程,包括数据集预处理、使用预处理数据训练BERT语料库、加载语料库和词典后用原始数据训练BERT模型,以及模型测试。
64 1
|
3月前
|
机器学习/深度学习 数据采集 监控
【NLP-新闻文本分类】2特征工程
本文讨论了特征工程的重要性和处理流程,强调了特征工程在机器学习中的关键作用,并概述了特征工程的步骤,包括数据预处理、特征提取、特征处理、特征选择和特征监控。
31 1
|
3月前
|
数据采集 自然语言处理 数据挖掘
【NLP-新闻文本分类】1 数据分析和探索
文章提供了新闻文本分类数据集的分析,包括数据预览、类型检查、缺失值分析、分布情况,指出了类别不均衡和句子长度差异等问题,并提出了预处理建议。
47 1
|
3月前
|
机器学习/深度学习 自然语言处理 数据挖掘
【NLP】深度学习的NLP文本分类常用模型
本文详细介绍了几种常用的深度学习文本分类模型,包括FastText、TextCNN、DPCNN、TextRCNN、TextBiLSTM+Attention、HAN和Bert,并提供了相关论文和不同框架下的实现源码链接。同时,还讨论了模型的优缺点、适用场景以及一些优化策略。
94 1
|
3月前
|
机器学习/深度学习 自然语言处理 算法
nlp文本提取关键词
8月更文挑战第21天
45 0
|
3月前
|
机器学习/深度学习 自然语言处理 PyTorch
【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案--6 提分方案
在讯飞英文学术论文分类挑战赛中的提分技巧和实现方法,包括数据增强、投票融合、伪标签等策略,以及加快模型训练的技巧,如混合精度训练和使用AdamW优化器等。
40 0
|
3月前
|
数据采集 机器学习/深度学习 存储
【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–5 Bert 方案
在讯飞英文学术论文分类挑战赛中使用BERT模型进行文本分类的方法,包括数据预处理、模型微调技巧、长文本处理策略以及通过不同模型和数据增强技术提高准确率的过程。
39 0

热门文章

最新文章