使用微调BERT进行情感分类任务

2024-05-07

本次实验的Notebook已经上传到了GitHub上,大家可以自行下载查看。

PPT网址

https://bert-r9hswwn.gamma.site/

情感分析模型微调与评估

本文档展示了如何使用BERT模型进行中文情感分析的微调与评估。包括数据准备、模型微调、模型评估等步骤。

1. 导入所需库

首先,我们需要导入相关的Python库和模块。

import torch
from transformers import AutoTokenizer, AutoModel, AdamW
from torch.utils.data import DataLoader, Dataset, random_split
import pandas as pd
from tqdm import tqdm
import random
from transformers import BertForSequenceClassification

2. 下载预训练的BERT模型

使用 modelscope 下载预训练的中文BERT模型。

from modelscope import snapshot_download
model_dir = snapshot_download('tiansz/bert-base-chinese')

3. 加载预训练模型和分词器

加载预训练的BERT模型和分词器。

tokenizer = AutoTokenizer.from_pretrained(model_dir)
model = BertForSequenceClassification.from_pretrained(model_dir)

注意:加载模型时,会有一些警告提示某些权重未初始化,这是正常的,因为我们需要对模型进行微调。

4. 定义数据集类

定义一个 SentimentDataset 类来处理情感分析的数据集。

class SentimentDataset(Dataset):
    def __init__(self, dataframe, tokenizer, max_length=128):
        self.dataframe = dataframe
        self.tokenizer = tokenizer
        self.max_length = max_length

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

    def __getitem__(self, idx):
        text = self.dataframe.iloc[idx]['review']  # 使用标签索引
        label = 1 if self.dataframe.iloc[idx]['label'] == 1 else 0  # 使用标签索引
        encoding = self.tokenizer(text, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt')
        return {
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'labels': torch.tensor(label, dtype=torch.long)
        }

5. 读取和处理数据

读取训练数据文件,并将其处理为数据集对象。

with open("train.txt", "r", encoding="utf-8") as file:
    lines = file.readlines()

data = []
for line in lines:
    label, text = line.strip().split("\t")
    data.append((int(label), text))

df = pd.DataFrame(data, columns=["label", "review"])
dataset = SentimentDataset(df, tokenizer, max_length=128)

6. 划分训练集和验证集

将数据集划分为训练集和验证集。

train_size = int(0.8 * len(dataset))
val_size = len(dataset) - train_size
train_dataset, val_dataset = random_split(dataset, [train_size, val_size])

创建数据加载器。

train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=8, shuffle=False)

7. 设置训练参数

设置训练参数和设备。

optimizer = AdamW(model.parameters(), lr=5e-5)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

8. 训练模型

开始训练模型。

model.train()
for epoch in range(3):
    for batch in tqdm(train_loader, desc="Epoch {}".format(epoch + 1)):
        input_ids = batch['input_ids'].to(device)
        attention_mask = batch['attention_mask'].to(device)
        labels = batch['labels'].to(device)
        
        optimizer.zero_grad()
        outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
        loss = outputs.loss
        loss.backward()
        optimizer.step()

9. 验证模型

在验证集上评估模型。

model.eval()
total_eval_accuracy = 0
for batch in tqdm(val_loader, desc="Evaluating"):
    input_ids = batch['input_ids'].to(device)
    attention_mask = batch['attention_mask'].to(device)
    labels = batch['labels'].to(device)

    with torch.no_grad():
        outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
        logits = outputs.logits
        preds = torch.argmax(logits, dim=1)
        accuracy = (preds == labels).float().mean()
        total_eval_accuracy += accuracy.item()

average_eval_accuracy = total_eval_accuracy / len(val_loader)
print("Validation Accuracy:", average_eval_accuracy)

10. 使用模型进行预测

使用微调后的模型进行情感预测。

def predict_sentiment(sentence):
    inputs = tokenizer(sentence, padding='max_length', truncation=True, max_length=128, return_tensors='pt').to(device)
    with torch.no_grad():
        outputs = model(**inputs)
    logits = outputs.logits
    probs = torch.softmax(logits, dim=1)
    positive_prob = probs[0][1].item()  # 1表示正面
    return positive_prob

def predict(sentence):
    positive_prob = predict_sentiment(sentence)
    threshold = 0.5  # 设置阈值
    if positive_prob > threshold:
        print("正面")
    else:
        print("负面")

predict("这家菜真好吃!")

11. 测试模型性能

加载测试数据集并评估模型性能。

import torch
from torch.utils.data import Dataset

class TestDataset(Dataset):
    def __init__(self, file_path, tokenizer, max_length=128):
        self.data = []
        with open(file_path, "r", encoding="utf-8") as file:
            for line in file:
                label, text = line.strip().split("\t")
                self.data.append((int(label), text))
        self.tokenizer = tokenizer
        self.max_length = max_length

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

    def __getitem__(self, idx):
        label, text = self.data[idx]
        encoding = self.tokenizer(text, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt')
        return {
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'labels': torch.tensor(label, dtype=torch.long)
        }

test_dataset = TestDataset("test.txt", tokenizer, max_length=128)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=8, shuffle=False)

model.eval()
total_eval_accuracy = 0
for batch in tqdm(test_loader, desc="Evaluating"):
    input_ids = batch['input_ids'].to(device)
    attention_mask = batch['attention_mask'].to(device)
    labels = batch['labels'].to(device)

    with torch.no_grad():
        outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
    
    logits = outputs.logits
    preds = torch.argmax(logits, dim=1)
    accuracy = (preds == labels).float().mean()
    total_eval_accuracy += accuracy.item()

average_eval_accuracy = total_eval_accuracy / len(test_loader)
print("Test Accuracy:", average_eval_accuracy)

12. 流程图

以下是整个流程的序列图:

sequenceDiagram
    participant 数据准备
    participant 模型微调
    participant 模型评估
    participant 代码实现
    participant 自动生成测试数据

    数据准备 -> 模型微调: 准备数据集
    模型微调 -> 模型评估: 微调模型
    模型评估 -> 代码实现: 评估模型性能
    代码实现 -> 自动生成测试数据: 编写评估代码
    自动生成测试数据 --> 代码实现: 生成测试数据
    代码实现 -> 模型评估: 使用测试数据评估模型性能

通过上述步骤,我们完成了中文情感分析模型的微调和评估过程。