大家好,我是锋哥。最近发布一条【AI大模型舆情分析】微博舆情分析可视化系统(pytorch2+基于BERT大模型训练微调+flask+pandas+echarts)高级实战。分上下节。

实战简介:

前面的2026版【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts+爬虫) 二次开发,前面课程舆情分析用得是snowNLP,我们现在改用基于BERT开源大模型微调,来实现舆情分析,提高舆情分析的准确率。重点讲解基本BERT大模型实现舆情分析,二分类问题。 

视频教程和源码领取:

链接:https://pan.baidu.com/s/1_NzaNr0Wln6kv1rdiQnUTg 
提取码:0000
 

BERT大模型进行微调训练实现舆情分析功能

基于BERT模型进行增量微调训练

1,自定义数据集

我们首先要安装下datasets库。

pip install datasets -i http://mirrors.aliyun.com/pypi/simple/   --trusted-host mirrors.aliyun.com

然后我们准备下训练和测试数据。

8千多条的训练集数据,2千多条的测试集数据。

我们之所以要自定义数据集,是因为需要去适配训练模型需要的数据格式。

自定义数据集参考代码:

from datasets import load_dataset
from torch.utils.data import Dataset


# 自定义数据集
class MyDataset(Dataset):

    def __init__(self, split):
        # 加载训练集
        train_dataset = load_dataset(path="csv", data_files="weibo_senti_8k_train.csv")
        # 加载测试集
        test_dataset = load_dataset(path="csv", data_files="weibo_senti_2k_test.csv")
        if split == 'train':
            self.data = train_dataset['train']
        elif split == 'test':
            self.data = test_dataset['train']

    # 获取数据集大小
    def __len__(self):
        return len(self.data)

    # 获取数据集的某个元素
    def __getitem__(self, index):
        sentence = self.data[index]['sentence']
        label = self.data[index]['label']
        return sentence, label


if __name__ == '__main__':
    train_dataset = MyDataset('train')
    test_dataset = MyDataset('test')
    print(train_dataset[0])
    print(test_dataset[0])
    for data in test_dataset:
        print(data)

运行结果:

2,对训练输入文本进行编码

对传入的数据进行训练之前,我们需要对数据进行编码。

我们通过分词器的batch_encode_plus方法进行批量编码;

实例代码:

from transformers import BertTokenizer

import config

# 加载分词器
tokenizer = BertTokenizer.from_pretrained(config.model_path + '/Bert-base-chinese')

# 准备测试文本
sents = ['床前明月光,疑似地上霜,举头望明月,低头思故乡', '今天天气不错', '很开心']

# 批量编码句子
out = tokenizer.batch_encode_plus(
    batch_text_or_text_pairs=sents,  # 输入的文本
    add_special_tokens=True,  # 添加特殊标记
    max_length=10,  # 最大长度
    padding='max_length',  # 填充
    truncation=True,  # 截断
    return_tensors='pt',  # 返回pytorch张量
    return_token_type_ids=True,  # 返回token_type_ids  区分不同句子或段落的类型标识
    return_attention_mask=True,  # 返回attention_mask  标记有效token位置的掩码
    return_special_tokens_mask=True  # 返回special_tokens_mask 标识特殊token(如[CLS]、[SEP])的位置掩码
)
print(out)
for k, v in out.items():
    print(k, v)

# 解码文本数据
for i in range(len(sents)):
    print(sents[i] + "--解码后:", tokenizer.decode(out['input_ids'][i]))

运行输出:

{'input_ids': tensor([[ 101, 2414, 1184, 3209, 3299, 1045, 8024, 4542,  849,  102],
        [ 101,  791, 1921, 1921, 3698,  679, 7231,  102,    0,    0],
        [ 101, 2523, 2458, 2552,  102,    0,    0,    0,    0,    0]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'special_tokens_mask': tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
        [1, 0, 0, 0, 1, 1, 1, 1, 1, 1]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
        [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]])}
