大家好,我是锋哥。最近连载更新《AI大模型应用开发入门-拥抱Hugging Face与Transformers生态》技术专题。
本课程主要介绍和讲解Hugging Face和Transformers,包括加载预训练模型,自定义数据集,模型推理,模型微调,模型性能评估等。是AI大模型应用开发的入门必备知识。 同时也配套视频教程《2027版 AI大模型应用开发入门-拥抱Hugging Face与Transformers生态 视频教程(无废话版) 玩命更新中~》
GPT-2模型简介
GPT-2(Generative Pretrained Transformer 2)是OpenAI开发的一个自然语言处理模型,基于Transformer架构。它是GPT系列的第二代,主要用于文本生成任务。GPT-2的一个显著特点是它在没有特定任务训练数据的情况下,依靠大规模的无监督预训练,可以生成连贯且流畅的文本。
主要特点:
- 预训练与微调: GPT-2采用了预训练和微调的方式进行训练。首先在大量的文本数据上进行无监督预训练,然后通过微调(fine-tuning)针对特定任务进行优化。
- Transformer架构: 它使用了Transformer模型中的解码器部分,这使得它能够高效地处理语言建模任务。Transformer基于自注意力机制,能够有效捕捉长程依赖关系。3. 生成能力: GPT-2的核心任务是生成与输入相关的文本。这使得它在自动文章生成、对话系统、机器翻译等任务中有广泛的应用。
- 模型规模: GPT-2有不同的版本,其中最大的模型包含15亿个参数,这使得它在生成文本时能够表现出非常高的质量。
GPT-2的工作原理:
1.输入文本: 用户给定一个起始文本(例如一句话或几段文字),GPT-2会以此为基础生成后续的内容。 2. 自回归生成: GPT-2是一个自回归模型,它生成文本时,每次生成一个单词,并将其作为下一次生成的条件。每个生成的词是基于前面生成的所有词来预测的。
应用场景:
- 文本生成: 用于生成文章、诗歌、故事等。
- 自动摘要: 自动为长篇文章生成简短的摘要。
- 对话系统: 为聊天机器人提供文本生成能力,使其能够进行自然的对话。
- 翻译: 用于机器翻译任务。
总的来说,GPT-2模型的简单性体现在其基于Transformer的设计和强大的生成能力上,使得它能够在许多自然语言处理任务中取得优异的表现。
GPT-2支持的中文模型库。
五个模型都是基于GPT-2架构的中文生成模型,但在训练数据、专门领域和适用场景上有显著区别。
| 模型 | 训练数据 | 主要功能 | 风格特点 | 典型应用 |
|---|---|---|---|---|
| gpt2-chinese-cluecorpussmall | 通用中文语料(新闻、百科、问答) | 通用文本生成 | 现代中文,日常用语 | 文章写作、对话生成、内容补全 |
| gpt2-chinese-ancient | 古文典籍(四书五经、史书、文言文) | 古文生成 | 文言文风格,仿古表达 | 古文创作、文言文翻译辅助 |
| gpt2-chinese-couplet | 对联数据库(传统对联) | 对联生成 | 对仗工整,平仄协调 | 创作对联、节日对联、趣味对句 |
| gpt2-chinese-lyric | 现代中文歌词(流行歌曲) | 歌词创作 | 口语化、押韵、情感表达 | 歌词创作、歌曲灵感 |
| gpt2-chinese-poem | 古典诗词(唐诗宋词等) | 诗词创作 | 格律严谨,意象丰富 | 诗词创作、文学创作 |
GPT-2中文文本生成模型实例
1,gpt2-chinese-cluecorpussmall 通用文本生成实例
示例代码:
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.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=100, # 生成的文本总长度
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()
运行结果:
2,gpt2-chinese-ancient 古文生成实例
示例代码:
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-ancient')
# 加载模型
model = AutoModelForCausalLM.from_pretrained('../gpt2-chinese-ancient')
# print(model)
# 设置为评估模式
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=100, # 生成的文本总长度
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()
运行结果:
3,gpt2-chinese-couplet 对联生成实例
实例代码:
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-couplet')
# 加载模型
model = AutoModelForCausalLM.from_pretrained('../gpt2-chinese-couplet')
# print(model)
# 设置为评估模式
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=19, # 生成的文本总长度
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()
运行结果:
4,gpt2-chinese-poem 古典诗词生成实例
实例代码:
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-poem')
# 加载模型
model = AutoModelForCausalLM.from_pretrained('../gpt2-chinese-poem')
# print(model)
# 设置为评估模式
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=35, # 生成的文本总长度
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()
运行结果:
5,gpt2-chinese-lyric 歌词创作生成实例
示例代码:
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-lyric')
# 加载模型
model = AutoModelForCausalLM.from_pretrained('../gpt2-chinese-lyric')
# print(model)
# 设置为评估模式
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=300, # 生成的文本总长度
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()
运行结果:
基于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,生成文本模型微调的评估方式
生成式大模型的微调评估是一个多维度、系统性的工程。以下从评估方法论、评估维度和实践流程三个层面进行梳理:
一、评估方法论分类
- 自动化评估
-
基于规则的评估:BLEU、ROUGE、METEOR(适合翻译、摘要等任务)
-
基于模型的评估:
- BERTScore:使用预训练模型计算语义相似度
- BLEURT:专门训练的评估模型
- GPT-as-judge:使用更强大的LLM(如GPT-4)作为评判者
- FactScore:针对事实性评估的专门指标
- 人工评估
- 评分式评估:从1-5分或1-7分评估回复质量
- 比较评估:将多个模型输出进行两两比较
- 维度专项评估:按流畅度、相关性、安全性等维度分别评估
- 任务特定评估
- 代码生成:Pass@k、测试用例通过率
- 数学推理:准确率、步骤正确性
- 对话系统:连贯性、一致性、用户满意度
二、核心评估维度
- 能力维度
- 指令遵循能力:是否理解并执行用户指令
- 任务完成质量:针对下游任务的专业表现
- 泛化能力:对未见过的指令或任务的适应能力
- 安全与对齐维度
-
安全性评估:
- 有害内容生成率
- 偏见与公平性分析
- 越狱攻击抵抗力
-
价值观对齐:符合人类价值观的程度
- 效率维度
- 推理速度:生成tokens/秒
- 内存使用:显存占用情况
- 部署成本:吞吐量和延迟的综合考量
三、业界主流评估框架
- 综合性基准测试集
- MT-Bench:多轮对话评估基准
- AlpacaEval:基于指令跟随的自动评估
- HELM:全面的语言模型评估框架
- Open LLM Leaderboard:Hugging Face的集成评估
- 中文特定评估
- C-Eval:中文学科知识评估
- CMMLU:中文多任务语言理解
- GAOKAO-Bench:高考题目评测
- 专业领域评估
- MedQA:医学领域评估
- HumanEval:代码生成评估
- MathQA:数学推理评估