Bert 实现情感分析任务

news2024/11/20 14:38:17

BERT

Bert (Bidirectional Encoder Representations from Transformers)预训练模型是 Google 2018开源的自然语言模型,主要有以下特点。

  1. 像它名字一样,BERT最显著的特点是其能够为文本中的每个标记考虑双向上下文。与传统的基于先前标记预测标记的语言模型(从左到右或单向模型)不同,它查看前后标记(一次查看整个序列),以理解和预测单词的上下文。
  2. 采用了 Transformer 架构,通过自注意力机制关注自身与句子中其他单词的关系。
  3. 通过 MLM 和 NSP 两个任务进行预训练,MLM 在预训练期间,随机遮蔽句子的词,模型的目标是仅基于其上下文预测掩蔽字的原始值。在NSP中,模型给出句子对,并必须预测第二句是否是原始文档中的后续句子。
  4. BERT 可以添加一个额外的输出层进行微调,而无需进行大量的任务特定修改。这包括问答、情感分析和语言推理等任务。微调步骤略微调整预训练参数,以专门为手头的特定任务定制模型,利用在预训练期间学习到的丰富表示。

本文通过微调 BERT 实现情感分析。

Transformer 模型中的QKV

开始代码之前,再回顾一下 Q、K、V,这是三个 Tansformer 中最重要的公式,

查询(Q) Q 在自注意力层中代表当前词,Q 帮助模型理解在特定上下文中哪些信息是重要的,可以理解为:问那一个词在句子中是需要关注的。

键(K) 用于与Q(查询)进行匹配,键的作用是作为一种标识,最终生成相关性得分。

值(V) 表示与每个Token关联的实际内容,当 Q 对应的Token被认为是重要的,相应的值就会在输出中获得更多的关注,V决定了自注意力的输出。

公式如下:
在这里插入图片描述

  1. 首先计算注意力得分:Q和 K 之间的点积,得到注意力得分。这里可以简单理解一下,两个向量的乘法,也是两个向量的内积,内积越大,说明其2个向量相似度越高。
  2. Softmax函数:将这些得分通过softmax函数进行标准化,这样得分就转换为概率形式,表明每个值(V)的相对重要性(权重)。
  3. 最后,模型将这些标准化的得分与对应的值(V)相乘,加权输出。

Pytorch 实现情感分析

安装依赖
pip install addict
数据准备
import random
import time
import numpy as np
from tqdm import tqdm
import torch 
from torch import nn
import torch.optim as optim
import torchtext

# 设定随机数的种子,
torch.manual_seed(1234)
np.random.seed(1234)
random.seed(1234)

#生成预处理和单词分割的函数
import re
import string
from utils.bert import BertTokenizer
#来自文件夹“utils”的bert.py


def preprocessing_text(text):
    '''IMDb预处理'''
    # 删除换行代码
    text = re.sub('<br />', '', text)

    #将逗号和句号以外的标点符号全部替换成空格
    for p in string.punctuation:
        if (p == ".") or (p == ","):
            continue
        else:
            text = text.replace(p, " ")

    #在句号和逗号前后插入空格
    text = text.replace(".", " . ")
    text = text.replace(",", " , ")
    return text


#将逗号和句号以外的标点符号全部替换成空格
tokenizer_bert = BertTokenizer(
    vocab_file="./vocab/bert-base-uncased-vocab.txt", do_lower_case=True)


#定义同时负责预处理和分词处理的函数
#指定分词处理的函数,注意不要使用tokenizer_bert,而应指定使tokenizer_bert.tokenize
def tokenizer_with_preprocessing(text, tokenizer=tokenizer_bert.tokenize):
    text = preprocessing_text(text)
    ret = tokenizer(text)  # tokenizer_bert
    return ret


#定义在读入数据时,对读到的内容应做的处理
max_length = 256

TEXT = torchtext.data.Field(sequential=True, tokenize=tokenizer_with_preprocessing, use_vocab=True,
                            lower=True, include_lengths=True, batch_first=True, fix_length=max_length, init_token="[CLS]", eos_token="[SEP]", pad_token='[PAD]', unk_token='[UNK]')
LABEL = torchtext.data.Field(sequential=False, use_vocab=False)

# (注释):再次确认各个参数
# sequential:  数据长度是否可变?由于文章长度是不固定的,因此指定True,标签则指定False
# tokenize:  用于指定读入文章时所需执行的预处理和分词处理函数
# use_vocab:指定是否将单词添加到词汇表中
# lower:指定是否将英文字母转换为小写字母
# include_length: 指定是否返回文章的单词数量
# batch_first:指定是否在开头处生成批次的维度信息
# fix_length::指定是否确保所有文章都为相同长度,长度不足的填充处理
# init_token, eos_token, pad_token, unk_token:指定使用什么单词来表示、文章开头、文章结尾、填充和未知单词

