深度学习第四站——基于bert实现酒店评论情感分析

一、Bert介绍

        Bert模型(Bidirectional Encoder Representations from Transformers),文本特征提取器,是一种由Google于2018年引入的自然语言处理(NLP)预训练技术。它基于Transformer架构,旨在理解句子中单词的上下文。

关键点:

  1. 双向上下文理解: 与以前的语言模型不同,BERT以双向方式阅读文本。传统的语言模型在处理文本时只关注前面或后面的上下文,而BERT考虑了句子中所有单词的上下文。

  2. 预训练: BERT通过在大规模文本数据上进行预训练,学习单词之间的关系和语境。这使得模型能够更好地理解自然语言的复杂性。其预训练包括两个自监督预训练:一是掩盖部分文本内容再预测原文,让bert更好的依据语境做出预测,二是预测句子是否连续的任务,能帮助bert对上下文关系有更好的理解。

  3. Transformer架构: BERT基于Transformer模型,该模型使用自注意力机制来处理输入序列的不同部分,使其能够捕捉长距离依赖关系。

  4. 微调: 一旦在大规模文本上进行了预训练,BERT可以通过在特定任务上进行微调来适应具体的应用,如文本分类、命名实体识别等。

        上图主要流程如下:       

       

S1:输入文本信息以及位置信息

设input=“我爱你”

        'input_ids': [101, 2769, 4263, 872, 102],开头为【cls】,结尾为【sep】,中间为我爱你三个字对应的词嵌入编码

        'token_type_ids': [0, 0, 0, 0, 0]:句子的所在id,本句为第0句

        'attention_mask': [1, 1, 1, 1, 1]:句子的掩码,1则有信息,0则无信息

  • input_ids: 包含文本信息的编码序列,其中包括[CLS]和[SEP]标记,以及每个字的词嵌入编码。这种表示方式用于将自然语言文本转换为模型可理解的数值输入。

  • token_type_ids: 表示每个字所属的句子,这里只有一个句子,因此所有值都为0。在多句子的情况下,可以使用不同的标识符来区分句子。

  • attention_mask: 表示每个字的掩码,其中1表示有信息,0表示无信息。这有助于模型在处理变长序列时关注有效的部分,而忽略填充部分。

S2:自注意力机制(Self-Attention)

  • 使用输入的编码、位置信息以及可能的位置嵌入构建Q(查询)、K(键)、V(值)矩阵。这些矩阵用于计算注意力分布。

  • 在Transformer中,获取Q(Query)、K(Key)、V(Value)是通过对输入进行线性映射来实现的。具体来说,对于每个输入序列中的元素(比如每个词),通过三个不同的线性变换(Q= q*Wq, K=k*Wk, V=v*Wv)来获得Q、K、V,三个W初始随机生成,后续在学习训练过程中迭代更新。

  • 对Q、K进行点积操作,然后通过softmax函数得到注意力分布。这一步使得每个字可以关注其他字的权重。

  • 将注意力分布与V相乘,得到每个字对其他字的加权表示。这使得每个字都能够融合来自其他字的信息。

  • 应用残差连接和层归一化,以减少梯度消失和爆炸。残差连接允许信息直接流经模块,减轻了训练中的梯度问题。层归一化有助于稳定训练过程。

S3:全连接层

  • 对经过自注意力机制的输出进行全连接层的处理。这可能包括线性变换、激活函数等。全连接层的作用是将得到的复杂特征映射到更高维度的表示空间,以便更好地捕捉输入序列的语义信息。

        

        bert模型下载地址:https://huggingface.co/bert-base-chinese/tree/main

二、主要代码

     数据读取处理

from torch.utils.data import Dataset, DataLoader
import torch
from sklearn.model_selection import train_test_split  # 把数据集分为训练集和测试集

def read_txt(path):
    data = []
    label = []
    with open(path, "r", encoding="utf-8") as f:
        for i, line in enumerate(f):  # 返回下标和行数
            if i == 0:
                continue  # 跳过第一行

            if i>200 and i<7500:
                continue
            line = line.strip("\n")
            line = line.split(",", 1)  # 只分一次
            label.append(line[0])  # 标签写入列表
            data.append(line[1])  # 写入数据
    return data, label


