基于GPT-2通用文本模型全量微调训练

大家好,我是python222_小锋老师,最近更新《AI大模型应用开发入门-拥抱Hugging Face与Transformers生态》专辑,感谢大家支持。

本课程主要介绍和讲解Hugging Face和Transformers,包括加载预训练模型,自定义数据集,模型推理,模型微调,模型性能评估等。是AI大模型应用开发的入门必备知识。

基于GPT-2通用文本模型全量微调训练

我们使用中文通用文本模型gpt2-chinese-cluecorpussmall,因为这个模型不具备对联文本生成能力,所以我们使用自己的对联数据集去完整训练模型,全量微调训练,最终训练出一个能实现对联文本生成的模型。

1,自定义数据集

首先我们准备下训练数据。

60多万条训练集数据。

我们之所以要自定义数据集,是因为需要去适配训练模型需要的数据格式。

自定义数据集示例代码:

复制代码
from datasets import load_dataset
from torch.utils.data import Dataset
​
​
# 自定义数据集
class MyDataset(Dataset):
​
    def __init__(self, split):
        # 加载训练集
        train_dataset = load_dataset(path="csv", data_files="./couplet_train_600k.csv")
        self.data = train_dataset['train']
​
    # 获取数据集大小
    def __len__(self):
        return len(self.data)
​
    # 获取数据集的某个元素
    def __getitem__(self, index):
        return self.data[index]['text1']
​
​
if __name__ == '__main__':
    train_dataset = MyDataset('train')
    # print(train_dataset[0])
    for data in train_dataset:
        print(data)

运行结果:

2,对训练输入文本进行编码

对传入的数据进行训练之前,我们需要对数据进行编码。

我们通过分词器的batch_encode_plus方法进行批量编码;

实例代码:

复制代码
from transformers import AutoTokenizer
​
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained('../gpt2-chinese-cluecorpussmall')
​
# 准备测试文本
sents = ['东风揉碎西江月 金惢染浓玉堂春', '涤净尘埃真境界 修成正果雅风光', '青史应同久 紫书倘可传']
​
# 批量编码句子
out = tokenizer.batch_encode_plus(
    batch_text_or_text_pairs=sents,  # 输入的文本
    add_special_tokens=True,  # 添加特殊标记
    max_length=50,  # 最大长度
    padding='max_length',  # 填充
    truncation=True,  # 截断
    return_tensors='pt'  # 返回pytorch张量
)
print(out)
for k, v in out.items():
    print(k, v)
​
# 解码文本数据
for i in range(len(sents)):
    print(sents[i] + "--编码后:", tokenizer.decode(out['input_ids'][i]))

运行结果:

3,定义全量微调,实现模型训练任务

我们使用中文通用文本模型gpt2-chinese-cluecorpussmall,因为这个模型不具备对联文本生成能力,所以我们使用自己的对联数据集去完整训练模型,全量微调训练,最终训练出一个能实现对联文本生成的模型。

示例代码:

复制代码
import torch
from datasets import load_dataset
from torch.utils.data import DataLoader, Dataset
from transformers import AutoTokenizer, AutoModelForCausalLM
​
​
# 自定义数据集
class MyDataset(Dataset):
​
    def __init__(self, split):
        # 加载训练集
        train_dataset = load_dataset(path="csv", data_files="./couplet_train_600k.csv")
        self.data = train_dataset['train']
​
    # 获取数据集大小
    def __len__(self):
        return len(self.data)
​
    # 获取数据集的某个元素
    def __getitem__(self, index):
        return self.data[index]['text1']
​
​
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained('../gpt2-chinese-cluecorpussmall')
​
# 使用设备(GPU/CPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
​
# 加载模型
model = AutoModelForCausalLM.from_pretrained('../gpt2-chinese-cluecorpussmall').to(device)
​
​
# 对传入数据进行编码
def collate_fn(data):
    # 批量编码句子
    out = tokenizer.batch_encode_plus(
        batch_text_or_text_pairs=data,  # 输入的文本
        add_special_tokens=True,  # 添加特殊标记
        max_length=50,  # 最大长度
        padding='max_length',  # 填充
        truncation=True,  # 截断
        return_tensors='pt'  # 返回pytorch张量
    )
    out['labels'] = out['input_ids'].clone()  # 创建标签 克隆一份
    return out
​
​
# 创建数据集
train_dataset = MyDataset('train')  # 训练集
train_loader = DataLoader(
    dataset=train_dataset,  # 数据集
    batch_size=10,  # 批次大小
    shuffle=True,  # 是否打乱数据
    drop_last=True,  # 丢弃最后一个批次数据
    collate_fn=collate_fn  # 对加载的数据进行编码
)
​
if __name__ == '__main__':
    EPOCH = 3  # 训练轮数
    optimizer = torch.optim.AdamW(model.parameters())  # 优化器
