找回密码
 立即注册
首页 业界区 业界 pytorch入门 - 微调huggingface大模型

pytorch入门 - 微调huggingface大模型

赏听然 昨天 18:39
在自然语言处理(NLP)领域,预训练语言模型如BERT已经成为主流。HuggingFace提供的Transformers库让我们能够方便地使用这些强大的模型。
本文将详细介绍如何使用PyTorch微调HuggingFace上的BERT模型,包括原理讲解、代码实现和逐行解释。
1. 微调原理

1.1 什么是微调(Fine-tuning)

微调是指在预训练模型的基础上,针对特定任务进行少量训练的过程。
BERT等预训练模型已经在大规模语料上学习了通用的语言表示能力,通过微调,我们可以将这些知识迁移到特定任务上。
1.2 BERT模型结构

BERT模型主要由以下部分组成:

  • 嵌入层(Embedding Layer)
  • 多层Transformer编码器
  • 池化层(Pooler)
在微调时,我们通常会在BERT的输出上添加一个任务特定的分类头(Classification Head)。
1.3 神经元数量计算

在我们的模型中,分类头是一个全连接层,其神经元数量计算如下:
  1. 输入维度:768 (BERT最后一层隐藏状态维度)
  2. 输出维度:2 (二分类任务)
  3. 参数数量 = (输入维度 × 输出维度) + 输出维度(偏置项)
  4.           = (768 × 2) + 2 = 1538
复制代码
2. 代码实现

2.1 数据集处理 (finetuing_my_dataset.py)
  1. from datasets import load_dataset, load_from_disk  # 导入HuggingFace的数据集加载工具
  2. from torch.utils.data import Dataset  # 导入PyTorch的数据集基类
  3. class MydataSet(Dataset):  # 自定义数据集类,继承自PyTorch的Dataset
  4.     def __init__(self, split):  # 初始化方法,split指定数据集划分
  5.         save_path = r".\cache\datasets\lansinuote\ChnSentiCorp\train"  # 数据集路径
  6.         self.dataset = load_from_disk(save_path)  # 从磁盘加载数据集
  7.         
  8.         # 根据split参数选择数据集划分
  9.         if split == "train":
  10.             self.dataset = self.dataset["train"]
  11.         elif split == "test":
  12.             self.dataset = self.dataset["test"]
  13.         elif split == "validation":
  14.             self.dataset = self.dataset["validation"]
  15.         else:
  16.             raise ValueError("split must be one of 'train', 'test', or 'validation'")
  17.     def __len__(self):  # 返回数据集大小
  18.         return len(self.dataset)
  19.     def __getitem__(self, idx):  # 获取单个样本
  20.         return self.dataset[idx]["text"], self.dataset[idx]["label"]  # 返回文本和标签
  21. if __name__ == "__main__":  # 测试代码
  22.     dataset = MydataSet(split="validation")  # 创建验证集实例
  23.     for i in range(50):  # 打印前50个样本
  24.         print(dataset[i])
  25.     print(dataset)  # 打印数据集信息
  26.     print(dataset[0])  # 打印第一个样本
复制代码
2.2 模型定义 (finetuing_net.py)
  1. from transformers import BertModel  # 导入BERT模型
  2. import torch  # 导入PyTorch
  3. # 设置设备(GPU或CPU)
  4. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  5. # 加载预训练BERT模型
  6. cache_dir = "./cache/bertbasechinese"  # 缓存目录
  7. pretrained = BertModel.from_pretrained(
  8.     "bert-base-chinese",  # 中文BERT模型
  9.     cache_dir=cache_dir
  10. ).to(device)  # 移动到指定设备
  11. class Model(torch.nn.Module):  # 自定义模型类
  12.     def __init__(self):
  13.         super(Model, self).__init__()  # 调用父类初始化
  14.         # 定义分类头: 768维输入, 2维输出(二分类)
  15.         self.fc = torch.nn.Linear(768, 2)  
  16.     def forward(self, input_ids, attention_mask=None, token_type_ids=None):
  17.         # 冻结BERT参数,不计算梯度
  18.         with torch.no_grad():
  19.             outputs = pretrained(
  20.                 input_ids=input_ids,  # 输入token IDs
  21.                 attention_mask=attention_mask,  # 注意力掩码
  22.                 token_type_ids=token_type_ids,  # 句子类型IDs
  23.             )
  24.         
  25.         # 使用[CLS]标记的隐藏状态作为分类特征
  26.         cls_output = outputs.last_hidden_state[:, 0]  # 形状(batch_size, 768)
  27.         logits = self.fc(cls_output)  # 通过分类头
  28.         out = logits.softmax(dim=-1)  # softmax归一化
  29.         return out
