基于GPT2的古诗生成器:LLM训练入门

发布于:2026-05-01 | 分类:machine learning


本项目在了解Transformer基本原理的基础上,从零构建一个会写诗的大语言模型,走通从预训练到监督微调的全流程,见证模型从牙牙学语到像模像样创作诗词的进化过程。核心目标不是训练一个"最强古诗生成器",而是**通过实操理解LLM训练的每一个环节**。

本项目着眼于工程应用,因此不准备从零构建Transformer模型,而是基于HuggingFace生态,直接复用其已经封装好的模型和训练逻辑,从而更多专注于数据集构建、模型配置和训练参数。

本项目所有代码在国家超算互联网免费提供试用的 64GB 显存异构加速卡上运行,特此感谢。

https://github.com/dothinking/llm_learning

项目全景:一条完整的LLM训练流水线

大模型训练的典型流程包括三个阶段:预训练、监督微调(全量SFT、LoRA)和对齐/偏好微调(例如DPO)。预训练让模型“会说人话”(掌握诗词的语言规律),监督微调让模型“听懂人话”(理解对话指令),偏好微调则进一步让模型“说得好听”(对齐人类偏好)。

                                  ┌────────┐                    
                                  │  SFT   ┼──────────────┐     
                                  │  SFT   │              │     
                                  └────▲───┘              │     
                                       │                  │     
┌──────────┐   ┌───────────┐   ┌───────┴──────┐    ┌──────▼────┐
│  poetry  │   │ tokenizer │   │ pre-training │    │ alignment │
│ dataset  ┼───►    BPE    ┼───►     GPT2     ┼────►    DPO    │
└──────────┘   └───────────┘   └───────┬──────┘    └──────▲────┘
                                       │                  │     
                                       │                  │     
                                  ┌────▼───┐              │     
                                  │  SFT   │              │     
                                  │  LoRA  ┼──────────────┘     
                                  └────────┘                    
阶段 目标 输入 → 输出 核心能力
预训练 学习语言的统计规律 诗句前半段 → 续写后半段 格律、韵律、常见意象搭配
SFT 学会遵循指令 "写一首思乡诗" → 完整的诗 指令理解、按需创作
LoRA 高效复现SFT效果 同上 仅训练2.13%参数达到同等效果
DPO 提升生成质量 好诗 vs 差诗 → 偏好对齐 风格优化、质量提升

本项目目录结构:

llm-learning/
├── dataset/                # 数据集
│   ├── dataset/poetry.csv  # 预训练数据集
│   └── dataset/sft.csv     # SFT数据集
├── pre_training/           # 预训练:分词器 + GPT2训练
│   ├── train_tokenizer.py  # 训练BPE分词器
│   ├── train.py            # 预训练主脚本
│   ├── chat.py             # 推理对话
│   └── final_model/        # 预训练产出
├── sft/                    # 监督微调
│   ├── train.py            # SFT训练脚本
│   ├── chat.py             # 推理对话
│   └── final_model/        # SFT产出
├── lora/                   # LoRA微调
│   ├── train.py            # LoRA训练脚本
│   ├── chat.py             # 推理对话
│   └── final_model/        # LoRA适配器权重
├── dpo/                    # 偏好对齐(TODO)
└── chat.py                 # 统一Web界面(FastAPI)

技术选型

本项目基于 PyTorch 和 HuggingFace 生态构建,这是目前LLM开发的事实标准。

角色 具体工具/库 用途
底层框架 PyTorch 张量计算与自动微分
模型调用 Transformers 模型加载、训练和推理
数据处理 Datasets 高效加载CSV/JSON数据集
分词技术 Tokenizers 训练自定义BPE分词器
参数高效微调 PEFT (LoRA) 低秩适配器微调
偏好优化 TRL (DPO) 直接偏好优化训练

动手实践之前,需要先准备好环境:

pip install torch>=2.1.0
pip install transformers>=4.40.0
pip install datasets>=2.18.0
pip install tokenizers>=0.19.0
pip install peft>=0.10.0
pip install trl>=0.8.0
pip install accelerate>=0.28.0

数据集构建

中国历史上留下了海量古诗词,开源社区已经整理好了85万+首的结构化数据,省去了数据清洗的繁琐工作。再加上个人对诗词的一点兴趣,因此选择古诗生成这个场景作为学习的起点。古诗有明确的格律约束(押韵、对仗、字数),但又允许一定的创作自由度。一个72M参数的小模型就能生成"像模像样"的作品,学习的正反馈来得很快。

本项目使用了 GitHub 上的三个开源数据集:

数据集 规模/首 用途
1 Werneror/Poetry 85万+ 预训练(学习诗词语言规律)
2 xiu-ze/Poetry 101万+ SFT数据集构造(含体裁、作者标签)
3 yuting-wei/CCPD 1.7万+ SFT数据集构造(含主题、情感标签)

预训练数据

从数据集1的CSV文件中提取纯诗文本,数据格式如下(训练时只使用了content列的诗词正文)。

