|
| 1 | +--- |
| 2 | +categories: [machine learning] |
| 3 | +tags: [ai] |
| 4 | +mathjax: true |
| 5 | +--- |
| 6 | + |
| 7 | +# 基于GPT2的古诗生成器:LLM训练入门 |
| 8 | + |
| 9 | +本项目在了解Transformer基本原理的基础上,从零构建一个会写诗的大语言模型,走通从预训练到监督微调的全流程,见证模型从牙牙学语到像模像样创作诗词的进化过程。核心目标不是训练一个"最强古诗生成器",而是**通过实操理解LLM训练的每一个环节**。 |
| 10 | + |
| 11 | +本项目着眼于工程应用,因此不准备从零构建Transformer模型,而是基于HuggingFace生态,直接复用其已经封装好的模型和训练逻辑,从而更多专注于数据集构建、模型配置和训练参数。 |
| 12 | + |
| 13 | +本项目所有代码在[国家超算互联网](https://www.scnet.cn/)免费提供试用的 64GB 显存异构加速卡上运行,特此感谢。 |
| 14 | + |
| 15 | +https://github.com/dothinking/llm_learning |
| 16 | + |
| 17 | +## 项目全景:一条完整的LLM训练流水线 |
| 18 | + |
| 19 | +大模型训练的典型流程包括三个阶段:预训练、监督微调(全量SFT、LoRA)和对齐/偏好微调(例如DPO)。预训练让模型“会说人话”(掌握诗词的语言规律),监督微调让模型“听懂人话”(理解对话指令),偏好微调则进一步让模型“说得好听”(对齐人类偏好)。 |
| 20 | + |
| 21 | +``` |
| 22 | + ┌────────┐ |
| 23 | + │ SFT ┼──────────────┐ |
| 24 | + │ SFT │ │ |
| 25 | + └────▲───┘ │ |
| 26 | + │ │ |
| 27 | +┌──────────┐ ┌───────────┐ ┌───────┴──────┐ ┌──────▼────┐ |
| 28 | +│ poetry │ │ tokenizer │ │ pre-training │ │ alignment │ |
| 29 | +│ dataset ┼───► BPE ┼───► GPT2 ┼────► DPO │ |
| 30 | +└──────────┘ └───────────┘ └───────┬──────┘ └──────▲────┘ |
| 31 | + │ │ |
| 32 | + │ │ |
| 33 | + ┌────▼───┐ │ |
| 34 | + │ SFT │ │ |
| 35 | + │ LoRA ┼──────────────┘ |
| 36 | + └────────┘ |
| 37 | +``` |
| 38 | + |
| 39 | + |
| 40 | +| 阶段 | 目标 | 输入 → 输出 | 核心能力 | |
| 41 | +|------|------|------------|---------| |
| 42 | +| **预训练** | 学习语言的统计规律 | 诗句前半段 → 续写后半段 | 格律、韵律、常见意象搭配 | |
| 43 | +| **SFT** | 学会遵循指令 | "写一首思乡诗" → 完整的诗 | 指令理解、按需创作 | |
| 44 | +| **LoRA** | 高效复现SFT效果 | 同上 | 仅训练2.13%参数达到同等效果 | |
| 45 | +| **DPO** | 提升生成质量 | 好诗 vs 差诗 → 偏好对齐 | 风格优化、质量提升 | |
| 46 | + |
| 47 | + |
| 48 | +本项目目录结构: |
| 49 | + |
| 50 | +``` |
| 51 | +llm-learning/ |
| 52 | +├── dataset/ # 数据集 |
| 53 | +│ ├── dataset/poetry.csv # 预训练数据集 |
| 54 | +│ └── dataset/sft.csv # SFT数据集 |
| 55 | +├── pre_training/ # 预训练:分词器 + GPT2训练 |
| 56 | +│ ├── train_tokenizer.py # 训练BPE分词器 |
| 57 | +│ ├── train.py # 预训练主脚本 |
| 58 | +│ ├── chat.py # 推理对话 |
| 59 | +│ └── final_model/ # 预训练产出 |
| 60 | +├── sft/ # 监督微调 |
| 61 | +│ ├── train.py # SFT训练脚本 |
| 62 | +│ ├── chat.py # 推理对话 |
| 63 | +│ └── final_model/ # SFT产出 |
| 64 | +├── lora/ # LoRA微调 |
| 65 | +│ ├── train.py # LoRA训练脚本 |
| 66 | +│ ├── chat.py # 推理对话 |
| 67 | +│ └── final_model/ # LoRA适配器权重 |
| 68 | +├── dpo/ # 偏好对齐(TODO) |
| 69 | +└── chat.py # 统一Web界面(FastAPI) |
| 70 | +``` |
| 71 | + |
| 72 | + |
| 73 | +## 技术选型 |
| 74 | + |
| 75 | +本项目基于 PyTorch 和 HuggingFace 生态构建,这是目前LLM开发的事实标准。 |
| 76 | + |
| 77 | +| 角色 | 具体工具/库 | 用途 | |
| 78 | +|------|------------|------| |
| 79 | +| 底层框架 | PyTorch | 张量计算与自动微分 | |
| 80 | +| 模型调用 | Transformers | 模型加载、训练和推理 | |
| 81 | +| 数据处理 | Datasets | 高效加载CSV/JSON数据集 | |
| 82 | +| 分词技术 | Tokenizers | 训练自定义BPE分词器 | |
| 83 | +| 参数高效微调 | PEFT (LoRA) | 低秩适配器微调 | |
| 84 | +| 偏好优化 | TRL (DPO) | 直接偏好优化训练 | |
| 85 | + |
| 86 | +动手实践之前,需要先准备好环境: |
| 87 | + |
| 88 | +```bash |
| 89 | +pip install torch>=2.1.0 |
| 90 | +pip install transformers>=4.40.0 |
| 91 | +pip install datasets>=2.18.0 |
| 92 | +pip install tokenizers>=0.19.0 |
| 93 | +pip install peft>=0.10.0 |
| 94 | +pip install trl>=0.8.0 |
| 95 | +pip install accelerate>=0.28.0 |
| 96 | +``` |
| 97 | + |
| 98 | +## 数据集构建 |
| 99 | + |
| 100 | +中国历史上留下了海量古诗词,开源社区已经整理好了85万+首的结构化数据,省去了数据清洗的繁琐工作。再加上个人对诗词的一点兴趣,因此选择古诗生成这个场景作为学习的起点。古诗有明确的格律约束(押韵、对仗、字数),但又允许一定的创作自由度。一个72M参数的小模型就能生成"像模像样"的作品,学习的正反馈来得很快。 |
| 101 | + |
| 102 | +本项目使用了 GitHub 上的三个开源数据集: |
| 103 | + |
| 104 | +|| 数据集 | 规模/首 | 用途 | |
| 105 | +|--------|------|------|------| |
| 106 | +|1| [Werneror/Poetry](https://github.com/Werneror/Poetry) | 85万+ | 预训练(学习诗词语言规律) | |
| 107 | +|2| [xiu-ze/Poetry](https://github.com/xiu-ze/Poetry) | 101万+ | SFT数据集构造(含体裁、作者标签) | |
| 108 | +|3| [yuting-wei/CCPD](https://github.com/yuting-wei/CCPD) | 1.7万+ | SFT数据集构造(含主题、情感标签) | |
| 109 | + |
| 110 | +### 预训练数据 |
| 111 | + |
| 112 | +从数据集1的CSV文件中提取纯诗文本,数据格式如下(训练时只使用了`content`列的诗词正文)。 |
| 113 | + |
| 114 | +```csv |
| 115 | +title,dynasty,author,content |
| 116 | +赠歌者杜氏入道三首 其三,元,潘纯,云髻高梳鬓不分,扫除虚室事元君... |
| 117 | +七岁游法兴寺,元,胡天游,山色摇光入袖凉,松阴十丈印回廊... |
| 118 | +``` |
| 119 | + |
| 120 | +### SFT数据集 |
| 121 | + |
| 122 | +基于数据集2和数据集3的原始数据,构造了三类指令-回答对。数据格式统一为: |
| 123 | + |
| 124 | +```csv |
| 125 | +instruction,answer |
| 126 | +创作一首思乡诗,客里春光又可怜,客怀乡思总凄然。风尘未扫燕南地,雨雪偏深蓟北天。... |
| 127 | +仿写李白的诗,头陀悬万仞,远眺望华峰。聊借金沙水,洗开九芙蓉。... |
| 128 | +``` |
| 129 | + |
| 130 | +| 指令类型 | 示例 | 来源 | |
| 131 | +|---------|------|------| |
| 132 | +| 体裁创作 | "以白日依山尽为出句,续写绝句/律诗" | 数据集2子集的5.4万+唐诗中随机选取绝句和律诗共计2万条 | |
| 133 | +| 主题创作 | "以思乡为主题,写一首诗/绝句/律诗" | 数据集3主题、多标签排列组合得到的3.9万+条记录中随机选取2万条 | |
| 134 | +| 诗人仿写 | "仿写李白的诗" | 数据集2子集的5.4万+唐诗中选取13位著名诗人作品共计9256条 | |
| 135 | + |
| 136 | + |
| 137 | + |
| 138 | +## 模型配置 |
| 139 | + |
| 140 | +以上诗词数据集的量级并不是很大,因此参考 GPT2-small 的规模选择了下表所示的模型参数。这个配置比标准GPT2-small(124M)更轻量,但保留了完整的Transformer架构,即便消费级GPU也可轻松完成训练。 |
| 141 | + |
| 142 | +| 参数 | 值 | 说明 | |
| 143 | +|------|-----|------| |
| 144 | +| 总参数量 | 72.26M | 标准GPT2-small为124M | |
| 145 | +| 隐藏层维度 | 768 | 与标准GPT2-small一致 | |
| 146 | +| Transformer层数 | 8 | 标准GPT2-small为12层 | |
| 147 | +| 注意力头数 | 12 | 与标准GPT2-small一致 | |
| 148 | +| 最大序列长度 | 256 | 一首诗+指令足够,标准GPT2-small为1024 | |
| 149 | +| 词表大小 | 20,001 | 自定义BPE分词器 | |
| 150 | + |
| 151 | + |
| 152 | +## 模型效果 |
| 153 | + |
| 154 | +最后,在深入技术细节之前,先直观感受一下各阶段模型的输出效果。 |
| 155 | + |
| 156 | +### 预训练模型:诗句接龙 |
| 157 | + |
| 158 | +预训练模型只会"续写"——给它一个开头,它顺着往下编。虽然有些句子略显生硬,但整体上确实学到了古诗的统计规律,或多或少能够感受到**押韵、对仗、意象**这些诗词元素。 |
| 159 | + |
| 160 | +> **输入**: |
| 161 | +> 落霞与孤鹜齐飞 |
| 162 | +> |
| 163 | +> **输出**: |
| 164 | +> |
| 165 | +> 落霞与孤鹜齐飞,近渚连洲锦绮围。若待他年垂钓处,秋风还到钓鱼矶。 |
| 166 | +> |
| 167 | +> 水静天青万象开,中流击楫独登台。不知黄鹤楼边月,能照当年霸楚才。 |
| 168 | +> |
| 169 | +> 漠漠蒹葭水拍天,烟波浩荡水连天。不知身在潇湘后,多少人家在枕边。 |
| 170 | +> |
| 171 | +> 秋风袅袅白蘋洲,杜若香中一钓舟。一片青山无别恨,数声何处起边愁。 |
| 172 | +> |
| 173 | +> 万里秋风下洞庭,五湖波浪正冥冥。月明吹笛... |
| 174 | +
|
| 175 | + |
| 176 | +即便给预训练模型一条指令,它也无法理解指令的含义,只能机械地进行“接龙”。 |
| 177 | + |
| 178 | +> **输入**: |
| 179 | +> |
| 180 | +> 写一首塞外风光律诗 |
| 181 | +> |
| 182 | +> **输出**: |
| 183 | +> |
| 184 | +> 写一首塞外风光律诗,一肩行李去如飞。 |
| 185 | +> |
| 186 | +> 江山满目非吾土,岁月催人两鬓丝。 |
| 187 | +> |
| 188 | +> 客里光阴如转毂,镜中华发已盈丝。 |
| 189 | +> |
| 190 | +> 他年准拟从公醉,莫待秋霜白接䍦。 |
| 191 | +> |
| 192 | +> 身行万里无长策,志在三边岂异人。 |
| 193 | +> |
| 194 | +> 千里家山归梦遥,百年天地白头新。 |
| 195 | +> |
| 196 | +> 春风杨柳堪行乐,夜雨芭蕉可寄人。 |
| 197 | +> |
| 198 | +> 他日相思问消息,为言留滞又经春。 |
| 199 | +> |
| 200 | +> 十年前此复同庚,老去那堪百感生。 |
| 201 | +> |
| 202 | +> 壮志未能忘魏阙,壮心犹未... |
| 203 | +
|
| 204 | +### SFT模型:按指令创作 |
| 205 | + |
| 206 | +经过监督微调后,模型学会了"听指令"。 |
| 207 | + |
| 208 | +> **输入**: |
| 209 | +> |
| 210 | +> 写一首塞外风光律诗 |
| 211 | +> |
| 212 | +> **输出**: |
| 213 | +> |
| 214 | +> 将军曾射石壕前,今日胡天识汉年。 |
| 215 | +> |
| 216 | +> 万里关山随汉节,十年烽火照甘泉。 |
| 217 | +> |
| 218 | +> 风悲瀚海吹边月,雪暗天山落塞烟。 |
| 219 | +> |
| 220 | +> 此去单于应未灭,汉家恩泽又经年。 |
| 221 | +
|
| 222 | + |
| 223 | +模型理解了"塞外风光"的主题要求,生成了"关山"、"烽火"、"瀚海"、"天山"、"单于"等典型边塞意象;同时也理解了“律诗”的体裁要求,押韵、颔联颈联的对仗、平仄(虽然还不完美)都有体现。 |
| 224 | + |
| 225 | +### LoRA模型:同等效果,仅训练2.13%参数 |
| 226 | + |
| 227 | +> **输入**: |
| 228 | +> |
| 229 | +> 写一首塞外风光律诗 |
| 230 | +> |
| 231 | +> **输出**: |
| 232 | +> |
| 233 | +> 北塞连秦望,南临瀚海清。 |
| 234 | +> |
| 235 | +> 天围平野阔,云接大荒平。 |
| 236 | +> |
| 237 | +> 铁骑寒嘶月,雕戈夜啸城。 |
| 238 | +> |
| 239 | +> 谁怜羁旅客,辛苦恋浮名! |
| 240 | +
|
| 241 | +LoRA模型仅训练了约 **154万** 参数(占总参数的2.13%),就达到了与全量SFT相当的效果。 |
| 242 | + |
| 243 | + |
| 244 | +## 总结 |
| 245 | + |
| 246 | +本文概述了基于GPT2的古诗生成器项目全貌。项目基于HuggingFace生态,使用85万+首古诗词完成预训练,构造4.9万条指令-回答对进行监督微调,完整走通了LLM训练的核心流程。72M参数的迷你GPT2即便在消费级GPU上也能轻松完成全部训练:预训练模型学会了诗句接龙,SFT模型具备了按主题、体裁、诗人风格创作的能力,LoRA模型仅训练2.13%参数便达到同等效果。 |
| 247 | + |
| 248 | +接下来三篇文章将逐一深入每个阶段的技术细节。例如,下一篇将开启 **预训练** :喂85万首诗词,让BPE分词器"认识"汉字,让GPT2模型从"牙牙学语"进化到具备"诗句接龙"的能力。 |
0 commit comments