复制代码
2.3 训练过程 (finetuing_train.py)
  1. import torch
  2. from finetuing_my_dataset import MydataSet
  3. from torch.utils.data import DataLoader
  4. from finetuing_net import Model
  5. from transformers import BertTokenizer
  6. from torch.optim import AdamW
  7. # 设置设备
  8. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  9. EPOCH = 100  # 训练轮数
  10. # 加载分词器
  11. token = BertTokenizer.from_pretrained(
  12.     "bert-base-chinese",
  13.     cache_dir="./cache/tokenizer/bert-base-chinese"
  14. )
  15. def collate_fn(batch):  # 数据批处理函数
  16.     sentes = [item[0] for item in batch]  # 提取文本
  17.     labels = [item[1] for item in batch]  # 提取标签
  18.    
  19.     # 使用分词器处理文本
  20.     data = token.batch_encode_plus(
  21.         sentes,
  22.         truncation=True,  # 截断过长的文本
  23.         max_length=350,  # 最大长度350
  24.         padding=True,  # 自动填充
  25.         return_tensors="pt",  # 返回PyTorch张量
  26.         return_length=True,  # 返回长度信息
  27.     )
  28.    
  29.     # 提取编码后的数据
  30.     input_ids = data["input_ids"]
  31.     attention_mask = data["attention_mask"]
  32.     token_type_ids = data["token_type_ids"]
  33.     labels = torch.LongTensor(labels)  # 转换标签为LongTensor
  34.    
  35.     return input_ids, attention_mask, token_type_ids, labels
  36. # 创建训练数据集和数据加载器
  37. train_dataset = MydataSet(split="train")
  38. train_dataloader = DataLoader(
  39.     train_dataset,
  40.     batch_size=32,  # 批大小32
  41.     shuffle=True,  # 打乱数据
  42.     drop_last=True,  # 丢弃最后不完整的批次
  43.     collate_fn=collate_fn,  # 使用自定义批处理函数
  44. )
  45. if __name__ == "__main__":
  46.     model = Model().to(device)  # 初始化模型
  47.     optimizer = AdamW(model.parameters(), lr=1e-5)  # 优化器
  48.     loss_func = torch.nn.CrossEntropyLoss()  # 损失函数
  49.    
  50.     model.train()  # 设置为训练模式
  51.     for epoch in range(EPOCH):  # 训练循环
  52.         for step, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_dataloader):
  53.             # 移动数据到设备
  54.             input_ids = input_ids.to(device)
  55.             attention_mask = attention_mask.to(device)
  56.             token_type_ids = token_type_ids.to(device)
  57.             labels = labels.to(device)
  58.             
  59.             # 前向传播
  60.             outputs = model(
  61.                 input_ids=input_ids,
  62.                 attention_mask=attention_mask,
  63.                 token_type_ids=token_type_ids,
  64.             )
  65.             loss = loss_func(outputs, labels)  # 计算损失
  66.             
  67.             # 反向传播和优化
  68.             optimizer.zero_grad()
  69.             loss.backward()
  70.             optimizer.step()
  71.             
  72.             # 每5步打印训练信息
  73.             if step % 5 == 0:
  74.                 out = outputs.argmax(dim=1)  # 预测类别
  75.                 acc = (out == labels).sum().item() / len(labels)  # 计算准确率
  76.                 print(f"Epoch: {epoch + 1}/{EPOCH}, Step: {step + 1}/{len(train_dataloader)}, Loss: {loss.item():.4f}, Acc: {acc:.4f}")
  77.         
  78.         # 保存模型
  79.         torch.save(model.state_dict(), f"./model/{epoch}finetuned_model_new.pth")
  80.         print(epoch, "参数保存成功")
