基于Bert的中文评价情感分析,此项目是基础的语言模型类的项目,基本的流程与其他语言模型类项目相似。当前用的是二分类(正向、负向)。

一、环境准备

IDE

VSCode、vim、pycharm都可以。
其中 pycharm 免费版的下载方式:
到pycharm官网,点击download,拉到页面底部,选设计版下载,设计版免费。

编译环境

准备好anaconda环境,安装依赖包

conda create -n pytorch python=3.10
conda activate pytorch
# 必要时需要开启能连接到源的代理
# export ALL_PROXY=socks5://127.0.0.1:7897 
pip3 install torch torchvision torchaudio
pip install transformers datasets tokenizers

二、编译运行

代码目录

|____run.py
|____MyData.py #格式化数据集
|____params
|____model
| |____bert-base-chinese
| | |____models--bert-base-chinese
| | | |____snapshots
| | | | |____c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f #下载好的模型
| | | | | |____model.safetensors
| | | | | |____tokenizer_config.json
| | | | | |____config.json
| | | | | |____tokenizer.json
| | | | | |____vocab.txt #字库
|____train.py #用于训练模型
|____data

其中 MyData.py、run.py、train.py是本地编写的代码
MyData.py

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

class MyDataset(Dataset):
    #初始化数据集
    def __init__(self,split):
        #从磁盘加载数据
        self.dataset = load_from_disk(r"/Users/wangdong/PycharmProjects/demo_02/data/ChnSentiCorp")
        if split == "train":
            self.dataset = self.dataset["train"]
        elif split == "test":
            self.dataset = self.dataset["test"]
        elif split == "validation":
            self.dataset = self.dataset["validation"]
        else:
            print("数据名错误!")

    #返回数据集长度
    def __len__(self):
        return len(self.dataset)

    #对每条数据单独做处理
    def __getitem__(self, item):
        text = self.dataset[item]["text"]
        label = self.dataset[item]["label"]

        return text,label

if __name__ == '__main__':
    dataset = MyDataset("train")
    for data in dataset:
        print(data)

train.py

#模型训练
import torch
from MyData import MyDataset
from torch.utils.data import DataLoader
from net import Model
from transformers import BertTokenizer
from torch.optim import AdamW

#定义设备信息
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
#定义训练的轮次(将整个数据集训练完一次为一轮)
EPOCH = 30000

#加载字典和分词器
token = BertTokenizer.from_pretrained(r"/Users/wangdong/PycharmProjects/demo_02/model/bert-base-chinese/models--bert-base-chinese/snapshots/c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f")

#将传入的字符串进行编码
def collate_fn(data):
    sents = [i[0]for i in data]
    label = [i[1] for i in data]
    #编码
    data = token.batch_encode_plus(
        batch_text_or_text_pairs=sents,
        # 当句子长度大于max_length(上限是model_max_length)时,截断
        truncation=True,
        max_length=512,
        # 一律补0到max_length
        padding="max_length",
        # 可取值为tf,pt,np,默认为list
        return_tensors="pt",
        # 返回序列长度
        return_length=True
    )
    input_ids = data["input_ids"]
    attention_mask = data["attention_mask"]
    token_type_ids = data["token_type_ids"]
    label = torch.LongTensor(label)
    return input_ids,attention_mask,token_type_ids,label



#创建数据集
train_dataset = MyDataset("train")
train_loader = DataLoader(
    dataset=train_dataset,
    #训练批次
    batch_size=90,
    #打乱数据集
    shuffle=True,
    #舍弃最后一个批次的数据,防止形状出错
    drop_last=True,
    #对加载的数据进行编码
    collate_fn=collate_fn
)
if __name__ == '__main__':
    #开始训练
    print(DEVICE)
    model = Model().to(DEVICE)
    #定义优化器
    optimizer = AdamW(model.parameters())
    #定义损失函数
    loss_func = torch.nn.CrossEntropyLoss()

    for epoch in range(EPOCH):
        for i,(input_ids,attention_mask,token_type_ids,label) in enumerate(train_loader):
            #将数据放到DVEVICE上面
            input_ids, attention_mask, token_type_ids, label = input_ids.to(DEVICE),attention_mask.to(DEVICE),token_type_ids.to(DEVICE),label.to(DEVICE)
            #前向计算(将数据输入模型得到输出)
            out = model(input_ids,attention_mask,token_type_ids)
            #根据输出计算损失
            loss = loss_func(out,label)
            #根据误差优化参数
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            #每隔5个批次输出训练信息
            if i%5 ==0:
                out = out.argmax(dim=1)
                #计算训练精度
                acc = (out==label).sum().item()/len(label)
                print(f"epoch:{epoch},i:{i},loss:{loss.item()},acc:{acc}")
        #每训练完一轮,保存一次参数
        torch.save(model.state_dict(),f"params/{epoch}_bert.pth")
        print(epoch,"参数保存成功!")