#从data文件夹中读取各个tsv文件
#使用BERT进行处理,执行时间大约为10分钟
train_val_ds, test_ds = torchtext.data.TabularDataset.splits(
    path='./data/', train='IMDb_train.tsv',
    test='IMDb_test.tsv', format='tsv',
    fields=[('Text', TEXT), ('Label', LABEL)])

#使用torchtext.data.Dataset的split函数将数据划分为训练数据和验证数据
train_ds, val_ds = train_val_ds.split(
    split_ratio=0.8, random_state=random.seed(1234))

#BERT是使用BERT掌握的所有单词来创建BertEmbedding模块的,因此将使用全部单词作为词汇表
# 为此不会使用训练数据来生成词汇表

# #首先为BERT准备字典型变量
from utils.bert import BertTokenizer, load_vocab

vocab_bert, ids_to_tokens_bert = load_vocab(
    vocab_file="./vocab/bert-base-uncased-vocab.txt")


#虽然很想写成TEXT.vocab.stoi= vocab_bert (stoi意为string_to_ID,将单词转换为 ID 的字典的形式
#但是如果不执行一次bulild_vocab,TEXT对象就不会初始化vocab的成员变量
#程序会产生“'Field' object has no attribute 'vocab'”这一错误信息

#首先调用build_vocab创建词汇表,然后替换BERT的词汇表
TEXT.build_vocab(train_ds, min_freq=1)
TEXT.vocab.stoi = vocab_bert

#创建DataLoader(在torchtext中被称为iterater)
batch_size = 32   #BERT中经常使用16和32

train_dl = torchtext.data.Iterator(
    train_ds, batch_size=batch_size, train=True)

val_dl = torchtext.data.Iterator(
    val_ds, batch_size=batch_size, train=False, sort=False)

test_dl = torchtext.data.Iterator(
    test_ds, batch_size=batch_size, train=False, sort=False)

#集中保存到字典对象中
dataloaders_dict = {"train": train_dl, "val": val_dl}

#确认执行结果,使用验证数据的数据集进行确认
batch = next(iter(val_dl))
print(batch.Text)
print(batch.Label)

#确认小批次中第一句话的内容
text_minibatch_1 = (batch.Text[0][1]).numpy()

#将ID还原成单词
text = tokenizer_bert.convert_ids_to_tokens(text_minibatch_1)

print(text)


准备模型

class BertForIMDb(nn.Module):
    '''在BERT模型中增加了IMDb的正面/负面分析功能的模型'''

    def __init__(self, net_bert):
        super(BertForIMDb, self).__init__()

       #BERT模块
        self.bert = net_bert  # BERTモデル

        #在head中添加正面 / 负面预测
        #输入是BERT输出的特征量的维度,输出是正面和负面这两种
        self.cls = nn.Linear(in_features=768, out_features=2)

        #权重初始化处理
        nn.init.normal_(self.cls.weight, std=0.02)
        nn.init.normal_(self.cls.bias, 0)

    def forward(self, input_ids, token_type_ids=None, attention_mask=None, output_all_encoded_layers=False, attention_show_flg=False):
        '''
        input_ids: 形状为[batch_size, sequence_length]的文章的单词ID序列
        token_type_ids:形状为[batch_size, sequence_length],表示每个单词是属于第一句还是第二句的id
        attention_mask:与Transformer的掩码作用相同的掩码
        output_all_encoded_layers:用于指定是返回全部12个Transformer的列表还是只返回最后一项的标识
        attention_show_flg:指定是否返回Self−Attention的权重的标识
        '''

        #BERT的基础模型部分的正向传播
        #进行正向传播处理る
        if attention_show_flg == True:
           '''指定attention_show时,也同时返回attention_probs'''
           encoded_layers, pooled_output, attention_probs = self.bert(
                input_ids, token_type_ids, attention_mask, output_all_encoded_layers, attention_show_flg)
        elif attention_show_flg == False:
            encoded_layers, pooled_output = self.bert(
                input_ids, token_type_ids, attention_mask, output_all_encoded_layers, attention_show_flg)

        #使用输入文章的第一个单词[CLS]的特征量进行正面/负面分类处理
        vec_0 = encoded_layers[:, 0, :]
        vec_0 = vec_0.view(-1, 768)  # 将size转换为batch size、hidden size
        out = self.cls(vec_0)

       #指定attention_show时,也同时返回attention_probs(位于最后一位的)
        if attention_show_flg == True:
            return out, attention_probs
        elif attention_show_flg == False:
            return out