复制代码
2.4 测试过程 (finetuing_test.py)
  1. import torch
  2. from finetuing_my_dataset import MydataSet
  3. from torch.utils.data import DataLoader
  4. from finetuing_net import Model
  5. from transformers import BertTokenizer
  6. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  7. # 加载分词器
  8. token = BertTokenizer.from_pretrained(
  9.     "bert-base-chinese",
  10.     cache_dir="./cache/tokenizer/bert-base-chinese"
  11. )
  12. def collate_fn(batch):  # 与训练时相同的批处理函数
  13.     sentes = [item[0] for item in batch]
  14.     labels = [item[1] for item in batch]
  15.     data = token.batch_encode_plus(
  16.         sentes,
  17.         truncation=True,
  18.         max_length=350,
  19.         padding=True,
  20.         return_tensors="pt",
  21.         return_length=True,
  22.     )
  23.     input_ids = data["input_ids"]
  24.     attention_mask = data["attention_mask"]
  25.     token_type_ids = data["token_type_ids"]
  26.     labels = torch.LongTensor(labels)
  27.     return input_ids, attention_mask, token_type_ids, labels
  28. # 创建测试数据集和数据加载器
  29. train_dataset = MydataSet(split="test")
  30. train_dataloader = DataLoader(
  31.     train_dataset,
  32.     batch_size=32,
  33.     shuffle=True,
  34.     drop_last=True,
  35.     collate_fn=collate_fn,
  36. )
  37. if __name__ == "__main__":
  38.     acc = 0  # 正确预测数
  39.     total = 0  # 总样本数
  40.     model = Model().to(device)  # 初始化模型
  41.     model.load_state_dict(torch.load("./model/3finetuned_model.pth"))  # 加载训练好的模型
  42.    
  43.     model.eval()  # 设置为评估模式
  44.    
  45.     for step, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_dataloader):
  46.         # 移动数据到设备
  47.         input_ids = input_ids.to(device)
  48.         attention_mask = attention_mask.to(device)
  49.         token_type_ids = token_type_ids.to(device)
  50.         labels = labels.to(device)
  51.         
  52.         # 前向传播(不计算梯度)
  53.         outputs = model(
  54.             input_ids=input_ids,
  55.             attention_mask=attention_mask,
  56.             token_type_ids=token_type_ids,
  57.         )
  58.         
  59.         out = outputs.argmax(dim=1)  # 预测类别
  60.         acc += (out == labels).sum().item()  # 累加正确预测数
  61.         total += len(labels)  # 累加总样本数
  62.    
  63.     print(acc / total)  # 输出准确率
复制代码
2.5 交互式预测 (finetuing_run.py)
  1. import torch
  2. from finetuing_net import Model
  3. from transformers import BertTokenizer
  4. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  5. # 类别名称
  6. names = [
  7.     "负向评价",  # 类别0
  8.     "正向评价",  # 类别1
  9. ]
  10. model = Model().to(device)  # 初始化模型
  11. # 加载分词器
  12. token = BertTokenizer.from_pretrained(
  13.     "bert-base-chinese",
  14.     cache_dir="./cache/tokenizer/bert-base-chinese"
  15. )
  16. def collate_fn(data):  # 单样本处理函数
  17.     sentes = []
  18.     sentes.append(data)  # 将输入文本加入列表
  19.    
  20.     # 使用分词器处理文本
  21.     data = token.batch_encode_plus(
  22.         sentes,
  23.         truncation=True,
  24.         padding="max_length",
  25.         max_length=350,
  26.         return_tensors="pt",
  27.         return_length=True,
  28.     )
  29.    
  30.     input_ids = data["input_ids"]
  31.     attention_mask = data["attention_mask"]
  32.     token_type_ids = data["token_type_ids"]
  33.    
  34.     return input_ids, attention_mask, token_type_ids
  35. def test():
  36.     model.load_state_dict(torch.load("./model/2finetuned_model.pth"))  # 加载训练好的模型
  37.     model.eval()  # 设置为评估模式
  38.    
  39.     while True:  # 交互式循环
  40.         text = input("请输入文本:")  # 获取用户输入
  41.         if text == "q":  # 输入q退出
  42.             print("退出测试")
  43.             break
  44.         
  45.         # 处理输入文本
  46.         input_ids, attention_mask, token_type_ids = collate_fn(text)
  47.         input_ids = input_ids.to(device)
  48.         attention_mask = attention_mask.to(device)
  49.         token_type_ids = token_type_ids.to(device)
  50.         
  51.         # 预测(不计算梯度)
  52.         with torch.no_grad():
  53.             outputs = model(input_ids, attention_mask, token_type_ids)
  54.             out = outputs.argmax(dim=1)  # 预测类别
  55.             print("模型预测", names[out], "\n")  # 输出预测结果
  56. if __name__ == "__main__":
  57.     test()  # 启动测试
复制代码
3. 关键点解析

3.1 数据处理流程


  • ​数据集加载​​:使用HuggingFace的load_from_disk加载预处理好的数据集
  • ​文本编码​​:使用BertTokenizer将文本转换为模型可接受的输入格式
  • ​批处理​​:collate_fn函数负责将多个样本打包成一个批次
3.2 模型结构


  • ​预训练BERT​​:固定参数,仅作为特征提取器
  • ​分类头​​:可训练的全连接层,将BERT输出映射到任务特定的类别空间
3.3 训练策略


  • ​优化器选择​​:使用AdamW优化器,适合Transformer模型
  • ​学习率​​:较小的学习率(1e-5)避免破坏预训练学到的知识
  • ​评估指标​​:准确率和交叉熵损失
4. 总结

本文详细介绍了如何使用PyTorch微调HuggingFace上的BERT模型,包括:

  • 数据集处理与加载
  • 模型定义与微调策略
  • 训练、测试和交互式预测的实现
  • 关键代码的逐行解释
通过微调预训练模型,我们可以在相对较小的数据集上获得良好的性能,这是现代NLP应用中的常用技术。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册