input_ids tensor([[ 101, 2414, 1184, 3209, 3299, 1045, 8024, 4542,  849,  102],
        [ 101,  791, 1921, 1921, 3698,  679, 7231,  102,    0,    0],
        [ 101, 2523, 2458, 2552,  102,    0,    0,    0,    0,    0]])
token_type_ids tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
special_tokens_mask tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
        [1, 0, 0, 0, 1, 1, 1, 1, 1, 1]])
attention_mask tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
        [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]])
床前明月光,疑似地上霜,举头望明月,低头思故乡--编码后: [CLS] 床 前 明 月 光 , 疑 似 [SEP]
今天天气不错--编码后: [CLS] 今 天 天 气 不 错 [SEP] [PAD] [PAD]
很开心--解码后: [CLS] 很 开 心 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD]

3,定义增量模型,也就是下游任务

我们定义增量模型,主要是在Bert模型最后加一个全连接层,实现二分类任务。进行前向传播的时候,我们要使用torch.no_grad()冻结Bert模型参数,不需计算梯度,获取最后一层Bert隐藏层输出,调用自定义的全连接层,进行增量模型训练。

示例代码:

# 使用设备(GPU/CPU)
import torch
from transformers import BertModel

import config

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

# 加载预训练模型
pretrained_model = BertModel.from_pretrained(config.model_path + '/Bert-base-chinese').to(device)


# 定义下游任务(增量模型)
class DownStreamModel(torch.nn.Module):

    def __init__(self):
        super(DownStreamModel, self).__init__()
        # 下游加一个全连接层,实现二分类任务
        self.fc = torch.nn.Linear(768, 2)

    def forward(self, input_ids, attention_mask, token_type_ids):
        with torch.no_grad():  # 冻结Bert模型参数,不需计算梯度
            # 获取最后一层隐藏层输出
            output = pretrained_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        # 增量模型参与训练
        out = self.fc(output.last_hidden_state[:, 0, :])
        return out

output.last_hidden_state[:, 0, :] 是用来获取模型输出的隐藏状态(通常是进入模型的最后一层隐藏状态)的一个特定切片。让我们逐个参数进行详细解析:

1. output

output 通常是通过对输入数据(如文本、图像等)运行某个深度学习模型(例如 BERT、GPT、Transformers 等)后返回的对象,它包含多个属性,其中一个重要的属性是 last_hidden_state

2. last_hidden_state

last_hidden_state 是模型最后一层的隐藏状态,它的形状通常是 (batch_size, sequence_length, hidden_size),具体的含义如下:

  • batch_size:处理的样本数量。在一次前向传播中,模型可以并行处理多个输入样本。

  • sequence_length:输入序列的长度。对于处理文本来说,它表示词汇的数量,可以是句子中词的个数。

  • hidden_size:每个隐藏状态的维度,代表模型的输出特征维数。通常在模型架构中定义,例如对于 BERT-base 隐藏大小为 768。

3. [:, 0, :]

这里的切片操作用来访问 last_hidden_state 的一部分,具体解释如下:

  • ::表示选择所有的样本。由于 batch_size 是第一个维度,所以把 : 放在这个位置表示选取当前批次的所有样本。

  • 0:表示选择第一个时刻的隐藏状态。在 NLP 中,特别是像 BERT 这样的模型中,第一位置的隐藏状态通常对应于 [CLS] token(分类 token),它是用来进行句子级别的任务(例如分类)的。

  • ::表示选择所有的特征维度(hidden_size)。这意味着我们提取每个样本的第一个位置(即 [CLS] token)对应的全部隐藏状态特征。

综合

因此,output.last_hidden_state[:, 0, :] 表示提取所有输入样本的 [CLS] token 的隐藏状态向量,这通常用于下游任务,如文本分类、问答、情感分析等,因为 [CLS] token 的表示通常是整段文本的语义聚合。

例子

