💻 源码
from datasets import load_dataset
from torch.utils.data import DataLoader
from transformers import BertTokenizer, BertModel
import torch
import torch.nn as nn
import torch.optim as optim
from tqdm import tqdm


# 加载预训练模型的分词器
tokenizer = BertTokenizer.from_pretrained("../models/bert-base-chinese")
# 加载预训练模型
bert_model = BertModel.from_pretrained("../models/bert-base-chinese")

# 1.数据预处理
def collate_fn(batch):
    # print("batch-->", batch)
    # 从这个batch中,把文本和标签(不用)拿出来
    sentences = [row['text'] for row in batch]
    # print("sentences-->", sentences)
    # 对文本进行数值化+张量化
    data = tokenizer.batch_encode_plus(sentences,
                                       padding='max_length',
                                       max_length=32,
                                       truncation=True,
                                       return_tensors='pt')
    # print("data-->", data)
    # 取出input_ids、token_type_ids、attention_mask
    input_ids = data['input_ids']
    token_type_ids = data['token_type_ids']
    attention_mask = data['attention_mask']
    # print("input_ids-->", input_ids, input_ids.shape) # [4, 32]
    # print("token_type_ids-->", token_type_ids)
    # print("attention_mask-->", attention_mask)
    # 手动构建y
    # 从数据中挖下标=16的token,当做label标签(y值)
    label = input_ids[:, 16].clone()
    # print("label-->", label)
    # 手动构建x
    # print("token1-->", tokenizer.mask_token)      # MASK
    # print("token2-->", tokenizer.mask_token_id)   # 103
    # 对原始数据进行MASK,构建x
    input_ids[:, 16] = tokenizer.mask_token_id
    # print("input_ids-->", input_ids)
    # 返回x和y
    return input_ids, token_type_ids, attention_mask, label


def load_data():
    # 加载数据
    dataset_tmp = load_dataset('csv', data_files='../data/train.csv', split='train')
    # 对数据进行过滤,过滤长度<=32的样本
    # print("len(dataset)--", len(dataset_tmp)) # 9600
    # print("(dataset[:3])--", dataset_tmp[:3])
    mydataset = dataset_tmp.filter(lambda x: len(x['text']) > 32)
    # print("len(dataset)--", len(mydataset)) # 9035

    # 把数据转换为DataLoader
    mydataloader = DataLoader(dataset=mydataset,
                              batch_size=4,
                              shuffle=True,
                              drop_last=True,
                              collate_fn=collate_fn)
    # 遍历 mydataloader
    for input_ids, token_type_ids, attention_mask, label in mydataloader:
        print("input_ids-->", input_ids)
        print("token_type_ids-->", token_type_ids)
        print("attention_mask-->", attention_mask)
        print("label-->", label)
        break


# 2.模型构建
class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.linear = nn.Linear(in_features=768, out_features=tokenizer.vocab_size)

    def forward(self, input_ids, token_type_ids, attention_mask):
        # 让数据先经过预训练模型,然后经过下游线性层
        with torch.no_grad():
            out = bert_model(input_ids=input_ids,
                             token_type_ids=token_type_ids,
                             attention_mask=attention_mask)
        # [4, 32, 768]:4句话,每句话32个token,每个token使用768维度的向量表示
        # print("out.last_hidden_state-->", out.last_hidden_state.shape)
        # out.last_hidden_state[:, 16]: 4句话,16表示每句话中下标为16的token,这就是之前MASK的位置,预测结果
        # return self.linear(out.last_hidden_state[:, 16])
        return self.linear(out.last_hidden_state[:, 16, :])


def test_mymodel():
    dataset_tmp = load_dataset('csv', data_files='../data/train.csv', split='train')
    mydataset = dataset_tmp.filter(lambda x: len(x['text']) > 32)
    # 把数据转换为DataLoader
    mydataloader = DataLoader(dataset=mydataset,
                              batch_size=4,
                              shuffle=True,
                              drop_last=True,
                              collate_fn=collate_fn)
    # 遍历 mydataloader
    model = MyModel()
    for input_ids, token_type_ids, attention_mask, label in mydataloader:
        # 把数据送给模型
        result = model(input_ids=input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask)
        # [4, 21128]:4句话,每句话都是21128个类别
        print("result-->", result.shape) # [4, 21128]
        break


# 3.模型训练
def train_model():
    # 准备数据
    dataset_tmp = load_dataset('csv', data_files='../data/train.csv', split='train')
    mydataset = dataset_tmp.filter(lambda x: len(x['text']) > 32)
    # 把数据转换为DataLoader
    mydataloader = DataLoader(dataset=mydataset,
                              batch_size=4,
                              drop_last=True,
                              shuffle=True,
                              collate_fn=collate_fn)
    # 准备模型
    model = MyModel()
    # 损失函数
    criterion = nn.CrossEntropyLoss()
    # 优化器
    optimizer = optim.AdamW(params=model.parameters(), lr=5e-4)
    # 冻结预训练模型参数
    for param in bert_model.parameters():
        param.requires_grad_(False)
    # 设置轮次epochs
    epochs = 1
    # 调整模型为训练模式
    model.train()
    # 外层遍历
    for epoch in range(epochs):
        # 内层遍历
        for i, (input_ids, token_type_ids, attention_mask, label) in enumerate(tqdm(mydataloader), start=1):
            # 前向传播
            out = model(input_ids=input_ids,
                        token_type_ids=token_type_ids,
                        attention_mask=attention_mask)
            # 损失计算
            loss = criterion(out, label)
            # print("loss-->", loss)
            # 梯度清零
            optimizer.zero_grad()
            # 反向传播
            loss.backward()
            # 梯度更新
            optimizer.step()
            # 打印日志
            if i % 10 == 0:
                # 得到预测结果
                y_pred = torch.argmax(out, dim=-1)
                # 累计预测对的
                accuracy = (y_pred == label).sum().item() / len(label)
                # 打印准确率
                print("epoch:", epoch + 1, "loss:", loss.item(), "accuracy:", accuracy)
        # 模型保存
        torch.save(model.state_dict(), '../save_model/model2_%d.bin' % (epoch + 1))


# 4.模型预测
def eval_model():
    # 加载数据
    dataset_tmp = load_dataset('csv', data_files='../data/test.csv', split='train')
    # 过滤数据
    mydataset = dataset_tmp.filter(lambda x: len(x['text']) > 32)
    # 转换为DataLoader
    mydataloader = DataLoader(dataset=mydataset,
                              shuffle=False,
                              drop_last=True,
                              batch_size=4,
                              collate_fn=collate_fn)
    # 准备模型
    model = MyModel()
    # 加载模型参数
    model.load_state_dict(torch.load('../save_model/model_2.pth'))
    # 设置模型为评估模式
    model.eval()
    # 定义2个变量
    correct = 0
    total = 0
    # 遍历,把数据送给模型,得到预测结果
    for i, (input_ids, token_type_ids, attention_mask, label) in enumerate(tqdm(mydataloader), start=1):
        # 把数据送给模型,得到预测结果
        out = model(input_ids=input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask)
        # 累计预测正确的结果和总样本
        y_pred = torch.argmax(out, dim=-1)
        correct += (y_pred == label).sum().item()
        total += len(label)
    # 计算准确率
    print("accuracy-->", correct / total)


if __name__ == '__main__':
    # load_data()
    # test_mymodel()
    # train_model()
    eval_model()