DAY
NIGHT
DOPAMINE

【小结】人工智能原理实践课T4 - Stable Diffusion v1.5 + LoRA 的古诗水墨画生成 文字编码器微调

第一部分:作业完成小结

1. 作业目标

1.1 任务最初是怎么开始的

这次会话最早并不是从“写完整课程报告”开始,而是从一个具体故障开始:

已经完成 Stable Diffusion v1.5 的 LoRA 微调,但微调后的水墨画和微调前几乎一样,需要定位原因。

因此,最初目标是排查 LoRA 为什么看起来没有生效。当时首先把问题拆成:

训练是否真的更新 LoRA → 权重是否正确保存 → 推理是否加载正确权重 → Adapter 是否真正参与推理 → 最后才判断模型有没有学到水墨风格。

很快发现了两个高优先级问题:训练输出目录与推理加载目录不一致,以及 PEFT LoRA 的注入方式和旧式 Diffusers 保存方式混用。也就是说,早期“效果没变化”首先是一个训练—保存—加载链路问题,而不是简单的“模型学不会”。fileciteturn1file0L11-L43

1.2 后来正式明确的课程作业要求

随着会话推进,用户补充了正式课程要求,任务扩展为六项:

  1. 使用开源项目部署 Stable Diffusion,跑通文生图流程,输入 5 条英文 Prompt,并详细说明代码原理、操作和算法流程。
  2. 学习开源项目后自行编写类似代码,加载 jpg-csv 或 parquet 数据集并进行模型微调。
  3. 自行寻找其他数据集,再完成一次模型微调。
  4. 分析第 (2) 项模型的实际效果、问题及改进方向。
  5. 调整生成参数,或者换模型,以获得更好的生成效果。
  6. 微调 Stable Diffusion 的其他组件,例如 Text Encoder,或进行 DreamBooth LoRA,并展示微调前后差异。fileciteturn1file1L337-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 页且不能过度精简。fileciteturn1file4L573-L609
  • 最终进入内容冻结阶段,不再修改正文,只调整格式。
  • 封面不显示页码;目录使用罗马数字 I;正文重新从阿拉伯数字 1 开始。L11-L52

2. 最终完成结果

2.1 最终采用的整体技术路线

最终实际形成的实验主线是:

原始 SD1.5 文生图 → 自建古诗-水墨画数据集 → 基础 LoRA → 额外 parquet 数据集 LoRA → 效果分析 → 参数优化 → UNet + Text Encoder 双通道 LoRA → 报告与 LaTeX 交付。

后期用户已经明确确认六项实验全部跑完。fileciteturn1file2L392-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 是整套实验中效果最好的方案,开始捕捉鸟、山、古寺、松、水流以及“空”“辽阔”等意境,但仍未完全解决语义对齐

这些实际实验观察是后来正式写报告的事实基础。fileciteturn1file3L473-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 也不是严格意义上的固定教师,因此这种对齐损失不能被描述成“实现完美语义对齐”。fileciteturn1file0L225-L254

最终实验仍然把 caption 作为语义桥梁使用,但正确结论只能是“促进对齐、取得部分改善”,不能说已经彻底解决跨语言语义问题。


2.5 最终报告成果

最终形成了完整课程报告,实验、模型结构图、数据集流程图、参数实验图和 Before/After 图均被整合进去。

随后完成了:

Markdown → 指定 LaTeX 模板 → 编译错误修复 → 篇幅调整 → 页码最终处理。

最终报告经历:

约 37 页 → 过度精简到 10 页 → 用户否定 → 恢复必要分析 → 正好 30 页。fileciteturn1file4L573-L609

最后不再改正文,只修页码:

  • 封面:不显示页码;
  • 目录:Roman,从 I 开始;
  • 正文:Arabic,从 1 开始。fileciteturn2file0L25-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) 后面。正式作业结构开始优先于聊天顺序。fileciteturn1file1L350-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:画面最自然细腻;
  • 双通道:已经能捕捉鸟、山、古寺、客、松、水流以及“空”“千里”的空间意境;
  • 后期训练出现过度学习题字/文字的倾向。citeturn1file3L475-L480

后续报告的实验结论开始基于这些事实,而不是由 AI 根据理论自行补全。


阶段十:报告事实又进行了一轮代码审计

在正式报告之前,还纠正了一个很重要的叙事错误。

早期为了让第 (2) 和第 (6) 看起来区别明显,曾建议:

第 (2):只训练 UNet
第 (6):UNet + Text Encoder

后来检查实际 train_lora.py 才发现:

第 (2) 的基础实验本身也微调了 Text Encoder LoRA。

因此最终正确区别改成:

第 (2):以 poem 为主的基础/单通道训练
第 (6):充分使用 caption + poem 双通道进行进一步语义适配

这是一个非常典型的例子:报告分类必须服从真实代码,而不能为了章节漂亮倒过来修改技术事实。


阶段十一:逐章写完正式课程报告和配图

报告最终形成九章主线:

  1. 绪论与实验环境
  2. 原始 SD1.5 文生图
  3. 自建双通道数据集
  4. 基础 LoRA
  5. 额外 parquet 数据集
  6. 效果分析与改进
  7. 参数实验
  8. UNet + Text Encoder 双通道 LoRA
  9. 总结与展望

期间还生成了:

  • 数据集构建流程图;
  • 基础模型结构图;
  • 第八章双通道模型架构图;
  • 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

此后任务真正进入交付完成状态。fileciteturn2file0L11-L52


4. 关键问题与解决方法

问题 为什么出现 怎么发现 怎么修正 得到的经验
LoRA 微调前后几乎一样 推理路径加载的不是刚训练出的 checkpoint 对比训练输出目录和 inference 路径 统一训练/保存/加载目录,并对缺失权重强制报错 生成效果不变时,先查权重链路
PEFT 与 Diffusers 保存方式混用 注入和保存来自两套接口 审计 get_peft_modelsave_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 模块等事实硬拼进本作业,只吸收了可复用的方法论。file1L75-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 太小。

而是先确认:

训练
↓
保存
↓
加载
↓
激活
↓
生成

这种顺序最终确实抓到了路径与保存格式问题,是整个任务能继续向前的基础。fileciteturn1file0L11-L43


6.2 对照实验被真正修成了控制变量实验

新版 Before 与旧版 Before 不同这个问题,如果当时被忽略,后续“LoRA 改善很明显”的结论很可能是不可信的。

通过逐项比较 Prompt、Negative Prompt 和 seed,最终让第 (6) 项具有了比较意义。


6.3 没有隐藏失败实验,而是让失败推动下一步

几个失败/不足都最终变成了实验逻辑:

基础 LoRA:
语义差、风格不稳
↓
额外数据集:
证明风格很好学,但中文语义仍差
↓
问题被进一步定位到文本语义
↓
双通道 + Text Encoder 改进

这比把每一步都写成“效果良好”更有说服力。


6.4 在正式写报告前让用户填写结果表,是很有效的一步

最终报告中最有价值的实验观察——例如:

  • Euler a 最自然细腻;
  • cartoon LoRA 风格成功但语义错位;
  • 双通道能捕捉鸟、山、古寺、水流;
  • 后期过度学习文字——

都来自真实实验结果,而不是理论推测。fileciteturn1file3L475-L480


6.5 后期敢于用代码事实推翻之前自己的说法

发现第 (2) 的代码实际上也训练 Text Encoder 后,没有为了保持前文一致而掩盖,而是重新定义第 (2)/(6) 的区别。

这是非常重要的科研写作习惯:

事实优先于叙事连续性。


6.6 LaTeX 最终实现了真正的内容/格式分离

最后一次页码修改没有继续调整报告内容,严格遵循:

内容冻结
→ 只修改 Roman / Arabic 分页逻辑
→ 再编译验证

这是正式交付阶段非常值得保留的做法。fileciteturn2file0L11-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 脚本不能证明它。

操作步骤

  1. 原样抄出所有评分项,不提前合并。

  2. 对每项分别定义:

    • 所需代码;
    • 所需输入;
    • 所需实验;
    • 所需输出;
    • 所需图表;
    • 报告需要解释的内容。
  3. 检查已有实验可以覆盖哪些评分点。

  4. 不能直接证明评分点的已有代码标记为“辅助材料”,不要硬映射。

  5. 给各评分项分配独立实验编号。

  6. 实验结束后逐项回填“已完成证据”。

  7. 最后用相同结构组织报告章节。

判断规则

  • 如果要求“微调” → 必须发生参数更新。
  • 如果要求“其他数据集” → 不能把原数据集另一 split 当成新数据集。
  • 如果要求“微调前后差异” → 必须有公平 Before/After。
  • 如果要求“分析效果” → 不能只展示图片而没有结论。
  • 如果实际代码与早期任务分类冲突 → 以实际代码重分类。