如果有一个输入批次大小为 4,并且每个输入序列的隐藏状态大小为 768,那么 output.last_hidden_state[:, 0, :] 的返回结果将是一个形状为 (4, 768) 的张量,其中每一行对应一个输入样本的 [CLS] token 的隐藏状态。

4,训练模型

前面我们已经定义好了数据集,以及文本编码处理,包括增量模型定义。接下来我们来进行增量模型微调训练。

实例代码:

import torch
from datasets import load_dataset
from torch.utils.data import Dataset, DataLoader
from transformers import BertTokenizer, BertModel

import config


# 自定义数据集
class MyDataset(Dataset):

    def __init__(self, split):
        # 加载训练集
        train_dataset = load_dataset(path="csv", data_files="weibo_senti_8k_train.csv")
        # 加载测试集
        test_dataset = load_dataset(path="csv", data_files="weibo_senti_2k_test.csv")
        if split == 'train':
            self.data = train_dataset['train']
        elif split == 'test':
            self.data = test_dataset['train']

    # 获取数据集大小
    def __len__(self):
        return len(self.data)

    # 获取数据集的某个元素
    def __getitem__(self, index):
        sentence = self.data[index]['sentence']
        label = self.data[index]['label']
        return sentence, label


# 加载分词器
tokenizer = BertTokenizer.from_pretrained(config.model_path + '/Bert-base-chinese')

# 使用设备(GPU/CPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

# 加载预训练模型
pretrained_model = BertModel.from_pretrained(config.model_path + '/Bert-base-chinese').to(device)


# 定义下游任务(增量模型)
class DownStreamModel(torch.nn.Module):

    def __init__(self):
        super(DownStreamModel, self).__init__()
        # 下游加一个全连接层,实现二分类任务
        self.fc = torch.nn.Linear(768, 2)

    def forward(self, input_ids, attention_mask, token_type_ids):
        with torch.no_grad():  # 冻结Bert模型参数,不需计算梯度
            # 获取最后一层隐藏层输出
            output = pretrained_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        # 增量模型参与训练
        out = self.fc(output.last_hidden_state[:, 0, :])
        return out


# 对传入数据进行编码
def collate_fn(data):
    sents = [i[0] for i in data]
    labels = [i[1] for i in data]
    # 编码
    # 批量编码句子
    out = tokenizer.batch_encode_plus(
        batch_text_or_text_pairs=sents,  # 输入的文本
        add_special_tokens=True,  # 添加特殊标记
        max_length=80,  # 最大长度
        padding='max_length',  # 填充
        truncation=True,  # 截断
        return_tensors='pt',  # 返回pytorch张量
        return_token_type_ids=True,  # 返回token_type_ids  区分不同句子或段落的类型标识
        return_attention_mask=True,  # 返回attention_mask  标记有效token位置的掩码
        return_special_tokens_mask=True  # 返回special_tokens_mask 标识特殊token(如[CLS]、[SEP])的位置掩码
    )
    return out['input_ids'], out['attention_mask'], out['token_type_ids'], torch.tensor(labels)


# 创建数据集
train_dataset = MyDataset('train')  # 训练集
train_loader = DataLoader(
    dataset=train_dataset,  # 数据集
    batch_size=200,  # 批次大小
    shuffle=True,  # 是否打乱数据
    drop_last=True,  # 丢弃最后一个批次数据
    collate_fn=collate_fn  # 对加载的数据进行编码
)

test_dataset = MyDataset('test')  # 测试集
test_loader = DataLoader(
    dataset=test_dataset,  # 数据集
    batch_size=200,  # 批次大小
    shuffle=True,  # 是否打乱数据
    drop_last=True,  # 丢弃最后一个批次数据
    collate_fn=collate_fn  # 对加载的数据进行编码
)