​
    model.train()  # 设置成训练模式
    for epoch in range(EPOCH):
        for i, data in enumerate(train_loader):
            for j in data.keys():  # 遍历数据
                data[j] = data[j].to(device)  # 移动数据到设备
            out = model(**data)  # 前向传播
            loss = out.loss  # 计算损失
            loss.backward()  # 反向传播
            optimizer.step()  # 优化器更新参数
            optimizer.zero_grad()  # 清空梯度
            if i % 30 == 0:  # 每隔30个批次输出训练结果
                labels = data['labels'][:, 1:]  # 获取真实标签,移除第一个特殊标记
                out = out['logits'].argmax(dim=2)[:, :-1]  # 获取预测结果,移除最后一个特殊标记
                acc = (out == labels).sum().item() / labels.numel()
                print("EPOCH:{}--第{}批次--损失:{}--准确率:{}".format(epoch + 1, i + 1, loss.item(), acc))
            if i % 1000 == 0:
                torch.save(model.state_dict(), "best_model.pth")
    print("训练完成")
    torch.save(model.state_dict(), "last_model.pth")

运行结果:

复制代码
EPOCH:1--第42721批次--损失:1.6447025537490845--准确率:0.7306122448979592
EPOCH:1--第42751批次--损失:1.6567798852920532--准确率:0.7224489795918367
EPOCH:1--第42781批次--损失:1.7314213514328003--准确率:0.7122448979591837
EPOCH:1--第42811批次--损失:1.4304875135421753--准确率:0.7489795918367347
EPOCH:1--第42841批次--损失:1.7530544996261597--准确率:0.7
EPOCH:1--第42871批次--损失:1.4136962890625--准确率:0.7489795918367347
EPOCH:1--第42901批次--损失:1.335880160331726--准确率:0.7877551020408163
EPOCH:1--第42931批次--损失:1.6236571073532104--准确率:0.7448979591836735
EPOCH:1--第42961批次--损失:1.5450268983840942--准确率:0.7326530612244898
EPOCH:1--第42991批次--损失:1.1529603004455566--准确率:0.8
EPOCH:1--第43021批次--损失:1.5731669664382935--准确率:0.7163265306122449
EPOCH:1--第43051批次--损失:1.4738476276397705--准确率:0.7408163265306122
EPOCH:1--第43081批次--损失:1.4993581771850586--准确率:0.736734693877551
EPOCH:1--第43111批次--损失:1.4280855655670166--准确率:0.7591836734693878
EPOCH:1--第43141批次--损失:1.8130102157592773--准确率:0.689795918367347
EPOCH:1--第43171批次--损失:1.3389391899108887--准确率:0.7693877551020408
EPOCH:1--第43201批次--损失:1.27996027469635--准确率:0.7591836734693878
EPOCH:1--第43231批次--损失:1.3075250387191772--准确率:0.7653061224489796
EPOCH:1--第43261批次--损失:1.2324328422546387--准确率:0.7816326530612245
EPOCH:1--第43291批次--损失:1.4553524255752563--准确率:0.7591836734693878
EPOCH:1--第43321批次--损失:1.3804436922073364--准确率:0.7653061224489796
EPOCH:1--第43351批次--损失:1.5597093105316162--准确率:0.7489795918367347
EPOCH:1--第43381批次--损失:1.602893352508545--准确率:0.7142857142857143
EPOCH:1--第43411批次--损失:1.3732272386550903--准确率:0.773469387755102
EPOCH:1--第43441批次--损失:1.560001015663147--准确率:0.746938775510204
EPOCH:1--第43471批次--损失:1.5979629755020142--准确率:0.746938775510204
EPOCH:1--第43501批次--损失:1.350143313407898--准确率:0.7714285714285715
EPOCH:1--第43531批次--损失:1.367422342300415--准确率:0.773469387755102
EPOCH:1--第43561批次--损失:1.5746468305587769--准确率:0.7387755102040816

训练时间非常长。我们训练到第一个轮次 4万多条数据的时候,来加载模型权重参数测试下;

测试代码:

复制代码
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
​
​
def test_text_generation():
    # 使用设备(GPU/CPU)
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    # print(device)
​
    # 加载分词器
    tokenizer = AutoTokenizer.from_pretrained('../gpt2-chinese-cluecorpussmall')
​
    # 加载模型
    model = AutoModelForCausalLM.from_pretrained('../gpt2-chinese-cluecorpussmall')
    # print(model)
​
    # 加载保存的模型权重
    model.load_state_dict(torch.load('best_model.pth'))
​
    # 设置为评估模式
    model.eval()
​
    model.to(device)
​
    # 准备输入数据
    input_ids = tokenizer.encode(
        text='春满神州花似锦-',  # 输入文本
        return_tensors='pt'  # 返回PyTorch张量
    ).to(device)