if __name__ == "__main__":  #仅在本文件下运行
    read_txt("../jiudian.txt")


class JdDataset(Dataset):  # 数据设置,以供Dataloader使用
    def __init__(self, data, label):
        self.X = data
        self.Y = [int(i) for i in label]  # label
        self.Y = torch.LongTensor(self.Y)

    def __getitem__(self, item):
        return self.X[item], self.Y[item]

    def __len__(self):
        return len(self.Y)


def get_data_loader(path, batchsize, val_size=0.2):  # 路径,批数,验证集比例
    data, label = read_txt(path)  # stratify是否按标签的比例取val数据集
    train_x, val_x, train_y, val_y = train_test_split(data, label, test_size=val_size, shuffle=True, stratify=label)
    train_set = JdDataset(train_x, train_y)
    val_set = JdDataset(val_x, val_y)

    train_loader = DataLoader(train_set, batchsize, shuffle=True)
    val_loader = DataLoader(val_set, batchsize, shuffle=True)
    return train_loader, val_loader

模型定义

import torch.nn as nn
import torch
from transformers import BertModel, BertConfig, BertTokenizer


class MyModel(nn.Module):  # 创建模型
    def __init__(self, bert_path, device, num_class):
        super(MyModel, self).__init__()
        self.device = device
        self.bert = BertModel.from_pretrained(bert_path)
        self.tokenizer = BertTokenizer.from_pretrained(bert_path)  # 分词器
        self.cls_head = nn.Linear(768, num_class)  # 分类头微调至n类

    def forward(self, text):  # "pt"返回pytorch张量
        input = self.tokenizer(text, return_tensors="pt", truncation=True, padding="max_length", max_length=128)
        input_ids = input["input_ids"].to(self.device)
        token_type_ids = input["token_type_ids"].to(self.device)
        attention_mask = input["attention_mask"].to(self.device)

        sequence_out, pooler_out = self.bert( input_ids,        #bert会有两个输出,一个是未池化输出128*768,一个是池化后的输出1*768
                                                token_type_ids,
                                                attention_mask,
                                                return_dict=False)

        out = self.cls_head(pooler_out)
        return out

训练流程

import torch
import time
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm

def train_val(para):

########################################################
    model = para['model']
    train_loader =para['train_loader']
    val_loader = para['val_loader']
    scheduler = para['scheduler']
    optimizer = para['optimizer']
    loss = para['loss']
    epoch = para['epoch']
    device = para['device']
    save_path = para['save_path']
    max_acc = para['max_acc']
    val_epoch = para['val_epoch']


#################################################
    plt_train_loss = []
    plt_train_acc = []
    plt_val_loss = []
    plt_val_acc = []
    val_rel = []

    for i in range(epoch):
        start_time = time.time()
        model.train()
        train_loss = 0.0
        train_acc = 0.0
        val_acc = 0.0
        val_loss = 0.0
        for batch in tqdm(train_loader):
            model.zero_grad()
            text, labels = batch[0], batch[1].to(device)
            pred = model(text)
            bat_loss = loss(pred, labels)
            bat_loss.backward()
            optimizer.step()
            scheduler.step()
            optimizer.zero_grad()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
            train_loss += bat_loss.item()    #.detach 表示去掉梯度
            train_acc += np.sum(np.argmax(pred.cpu().data.numpy(),axis=1)== labels.cpu().numpy())
        plt_train_loss . append(train_loss/train_loader.dataset.__len__())
        plt_train_acc.append(train_acc/train_loader.dataset.__len__())
        if i % val_epoch == 0:
            model.eval()
            with torch.no_grad():
                for batch in tqdm(val_loader):
                    val_text, val_labels = batch[0], batch[1].to(device)
                    val_pred = model(val_text)
                    val_bat_loss = loss(val_pred, val_labels)
                    val_loss += val_bat_loss.cpu().item()

                    val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == val_labels.cpu().numpy())
                    val_rel.append(val_pred)

            if val_acc > max_acc:
                torch.save(model, save_path+str(epoch)+"ckpt")
                max_acc = val_acc
            plt_val_loss.append(val_loss/val_loader.dataset.__len__())
            plt_val_acc.append(val_acc/val_loader.dataset.__len__())
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f | valAcc: %3.6f valLoss: %3.6f  ' % \
                  (i, epoch, time.time()-start_time, plt_train_acc[-1], plt_train_loss[-1], plt_val_acc[-1], plt_val_loss[-1])
                  )
            if i % 50 == 0:
                torch.save(model, save_path+'-epoch:'+str(i)+ '-%.2f'%plt_val_acc[-1])
        else:
            plt_val_loss.append(plt_val_loss[-1])
            plt_val_acc.append(plt_val_acc[-1])
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f   ' % \
                  (i, epoch, time.time()-start_time, plt_train_acc[-1], plt_train_loss[-1])
                  )
    plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title('loss')
    plt.legend(['train', 'val'])
    plt.show()

    plt.plot(plt_train_acc)
    plt.plot(plt_val_acc)
    plt.title('Accuracy')
    plt.legend(['train', 'val'])
    plt.savefig('acc.png')
    plt.show()

