一:什么是Model Head

  • Model Head是连接在模型后的层,通常为1个或多个全连接层。
  • Model Head将模型的编码表示结果进行映射,以解决不同类型的任务。

二:Model的基本使用方法

2.1 模型的加载与保存

from transformers import AutoConfig, AutoModel, AutoTokenizer

2.1.1 在线加载

model = AutoModel.from_pretrained("hfl/rbt3")

2.1.2 模型下载

# 方法1:该方法会下载仓库下的所有文件
!git clone "https://huggingface.co/hfl/rbt3"
# 方法2:指定只下载某些文件,比如只下载pytorch框架的模型,而不下载TensorFlow的模型。因为有的仓库会提供几种框架的模型。
!git lfs clone "https://huggingface.co/hfl/rbt3" --include="*.bin"
# 方法3:排除掉仓库中的一些文件
!git lfs clone "https://huggingface.co/hfl/rbt3" --exclude="*.bin"

2.1.3 加载本地保存的模型

model = AutoModel.from_pretrained("rbt3")  # 引号内填写模型的存储地址

2.14 模型加载参数

model.config

输出:

BertConfig {
  "_attn_implementation_autoset": true,
  "_name_or_path": "hfl/rbt3",
  "architectures": [
    "BertForMaskedLM"
  ],
  "attention_probs_dropout_prob": 0.1,
  "classifier_dropout": null,
  "directionality": "bidi",
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 768,
  "initializer_range": 0.02,
  "intermediate_size": 3072,
  "layer_norm_eps": 1e-12,
  "max_position_embeddings": 512,
  "model_type": "bert",
  "num_attention_heads": 12,
  "num_hidden_layers": 3,
  "output_past": true,
  "pad_token_id": 0,
  "pooler_fc_size": 768,
  "pooler_num_attention_heads": 12,
  "pooler_num_fc_layers": 3,
  "pooler_size_per_head": 128,
  "pooler_type": "first_token_transform",
  "position_embedding_type": "absolute",
  "transformers_version": "4.47.1",
  "type_vocab_size": 2,
  "use_cache": true,
  "vocab_size": 21128
}

2.2 模型的调用

sen = "弱小的我也有大梦想!"
tokenizer = AutoTokenizer.from_pretrained("hfl/rbt3")
inputs = tokenizer(sen, return_tensors="pt")
inputs

输出:

{'input_ids': tensor([[ 101, 2483, 2207, 4638, 2769,  738, 3300, 1920, 3457, 2682, 8013,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}

2.2.1 不带Model Head的模型调用

model = AutoModel.from_pretrained("hfl/rbt3", output_attentions=True)
output = model(**inputs)
output

输出:

BaseModelOutputWithPoolingAndCrossAttentions(last_hidden_state=tensor([[[ 0.6804,  0.6664,  0.7170,  ..., -0.4102,  0.7839, -0.0262],
         [-0.7378, -0.2748,  0.5034,  ..., -0.1359, -0.4331, -0.5874],
         [-0.0212,  0.5642,  0.1032,  ..., -0.3617,  0.4646, -0.4747],
         ...,
         
          ...,
          [4.8866e-01, 2.6738e-04, 3.1088e-04,  ..., 9.8990e-04,
           1.7911e-02, 4.8672e-01],
          [4.0732e-01, 3.8137e-02, 9.6832e-03,  ..., 4.4490e-02,
           2.2998e-02, 4.0793e-01],
          [1.7047e-01, 3.6989e-02, 2.3646e-02,  ..., 4.6833e-02,
           2.5233e-01, 1.6721e-01]]]], grad_fn=<SoftmaxBackward0>)), cross_attentions=None)

2.2.2 带Model Head的模型调用

from transformers import AutoModelForSequenceClassification, BertForSequenceClassification
clz_model = AutoModelForSequenceClassification.from_pretrained("rbt3", num_labels=10)
clz_model(**inputs)

输出:

SequenceClassifierOutput(loss=None, logits=tensor([[ 0.3620, -0.2555,  0.3777,  0.0050, -0.0971, -0.0394, -0.8333, -0.0203,
          0.0979,  0.5094]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)

从输出结果可以看到,输出的维度是我们指定的10维。也可以从模型的配置中查看到最后的输出维度。

clz_model.config.num_labels

输出:

10

三:BERT文本分类代码实例

step1 导入相关包

from transformers import AutoTokenizer, AutoModelForSequenceClassification

step2 加载数据

import pandas as pd

data = pd.read_csv("./ChnSentiCorp_htl_all.csv")
data
# 删除空数据
data = data.dropna()
data

step3 创建Dataset

from torch.utils.data import Dataset

"""
1. 创建自己的Dataset,继承torch中的Dataset
2. 实现三个必要的方法:
    初始化方法__init__():初始化父类,加载自己的数据集。
    __getitem__():定义如何返回一条数据。
    __len__():返回数据集中数据的数量。
"""

class MyDataset(Dataset):

    def __init__(self):
        super().__init__()
        self.data = pd.read_csv("./ChnSentiCorp_htl_all.csv")
        self.data = self.data.dropna()

    def __getitem__(self, index):
        return self.data.iloc[index]["review"], self.data.iloc[index]["label"]
    
    def __len__(self):
        return len(self.data)

Step4 划分数据集

有了Dataset数据集后,还需要对数据集进行划分,一部分用于训练,一部分用于验证。

from torch.utils.data import random_split

# random_split 函数:该函数接受一个数据集和一个长度列表,并返回两个(或多个)子集。
# lengths=[0.9, 0.1]:指定分割的比例。在这个例子中,数据集将被分割为 90% 的训练集和 10% 的验证集。
trainset, validset = random_split(dataset, lengths=[0.9, 0.1])
len(trainset), len(validset)

Step5 创建Dataloader

有了划分后的Dataset还不够,因为Dataset中的__getitem__()一次只能取出一条数据,而我们训练时,需要按批次取出数据,所以需要一个数据加载器来满足我们的需求。

在创建数据加载器之前,我们需要定义一个函数来告诉数据加载器如何来处理批次数据(如:一条数据处理成多少维的向量,维度不够如何补全,维度超了是否截断等等)

import torch

tokenizer = AutoTokenizer.from_pretrained("hfl/rbt3")

def collate_func(batch):
    texts, labels = [], []
    for item in batch:
        texts.append(item[0])
        labels.append(item[1])
    inputs = tokenizer(texts, max_length=128, padding="max_length", truncation=True, return_tensors="pt")
# inputs是一个字典,所以可以直接将labels放入字典中
    inputs["labels"] = torch.tensor(labels)
    return inputs

创建数据加载器:

from torch.utils.data import DataLoader

trainloader = DataLoader(trainset, batch_size=32, shuffle=True, collate_fn=collate_func)
validloader = DataLoader(validset, batch_size=64, shuffle=False, collate_fn=collate_func)

Step6 创建模型及优化器

from torch.optim import Adam

model = AutoModelForSequenceClassification.from_pretrained("hfl/rbt3")

if torch.cuda.is_available():
    model = model.cuda()
# 优化器需要知道优化什么,以及如何优化。所以需要传入模型的参数和学习率
optimizer = Adam(model.parameters(), lr=2e-5)

Step7 训练与验证

def evaluate():
    model.eval()
    acc_num = 0
    with torch.inference_mode():
        for batch in validloader:
            if torch.cuda.is_available():
                batch = {k: v.cuda() for k, v in batch.items()}
            output = model(**batch)
            pred = torch.argmax(output.logits, dim=-1)
            acc_num += (pred.long() == batch["labels"].long()).float().sum()
    return acc_num / len(validset)

def train(epoch=3, log_step=100):
    global_step = 0
    for ep in range(epoch):
        model.train()
        for batch in trainloader:
            if torch.cuda.is_available():
                batch = {k: v.cuda() for k, v in batch.items()}
            optimizer.zero_grad()
            output = model(**batch)
            output.loss.backward()
            optimizer.step()
            if global_step % log_step == 0:
                print(f"ep: {ep}, global_step: {global_step}, loss: {output.loss.item()}")
            global_step += 1
        acc = evaluate()
        print(f"ep: {ep}, acc: {acc}")

Step8 模型训练

train()

Step9 模型预测

sen = "我觉得这家酒店不错,饭很好吃!"
id2_label = {0: "差评!", 1: "好评!"}
model.eval()
with torch.inference_mode():
    inputs = tokenizer(sen, return_tensors="pt")
    inputs = {k: v.cuda() for k, v in inputs.items()}
    logits = model(**inputs).logits
    pred = torch.argmax(logits, dim=-1)
    print(f"输入:{sen}\n模型预测结果:{id2_label.get(pred.item())}")

更简单的方法:使用之前我们讲解过的pipeline()

from transformers import pipeline

model.config.id2label = id2_label
pipe = pipeline("text-classification", model=model, tokenizer=tokenizer, device=0)
pipe(sen)

更多推荐