if __name__ == '__main__':
    model = DownStreamModel().to(device)  # 创建模型
    optimizer = torch.optim.AdamW(model.parameters())  # 优化器
    criterion = torch.nn.CrossEntropyLoss()  # 定义损失函数
    best_val_acc = 0  # 保存最好的准确率
    EPOCH = 3  # 训练轮数
    for epoch in range(EPOCH):  # 训练轮数
        for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_loader):  # 批次数据
            out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device),
                        token_type_ids=token_type_ids.to(device))  # 模型输出
            loss = criterion(out, labels.to(device))  # 计算损失
            optimizer.zero_grad()  # 清空梯度
            loss.backward()  # 反向传播
            optimizer.step()  # 优化器更新参数
            # 每隔5个批次输出训练结果
            if i % 5 == 0:
                out = out.argmax(dim=1)  # 获取预测结果
                acc = (out == labels.to(device)).sum().item() / len(labels)  # 计算准确率
                print("EPOCH:{}--第{}批次--损失:{}--准确率:{}".format(epoch + 1, i + 1, loss.item(), acc))

运行结果:

cuda
EPOCH:1--第1批次--损失:0.5008983016014099--准确率:0.78
EPOCH:1--第6批次--损失:0.5052810907363892--准确率:0.75
EPOCH:1--第11批次--损失:0.4850313067436218--准确率:0.755
EPOCH:1--第16批次--损失:0.4301462471485138--准确率:0.83
EPOCH:1--第21批次--损失:0.39388778805732727--准确率:0.85
EPOCH:1--第26批次--损失:0.3695535361766815--准确率:0.855
EPOCH:1--第31批次--损失:0.35825812816619873--准确率:0.855
EPOCH:1--第36批次--损失:0.3288692533969879--准确率:0.875
EPOCH:2--第1批次--损失:0.31738394498825073--准确率:0.885
EPOCH:2--第6批次--损失:0.3121739625930786--准确率:0.87
EPOCH:2--第11批次--损失:0.30510687828063965--准确率:0.895
EPOCH:2--第16批次--损失:0.305753618478775--准确率:0.865
EPOCH:2--第21批次--损失:0.24456100165843964--准确率:0.92
EPOCH:2--第26批次--损失:0.2233615517616272--准确率:0.93
EPOCH:2--第31批次--损失:0.2816208302974701--准确率:0.89
EPOCH:2--第36批次--损失:0.24931633472442627--准确率:0.915
EPOCH:3--第1批次--损失:0.3053100109100342--准确率:0.885
EPOCH:3--第6批次--损失:0.2515011727809906--准确率:0.9
EPOCH:3--第11批次--损失:0.24241474270820618--准确率:0.915
EPOCH:3--第16批次--损失:0.2211739420890808--准确率:0.925
EPOCH:3--第21批次--损失:0.24276195466518402--准确率:0.91
EPOCH:3--第26批次--损失:0.27010777592658997--准确率:0.895
EPOCH:3--第31批次--损失:0.24304606020450592--准确率:0.9
EPOCH:3--第36批次--损失:0.26476508378982544--准确率:0.905

5,评估模型

模型训练好之后,我们要对模型进行性能评估,以及保存最优模型参数。

示例代码:

import torch
from datasets import load_dataset
from torch.utils.data import Dataset, DataLoader
from transformers import BertTokenizer, BertModel

import config


# 自定义数据集
class MyDataset(Dataset):

    def __init__(self, split):
        # 加载训练集
        train_dataset = load_dataset(path="csv", data_files="weibo_senti_8k_train.csv")
        # 加载测试集
        test_dataset = load_dataset(path="csv", data_files="weibo_senti_2k_test.csv")
        if split == 'train':
            self.data = train_dataset['train']
        elif split == 'test':
            self.data = test_dataset['train']

    # 获取数据集大小
    def __len__(self):
        return len(self.data)

    # 获取数据集的某个元素
    def __getitem__(self, index):
        sentence = self.data[index]['sentence']
        label = self.data[index]['label']
        return sentence, label


# 加载分词器
tokenizer = BertTokenizer.from_pretrained(config.model_path + '/Bert-base-chinese')