#网络设置完毕
net = BertForIMDb(net_bert)

#设置为训练模式
net.train()

print('网络设置完毕')

#只处理位于最后的BertLayer模块的梯度计算和添加的分类适配器

#1.首先,将所有的梯度计算设置为False
for name, param in net.named_parameters():
    param.requires_grad = False

#2.设置对位于最后的BertLayer模块进行梯度计算
for name, param in net.bert.encoder.layer[-1].named_parameters():
    param.requires_grad = True

#3.设置打开识别器的梯度计算
for name, param in net.cls.named_parameters():
    param.requires_grad = True

#设置最优化算法

#BERT的原有部分作为精调
optimizer = optim.Adam([
    {'params': net.bert.encoder.layer[-1].parameters(), 'lr': 5e-5},
    {'params': net.cls.parameters(), 'lr': 5e-5}
], betas=(0.9, 0.999))

#设置损失函数
criterion = nn.CrossEntropyLoss()
# nn.LogSoftmax()を計算してからnn.NLLLoss(negative log likelihood loss)を計算

#创建用于训练模型的函数


def train_model(net, dataloaders_dict, criterion, optimizer, num_epochs):

    #确认GPU是否可用
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("使用的设备:", device)
    print('-----start-------')

    #将网络载入GPU中
    net.to(device)

    #如果网络结构比较固定,则开启硬件加速
    torch.backends.cudnn.benchmark = True

    #小批次的尺寸
    batch_size = dataloaders_dict["train"].batch_size

   #epoch的循环
    for epoch in range(num_epochs):
        #每轮epoch的训练和验证循环
        for phase in ['train', 'val']:
            if phase == 'train':
                net.train()  #将模型设置为训练模式
            else:
                net.eval()  #将模型设置为验证模式

            epoch_loss = 0.0  #epoch的损失总和
            epoch_corrects = 0  #epoch的正确答案数量
            iteration = 1

            #保存开始时间
            t_epoch_start = time.time()
            t_iter_start = time.time()

            #以minibatch为单位从数据加载器中读取数据的循环
            for batch in (dataloaders_dict[phase]):
                #batch是Text和Lable的字典型变量

                #如果能使用GPU,则将数据送入GPU中
                inputs = batch.Text[0].to(device)  # 文章
                labels = batch.Label.to(device)  # 标签

                #初始化optimizer
                optimizer.zero_grad()

              #计算正向传播
                with torch.set_grad_enabled(phase == 'train'):

                    #输入BertForIMDb中
                    outputs = net(inputs, token_type_ids=None, attention_mask=None,
                                  output_all_encoded_layers=False, attention_show_flg=False)

                    loss = criterion(outputs, labels)  #计算损失

                    _, preds = torch.max(outputs, 1)  #对标签进行预测

                    #训练时执行反向传播
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()

                        if (iteration % 10 == 0): #每10个iter显示一次loss
                            t_iter_finish = time.time()
                            duration = t_iter_finish - t_iter_start
                            acc = (torch.sum(preds == labels.data)
                                   ).double()/batch_size
                            print('迭代 {} || Loss: {:.4f} || 10iter: {:.4f} sec. ||本次迭代的准确率:{}'.format(
                                iteration, loss.item(), duration, acc))
                            t_iter_start = time.time()

                    iteration += 1

                   #更新损失和正确答案数量的合计值
                    epoch_loss += loss.item() * batch_size
                    epoch_corrects += torch.sum(preds == labels.data)

            #每轮epoch的loss和准确率
            t_epoch_finish = time.time()
            epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset)
            epoch_acc = epoch_corrects.double(
            ) / len(dataloaders_dict[phase].dataset)

            print('Epoch {}/{} | {:^5} |  Loss: {:.4f} Acc: {:.4f}'.format(epoch+1, num_epochs,
                                                                           phase, epoch_loss, epoch_acc))
            t_epoch_start = time.time()

    return net


开始训练

#执行学习和验证处理
num_epochs = 2
net_trained = train_model(net, dataloaders_dict,
                          criterion, optimizer, num_epochs=num_epochs)

#对完成学习的网络参数进行保存
save_path = './weights/bert_fine_tuning_IMDb.pth'
torch.save(net_trained.state_dict(), save_path)

测试

#对使用测试数据时模型的准确率进行求解
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

net_trained.eval()   #将模型设置为验证模式
net_trained.to(device) #如果GPU可用,则将数据送入GPU中

#记录epoch的正确答案数量的变量
epoch_corrects = 0

