【小结】人工智能原理实践课T4 - Stable Diffusion v1.5 + LoRA 的古诗水墨画生成 文字编码器微调
第一部分:作业完成小结
1. 作业目标
1.1 任务最初是怎么开始的
这次会话最早并不是从“写完整课程报告”开始,而是从一个具体故障开始:
已经完成 Stable Diffusion v1.5 的 LoRA 微调,但微调后的水墨画和微调前几乎一样,需要定位原因。
因此,最初目标是排查 LoRA 为什么看起来没有生效。当时首先把问题拆成:
训练是否真的更新 LoRA → 权重是否正确保存 → 推理是否加载正确权重 → Adapter 是否真正参与推理 → 最后才判断模型有没有学到水墨风格。
很快发现了两个高优先级问题:训练输出目录与推理加载目录不一致,以及 PEFT LoRA 的注入方式和旧式 Diffusers 保存方式混用。也就是说,早期“效果没变化”首先是一个训练—保存—加载链路问题,而不是简单的“模型学不会”。fileciteturn1file0L11-L43
1.2 后来正式明确的课程作业要求
随着会话推进,用户补充了正式课程要求,任务扩展为六项:
- 使用开源项目部署 Stable Diffusion,跑通文生图流程,输入 5 条英文 Prompt,并详细说明代码原理、操作和算法流程。
- 学习开源项目后自行编写类似代码,加载 jpg-csv 或 parquet 数据集并进行模型微调。
- 自行寻找其他数据集,再完成一次模型微调。
- 分析第 (2) 项模型的实际效果、问题及改进方向。
- 调整生成参数,或者换模型,以获得更好的生成效果。
- 微调 Stable Diffusion 的其他组件,例如 Text Encoder,或进行 DreamBooth LoRA,并展示微调前后差异。fileciteturn1file1L337-L349
因此,任务的范围发生过明确变化:
最初:修复“LoRA 为什么没效果”
后来:完成六项课程实验
最后:整理九章课程报告并完成 LaTeX 正式交付。
这不是从一开始就设计好的完整路线,而是在故障排查过程中逐渐扩展形成的。
1.3 中途补充并最终影响交付的限制
除了六项实验要求,后续又形成了一批实际约束。
实验层面:
- 基础模型统一使用 Stable Diffusion v1.5。
- 自建数据集最终采用
image_file + caption + poem三列。 - 正式 Before/After 比较必须控制 Prompt、Negative Prompt、Scheduler、Steps、CFG、Seed、分辨率等变量。
- 第 (3) 项额外数据集使用 parquet 格式的
poem_cartoon_images。 - 最终模型不能仅根据训练 loss 选择,而要通过固定测试 Prompt 比较不同 checkpoint 的实际生成效果。
- 第 (2) 与第 (6) 最终不能错误地区分成“是否训练 Text Encoder”,因为实际代码检查发现基础训练本身也给 Text Encoder 注入过 LoRA;真正的区别应是基础/单通道 poem 实验与caption + poem 双通道语义实验。
报告层面:
- 报告按照课程任务形成完整章节。
- 保留真实失败和局限,不能为了“结果漂亮”夸大。
- 后来删除“健壮性设计”“实验简介”“5.3 格式转换”等非重点内容。
- 正文不再出现大量具体路径、代码文件名、代码块和重复训练配置。
- 除参数对比章节外,不大量罗列具体超参数。
- Loss 改为行内公式。
- 第六章问题采用 enumerate,按“问题—表现—影响”组织。
- 报告先从约 37 页压缩,但 10 页版本又被用户否定,最终要求变成正好 30 页且不能过度精简。fileciteturn1file4L573-L609
- 最终进入内容冻结阶段,不再修改正文,只调整格式。
- 封面不显示页码;目录使用罗马数字
I;正文重新从阿拉伯数字1开始。L11-L52
2. 最终完成结果
2.1 最终采用的整体技术路线
最终实际形成的实验主线是:
原始 SD1.5 文生图 → 自建古诗-水墨画数据集 → 基础 LoRA → 额外 parquet 数据集 LoRA → 效果分析 → 参数优化 → UNet + Text Encoder 双通道 LoRA → 报告与 LaTeX 交付。
后期用户已经明确确认六项实验全部跑完。fileciteturn1file2L392-L405
2.2 六项实验最终结果
| 项目 | 最终实际完成内容 | 最终结论 |
|---|---|---|
| (1) 原始文生图 | 原始 SD1.5 输入 5 条英文 Prompt | 成功跑通部署、推理、采样与保存流程;原始模型对英文 Prompt 响应较好 |
| (2) 自建数据基础 LoRA | 使用自建水墨画图像 + metadata.csv,进行基础古诗 LoRA 微调 |
流程跑通,但语义与古诗匹配较差,水墨风格也不稳定 |
| (3) 额外数据集 | 使用 poem_cartoon_images parquet 数据集训练独立 LoRA |
风格迁移明显成功,表现出插画/版画、近似浮世绘特征,但中文古诗语义仍错位 |
| (4) 效果分析 | 分析基础 LoRA 的问题 | 核心问题集中在中文理解弱、poem—image 弱对齐、风格不稳定及训练数据文字干扰 |
| (5) 参数实验 | 比较 DPM++、DDIM、Euler a、Steps、CFG 等 | Euler a 更自然细腻;DPM++/DDIM 更稳定地呈现黑白水墨,其中 DDIM 较细腻 |
| (6) 双通道改进 | caption + poem,联合 UNet LoRA 与 Text Encoder LoRA | 是整套实验中效果最好的方案,开始捕捉鸟、山、古寺、松、水流以及“空”“辽阔”等意境,但仍未完全解决语义对齐 |
这些实际实验观察是后来正式写报告的事实基础。fileciteturn1file3L473-L480
2.3 自建数据集成果
最终数据集以 Kaggle Chinese Landscape Painting Dataset 为图像来源:
- 原始约 2192 张中国山水画;
- 实际使用 1000 张;
- 图像统一到 512×512;
caption:英文视觉描述;poem:根据图像反向生成的中文古诗;- 最终数据结构为:
image_file, caption, poem
这套数据不仅支持基础 LoRA,也为最终的 caption-poem 双通道实验提供了数据基础。L539-L553
2.4 最终模型方案
最终效果最佳的方案可以概括为:
**Stable Diffusion v1.5
- UNet LoRA
- Text Encoder LoRA
- caption / poem 双通道信息
- 固定水墨风格触发词
- 英文水墨风格模板
- 中文古诗。**
这里必须保留一个重要限定:
早期对“逐 token 的 caption-poem MSE 对齐”曾提出过理论质疑——中文诗句和英文 caption 的 token 位置与语义结构并不天然一一对应,同一动态 Text Encoder 也不是严格意义上的固定教师,因此这种对齐损失不能被描述成“实现完美语义对齐”。fileciteturn1file0L225-L254
最终实验仍然把 caption 作为语义桥梁使用,但正确结论只能是“促进对齐、取得部分改善”,不能说已经彻底解决跨语言语义问题。
2.5 最终报告成果
最终形成了完整课程报告,实验、模型结构图、数据集流程图、参数实验图和 Before/After 图均被整合进去。
随后完成了:
Markdown → 指定 LaTeX 模板 → 编译错误修复 → 篇幅调整 → 页码最终处理。
最终报告经历:
约 37 页 → 过度精简到 10 页 → 用户否定 → 恢复必要分析 → 正好 30 页。fileciteturn1file4L573-L609
最后不再改正文,只修页码:
- 封面:不显示页码;
- 目录:Roman,从
I开始; - 正文:Arabic,从
1开始。fileciteturn2file0L25-L52
因此,到会话最终阶段,实验、报告正文和正式 LaTeX 交付链路都已经完成。
3. 完成过程
阶段一:先定位“为什么 LoRA 没效果”
最开始最重要的判断是:
不应先增加 Epoch 或继续调学习率,而应先证明 LoRA 是否真的被使用。
按照训练、保存、加载、推理逐层排查后,发现:
- 训练输出路径与推理加载路径不一致;
- PEFT 注入和旧 Diffusers 保存方式混用;
- 原推理脚本可能在找不到 LoRA 时静默继续执行。
因此第一轮修改不是模型算法创新,而是把LoRA 生命周期修通。-L81
阶段二:重写完整的训练与验证链
随后不再零散打补丁,而是重新整理:
训练脚本
微调前推理
微调后推理
LoRA 生效验证
README / 运行顺序
并统一保存成 pytorch_lora_weights.safetensors,推理阶段一次性加载完整 adapter。
这个决策直接解决了早期“训练一套、保存一套、推理又用另一套”的混乱。
阶段三:实际运行暴露接口和数据问题
新代码运行后很快出现第一个实际错误:
代码要求 img_file
CSV 实际列名 image_file
因此 Dataset 加载器改为兼容两种列名。
随后验证脚本又出现:
unrecognized arguments: --poem
最终直接把验证脚本重写成一个明确支持单条古诗、同 Prompt 同 Seed 对比 Base 与 LoRA 的版本。
这一阶段说明:真正跑代码后暴露出的 schema 和 CLI 问题,比理论讨论更优先。
阶段四:明确训练过程本身是否正常
训练日志确认:
- UNet LoRA 注入成功;
- Text Encoder LoRA 注入成功;
- 只有少量低秩参数参与训练;
- checkpoint 能定期保存;
- loss 有波动但没有 NaN 或爆炸。
同时澄清了两个概念:
一个 Epoch 本来就包含很多 Training Step。
1000 张图、batch size 1、梯度累积 4 时,大约每 Epoch 产生 250 次 optimizer update。
另外,没有使用传统按训练 loss 的 Early Stopping。扩散训练中随机 timestep 和噪声会造成 loss 波动,因此最终更合理的方法变成:
定期保存 checkpoint → 固定验证 Prompt → 比较生成图 → 选择视觉效果最佳 Epoch。
阶段五:发现 Before/After 实验本身不公平
新版 infer_before.py 生成结果与旧版不同。
最初怀疑模型不同,但比较代码后确认基础模型路径完全相同,真正变化的是:
- Prompt;
- Negative Prompt;
- Seed 使用方式。
旧版:
原始 SD1.5 + 直接中文古诗
新版:
原始 SD1.5 + ink_wash_style + 英文水墨模板 + 中文古诗
因此两组图片不可能相同。
这个发现改变了后续正式对比设计:
若要证明 LoRA 的效果,Before 和 After 必须使用完全一样的 Prompt 和随机条件,唯一差别应为“是否加载 LoRA”。
这成为后续第 (6) 项正式 Before/After 的控制变量规则。
阶段六:正式课程要求出现,实验重新按评分点归位
用户随后给出完整六项作业要求。
这时一个非常关键的纠偏是:
infer_before.py只是生成基准图,它本身绝对不能算“完成模型微调”。
因此把六项工作重新映射到:
(1) 原始模型
(2) 自建数据基础微调
(3) 额外数据集
(4) 模型效果分析
(5) 参数实验
(6) 组件级进一步微调
并决定额外 parquet 数据集仍然放在第 (3) 项,而不是为了实验顺序方便移动到第 (6) 后面。正式作业结构开始优先于聊天顺序。fileciteturn1file1L350-L376
阶段七:完成额外 parquet 数据集实验
额外数据集最终采用:
image
text
两列 parquet。
先转换成:
image_file, caption
然后:
- 冻结 VAE;
- 冻结 Text Encoder;
- 只训练 UNet LoRA;
- 英文
text作为 caption; - 推理时仍用中文古诗进行测试。
这一实验最终非常有价值,因为它产生了一个清晰结果:
画风改变得很成功,但语义依然不匹配。
因此后续第一次能够把问题分离成:
UNet 风格学习问题和Text Encoder / 中文语义问题。
阶段八:再次遇到 LoRA 权重加载错误
cartoon LoRA 推理时报:
Target modules {... base_model.model...} not found in the base model
问题定位到保存后的 key 带有:
base_model.model.
而原始 Diffusers UNet 的模块路径不带这一层包装。
当时先提供 checkpoint key 修复脚本,同时修改后续保存逻辑,避免之后 checkpoint 继续产生同样问题。
这个问题进一步强化了一个判断:
“checkpoint 文件存在”远远不等于“checkpoint 是可以被目标模型正确恢复的”。
阶段九:六项实验结束后,先整理事实再写报告
实验全部完成后,没有立即继续编故事,而是先让用户填写:
- 六项任务完成情况;
- 训练参数;
- 生成效果;
- 参数实验观察;
- 自建数据集信息。
这个步骤非常关键。
用户明确给出了真正观察到的现象,例如:
- 基础 LoRA:效果一般,语义与古诗错位;
- cartoon LoRA:画风改变很成功,但语义仍错位;
- Euler a:画面最自然细腻;
- 双通道:已经能捕捉鸟、山、古寺、客、松、水流以及“空”“千里”的空间意境;
- 后期训练出现过度学习题字/文字的倾向。citeturn1file3L475-L480
后续报告的实验结论开始基于这些事实,而不是由 AI 根据理论自行补全。
阶段十:报告事实又进行了一轮代码审计
在正式报告之前,还纠正了一个很重要的叙事错误。
早期为了让第 (2) 和第 (6) 看起来区别明显,曾建议:
第 (2):只训练 UNet
第 (6):UNet + Text Encoder
后来检查实际 train_lora.py 才发现:
第 (2) 的基础实验本身也微调了 Text Encoder LoRA。
因此最终正确区别改成:
第 (2):以 poem 为主的基础/单通道训练
第 (6):充分使用 caption + poem 双通道进行进一步语义适配
这是一个非常典型的例子:报告分类必须服从真实代码,而不能为了章节漂亮倒过来修改技术事实。
阶段十一:逐章写完正式课程报告和配图
报告最终形成九章主线:
- 绪论与实验环境
- 原始 SD1.5 文生图
- 自建双通道数据集
- 基础 LoRA
- 额外 parquet 数据集
- 效果分析与改进
- 参数实验
- UNet + Text Encoder 双通道 LoRA
- 总结与展望
期间还生成了:
- 数据集构建流程图;
- 基础模型结构图;
- 第八章双通道模型架构图;
- Before/After 对比;
- 参数实验图等。
阶段十二:Markdown → LaTeX 后进入交付调试
最初直接把 Markdown 转进模板后出现大量 Pandoc 痕迹,例如:
p{(\columnwidth - 2\tabcolsep) * \real{0.3514}}
导致:
Undefined control sequence
Illegal unit of measure
此外还发现:
- Windows 路径
\被 LaTeX 当成控制命令; - Markdown 章标题层级转换错误;
- Pandoc 代码块依赖环境过多;
- 多图表格 caption 不完整。
最终处理方式包括:
- 修正/替换 Pandoc 生成的复杂表格;
- 补必要的
calc支持; - 路径统一改
/; - 统一章节层级;
- 复杂图片布局直接手写
figure + tabular + minipage; - 简化为模板原生可编译结构;
- 使用 XeLaTeX 连续编译验证。
阶段十三:篇幅发生一次明显返工
最初版本约 37 页。
用户要求不超过 30 页后,第一版压缩到了 10 页。
虽然形式上满足“≤30”,但用户明确否定:
“不要太精简,我要求正好30页。”
因此要求在这一阶段又发生了变化:
原要求:不超过 30 页
后续明确:正好 30 页,而且必须保留足够论述
最终不是通过塞空白页,而是恢复有价值的结果解释、课程任务完成说明并调整图文比例,得到正好 30 页的版本。L591-L609
阶段十四:最终 Content Freeze
最后用户明确:
“不要改动任何内容性的东西,只调格式。”
这意味着实验结论、章节文字、图片和表格全部冻结。
最终只修:
封面:无页码
目录:Roman I
正文:Arabic 1
此后任务真正进入交付完成状态。fileciteturn2file0L11-L52
4. 关键问题与解决方法
| 问题 | 为什么出现 | 怎么发现 | 怎么修正 | 得到的经验 |
|---|---|---|---|---|
| LoRA 微调前后几乎一样 | 推理路径加载的不是刚训练出的 checkpoint | 对比训练输出目录和 inference 路径 | 统一训练/保存/加载目录,并对缺失权重强制报错 | 生成效果不变时,先查权重链路 |
| PEFT 与 Diffusers 保存方式混用 | 注入和保存来自两套接口 | 审计 get_peft_model 与 save_attn_procs |
统一使用可被 pipeline 正确恢复的 LoRA state dict / save_lora_weights |
Adapter 生命周期必须使用兼容接口 |
img_file / image_file 不一致 |
代码根据记忆假定了 CSV schema | 第一次正式训练直接 ValueError | Dataset 兼容两种列名并清理列名空格 | 长训练前先做 schema preflight |
| 验证脚本参数不匹配 | 文件版本和运行命令没有锁定 | argparse 报 --poem 不支持 |
重写明确的验证脚本 | 不能假定当前目录里的脚本就是预期版本 |
| 看到 loss 不降就怀疑训练异常 | 扩散训练本身有随机 timestep/noise | 观察 loss 波动但无 NaN/爆炸 | 不按训练 loss 做简单 early stop,用 checkpoint 生成质量选择 | 评价指标必须匹配任务类型 |
| 新旧 Before 图片不同 | Prompt、Negative Prompt、Seed 都变了 | 逐项比较两个 inference 脚本 | 正式 Before/After 固定所有变量,仅改变 LoRA | 控制变量比“图片差异明显”更重要 |
| 第 (2) 与第 (6) 的技术区别写错 | 为了章节区分,早期过度简化为“是否微调 Text Encoder” | 后来重新查实际代码 | 改成“基础 poem 单通道”与“caption+poem 双通道” | 报告结构必须服从真实实现 |
| cartoon LoRA 无法加载 | PEFT wrapper 前缀进入 state dict key | Diffusers 报 target module 不存在 | 修 key,并修改以后保存逻辑 | checkpoint 必须做 key/shape/target-module 验证 |
| 模型后期生成类似文字 | 水墨训练图中本身存在题字、印章 | 多 checkpoint 视觉比较 | 报告中保留为限制,并提出清洗无题字数据 | 模型会学习数据中“不想要但稳定存在”的相关特征 |
LaTeX 出现 \real、单位错误 |
Pandoc 自动生成表格依赖模板没有的表达式 | XeLaTeX 编译错误 | 改表格、补必要宏包、手工重写复杂布局 | 转换成功不等于目标模板可编译 |
| 37 页压成 10 页 | “≤30页”被执行成过度压缩;之后用户又明确改成“正好30页” | 用户反馈太精简 | 恢复必要分析,控制图文密度到 30 页 | 页数要求和信息密度是两个独立约束 |
| 最终页码错误 | \thispagestyle{empty} 隐藏目录页码,正文切换不完整 |
最终格式检查 | Roman TOC + Arabic body | 内容冻结后只改渲染层 |
还有一个发生在总结阶段、但很值得保留的问题:后期用户曾提供几段总结,其中混入了另一项“MNIST/LeNet5 垃圾分类”任务。最终没有把其中的准确率、SE 模块等事实硬拼进本作业,只吸收了可复用的方法论。file1L75-L91
这说明历史总结同样需要做来源隔离。
5. 重要决策
决策一:先证明 LoRA 真生效,再讨论模型能力
没有一开始就继续加 Epoch、换网络或换数据。
原因很简单:
如果推理根本没加载 LoRA,那么任何“模型为什么学不会”的分析都没有意义。
这是整个任务最重要的第一决策。
决策二:正式 Before/After 使用严格控制变量
最终决定:
Prompt 相同
Negative Prompt 相同
Scheduler 相同
Steps 相同
CFG 相同
Seed 相同
分辨率相同
基础模型相同
唯一变化:
是否加载 LoRA
这使最终第八章的“微调前后差异”具备实验意义。
决策三:第 (3) 的额外数据集仍按课程编号单独完成
虽然技术上可以放到后面,但最终没有为了时间线方便改变作业结构。
因为课程第 (3) 本身明确要求:
自行寻找其他数据集并微调。
所以 poem_cartoon_images 被保留为一个独立实验。
决策四:第 (2) 与第 (6) 最终按“单通道/双通道”区分
早期方案:
(2) UNet
(6) UNet + Text Encoder
被真实代码推翻。
最终:
(2) poem 为主的基础 LoRA
(6) caption + poem 双通道 + UNet/Text Encoder 联合适配
这个修正非常重要,否则报告会出现事实错误。
决策五:不把风格变化当成语义改善
cartoon LoRA 的风格迁移很成功,但内容仍与中文古诗错位。
最终没有写成:
“额外数据集进一步提升了古诗理解。”
而是把它写成:
“LoRA 很容易学习风格,但中文语义是另一个问题。”
这个失败结果直接推动了第 (6) 项。
决策六:生成模型 checkpoint 以固定样本视觉效果选择
没有简单使用“loss 最低 = 最佳模型”。
最终采用:
定期 checkpoint
→ 固定古诗
→ 固定推理条件
→ 对比不同 epoch
→ 选择综合效果最好的一版
这更符合当前实验的评价目标。
决策七:报告先基于实验事实表,再形成叙事
用户实际填完结果表以后,才真正确定:
- 哪个实验成功;
- 哪个只成功了一半;
- 哪些具体意象被识别;
- 哪些不足真实出现。
这显著减少了“理论上应该怎样”被误写成“实验实际就是怎样”。
决策八:最终进入 Content Freeze
最后一轮格式调试明确禁止再“顺便润色正文”。
这是非常正确的交付决策:
实验事实和报告内容冻结后,LaTeX 调试就只能处理渲染层。
6. 做得好的地方
6.1 最早建立的 LoRA 链路排查顺序非常有效
不是看到图片不好就立即归因于:
- 数据不够;
- Epoch 不够;
- 中文太难;
- LoRA rank 太小。
而是先确认:
训练
↓
保存
↓
加载
↓
激活
↓
生成
这种顺序最终确实抓到了路径与保存格式问题,是整个任务能继续向前的基础。fileciteturn1file0L11-L43
6.2 对照实验被真正修成了控制变量实验
新版 Before 与旧版 Before 不同这个问题,如果当时被忽略,后续“LoRA 改善很明显”的结论很可能是不可信的。
通过逐项比较 Prompt、Negative Prompt 和 seed,最终让第 (6) 项具有了比较意义。
6.3 没有隐藏失败实验,而是让失败推动下一步
几个失败/不足都最终变成了实验逻辑:
基础 LoRA:
语义差、风格不稳
↓
额外数据集:
证明风格很好学,但中文语义仍差
↓
问题被进一步定位到文本语义
↓
双通道 + Text Encoder 改进
这比把每一步都写成“效果良好”更有说服力。
6.4 在正式写报告前让用户填写结果表,是很有效的一步
最终报告中最有价值的实验观察——例如:
- Euler a 最自然细腻;
- cartoon LoRA 风格成功但语义错位;
- 双通道能捕捉鸟、山、古寺、水流;
- 后期过度学习文字——
都来自真实实验结果,而不是理论推测。fileciteturn1file3L475-L480
6.5 后期敢于用代码事实推翻之前自己的说法
发现第 (2) 的代码实际上也训练 Text Encoder 后,没有为了保持前文一致而掩盖,而是重新定义第 (2)/(6) 的区别。
这是非常重要的科研写作习惯:
事实优先于叙事连续性。
6.6 LaTeX 最终实现了真正的内容/格式分离
最后一次页码修改没有继续调整报告内容,严格遵循:
内容冻结
→ 只修改 Roman / Arabic 分页逻辑
→ 再编译验证
这是正式交付阶段非常值得保留的做法。fileciteturn2file0L11-L52
7. 可以改进的地方
7.1 一开始就应该先锁定整个课程评分矩阵
会话前半段主要围绕“当前 LoRA 为什么不工作”,直到后来用户才给出六项正式要求。
如果更早就建立:
| 评分点 | 实验 | 代码 | 输入 | 输出 | 证据 |
|---|
就能更早避免:
- 把 inference 当成微调;
- 临时思考第 (3) 放到哪里;
- 为了区分第 (2)/(6) 人为设计不存在的模型差异。
7.2 第一版完整代码生成前应先做数据/schema 与环境 preflight
AI 一开始根据用户口述使用了 img_file,但真实 CSV 是 image_file。
这本来可以在生成长训练脚本前通过一个十行检查解决:
打印 CSV columns
打印前 3 行
检查图片是否存在
检查 dtype
检查空值
同样,验证脚本的 CLI 参数也应该先用:
python script.py --help
锁定。
7.3 有些 AI 判断过于自信
例如验证脚本出现 --poem 不支持时,当时判断:
“你当前的 validate_lora_effect.py 不是我之前给的版本,很可能被覆盖。”
报错只能够确定:
当前脚本的 argparse 不支持
--poem。
它不能单凭这一点证明文件一定被覆盖。
更好的表述应该是:
“当前实际运行的脚本接口与预期版本不一致,先以
--help和文件内容为准。”
7.4 LoRA 保存格式问题应该在训练前做最小 round-trip 测试
cartoon LoRA 后来又出现 base_model.model. key 前缀问题。
如果在正式 20 Epoch 训练前做:
创建模型
→ 注入 LoRA
→ 训练 1 step
→ 保存
→ 新建 pipeline
→ 重新加载
→ 检查 active adapter
→ 生成 1 张图
就能在一分钟级别发现兼容问题,而不是等到 checkpoint 已经训练出来才修。
7.5 双通道 MSE 的理论局限应该从一开始就和“实验采用”分开写
早期已经指出:
- poem 与 caption token 位置不同;
- 直接
[77,768]MSE 缺乏严格语义对应; - 同一动态 Text Encoder 不是真正固定教师。
但后面写报告时又比较自然地把它描述成“语义对齐损失”。
更好的表达应该始终保持:
“本实验采用一种简化的 embedding 对齐约束,作为探索性方案;它能够提供额外约束,但不保证严格跨语言语义对齐。”
这样技术叙述会更严谨。
7.6 报告不应该在实验事实表完成前就写得太深
前面已经开始逐章写报告,后来用户再填写实验结果表,又不得不纠正:
- 第 (2) 数据输入;
- Text Encoder 是否微调;
- Batch Size / gradient accumulation;
- Rank 与 LoRA Scale 混淆;
- API key;
- 自动保存频率等。
更优顺序应是:
实验事实冻结 → 报告大纲 → 正文。
而不是边猜参数边写长篇正文。
7.7 Markdown 自动转换到 LaTeX 模板不是最稳的路线
Pandoc 自动生成了:
\real{}表格列宽;- 错误标题层级;
- 复杂代码块环境;
- 不适合 Windows/LaTeX 的路径;
- 难控制的多图布局。
对于课程报告这种模板要求明确的任务,更稳的方式是:
从一开始把 Markdown 只作为内容源,表格、图片和章节层级在 LaTeX 中按模板规则重建。
这样会减少大量末期 debug。
7.8 页数目标应该尽早区分“上限”和“精确值”
当用户说“不能超过30页”时,10 页在字面上确实合格。
后来用户又补充“正好30页”。
因此这既有需求变化,也暴露出一个通用问题:
页数约束必须明确是 ≤N、约N、还是恰好N。
否则排版工作很容易返工。
8. 如果重新做一次
更优路径如下。
第一步 → 先建立作业验收矩阵
在写代码之前就明确:
(1) 原始模型文生图 → 代码 + 5 张图 + 流程说明
(2) 自建数据微调 → 训练代码 + before/after
(3) 新数据集 → 数据准备 + 独立微调
(4) 效果分析 → 真实现象 + 原因 + 改进
(5) 参数实验 → 控制变量表 + 对比图
(6) 组件级微调 → 联合组件训练 + 公平 before/after
第二步 → 做数据与环境 preflight
正式训练前一次性打印并验证:
diffusers / peft / transformers 版本
基础模型目录
CSV 列名
图片路径
样本数量
Prompt 字段
GPU / dtype
LoRA target modules
第三步 → 做 LoRA 1-step round-trip
不要一上来跑 20~30 Epoch。
先:
注入
→ 1 step
→ 保存
→ 全新 pipeline 加载
→ get_active_adapters()
→ 相同 seed 下 scale 0 / 2 对比
只有链路通过才正式训练。
第四步 → 固定实验协议
提前定义:
Before / After 相同:
Prompt
Negative Prompt
Scheduler
Steps
CFG
Seed
Resolution
唯一差别:
LoRA off / on
之后所有正式对照都禁止改协议。
第五步 → 完成基础自建数据实验
先完成第 (2),记录:
- 数据数量;
- 实际微调模块;
- checkpoint;
- 代表效果;
- 失败现象。
不要急着写结论。
第六步 → 完成独立额外数据集实验
第 (3) 明确作为独立实验:
parquet
→ 标准化
→ 独立 LoRA
→ 相同测试古诗
观察“风格”和“语义”的差异。
第七步 → 根据真实问题设计第 (6)
如果第 (2)/(3) 已经证明:
风格可学
中文语义仍差
那么第 (6) 再针对文本编码与跨语言信息进行改进。
这样实验逻辑是由结果推动,不是为了“凑六项”。
第八步 → checkpoint 使用固定验证集选择
每 N Epoch 保存:
checkpoint
→ 固定代表性古诗
→ 固定 seed
→ 自动生成网格
记录最佳 checkpoint,而不是靠回忆选择。
第九步 → 实验结束后先填“事实表”
把:
方法
真实代码行为
数据
实际参数
图片
观察
不足
全部冻结。
第十步 → 再开始写报告
报告只允许引用事实表中的内容。
所有理论解释标成:
“可能原因 / 分析认为 / 可以解释为”
不要把解释写成直接测得事实。
第十一步 → 直接在目标 LaTeX 模板内重建结构
避免整个 Markdown 原样 Pandoc 化。
尤其:
- 多图;
- 表格;
- 代码;
- caption;
- section hierarchy
直接按目标模板写。
第十二步 → Content Freeze 后只处理交付格式
最后统一验证:
PDF 编译
图片
Caption
目录
页数
封面
Roman TOC
Arabic body
引用
正文绝不再顺手重写。
第二部分:可复用 Skills
下面只保留这次任务里真正值得在以后重复调用的 6 个 Skills。
Skill 1:先把课程要求映射成可验证实验
适用场景
课程设计、实验作业、毕业设计小实验、竞赛任务等同时要求代码、实验、比较和报告时。
目标
避免出现:
“做了很多东西,但老师要求的那一项其实没真正完成。”
核心原则
评分要求必须映射到可观察证据。
“代码能运行”不是证据链的终点。
例如:
“加载数据集并微调”
至少要求存在:
数据加载
训练循环
可训练参数变化
保存权重
微调后结果
单独一个 inference 脚本不能证明它。
操作步骤
-
原样抄出所有评分项,不提前合并。
-
对每项分别定义:
- 所需代码;
- 所需输入;
- 所需实验;
- 所需输出;
- 所需图表;
- 报告需要解释的内容。
-
检查已有实验可以覆盖哪些评分点。
-
不能直接证明评分点的已有代码标记为“辅助材料”,不要硬映射。
-
给各评分项分配独立实验编号。
-
实验结束后逐项回填“已完成证据”。
-
最后用相同结构组织报告章节。
判断规则
- 如果要求“微调” → 必须发生参数更新。
- 如果要求“其他数据集” → 不能把原数据集另一 split 当成新数据集。
- 如果要求“微调前后差异” → 必须有公平 Before/After。
- 如果要求“分析效果” → 不能只展示图片而没有结论。
- 如果实际代码与早期任务分类冲突 → 以实际代码重分类。
常见错误
为了让报告目录看起来整齐,先设定:
实验 A = UNet
实验 B = Text Encoder
后来才发现代码实际并不是这样。
正确做法
让报告章节解释真实实验差异,而不是让实验事实迁就章节标题。
完成检查
Skill 2:用 LoRA Round-Trip 验证训练—保存—加载链
适用场景
任何 LoRA、Adapter、PEFT 或参数高效微调出现:
- 微调前后差不多;
- checkpoint 已存在但效果没变化;
- load 成功日志不明确;
- target module 报错;
- 训练正常但推理异常。
目标
先回答最基础的问题:
这个 Adapter 到底有没有真正训练、保存、重新加载并参与推理?
核心原则
文件存在 ≠ 权重有效。
加载函数没抛错 ≠ Adapter 生效。
操作步骤
-
检查训练时可训练参数数量。
-
确认 LoRA target module 实际存在。
-
只训练 1~几个 step。
-
保存 LoRA。
-
打印保存文件大小和 key 示例。
-
新建一个完全独立的基础 pipeline。
-
加载刚保存的 LoRA。
-
检查 active adapters。
-
固定同一个 Prompt、Seed 和推理参数。
-
分别生成:
- LoRA scale = 0;
- LoRA scale = 正常值;
- 必要时 LoRA scale = 较大值。
-
如果图片始终一致,再查:
- 路径;
- key 前缀;
- state dict 格式;
- base model 是否匹配。
判断规则
- 如果训练和推理目录不同 → 先修路径,不讨论模型效果。
- 如果 PEFT 注入却用另一套不兼容保存 API → 先统一格式。
- 如果 key 出现 wrapper 前缀且基础模型不存在该路径 → 先修保存方式。
- 如果 scale 0 与 scale 2 仍完全相同 → Adapter 链路高度可疑。
- 如果最小 round-trip 没通过 → 不要进行长训练。
常见错误
训练 20~30 Epoch 后才第一次尝试加载 checkpoint。
这会把“接口 bug”变成数小时的无效计算。
正确做法
正式训练前先完成:
1-step train → save → reload → inference。
完成检查
Skill 3:构造公平的生成模型 Before/After
适用场景
LoRA、DreamBooth、ControlNet、Prompt tuning、模型版本或其他生成模型改进需要证明效果时。
目标
让生成差异能够合理归因于被测试变量本身。
核心原则
对照组的意义不是“看起来不一样”,而是“只改变一个变量”。
操作步骤
- 先列出全部可能影响生成结果的因素:
Base model
Prompt
Negative Prompt
Scheduler
Steps
CFG
Seed
Resolution
VAE
LoRA scale
- 标记本实验真正要测试的变量。
- 除该变量外,其余全部冻结。
- 每条测试 Prompt 使用固定 Seed。
- Before 与 After 成对保存。
- 文件命名中包含相同 sample id。
- 每次正式比较前打印实际 Prompt。
- 将实验参数保存到 CSV/JSON。
- 报告只比较同一协议产生的图片。
判断规则
- 如果 Prompt 变了 → 不能把全部差异归因于 LoRA。
- 如果 Seed 变了 → 视觉差异只能作为弱证据。
- 如果 Scheduler 同时变了 → 这是联合改动,不是纯微调对比。
- 如果要评价原模型的中文能力 → 可以使用纯中文 Prompt,但不要和另一套水墨模板结果直接当 LoRA Before/After。
常见错误
Before:
中文古诗
After:
水墨触发词 + 英文风格描述 + 中文古诗 + LoRA
然后结论写:
“LoRA 显著改善了水墨画。”
这里的变化至少来自 Prompt 和 LoRA 两个因素。
正确做法
正式效果实验:
Before = same prompt + LoRA off
After = same prompt + LoRA on
另外单独设置“原始中文 Prompt 能力”实验。
完成检查
Skill 4:在长训练前做 Schema / CLI / Checkpoint Preflight
适用场景
涉及 CSV、parquet、自定义 Dataset、多脚本、多个 checkpoint 或远程训练环境的任务。
目标
在正式计算开始前发现:
- 列名错误;
- 路径错误;
- 参数名错误;
- 空数据;
- checkpoint 不兼容;
- 脚本版本不一致。
核心原则
能在 30 秒发现的问题,不应等到训练 5 小时后才发现。
操作步骤
-
打印环境关键版本。
-
打印数据表列名。
-
打印前三条记录。
-
检查必要列是否存在。
-
随机检查若干图片路径。
-
打印样本图片尺寸和文本。
-
运行 Dataset
__getitem__(0)。 -
运行一个 DataLoader batch。
-
对所有 CLI 脚本运行
--help。 -
打印模型 target module 名称。
-
如果使用 checkpoint:
- 打印前几个 key;
- 比较 missing/unexpected keys;
- 检查 shape。
-
最后只跑 1 个训练 step。
判断规则
img_file与image_file不一致 → 明确 rename/compatibility,不要让错误进入训练。- 实际脚本
--help没有某参数 → 以当前脚本为准,不假设文件版本。 dst is None→ 禁止直接访问.shape。- checkpoint key 与基础模型命名空间不匹配 → 在正式训练前修正保存方式。
- 用户记忆和代码冲突 → 用真实代码/日志确认。
常见错误
根据用户一句:
“第一列叫 img_file”
直接生成整套代码。
但真实文件可能已经是:
image_file
正确做法
接口相关事实优先读取实际数据。
完成检查
Skill 5:用失败结果推动下一项实验,而不是隐藏失败
适用场景
实验效果不理想,但课程/研究任务要求继续分析和改进时。
目标
把失败结果转化为问题定位证据。
核心原则
失败实验只有在以下情况下才没有价值:
没有分析它排除了什么,也没有影响下一步设计。
操作步骤
-
将结果拆成多个评价维度。
例如生成任务可以拆成:- 风格;
- 语义;
- 构图;
- 细节;
- 稳定性。
-
判断哪些维度成功、哪些失败。
-
把成功维度用于排除某类问题。
-
把失败维度映射到对应组件。
-
下一实验只针对最可能的瓶颈修改。
-
在报告中形成:
实验
→ 现象
→ 能说明什么
→ 仍不能说明什么
→ 下一步为什么这样设计
判断规则
- 风格明显变化、语义不变 → 优先怀疑文本语义/条件对齐,而不是继续加强风格 LoRA。
- 推理完全不变 → 先怀疑 Adapter 链路。
- 多个 Scheduler 变化明显 → 推理策略是独立影响因素。
- 训练后出现文字 → 检查数据是否稳定包含题字/水印。
- 结果只能支持“部分改善” → 不写“解决”。
常见错误
把:
“cartoon LoRA 成功变成浮世绘,但古诗语义仍错位”
写成:
“使用额外数据集进一步提高了古诗生成能力。”
这是把两个不同评价维度混为一谈。
正确做法
写成:
该实验验证了 LoRA 的风格适配能力,同时进一步表明中文语义对齐仍是独立瓶颈。
完成检查
Skill 6:先冻结实验事实,再完成 LaTeX 交付
适用场景
实验报告已经完成,需要迁移到学校/课程 LaTeX 模板,并受到页数、目录、图片和页码要求限制。
目标
避免最后排版时再次破坏实验内容,并减少 Pandoc/模板兼容返工。
核心原则
内容问题和渲染问题必须分阶段处理。
操作步骤
-
写报告前建立实验事实表。
-
核对:
- 实际代码;
- 实际数据;
- 实际参数;
- 实际结果;
- 实际图片。
-
内容确认后标记 Content Freeze。
-
迁移到目标 LaTeX 模板。
-
优先处理第一条真实编译错误。
-
检查典型自动转换问题:
- Pandoc table width;
- 未定义宏;
\路径;- 标题层级;
- 代码环境;
- figure/caption。
-
多图布局优先手工
figure + minipage。 -
实际编译 PDF 后再检查页数。
-
调页数时依次考虑:
- 删除重复内容;
- 恢复必要分析;
- 图像大小;
- 表格密度;
- 分页;
而不是使用空白页凑数。
-
最后只处理:
- 封面;
- TOC;
- Roman/Arabic 页码;
- 最终页数。
判断规则
- 如果用户说“不改内容” → 不得再润色正文。
- 如果要求“不超过30页” → 先确认是不是上限,而不是默认要极短。
- 如果后来改成“正好30页” → 按新要求重新平衡信息密度。
- 如果 Pandoc 生成结构和模板冲突 → 手工重写局部,不要无限追加宏包。
- 如果目录页要求 Roman → 显式设置 Roman 并重置页码。
- 正文要求 Arabic 1 → 再切换并重置。
常见错误
把 37 页一次压成 10 页。
形式满足了上限,但破坏了“课程设计报告应有的信息量”。
正确做法
同时优化两个目标:
页数
+
有效信息密度
完成检查
第三部分:跨 Skill 总结
1. 本次任务形成的核心工作流
把上面的 Skills 串起来,未来类似课程实验最优流程是:
接收任务
↓
读取原始作业要求,不先写代码
↓
建立“评分点 → 实验 → 证据”矩阵
↓
确认真实数据 schema、脚本版本、模型版本和现有 checkpoint
↓
最小 Preflight
CSV / 图片 / CLI / target module / 一个 batch
↓
最小 LoRA Round-Trip
1 step → save → reload → same-seed inference
↓
链路确认无误后再进行正式训练
↓
固定实验协议
Prompt / Seed / Scheduler / CFG / Steps 等控制变量
↓
完成基础实验
↓
从失败结果定位下一瓶颈
风格问题、语义问题、数据问题、推理参数问题分开
↓
完成额外数据与组件级改进实验
↓
冻结 checkpoint
↓
用固定验证样本选最佳版本
↓
填写实验事实表
代码实际行为 / 结果 / 图片 / 不足
↓
再开始正式写报告
↓
报告内容冻结
↓
迁移 LaTeX
↓
编译调试 / 图片 / 页数 / 目录 / 页码
↓
最终交付
2. 最值得保留的 5 条经验
第一条:生成结果“没变化”时,先验证权重链路
这是本次任务最早、也最重要的一条经验。
如果 LoRA 没被正确保存或加载,继续研究:
- 数据集;
- Rank;
- Epoch;
- Prompt;
- 中文能力
全部没有意义。
第二条:Before/After 的可信度来自控制变量
生成模型随机性很大。
因此:
“两张图看起来差很多”
本身不是很强的证据。
真正可信的是:
同模型、同 Prompt、同 Seed、同采样参数,只有 Adapter 开关不同。
第三条:风格学习与语义理解必须分开评价
本次额外数据集实验尤其清楚地证明:
LoRA 可以成功改变画风,但不代表模型理解了中文古诗。
以后做生成模型时至少要把:
Style
Content
Semantic alignment
Composition
Quality
分开评价。
第四条:报告必须晚于“实验事实冻结”
先让实验结果、代码行为和图片都确认,再写长报告。
否则非常容易出现:
为了让章节区别明显,把不存在的技术差异写出来。
第五条:正式交付要有 Content Freeze
最终阶段只允许:
- LaTeX;
- 页码;
- 图片大小;
- 分页;
- caption;
- 编译修复。
不要在处理 Roman/Arabic 页码时顺手重写实验结论。
3. 下次开始类似任务前应该先问的问题
- 作业每个评分点分别要看到什么证据才能算完成?
- 最终需要提交的是代码、图片、报告,还是还包括模型权重?
- 真实数据 schema 是什么?列名、编码、图片路径是否已经实际检查?
- 现在运行的到底是哪一版脚本?
--help支持哪些参数? - LoRA 使用哪套注入、保存、加载接口?能否先做 1-step round-trip?
- 哪些实验需要 Before/After?必须固定哪些变量?
- 第一个实验失败后,究竟是风格失败、语义失败还是权重根本没生效?
- 不同课程任务之间真正的技术差异是什么?这个差异是否与实际代码一致?
- 生成模型的最佳 checkpoint 按什么选择:loss、固定验证样本、人工评分还是定量指标?
- 报告中的实验结论哪些是实际观察,哪些只是原因推断?
- 报告页数要求到底是“最多 N 页”“约 N 页”还是“必须正好 N 页”?
- 什么时候正式 Content Freeze,此后只允许修改格式?
最终收束
这次作业真正形成的不是“几个 LoRA 脚本”,而是一条完整的实验闭环:
从“LoRA 微调为什么没效果”的故障排查开始,先修通训练—保存—加载链,随后用控制变量建立可信 Before/After,再按六项课程要求完成原始文生图、自建数据基础微调、额外 parquet 数据微调、失败分析、参数实验以及 UNet + Text Encoder 双通道改进;最终用真实实验结果写成完整报告,并经过 LaTeX 编译调试、37→10→30 页的篇幅纠偏和最终页码 Content Freeze,完成正式交付。
其中最值得以后直接复用的思想只有几条:先验证链路,再评价模型;先控制变量,再谈提升;把失败当诊断证据;让真实代码约束报告叙事;内容冻结以后只处理交付层。
另外,后期混入的 MNIST/LeNet5 垃圾分类总结已经确认属于另一项任务,因此这里只保留其中可泛化的方法论,没有把它的模型、准确率或实验事实混入本次古诗水墨画作业。-L91
浙公网安备 33010602011771号