# 使用设备(GPU/CPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

# 加载预训练模型
pretrained_model = BertModel.from_pretrained(config.model_path + '/Bert-base-chinese').to(device)


# 定义下游任务(增量模型)
class DownStreamModel(torch.nn.Module):

    def __init__(self):
        super(DownStreamModel, self).__init__()
        # 下游加一个全连接层,实现二分类任务
        self.fc = torch.nn.Linear(768, 2)

    def forward(self, input_ids, attention_mask, token_type_ids):
        with torch.no_grad():  # 冻结Bert模型参数,不需计算梯度
            # 获取最后一层隐藏层输出
            output = pretrained_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        # 增量模型参与训练
        out = self.fc(output.last_hidden_state[:, 0, :])
        return out


# 对传入数据进行编码
def collate_fn(data):
    sents = [i[0] for i in data]
    labels = [i[1] for i in data]
    # 编码
    # 批量编码句子
    out = tokenizer.batch_encode_plus(
        batch_text_or_text_pairs=sents,  # 输入的文本
        add_special_tokens=True,  # 添加特殊标记
        max_length=80,  # 最大长度
        padding='max_length',  # 填充
        truncation=True,  # 截断
        return_tensors='pt',  # 返回pytorch张量
        return_token_type_ids=True,  # 返回token_type_ids  区分不同句子或段落的类型标识
        return_attention_mask=True,  # 返回attention_mask  标记有效token位置的掩码
        return_special_tokens_mask=True  # 返回special_tokens_mask 标识特殊token(如[CLS]、[SEP])的位置掩码
    )
    return out['input_ids'], out['attention_mask'], out['token_type_ids'], torch.tensor(labels)


# 创建数据集
train_dataset = MyDataset('train')  # 训练集
train_loader = DataLoader(
    dataset=train_dataset,  # 数据集
    batch_size=200,  # 批次大小
    shuffle=True,  # 是否打乱数据
    drop_last=True,  # 丢弃最后一个批次数据
    collate_fn=collate_fn  # 对加载的数据进行编码
)

test_dataset = MyDataset('test')  # 测试集
test_loader = DataLoader(
    dataset=test_dataset,  # 数据集
    batch_size=200,  # 批次大小
    shuffle=True,  # 是否打乱数据
    drop_last=True,  # 丢弃最后一个批次数据
    collate_fn=collate_fn  # 对加载的数据进行编码
)

if __name__ == '__main__':
    model = DownStreamModel().to(device)  # 创建模型
    optimizer = torch.optim.AdamW(model.parameters())  # 优化器
    criterion = torch.nn.CrossEntropyLoss()  # 定义损失函数
    best_val_acc = 0  # 保存最好的准确率
    EPOCH = 3  # 训练轮数
    for epoch in range(EPOCH):  # 训练轮数
        for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_loader):  # 批次数据
            out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device),
                        token_type_ids=token_type_ids.to(device))  # 模型输出
            loss = criterion(out, labels.to(device))  # 计算损失
            optimizer.zero_grad()  # 清空梯度
            loss.backward()  # 反向传播
            optimizer.step()  # 优化器更新参数
            # 每隔5个批次输出训练结果
            if i % 5 == 0:
                out = out.argmax(dim=1)  # 获取预测结果
                acc = (out == labels.to(device)).sum().item() / len(labels)  # 计算准确率
                print("EPOCH:{}--第{}批次--损失:{}--准确率:{}".format(epoch + 1, i + 1, loss.item(), acc))

    # 验证模型
    model.eval()  # 评估模式 Dropout关闭
    with torch.no_grad():  # 评估模式,不需要计算梯度
        for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(test_loader):
            out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device),
                        token_type_ids=token_type_ids.to(device))
            out = out.argmax(dim=1)
            acc = (out == labels.to(device)).sum().item() / len(labels)
            if acc > best_val_acc:
                best_val_acc = acc
                torch.save(model.state_dict(), "best_model.pth")
                print(f"测试集准确率EPOCH:{epoch}-第{i}批次:模型保存,准确率:{acc}")
        # 保存最后一轮模型
        torch.save(model.state_dict(), "last_model.pth")
        print(f"最后一轮模型保存,:准确率:{acc}")