run.py

#模型使用接口(主观评估)
#模型训练
import torch
from net import Model
from transformers import BertTokenizer

#定义设备信息
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

#加载字典和分词器
token = BertTokenizer.from_pretrained(r"/Users/wangdong/PycharmProjects/demo_02/model/bert-base-chinese/models--bert-base-chinese/snapshots/c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f")
model = Model().to(DEVICE)
names = ["负向评价","正向评价"]

#将传入的字符串进行编码
def collate_fn(data):
    sents = []
    sents.append(data)
    #编码
    data = token.batch_encode_plus(
        batch_text_or_text_pairs=sents,
        # 当句子长度大于max_length(上限是model_max_length)时,截断
        truncation=True,
        max_length=512,
        # 一律补0到max_length
        padding="max_length",
        # 可取值为tf,pt,np,默认为list
        return_tensors="pt",
        # 返回序列长度
        return_length=True
    )
    input_ids = data["input_ids"]
    attention_mask = data["attention_mask"]
    token_type_ids = data["token_type_ids"]
    return input_ids,attention_mask,token_type_ids

def test():
    #加载模型训练参数
    model.load_state_dict(torch.load("params/11_bert.pth"))
    #开启测试模型
    model.eval()

    while True:
        data = input("请输入测试数据(输入‘q’退出):")
        if data=='q':
            print("测试结束")
            break
        input_ids,attention_mask,token_type_ids = collate_fn(data)
        input_ids, attention_mask, token_type_ids = input_ids.to(DEVICE),attention_mask.to(DEVICE),token_type_ids.to(DEVICE)

        #将数据输入到模型,得到输出
        with torch.no_grad():
            out = model(input_ids,attention_mask,token_type_ids)
            out = out.argmax(dim=1)
            print("模型判定:",names[out],"\n")

if __name__ == '__main__':
    test()

训练

python train.py

运行

python run.py

结果如下:

请输入测试数据(输入‘q’退出):我真的是要感谢你的八辈祖宗。
模型判定: 负向评价 

请输入测试数据(输入‘q’退出):这个饭店物美价廉,下次我再也不来了。
模型判定: 正向评价 

请输入测试数据(输入‘q’退出):什么垃圾玩意儿
模型判定: 负向评价 

请输入测试数据(输入‘q’退出):这个世界上还是好人多啊
模型判定: 负向评价 

可以看出,模型的能力有限,稍稍隐晦一点的语句就判断不出来了。这应该训练数据有关。

三、笔记

1. 语言模型项目的一般步骤

需求/数据:来源于甲方
模型选型/设计:一般选增量的方式
模型训练
|--加载模型训练
|--观察状态
效果评估
|--客观评估(固定指标、客观数据来评价模型是否有效)
|--主观评估(认为挑选部分代表性数据,观察模型输出结果)
部署

2. 需要弄清的关键词

这些词,每个人都有不同的理解,最好用 AI 自行学习。
损失精度、梯度下降、泛化性、欠拟合、拟合、过拟合、

四、常见问题

  1. 安装torch时报错:"ERROR: No matching distribution found for torch"
export ALL_PROXY=socks5://127.0.0.1:7897 #根据自己的代理修改
pip3 install torch torchvision torchaudio
  1. 安装环境时报错ERROR: Could not install packages due to an OSError: Missing dependencies for SOCKS support.
    没有安装pysocks导致的。
unset all_proxy
unset ALL_PROXY
pip install pysocks
  1. 嵌入式 Bert 模型的参数
BertModel(
  (embeddings): BertEmbeddings(
    (word_embeddings): Embedding(21128, 768, padding_idx=0)
    (position_embeddings): Embedding(512, 768)
    (token_type_embeddings): Embedding(2, 768)
    (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
    (dropout): Dropout(p=0.1, inplace=False)
  )
posted on 2025-06-16 12:28  步孤天  阅读(129)  评论(0)    收藏  举报