当前位置: 首页 > article >正文

大模型微调|使用LoRA微调Qwen2.5-7B-Instruct完成文本分类任务

前言:近期跑baseline需要用到大模型,本篇博客记录如何使用LoRA完成Qwen2.5-7B-Instruct微调及报错相应解决方法。

目录

  • 模型选取
  • 数据集
  • 代码实现
  • 一些 Debug

模型选取

计算资源:可供使用的显卡是H800(显存80G),因此选取 Qwen-2.5-7B-Instruct + LoRA 的方式进行微调,batch size 为32时,实际使用显存约为72G.(可以通过降低 batch size 的方式降低显存,batch size=8时,显存占用约42G。)

数据集

三个csv文件,分别为训练集、验证集和测试集;每个文件中有descriptionErank两列,分别表示文本和Ground Truth标签。下述代码中的load_data_s(·)函数实现数据读取;TextDataset类构建数据集对象。


代码实现

使用的全部代码如下,使用时将注意路径替换为自己的数据集和模型所在路径。

import os
import torch
import numpy as np
from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments
from peft import get_peft_model, LoraConfig, TaskType
import pandas as pd
from torch.utils.data import Dataset
import torchvision
torchvision.disable_beta_transforms_warning()
os.environ["TOKENIZERS_PARALLELISM"] = "false"  # 禁用TOKENIZERS_PARALLELISM警告

# 自定义数据集类
class TextDataset(Dataset):
    def __init__(self, encodings, labels):
        self.encodings = encodings
        self.labels = labels

    def __getitem__(self, idx):
        item = {key: torch.tensor(val[idx]) for key, val in self.encodings.items()}
        item['labels'] = torch.tensor(self.labels[idx])
        return item

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


def load_data_s(file_path):
    '''
    加载打分数据
    '''
    df = pd.read_csv(file_path)

    texts = [t.__str__().strip() for t in df['descriptionE']]
    labels = np.array(df['rank'])  # rank in [1,2,3,4,5] 
    labels = labels - 1   # #变成 [0,1,2,3,4]

    return texts, labels


if __name__ == "__main__":

    # load data
    train_texts, train_labels = load_data_s("./data/trainset.csv")
    val_texts, val_labels = load_data_s("./data/valset.csv")
    test_texts, test_labels = load_data_s("./data/testset.csv")
    train_labels, val_labels, test_labels = torch.LongTensor(train_labels), torch.LongTensor(val_labels), torch.LongTensor(test_labels)
    train_size = len(train_texts)

    # 加载预训练的Qwen模型和分词器
    model_name_or_path = "./PLMs/Qwen2.5-7B-Instruct"
    tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)
    tokenizer.pad_token = tokenizer.eos_token
    model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, num_labels=5)
    model.config.pad_token_id = tokenizer.pad_token_id

    # 配置LoRA参数
    lora_config = LoraConfig(
        task_type=TaskType.SEQ_CLS,  # 指定任务类型为序列分类
        inference_mode=False,
        r=8,  # 低秩矩阵的秩
        lora_alpha=32,  # LoRA的缩放因子
        lora_dropout=0.1,  # dropout概率
        bias="none",  # 是否调整偏置
        target_modules=["q_proj", "v_proj"],  # 微调目标模块
    )
    print(lora_config.target_modules)

    # 将模型转换为LoRA模型
    model = get_peft_model(model, lora_config)

    # 将数据转换为模型输入格式
    train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=128)
    val_encodings = tokenizer(val_texts, truncation=True, padding=True, max_length=128)
    test_encodings = tokenizer(test_texts, truncation=True, padding=True, max_length=128)

    # 创建数据集对象
    train_dataset = TextDataset(train_encodings, train_labels)
    val_dataset = TextDataset(val_encodings, val_labels)
    test_dataset = TextDataset(test_encodings, test_labels)

    # 定义训练参数
    training_args = TrainingArguments(
        output_dir='./results',
        num_train_epochs=3,
        per_device_train_batch_size=32,
        per_device_eval_batch_size=32,
        warmup_steps=10,
        weight_decay=0.01,
        logging_dir='./logs',
        logging_steps=10,
        evaluation_strategy="epoch",  # 每个周期结束后进行评估
        save_strategy="epoch",  # 每个周期结束后保存模型
        load_best_model_at_end=True,  # 在训练结束时加载效果最好的模型
    )

    # 使用Trainer进行训练
    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=train_dataset,
        eval_dataset=val_dataset,
    )
    
    # 开始训练
    trainer.train()

    # 评估模型在测试集上的表现
    trainer.evaluate(test_dataset)

    # 保存微调后的模型
    model.save_pretrained('./ckpts/lora_qwen_model')

运行时长:约35h


一些 Debug

  1. ValueError: greenlet.greenlet size changed, may indicate binary incompatibility. Expected 152 from C header, got 40 from PyObject

    解决方案:pip install greenlet==1.1.3 gevent==22.8.0

  2. ValueError: Cannot handle batch sizes > 1 if no padding token is defined.

    解决方案:上述代码中添加的这两行:
    tokenizer.pad_token = tokenizer.eos_token
    model.config.pad_token_id = tokenizer.pad_token_id
    (这里只是选取了其中一种方式,当然还有其他pad_token赋值方式,参见此篇文章)

后记:感谢Copilot和GPT-4o对本项目的大力支持!

参考资料

  • 【解决】RuntimeWarning: greenlet.greenlet size changed, may indicate binary incompatibility._valueerror: greenlet.greenlet size changed, may in-CSDN博客
  • python - “AssertionError: Cannot handle batch sizes > 1 if no padding token is > defined” and pad_token = eos_token - Stack Overflow

http://www.kler.cn/a/505146.html

相关文章:

  • 集合帖:区间问题
  • Python的秘密基地--[章节11] Python 性能优化与多线程编程
  • GLM: General Language Model Pretraining with Autoregressive Blank Infilling论文解读
  • 活动预告 | CCF开源发展委员会开源供应链安全技术研讨会(2025第一期)——“大模型时代的开源供应链安全风控技术”...
  • 关于Profinet 从站转 EtherNet/IP 从站网关详细说明
  • CT重建笔记(二)
  • Unity|Tetris|俄罗斯方块复刻1(C#)
  • RK3588-NPU pytorch-image-models 模型编译测试
  • cursor重构谷粒商城01——为何要重构谷粒商城
  • 2025开年解读:AI面试 VS 传统面试本质上区别有哪些?
  • Linux网络知识——网络命名空间虚拟网卡
  • 数据结构知识点
  • Java 面试题 - ArrayList 和 LinkedList 的区别,哪个集合是线程安全的?
  • 接口自动化测试难点—数据库验证解决方案
  • HTTP 头部字段的作用
  • HarmonyOS 鸿蒙 ArkTs(5.0.1 13)实现Scroll下拉到顶刷新/上拉触底加载,Scroll滚动到顶部
  • Object类和hashcode方法
  • 链接加载与ATT汇编
  • 【DevOps】Pipeline功能语法
  • 从0到1搭建推荐系统 -- 数据驱动的算法与架构设计(带数据集)
  • 脚本练习3
  • 统计学习算法——逻辑斯谛回归
  • vue3计算属性
  • G1原理—5.G1垃圾回收过程之Mixed GC
  • 报告分享 | 大语言模型安全和隐私研究综述
  • 使用 WPF 和 C# 绘制覆盖网格的 3D 表面