运行结果:

cuda
EPOCH:1--第1批次--损失:0.6369971632957458--准确率:0.695
EPOCH:1--第6批次--损失:0.6052521467208862--准确率:0.685
EPOCH:1--第11批次--损失:0.5286625623703003--准确率:0.77
EPOCH:1--第16批次--损失:0.4174182415008545--准确率:0.83
EPOCH:1--第21批次--损失:0.41344115138053894--准确率:0.81
EPOCH:1--第26批次--损失:0.3910037875175476--准确率:0.87
EPOCH:1--第31批次--损失:0.3680213987827301--准确率:0.85
EPOCH:1--第36批次--损失:0.3817676901817322--准确率:0.855
EPOCH:2--第1批次--损失:0.3963841199874878--准确率:0.84
EPOCH:2--第6批次--损失:0.3516421914100647--准确率:0.85
EPOCH:2--第11批次--损失:0.330654114484787--准确率:0.845
EPOCH:2--第16批次--损失:0.3345922529697418--准确率:0.865
EPOCH:2--第21批次--损失:0.3087370693683624--准确率:0.885
EPOCH:2--第26批次--损失:0.25769323110580444--准确率:0.92
EPOCH:2--第31批次--损失:0.2792946696281433--准确率:0.885
EPOCH:2--第36批次--损失:0.29899129271507263--准确率:0.905
EPOCH:3--第1批次--损失:0.3139827847480774--准确率:0.855
EPOCH:3--第6批次--损失:0.27809959650039673--准确率:0.895
EPOCH:3--第11批次--损失:0.2725857198238373--准确率:0.885
EPOCH:3--第16批次--损失:0.30161210894584656--准确率:0.885
EPOCH:3--第21批次--损失:0.26055067777633667--准确率:0.925
EPOCH:3--第26批次--损失:0.22951321303844452--准确率:0.895
EPOCH:3--第31批次--损失:0.2995443642139435--准确率:0.87
EPOCH:3--第36批次--损失:0.3246515691280365--准确率:0.87
测试集准确率EPOCH:2-第0批次:模型保存,准确率:0.975
最后一轮模型保存,:准确率:0.955

把训练好的模型参数进行业务功能封装

前面我们把模型参数训练好了,接下来,我们需要封装下一个接口方法,提供给应用调用。

llm目录下,新建weibo_train.py

import torch
from transformers import BertTokenizer, BertModel

import config

# 加载分词器
tokenizer = BertTokenizer.from_pretrained(config.model_path + '/Bert-base-chinese')

# 使用设备(GPU/CPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

# 加载预训练模型
pretrained_model = BertModel.from_pretrained(config.model_path + '/Bert-base-chinese').to(device)


# 定义下游任务(增量模型)
class DownStreamModel(torch.nn.Module):

    def __init__(self):
        super(DownStreamModel, self).__init__()
        # 下游加一个全连接层,实现二分类任务
        self.fc = torch.nn.Linear(768, 2)

    def forward(self, input_ids, attention_mask, token_type_ids):
        with torch.no_grad():  # 冻结Bert模型参数,不需计算梯度
            # 获取最后一层隐藏层输出
            output = pretrained_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        # 增量模型参与训练
        out = self.fc(output.last_hidden_state[:, 0, :])
        return out