for batch in tqdm(test_dl):  #test数据的DataLoader
    #test数据的DataLoader
    #如果GPU可用,则将数据送入GPU中
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    inputs = batch.Text[0].to(device)  # 文章
    labels = batch.Label.to(device)  #标签

    #计算正向传播
    with torch.set_grad_enabled(False):

        #输入BertForIMDb中
        outputs = net_trained(inputs, token_type_ids=None, attention_mask=None,
                              output_all_encoded_layers=False, attention_show_flg=False)

        loss = criterion(outputs, labels) #计算损失
        _, preds = torch.max(outputs, 1)  #进行标签预测
        epoch_corrects += torch.sum(preds == labels.data) #更新正确答案数量的合计

#准确率
epoch_acc = epoch_corrects.double() / len(test_dl.dataset)

print('处理 {} 个测试数据的准确率:{:.4f}'.format(len(test_dl.dataset), epoch_acc))

正确率达到 90%
在这里插入图片描述
Bert 模型比之前的 Transformer 模型实现的情感分析效果要好,但是BERT 只是实现了 Encoder Layer,如果需要做更复杂的任务还需要 Decoder Layer,例如翻译,对话等等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1655023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue+ant-design+formBuiler表单构建器——技能提升——form design——亲测有效

最近看到后端同事在弄一个后台管理系统&#xff0c;额&#xff0c;前端真的是夹缝中生存啊&#xff0c;AI抢饭碗&#xff0c;后端也想干前端的活儿。。。 他用到了表单构建器&#xff0c;具体效果如下: 网上有很多适用于ElementUi和ant-design的form design插件&#xff0c;下…

FX110书籍推荐:如何快速成为一名专业股票投资人?

股票投资领域有一本神作《股票交易入门》&#xff0c;它是股票从业人员的入门必备书籍。 关于股票入门的书籍很多&#xff0c;但这本书涉及的知识面最全、实用性最强。从这本书里&#xff0c;我们可以领略到股票交易世界的跌宕起伏而又波澜壮阔的魅力。本书作者 本书的作者是美…

大模型的不足与解决方案

文章目录 ⭐ 不具备记忆能力 上下文窗口受限⭐ 实时信息更新慢 新旧知识难区分⭐ 内部操作很灵活 外部系统难操作⭐ 无法为专业问题 提供靠谱的答案⭐ 解决方案的结果 各有不同的侧重 在前面三个章节呢&#xff0c;为大家从技术的角度介绍了大模型的历程与发展&#xff0c;也为…

【AutoGPT】踩坑帖(follow李鱼皮)

本文写于2024年5月7日 参考视频&#xff1a;AutoGPT傻瓜式使用教程真实体验&#xff01; 对应文章&#xff1a;炸裂的AutoGPT&#xff0c;帮我做了个网站&#xff01; 平台&#xff1a;GitPod 云托管服务 原仓库已经改动很大&#xff0c;应使用的Repo为&#xff1a;Auto-GPT-ZH…

uniapp——点赞、取消点赞

案例 更新点赞状态&#xff0c;而不是每次都刷新整个列表。避免页面闪烁&#xff0c;提升用户体验 代码 <view class"funcBtn zan" click"onZan(index,item.id)"><image src"/static/images/circle/zan.png" mode"aspectFill&…

element-ui table sortable排序 掉后端接口方式

实例: 官方解释:如果需要后端排序&#xff0c;需将sortable设置为custom&#xff0c;同时在 Table 上监听sort-change事件&#xff0c;在事件回调中可以获取当前排序的字段名和排序顺序&#xff0c;从而向接口请求排序后的表格数据。 1.table上要加 sort-change"sortCha…

51单片机keil编程中遇到的问题(持续更新)

字符无法打印报错 查看特殊功能寄存器名字的时候也会报错&#xff0c;因为无法编译通过&#xff0c;导致头文件的定义内容无法查找 keil编译中 error C127: ‘xx’: invalid storage class 这种一般是在编写头文件或源文件时&#xff0c;在声明函数的结尾没有添加分号&…

C语言 | Leetcode C语言题解之第75题颜色分类

题目&#xff1a; 题解&#xff1a; void swap(int *a, int *b) {int t *a;*a *b, *b t; }void sortColors(int *nums, int numsSize) {int p0 0, p2 numsSize - 1;for (int i 0; i < p2; i) {while (i < p2 && nums[i] 2) {swap(&nums[i], &num…

大模型微调之 在亚马逊AWS上实战LlaMA案例(六)