title,dynasty,author,content
赠歌者杜氏入道三首 其三,元,潘纯,云髻高梳鬓不分,扫除虚室事元君...
七岁游法兴寺,元,胡天游,山色摇光入袖凉,松阴十丈印回廊...

SFT数据集

基于数据集2和数据集3的原始数据,构造了三类指令-回答对。数据格式统一为:

instruction,answer
创作一首思乡诗,客里春光又可怜,客怀乡思总凄然。风尘未扫燕南地,雨雪偏深蓟北天。...
仿写李白的诗,头陀悬万仞,远眺望华峰。聊借金沙水,洗开九芙蓉。...
指令类型 示例 来源
体裁创作 "以白日依山尽为出句,续写绝句/律诗" 数据集2子集的5.4万+唐诗中随机选取绝句和律诗共计2万条
主题创作 "以思乡为主题,写一首诗/绝句/律诗" 数据集3主题、多标签排列组合得到的3.9万+条记录中随机选取2万条
诗人仿写 "仿写李白的诗" 数据集2子集的5.4万+唐诗中选取13位著名诗人作品共计9256条

模型配置

以上诗词数据集的量级并不是很大,因此参考 GPT2-small 的规模选择了下表所示的模型参数。这个配置比标准GPT2-small(124M)更轻量,但保留了完整的Transformer架构,即便消费级GPU也可轻松完成训练。

参数 说明
总参数量 72.26M 标准GPT2-small为124M
隐藏层维度 768 与标准GPT2-small一致
Transformer层数 8 标准GPT2-small为12层
注意力头数 12 与标准GPT2-small一致
最大序列长度 256 一首诗+指令足够,标准GPT2-small为1024
词表大小 20,001 自定义BPE分词器

模型效果

最后,在深入技术细节之前,先直观感受一下各阶段模型的输出效果。

预训练模型:诗句接龙

预训练模型只会"续写"——给它一个开头,它顺着往下编。虽然有些句子略显生硬,但整体上确实学到了古诗的统计规律,或多或少能够感受到**押韵、对仗、意象**这些诗词元素。

输入: 落霞与孤鹜齐飞

输出

落霞与孤鹜齐飞,近渚连洲锦绮围。若待他年垂钓处,秋风还到钓鱼矶。

水静天青万象开,中流击楫独登台。不知黄鹤楼边月,能照当年霸楚才。

漠漠蒹葭水拍天,烟波浩荡水连天。不知身在潇湘后,多少人家在枕边。

秋风袅袅白蘋洲,杜若香中一钓舟。一片青山无别恨,数声何处起边愁。

万里秋风下洞庭,五湖波浪正冥冥。月明吹笛...

即便给预训练模型一条指令,它也无法理解指令的含义,只能机械地进行“接龙”。

输入

写一首塞外风光律诗

输出

写一首塞外风光律诗,一肩行李去如飞。

江山满目非吾土,岁月催人两鬓丝。

客里光阴如转毂,镜中华发已盈丝。

他年准拟从公醉,莫待秋霜白接䍦。

身行万里无长策,志在三边岂异人。

千里家山归梦遥,百年天地白头新。

春风杨柳堪行乐,夜雨芭蕉可寄人。

他日相思问消息,为言留滞又经春。

十年前此复同庚,老去那堪百感生。

壮志未能忘魏阙,壮心犹未...

SFT模型:按指令创作

经过监督微调后,模型学会了"听指令"。

输入

写一首塞外风光律诗

输出

将军曾射石壕前,今日胡天识汉年。

万里关山随汉节,十年烽火照甘泉。

风悲瀚海吹边月,雪暗天山落塞烟。

此去单于应未灭,汉家恩泽又经年。

模型理解了"塞外风光"的主题要求,生成了"关山"、"烽火"、"瀚海"、"天山"、"单于"等典型边塞意象;同时也理解了“律诗”的体裁要求,押韵、颔联颈联的对仗、平仄(虽然还不完美)都有体现。

LoRA模型:同等效果,仅训练2.13%参数

输入

写一首塞外风光律诗

输出

北塞连秦望,南临瀚海清。

天围平野阔,云接大荒平。

铁骑寒嘶月,雕戈夜啸城。

谁怜羁旅客,辛苦恋浮名!

LoRA模型仅训练了约 154万 参数(占总参数的2.13%),就达到了与全量SFT相当的效果。

总结

本文概述了基于GPT2的古诗生成器项目全貌。项目基于HuggingFace生态,使用85万+首古诗词完成预训练,构造4.9万条指令-回答对进行监督微调,完整走通了LLM训练的核心流程。72M参数的迷你GPT2即便在消费级GPU上也能轻松完成全部训练:预训练模型学会了诗句接龙,SFT模型具备了按主题、体裁、诗人风格创作的能力,LoRA模型仅训练2.13%参数便达到同等效果。

接下来三篇文章将逐一深入每个阶段的技术细节。例如,下一篇将开启 预训练 :喂85万首诗词,让BPE分词器"认识"汉字,让GPT2模型从"牙牙学语"进化到具备"诗句接龙"的能力。