中文句子关系推断

中文,句子,关系,推断 · 浏览次数 : 5

小编点评

## 摘要内容 此摘要内容包含 HuggingFace自然语言处理教程,介绍如何基于BERT中文模型进行中文句子关系推断任务实战。 **主要内容包括:** * HuggingFace自然语言处理教程简介 * 基于BERT中文模型的任务实战介绍 * 中文句子关系推断任务介绍 * 实验代码链接 * 归纳总结 **其他信息:** * 此摘要内容包含 HuggingFace自然语言处理教程的第9章,中文句子关系推断.py * 实验代码链接包含该教程的第9章代码 * 归纳总结内容包含正确率、总数量以及正确率的平均值 * 可以带简单的排版进行摘要

正文

本文通过ChnSentiCorp数据集介绍了中文句子关系推断任务过程,主要使用预训练语言模型bert-base-chinese直接在测试集上进行测试,也简要介绍了模型训练流程,不过最后没有保存训练好的模型。

一.任务简介和数据集
通过模型来判断2个句子是否连续,使用ChnSentiCorp数据集,不清楚的可以参考中文情感分类介绍。句子关系推断数据集样例如下所示:

二.准备数据集
1.使用编码工具
编码工具详细介绍可参考使用编码工具。如下所示:

def load_encode_tool(pretrained_model_name_or_path):
    token = BertTokenizer.from_pretrained(Path(f'{pretrained_model_name_or_path}'))
    return token
if __name__ == '__main__':
    # 测试编码工具
    pretrained_model_name_or_path = r'L:\20230713_HuggingFaceModel\bert-base-chinese'
    token = load_encode_tool(pretrained_model_name_or_path)
    print(token)
    # 测试编码句子
    out = token.batch_encode_plus(
        batch_text_or_text_pairs=[('不是一切大树,''都被风暴折断。'),('不是一切种子,''都找不到生根的土壤。')],
        truncation=True,
        padding='max_length',
        max_length=18,
        return_tensors='pt',
        return_length=True, # 返回长度
    )
    # 查看编码输出
    for k, v in out.items():
        print(k, v.shape)
    print(token.decode(out['input_ids'][0]))
    print(token.decode(out['input_ids'][1]))

输出结果如下所示:

BertTokenizer(name_or_path='L:\20230713_HuggingFaceModel\bert-base-chinese', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=False, padding_side='right', truncation_side='right', special_tokens={'unk_token''[UNK]''sep_token''[SEP]''pad_token''[PAD]''cls_token''[CLS]''mask_token''[MASK]'}, clean_up_tokenization_spaces=True)
input_ids torch.Size([2, 18])
token_type_ids torch.Size([2, 18])
length torch.Size([2])
attention_mask torch.Size([2, 18])
[CLS] 不 是 一 切 大 树 , [SEP] 都 被 风 暴 折 断 。 [SEP] [PAD]
[CLS] 不 是 一 切 种 子 , [SEP] 都 找 不 到 生 根 的 土 [SEP]

编码结果如下所示:

2.定义数据集
首先在__init__()中加载ChnSentiCorp数据集,然后过滤掉小于40个字的句子。在__getitem__()中将一句话分隔为各20个字的两句话,并且有50%概率把后半句替换为无关的句子,这样就构建完成本文所需要数据集。

class Dataset(torch.utils.data.Dataset):
    def __init__(self, split):
        pretrained_model_name_or_path = r'L:\20230713_HuggingFaceModel\ChnSentiCorp'
        dataset = load_from_disk(pretrained_model_name_or_path)[split]
        # 过滤长度大于40的句子
        self.dataset = dataset.filter(lambda data: len(data['text']) > 40)
    def __len__(self):
        return len(self.dataset)
    def __getitem__(self, i):
        text = self.dataset[i]['text']
        # 将一句话切分为前半句和后半句
        sentence1 = text[:20]
        sentence2 = text[20:40]
        # 随机整数,取值为0和1
        label = random.randint(0, 1)
        # 有一半概率把后半句替换为无关的句子
        if label == 1:
            j = random.randint(0, len(self.dataset) - 1) # 随机取出一句话
            sentence2 = self.dataset[j]['text'][20:40] # 取出后半句
        return sentence1, sentence2, label # 返回前半句、后半句和标签
if __name__ == '__main__':
    # 加载数据集
    dataset = Dataset('train')
    sentence1, sentence2, label = dataset[7]
    print(len(dataset), sentence1, sentence2, label)

输出结果如下所示:

8001
地理位置佳,在市中心。酒店服务好、早餐品
种丰富。我住的商务数码房电脑宽带速度满意
0

其中,8001表示训练数据集,每条训练数据包括两句话和一个标签,标签表示这两句话是否有关。

3.定义计算设备

# 定义计算设备
device = 'cpu'
if torch.cuda.is_available():
    device = 'cuda'

4.定义数据整理函数
collate_fn(data)函数中的data表示一个batch的数据,每条记录包括句子对和标签,该函数功能为编码一个batch文本数据。

def collate_fn(data):
    sents = [i[:2] for i in data]
    labels = [i[2] for i in data]
    data = token.batch_encode_plus(batch_text_or_text_pairs=sents, # 输入句子对
                                   truncation=True, # 截断
                                   padding='max_length'# [PAD]
                                   max_length=45, # 最大长度
                                   return_tensors='pt'# 返回pytorch张量
                                   return_length=True, # 返回长度
                                   add_special_tokens=True) # 添加特殊符号
    # input_ids:编码之后的数字
    # attention_mask:补零的位置是0, 其他位置是1
    # token_type_ids:第1个句子和特殊符号的位置是0, 第2个句子的位置是1
    input_ids = data['input_ids'].to(device)
    attention_mask = data['attention_mask'].to(device)
    token_type_ids = data['token_type_ids'].to(device)
    labels = torch.LongTensor(labels).to(device)
    return input_ids, attention_mask, token_type_ids, labels

输入参数data数据样例如下所示:

data = [('酒店还是非常的不错,我预定的是套间,服务''非常好,随叫随到,结账非常快。',
0),
('外观很漂亮,性价比感觉还不错,功能简''单,适合出差携带。蓝牙摄像头都有了。',
0),
('《穆斯林的葬礼》我已闻名很久,只是一直没''怎能享受4星的服务,连空调都不能
用的。'
, 1)]

5.定义数据集加载器

# 数据集加载器
loader = torch.utils.data.DataLoader(dataset=dataset, batch_size=8, collate_fn=collate_fn, shuffle=True, drop_last=True)
# print(len(loader))

三.定义模型
1.加载预训练模型

pretrained_model_name_or_path = r'L:\20230713_HuggingFaceModel\bert-base-chinese'
pretrained = BertModel.from_pretrained(Path(f'{pretrained_model_name_or_path}'))
pretrained.to(device)

2.定义下游任务模型
下游任务模型为线性神经网络,权重矩阵为768×2,即把一个768维度的向量转换到2维空间(相关或无关)。其中,out.last_hidden_state[:, 0, :]表示只使用了第1个词([CLS])的特征用于相关或无关的判断。如下所示:

class Model(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = torch.nn.Linear(768, 2)

    def forward(self, input_ids, attention_mask, token_type_ids):
        # 使用预训练模型抽取数据特征
        with torch.no_grad():
            out = pretrained(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        # 对抽取的特征只取第1个字的结果进行分类即可
        out = self.fc(out.last_hidden_state[:, 0, :])
        out = out.softmax(dim=1)
        return out #torch.Size([16, 2])

四.训练和测试
1.训练

def train():
    # 定义优化器
    optimizer = AdamW(model.parameters(), lr=5e-5)
    # 定义1oss函数
    criterion = torch.nn.CrossEntropyLoss()
    # 定义学习率调节器
    scheduler = get_scheduler(name='linear', num_warmup_steps=0, num_training_steps=len(loader), optimizer=optimizer)
    # 将模型切换到训练模式
    model.train()
    # 按批次遍历训练集中的数据
    for epoch in range(5):
        # 按批次遍历训练集中的数据
        for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader):
            # 模型计算
            out = model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
            # 计算loss并使用梯度下降法优化模型参数
            loss = criterion(out, labels)
            loss.backward() # 反向传播
            optimizer.step() # 梯度下降法优化模型参数
            scheduler.step() # 学习率调节器
            optimizer.zero_grad() # 清空梯度
            # 输出各项数据的情况,便于观察
            if i % 20 == 0:
                out = out.argmax(dim=1) # 取出最大值的索引
                accuracy = (out == labels).sum().item() / len(labels) # 计算准确率
                lr = optimizer.state_dict()['param_groups'][0]['lr'# 获取当前学习率
                print(epoch, 1, loss.item(), lr, accuracy)

2.测试

def test():
    # 定义测试数据集加载器
    dataset = Dataset('test')
    loader_test = torch.utils.data.DataLoader(dataset=dataset,  batch_size=32, collate_fn=collate_fn, shuffle=True, drop_last=True)
    # 将下游任务模型切换到运行模式
    model.eval()
    correct = 0
    total = 0
    # 按批次遍历测试集中的数据
    for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader_test):
        # 计算5个批次即可,不需要全部遍历
        if i == 5:
            break
        print(i)
        # 计算
        with torch.no_grad():
            out = model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        # 统计正确率
        out = out.argmax(dim=1)
        correct += (out == labels).sum().item()
        total += len(labels)
    print(correct / total)

参考文献:
[1]HuggingFace自然语言处理详解:基于BERT中文模型的任务实战
[2]代码链接:https://github.com/ai408/nlp-engineering/blob/main/20230625_HuggingFace自然语言处理详解/第9章:中文句子关系推断.py

与中文句子关系推断相似的内容:

中文句子关系推断

本文通过ChnSentiCorp数据集介绍了中文句子关系推断任务过程,主要使用预训练语言模型bert-base-chinese直接在测试集上进行测试,也简要介绍了模型训练流程,不过最后没有保存训练好的模型。 一.任务简介和数据集 通过模型来判断2个句子是否连续,使用ChnSentiCorp数据集,不

TextCNN和TextRNN:原理与实践

1.TextCNN原理 CNN的核心点在于可以捕获信息的局部相关性,具体到文本分类任务中可以利用CNN来提取句子中类似N-Gram的关键信息。 (1)一维卷积:使用不同尺寸的kernel_size来模拟语言模型中的N-Gram,提取句子中的信息。即TextCNN中的卷积用的是一维卷积,通过不同ker

驱动开发:内核解锁与强删文件

在某些时候我们的系统中会出现一些无法被正常删除的文件,如果想要强制删除则需要在驱动层面对其进行解锁后才可删掉,而所谓的解锁其实就是释放掉文件描述符(句柄表)占用,文件解锁的核心原理是通过调用`ObSetHandleAttributes`函数将特定句柄设置为可关闭状态,然后在调用`ZwClose`将其文件关闭,强制删除则是通过`ObReferenceObjectByHandle`在对象上提供相应的权

中文完形填空

本文通过ChnSentiCorp数据集介绍了完型填空任务过程,主要使用预训练语言模型bert-base-chinese直接在测试集上进行测试,也简要介绍了模型训练流程,不过最后没有保存训练好的模型。 一.完形填空 完形填空应该大家都比较熟悉,就是把句子中的词挖掉,根据上下文推测挖掉的词是什么。 二.

使用编码工具

本文主要介绍了对句子编码的过程,以及如何使用PyTorch中自带的编码工具,包括基本编码encode()、增强编码encode_plus()和批量编码batch_encode_plus()。 一.对一个句子编码例子 假设想在要对句子'the quick brown fox jumps over a

驱动开发:内核中进程与句柄互转

在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(PID)来标识,而句柄是指对内核对象的引用。在Windows内核中,`EProcess`结构表示一个进程,而HANDLE是一个句柄。为了实现进程与句柄之间的转换,我们需要使用一些内核函数。对于进程PID和句柄的互相转换,可以使用函数如`OpenProcess`和`GetProcessId`。OpenProcess函

一文了解MySQL中的多版本并发控制

最近在阅读《认知觉醒》这本书,里面有句话非常打动我:通过自己的语言,用最简单的话把一件事情讲清楚,最好让外行人也能听懂。希望借助今天这篇文章,能用大白话说清楚这个相对比较底层和复杂的MVCC机制

在langchain中使用带简短知识内容的prompt template

# 简介 langchain中有个比较有意思的prompt template叫做FewShotPromptTemplate。 他是这句话的简写:"Prompt template that contains few shot examples." 什么意思呢?就是说在Prompt template带了

[转帖]Bash脚本编程学习笔记06:条件结构体

简介 在bash脚本编程中,条件结构体使用if语句和case语句两种句式。 if语句 单分支if语句 if TEST; then CMD fi TEST:条件判断,多数情况下可使用test命令来实现,返回值为0的话则执行CMD,否则就离开该条件结构体,脚本继续往下执行。 [root@c7-serve

如何洞察 C# 程序的 GDI 句柄泄露

## 一:背景 ### 1. 讲故事 前段时间有位朋友找到我,说他的程序界面操作起来很慢并且卡顿等一些不正常现象,从任务管理器看了下 `GDI句柄` 已经到 1w 了,一时也找不出什么代码中哪里有问题,让我帮忙看下,其实这种问题看内存dump作用不是很大,主要是写脚本很麻烦,这一篇我们就来简单聊聊如