# 对传入数据进行编码
def collate_fn(data):
    # 编码
    # 批量编码句子
    out = tokenizer.batch_encode_plus(
        batch_text_or_text_pairs=data,  # 输入的文本
        add_special_tokens=True,  # 添加特殊标记
        max_length=80,  # 最大长度
        padding='max_length',  # 填充
        truncation=True,  # 截断
        return_tensors='pt',  # 返回pytorch张量
        return_token_type_ids=True,  # 返回token_type_ids  区分不同句子或段落的类型标识
        return_attention_mask=True,  # 返回attention_mask  标记有效token位置的掩码
        return_special_tokens_mask=True  # 返回special_tokens_mask 标识特殊token(如[CLS]、[SEP])的位置掩码
    )
    return out['input_ids'], out['attention_mask'], out['token_type_ids']


model = DownStreamModel().to(device)  # 创建模型
model.load_state_dict(torch.load(config.model_path + "/best_model.pth"))


def check_data(data):
    # 验证模型
    model.eval()  # 评估模式 Dropout关闭
    with torch.no_grad():  # 评估模式,不需要计算梯度
        input_ids, attention_mask, token_type_ids = collate_fn(data)
        out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device),
                    token_type_ids=token_type_ids.to(device))
        output = out.argmax(dim=1)
        sentiment = output.item()
        sentiment_label = "正面" if sentiment == 1 else "负面"
        return sentiment_label


if __name__ == '__main__':
    result = check_data(["我非常难过"])
    print(result)

运行结果:

使用BERT大模型微调对微博热词进行情感分析

找到视图层业务逻辑代码:

改成:

# 改成使用大模型微调进行舆情分析
sentiments = check_data([defaultHotWord])

重启测试,明显比之前大模型微调前的准确率提高很多。

使用BERT大模型对微博文章内容进行情感分析

找到微博舆情分析后端视图业务逻辑代码:

改成:

# 改成使用大模型微调进行舆情分析
sentiments = check_data([article[1]])

重启运行,分析结果明显比大模型微调之前准确率高很多。

使用BERT大模型对微博舆情分析以及可视化操作

找到数据可视化微博舆情分析后端视图层业务逻辑代码,包括柱状图,树形图,饼状图。代码都需要修改;

修改代码后:

@pb.route('sentimentAnalysis')
def sentimentAnalysis():
    """
    舆情数据分析
    :return:
    """
    xHotBarData = ['正面', '负面']
    yHotBarData = [0, 0]
    # 只读取前100条
    df = pd.read_csv('./fenci/comment_fre.csv', nrows=100)
    for value in df.values:
        # 情感分析
        # stc = SnowNLP(value[0]).sentiments
        # if stc > 0.6:
        #     yHotBarData[0] += 1
        # elif stc < 0.2:
        #     yHotBarData[2] += 1
        # else:
        #     yHotBarData[1] += 1

        # 使用大模型进行情感分析
        # sentiment_label = data_classfication(value[0])
        # 使用大模型微调进行情感分析
        sentiment_label = check_data([value[0]])
        if sentiment_label == '正面':
            yHotBarData[0] += 1
        else:
            yHotBarData[1] += 1

    hotTreeMapData = [{
        'name': xHotBarData[0],
        'value': yHotBarData[0]
    }, {
        'name': xHotBarData[1],
        'value': yHotBarData[1]
    }]

    commentPieData = [{
        'name': '正面',
        'value': 0
    }, {
        'name': '负面',
        'value': 0
    }]
    articlePieData = [{
        'name': '正面',
        'value': 0
    }, {
        'name': '负面',
        'value': 0
    }]
    commentList = commentDao.getAllComment()
    for comment in commentList:
        # 情感分析
        # stc = SnowNLP(comment[1]).sentiments
        # if stc > 0.6:
        #     commentPieData[0]['value'] += 1
        # elif stc < 0.2:
        #     commentPieData[2]['value'] += 1
        # else:
        #     commentPieData[1]['value'] += 1
        # 使用大模型进行情感分析
        # sentiment_label = data_classfication(comment[1])
        # 使用大模型微调进行情感分析
        sentiment_label = check_data([comment[1]])
        if sentiment_label == '正面':
            commentPieData[0]['value'] += 1
        else:
            commentPieData[1]['value'] += 1

    articleList = articleDao.getAllArticle()
    for article in articleList:
        # 情感分析
        # stc = SnowNLP(article[1]).sentiments
        # if stc > 0.6:
        #     articlePieData[0]['value'] += 1
        # elif stc < 0.2:
        #     articlePieData[2]['value'] += 1
        # else:
        #     articlePieData[1]['value'] += 1
        # 使用大模型进行情感分析
        # sentiment_label = data_classfication(article[1])
        # 使用大模型微调进行情感分析
        sentiment_label = check_data([article[1]])
        if sentiment_label == '正面':
            articlePieData[0]['value'] += 1
        else:
            articlePieData[1]['value'] += 1

    df2 = pd.read_csv('./fenci/comment_fre.csv', nrows=15)
    xhotData15 = [x[0] for x in df2.values][::-1]
    yhotData15 = [x[1] for x in df2.values][::-1]
    return render_template('sentimentAnalysis.html',
                           xHotBarData=xHotBarData,
                           yHotBarData=yHotBarData,
                           hotTreeMapData=hotTreeMapData,
                           commentPieData=commentPieData,
                           articlePieData=articlePieData,
                           xhotData15=xhotData15,
                           yhotData15=yhotData15)