​
    # 生成文本
    output_sequences = model.generate(
        input_ids=input_ids,
        max_length=25,  # 生成的文本总长度
        num_return_sequences=1,  # 返回的生成序列数量
        no_repeat_ngram_size=2,  # 避免重复的n-gram 防止相同词组重复出现,从而提高生成文本的多样性和自然性。
        temperature=0.7,  # 温度参数控制随机性
        top_k=50,  # 仅从前k个概率最高的单词中采样
        top_p=0.95,  # 只从前95%概率质量的词汇中进行随机采样 核采样策略
        do_sample=True  # 开启采样
    )
​
    # print(output_sequences)
​
    # 解码并打印生成的文本
    for sequence in output_sequences:
        generated_text = tokenizer.decode(sequence, skip_special_tokens=True)
        print(generated_text)
​
​
if __name__ == '__main__':
    for i in range(3):
        test_text_generation()

运行结果:

总体看来,还是有点效果的。虽然训练还不足。但是我们发现一个明显的问题,格式有时候不对,原因是一些未识别的生僻词,不在词汇表里,返回了UNK。模型是负责输出,但是格式的话,我们得通过编码进行控制。比如下方判断长度。

复制代码
# 解码并打印生成的文本
    for sequence in output_sequences:
        generated_text = tokenizer.decode(sequence, skip_special_tokens=True)
        if len(generated_text) == 29:
            print(generated_text)
4,生成文本模型微调的评估方式

生成式大模型的微调评估是一个多维度、系统性的工程。以下从评估方法论、评估维度和实践流程三个层面进行梳理:

一、评估方法论分类
  1. 自动化评估
  • 基于规则的评估:BLEU、ROUGE、METEOR(适合翻译、摘要等任务)

  • 基于模型的评估

    • BERTScore:使用预训练模型计算语义相似度

    • BLEURT:专门训练的评估模型

    • GPT-as-judge:使用更强大的LLM(如GPT-4)作为评判者

    • FactScore:针对事实性评估的专门指标

  1. 人工评估
  • 评分式评估:从1-5分或1-7分评估回复质量

  • 比较评估:将多个模型输出进行两两比较

  • 维度专项评估:按流畅度、相关性、安全性等维度分别评估

  1. 任务特定评估
  • 代码生成:Pass@k、测试用例通过率

  • 数学推理:准确率、步骤正确性

  • 对话系统:连贯性、一致性、用户满意度

二、核心评估维度
  1. 能力维度
  • 指令遵循能力:是否理解并执行用户指令

  • 任务完成质量:针对下游任务的专业表现

  • 泛化能力:对未见过的指令或任务的适应能力

  1. 安全与对齐维度
  • 安全性评估

    • 有害内容生成率

    • 偏见与公平性分析

    • 越狱攻击抵抗力

  • 价值观对齐:符合人类价值观的程度

  1. 效率维度
  • 推理速度:生成tokens/秒

  • 内存使用:显存占用情况

  • 部署成本:吞吐量和延迟的综合考量

三、业界主流评估框架
  1. 综合性基准测试集
  • MT-Bench:多轮对话评估基准

  • AlpacaEval:基于指令跟随的自动评估

  • HELM:全面的语言模型评估框架

  • Open LLM Leaderboard:Hugging Face的集成评估

  1. 中文特定评估
  • C-Eval:中文学科知识评估

  • CMMLU:中文多任务语言理解

  • GAOKAO-Bench:高考题目评测

  1. 专业领域评估
  • MedQA:医学领域评估

  • HumanEval:代码生成评估

  • MathQA:数学推理评估

相关推荐
向量引擎1 天前
[硬核架构] 2026 企业级 AI 网关落地指南:从“连接超时”到“秒级响应”的架构演进(附 Python/Java 源码)
人工智能·python·gpt·ai作画·架构·aigc·api调用
百***78753 天前
【实操】一步API对接GPT-5.2全流程(多语言示例+高并发优化+避坑指南)
网络·gpt
墨_浅-3 天前
BERT与GPT:Transformer的双子星
gpt·bert·transformer
百***78753 天前
一步API+GPT-5.2生产级落地指南:架构设计+高可用+成本控制
开发语言·gpt·架构
程序员:钧念4 天前
机器学习与深度学习的区别
人工智能·python·gpt·深度学习·机器学习
没学上了5 天前
Vlm-GPT简介
gpt
百***07456 天前
GPT-5.2:重构通用智能边界,迈向生产级AI新纪元
开发语言·python·gpt
百***78757 天前
Grok-4.1与GPT-5.2深度对比:技术差异、适用场景及Python集成指南
java·python·gpt
独自破碎E7 天前
解释一下向量数据库中的HNSW、LSH和PQ
gpt·语言模型