主函数

import torch.nn as nn
import torch
import random
import numpy  as np
import os

from model_utils.data import get_data_loader
from model_utils.model import MyModel
from model_utils.train import train_val


def seed_everything(seed):
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.deterministic = True
    random.seed(seed)
    np.random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)


model_name = 'MyModel'
num_class = 2
batchSize = 8
learning_rate = 0.0001
loss = nn.CrossEntropyLoss()  #交叉熵损失
epoch = 5
device = 'cuda' if torch.cuda.is_available() else 'cpu'

data_path = "jiudian.txt"
bert_path = 'bert-base-chinese'
save_path = 'model_save/'
seed_everything(1)
##########################################

train_loader, val_loader = get_data_loader(data_path, batchsize=batchSize)

model = MyModel(bert_path, device, num_class=2).to(device)   #模型也得放在gpu上


param_optimizer = list(model.parameters())
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate,weight_decay=0.0001)    #优化器
scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer,T_0=20,eta_min=1e-9)   #学习率变化




trainpara = {'model': model,
             'train_loader': train_loader,
             'val_loader': val_loader,
             'scheduler': scheduler,
             'optimizer': optimizer,
             'learning_rate': learning_rate,
             'warmup_ratio' : 0.1,
             'weight_decay' : 0.0001,
             'use_lookahead' : True,
             'loss': loss,
             'epoch': epoch,
             'device': device,
             'save_path': save_path,
             'max_acc': 0.85,
             'val_epoch' : 1
             }
train_val(trainpara)

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
《Huggingface自然语言处理详解——基于BERT中文模型的任务实战》源码是一份用于展示如何使用Huggingface库进行自然语言处理任务的示例代码。该代码基于BERT中文模型,旨在帮助读者理解如何使用该模型进行文本分类、情感分析等任务。 该源码由以下几个部分组成: 1. 导入必要的库:代码开始部分导入了需要使用的Huggingface库,包括`transformers`和`tokenizers`。这些库提供了BERT模型以及相关的处理函数和工具。 2. 数据预处理:接下来,源码介绍了如何进行数据预处理。这包括读取数据集、分词、编码等步骤。具体来说,使用`BertTokenizer`类对文本进行分词,并使用`BertTokenizer.from_pretrained`加载预训练的BERT中文模型。 3. 构建模型:源码构建了一个简单的BERT分类模型,使用`BertForSequenceClassification`类来实现。这个模型有一个BERT编码器和一个用于分类的线性层。 4. 训练模型:通过`Trainer`类,源码进行了模型的训练。在训练过程中,使用了一个训练集和一个验证集,并定义了相应的训练参数,如学习率、批大小等。训练过程中,模型参数逐渐更新,直到达到设定的最大训练轮数。 5. 模型评估:训练完成后,源码进行模型评估。通过计算准确率、查全率、查准率等指标,评估模型在验证集上的性能。 总的来说,《Huggingface自然语言处理详解——基于BERT中文模型的任务实战》源码提供了一个完整的BERT模型应用示例,通过该源码,读者能够了解如何使用Huggingface库中的BERT模型进行中文文本分类任务,并学会了如何进行数据预处理、构建模型、训练和评估模型等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岸丶丶丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值