重启系统测试,准备率得到了质的提升。

应用系统进行【批量】调用大模型,提高系统响应速度

我们之前单个传入大模型进行情感分析,效率略低,我们一般建议批量的进行情感分析,但是传入的数据量也不能太大。我们之前封装的,可以传入一个列表,但是我们不可以传入一个非常大的列表,否则GPU内存会溢出。

我们如果在业务层进行控制传入的元素量,比较麻烦。所以我们封装一个批量导入的方法。

打开weibo_train.py

def check_data(data):
    # 验证模型
    model.eval()  # 评估模式 Dropout关闭
    with torch.no_grad():  # 评估模式,不需要计算梯度
        input_ids, attention_mask, token_type_ids = collate_fn(data)
        out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device),
                    token_type_ids=token_type_ids.to(device))
        output = out.argmax(dim=1)
        # sentiment = output.item()
        # sentiment_label = "正面" if sentiment == 1 else "负面"
        # print("预测结果:", sentiment_label)
        return output.tolist()

# 批量处理
def check_batch_datas(dataList, batch_num):
    outputList = []
    for i in range(0, len(dataList), batch_num):
        batch_data = dataList[i:i + batch_num]
        outputList.extend(check_data(batch_data))
    return outputList

我们以对微博文章进行大模型情感分析模块为例,修改成批量调用:

@pb.route('articleData')
def articleData():
    """
    微博舆情分析
    :return:
    """
    articleOldList = articleDao.getAllArticle()
    # 获取所有帖子标题
    articleTitleList = [article[1] for article in articleOldList]
    # 批量使用大模型进行舆情分析
    sentimentsList = check_batch_datas(articleTitleList, batch_num=10)
    # 追加情感分析属性
    articleNewList = []
    for i in range(len(articleOldList)):
        if sentimentsList[i] == 1:
            articleNewList.append(articleOldList[i] + ('正面',))
        else:
            articleNewList.append(articleOldList[i] + ('负面',))

        # for article in articleOldList:
        #     article = list(article)
        # 情感分析
        sentiments = ''
        # stc = SnowNLP(article[1]).sentiments
        # if stc > 0.6:
        #     sentiments = '正面'
        # elif stc < 0.2:
        #     sentiments = '负面'
        # else:
        #     sentiments = '中性'
        # 改成使用大模型进行舆情分析
        # sentiments = data_classfication(article[1])
        # 改成使用大模型微调进行舆情分析
        # sentiments = check_data([article[1]])
        # article.append(sentiments)
        # articleNewList.append(article)
    return render_template('articleData.html', articleList=articleNewList)

运行,效果还是有显著提高的。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