常见错误

为了让报告目录看起来整齐,先设定:

实验 A = UNet
实验 B = Text Encoder

后来才发现代码实际并不是这样。

正确做法

让报告章节解释真实实验差异,而不是让实验事实迁就章节标题。

完成检查


Skill 2:用 LoRA Round-Trip 验证训练—保存—加载链

适用场景

任何 LoRA、Adapter、PEFT 或参数高效微调出现:

  • 微调前后差不多;
  • checkpoint 已存在但效果没变化;
  • load 成功日志不明确;
  • target module 报错;
  • 训练正常但推理异常。

目标

先回答最基础的问题:

这个 Adapter 到底有没有真正训练、保存、重新加载并参与推理?

核心原则

文件存在 ≠ 权重有效。
加载函数没抛错 ≠ Adapter 生效。

操作步骤

  1. 检查训练时可训练参数数量。

  2. 确认 LoRA target module 实际存在。

  3. 只训练 1~几个 step。

  4. 保存 LoRA。

  5. 打印保存文件大小和 key 示例。

  6. 新建一个完全独立的基础 pipeline。

  7. 加载刚保存的 LoRA。

  8. 检查 active adapters。

  9. 固定同一个 Prompt、Seed 和推理参数。

  10. 分别生成:

    • LoRA scale = 0;
    • LoRA scale = 正常值;
    • 必要时 LoRA scale = 较大值。
  11. 如果图片始终一致,再查:

    • 路径;
    • 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、模型版本或其他生成模型改进需要证明效果时。

目标

让生成差异能够合理归因于被测试变量本身

核心原则

对照组的意义不是“看起来不一样”,而是“只改变一个变量”。

操作步骤

  1. 先列出全部可能影响生成结果的因素:
Base model
Prompt
Negative Prompt
Scheduler
Steps
CFG
Seed
Resolution
VAE
LoRA scale
  1. 标记本实验真正要测试的变量。
  2. 除该变量外,其余全部冻结。
  3. 每条测试 Prompt 使用固定 Seed。
  4. Before 与 After 成对保存。
  5. 文件命名中包含相同 sample id。
  6. 每次正式比较前打印实际 Prompt。
  7. 将实验参数保存到 CSV/JSON。
  8. 报告只比较同一协议产生的图片。

判断规则

  • 如果 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 小时后才发现。

操作步骤

  1. 打印环境关键版本。

  2. 打印数据表列名。

  3. 打印前三条记录。

  4. 检查必要列是否存在。

  5. 随机检查若干图片路径。

  6. 打印样本图片尺寸和文本。

  7. 运行 Dataset __getitem__(0)

  8. 运行一个 DataLoader batch。

  9. 对所有 CLI 脚本运行 --help

  10. 打印模型 target module 名称。

  11. 如果使用 checkpoint:

    • 打印前几个 key;
    • 比较 missing/unexpected keys;
    • 检查 shape。
  12. 最后只跑 1 个训练 step。

判断规则

  • img_fileimage_file 不一致 → 明确 rename/compatibility,不要让错误进入训练。
  • 实际脚本 --help 没有某参数 → 以当前脚本为准,不假设文件版本。
  • dst is None → 禁止直接访问 .shape
  • checkpoint key 与基础模型命名空间不匹配 → 在正式训练前修正保存方式。
  • 用户记忆和代码冲突 → 用真实代码/日志确认。

常见错误

根据用户一句:

“第一列叫 img_file”

直接生成整套代码。

但真实文件可能已经是:

image_file

正确做法

接口相关事实优先读取实际数据。

完成检查


Skill 5:用失败结果推动下一项实验,而不是隐藏失败

适用场景

实验效果不理想,但课程/研究任务要求继续分析和改进时。

目标

把失败结果转化为问题定位证据

核心原则

失败实验只有在以下情况下才没有价值:

没有分析它排除了什么,也没有影响下一步设计。

操作步骤

  1. 将结果拆成多个评价维度。
    例如生成任务可以拆成:

    • 风格;
    • 语义;
    • 构图;
    • 细节;
    • 稳定性。
  2. 判断哪些维度成功、哪些失败。

  3. 把成功维度用于排除某类问题。

  4. 把失败维度映射到对应组件。

  5. 下一实验只针对最可能的瓶颈修改。

  6. 在报告中形成:

实验
→ 现象
→ 能说明什么
→ 仍不能说明什么
→ 下一步为什么这样设计

判断规则

  • 风格明显变化、语义不变 → 优先怀疑文本语义/条件对齐,而不是继续加强风格 LoRA。
  • 推理完全不变 → 先怀疑 Adapter 链路。
  • 多个 Scheduler 变化明显 → 推理策略是独立影响因素。
  • 训练后出现文字 → 检查数据是否稳定包含题字/水印。
  • 结果只能支持“部分改善” → 不写“解决”。

常见错误

把:

“cartoon LoRA 成功变成浮世绘,但古诗语义仍错位”

写成:

“使用额外数据集进一步提高了古诗生成能力。”

这是把两个不同评价维度混为一谈。

正确做法

写成:

该实验验证了 LoRA 的风格适配能力,同时进一步表明中文语义对齐仍是独立瓶颈。

完成检查


Skill 6:先冻结实验事实,再完成 LaTeX 交付

适用场景

实验报告已经完成,需要迁移到学校/课程 LaTeX 模板,并受到页数、目录、图片和页码要求限制。

目标

避免最后排版时再次破坏实验内容,并减少 Pandoc/模板兼容返工。

核心原则

内容问题和渲染问题必须分阶段处理。

操作步骤

  1. 写报告前建立实验事实表。

  2. 核对:

    • 实际代码;
    • 实际数据;
    • 实际参数;
    • 实际结果;
    • 实际图片。
  3. 内容确认后标记 Content Freeze。

  4. 迁移到目标 LaTeX 模板。

  5. 优先处理第一条真实编译错误。

  6. 检查典型自动转换问题:

    • Pandoc table width;
    • 未定义宏;
    • \ 路径;
    • 标题层级;
    • 代码环境;
    • figure/caption。
  7. 多图布局优先手工 figure + minipage

  8. 实际编译 PDF 后再检查页数。

  9. 调页数时依次考虑:

    • 删除重复内容;
    • 恢复必要分析;
    • 图像大小;
    • 表格密度;
    • 分页;
      而不是使用空白页凑数。
  10. 最后只处理:

    • 封面;
    • 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. 下次开始类似任务前应该先问的问题

  1. 作业每个评分点分别要看到什么证据才能算完成?
  2. 最终需要提交的是代码、图片、报告,还是还包括模型权重?
  3. 真实数据 schema 是什么?列名、编码、图片路径是否已经实际检查?
  4. 现在运行的到底是哪一版脚本?--help 支持哪些参数?
  5. LoRA 使用哪套注入、保存、加载接口?能否先做 1-step round-trip?
  6. 哪些实验需要 Before/After?必须固定哪些变量?
  7. 第一个实验失败后,究竟是风格失败、语义失败还是权重根本没生效?
  8. 不同课程任务之间真正的技术差异是什么?这个差异是否与实际代码一致?
  9. 生成模型的最佳 checkpoint 按什么选择:loss、固定验证样本、人工评分还是定量指标?
  10. 报告中的实验结论哪些是实际观察,哪些只是原因推断?
  11. 报告页数要求到底是“最多 N 页”“约 N 页”还是“必须正好 N 页”?
  12. 什么时候正式 Content Freeze,此后只允许修改格式?

最终收束

这次作业真正形成的不是“几个 LoRA 脚本”,而是一条完整的实验闭环:

从“LoRA 微调为什么没效果”的故障排查开始,先修通训练—保存—加载链,随后用控制变量建立可信 Before/After,再按六项课程要求完成原始文生图、自建数据基础微调、额外 parquet 数据微调、失败分析、参数实验以及 UNet + Text Encoder 双通道改进;最终用真实实验结果写成完整报告,并经过 LaTeX 编译调试、37→10→30 页的篇幅纠偏和最终页码 Content Freeze,完成正式交付。

其中最值得以后直接复用的思想只有几条:先验证链路,再评价模型;先控制变量,再谈提升;把失败当诊断证据;让真实代码约束报告叙事;内容冻结以后只处理交付层。

另外,后期混入的 MNIST/LeNet5 垃圾分类总结已经确认属于另一项任务,因此这里只保留其中可泛化的方法论,没有把它的模型、准确率或实验事实混入本次古诗水墨画作业。-L91

posted @ 2026-08-22 11:07  Searshkiu  阅读(2)  评论(0)    收藏  举报