大模型微调之 在亚马逊AWS上实战LlaMA案例&#xff08;六&#xff09; 通过 SageMaker Python SDK 进行微调Llama2 可以使用 SageMaker Python SDK 微调 Llama 2 模型。以下是在数据集上微调 Llama 2 7B 的示例代码&#xff1a; import os import boto3 from sagemaker.sessi…

Python代码生成类型注解库之monkeytype使用详解

概要 MonkeyType 是由 Instagram 开发的一个 Python 库,用于自动为 Python 代码生成类型注解。通过监控运行时的类型信息,MonkeyType 能够帮助开发者为现有的 Python 代码库增加类型提示,从而提高代码的可读性和健壮性。 安装 安装 MonkeyType 非常简单,可以通过 pip 进行…

证照之星是什么软件 证照之星哪个版本好用?证照之星支持哪些相机 证照之星XE免费版

许多人都需要使用证件照&#xff0c;为了满足这一需求&#xff0c;人们会使用照相机、手机、电脑等工具进行拍摄。除此之外&#xff0c;市面上还存在专门的证件照拍摄软件&#xff0c;比如证照之星。那么&#xff0c;各位小伙伴是否了解证照之星哪个版本好用&#xff0c;证照之…

93、动态规划-最长回文子串

思路 首先从暴力递归开始&#xff0c;回文首尾指针相向运动肯定想等。就是回文&#xff0c;代码如下&#xff1a; public String longestPalindrome(String s) {if (s null || s.length() 0) {return "";}return longestPalindromeHelper(s, 0, s.length() - 1);…

Day 42 0-1背包理论基础 416. 分割等和子集

01背包理论基础 先了解背包问题的区别和分类&#xff1a; ​ 由于所有的问题的原理都可以转化为01背包&#xff1b;通过纯01背包问题&#xff0c;把01背包原理讲清楚&#xff1b; 01背包 ​ 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;…

华为OD机试【全量和已占用字符集】(java)(100分)

1、题目描述 给定两个字符集合&#xff0c;一个是全量字符集&#xff0c;一个是已占用字符集&#xff0c;已占用字符集中的字符不能再使用。 2、输入描述 输入一个字符串 一定包含&#xff0c;前为全量字符集 后的为已占用字符集&#xff1b;已占用字符集中的字符一定是全量…

【c++】继承学习(三)菱形继承的挑战与虚拟继承的策略

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章来讲解继承的第三部分&#xff0c;有关多继承和菱形继承的内容 目录 1.菱形继承2.虚拟继承3.虚拟继承解决数据冗余和二义性的原理4.继承的总结和反思继承…

使用ffmpeg对视频进行转码(支持浏览器播放)

在开发中&#xff0c;算法保存的mp4视频文件通过路径打开该视频发现视频播放不了&#xff0c;需要转码进行播放。使用java代码进行转码。代码如下&#xff0c;inputFilePath是转之前的视频路径&#xff0c;outputFilePath是转之后的视频路径。ffmpeg命令中libx264也可以改为其它…

分类任务的基础学习

1.什么是分类&#xff1f; 2.局限性&#xff1a; 当样本量逐渐变大的时候&#xff0c;准确率会下降——>因为线性回归曲线距离我们的原点越远&#xff0c;预测就会开始不准确&#xff0c;因为 x前面的倍数就会越来越小&#xff0c;这就导致了样本量变大&#xff0c;但是那些…

攻略:ChatGPT3.5~4.0(中文版)国内无限制免费版(附网址)【2024年5月最新更新】

一、什么是ChatGPT&#xff1f; 1、ChatGPT的全名是Chat Generative Pre-trained Transformer&#xff0c;其中"chat"表示聊天。"GPT"则是由三部分组成&#xff1a;生成式&#xff08;generative&#xff09;意味着具有创造力&#xff1b;预训练&#xff0…

PDPS15---安装教程---附安装包

目录 第1章 文件准备 1.1 安装包列表 第2章 安装Perl 2.1 Perl安装和路径选择 第3章 安装Java 3.1 Java安装和路径选择 第4章 安装Oracle 4.1 双击Setup 第5章 数据库(Oracle)和注册表(Perl) 5.1 数据库创建 5.2 注册表修改 第6章 安装Tecnomatix 6.1 安装Tecnoma…

电子硬件设计-Xilinx FPGA/SoC前期功耗评估方法(1)

目录 1. 简介 2. 使用方法 2.1 设计输入 2.2 查看结果 3. 额外说明 4. 总结 1. 简介 XPE (Xilinx Power Estimator, 功耗估算器) 电子表格是一种功耗估算工具&#xff0c;用于项目的预设计和预实现阶段。 该工具可以帮助工程师进行架构评估、器件选择、合适的电源组件以…