Stable Diffusion微调实战:把AI画风调成你的专属滤镜,顺带踩坑埋
微调Stable Diffusion不是玄学,胜似玄学:同样的30张猫图,有人出图是“ins滤镜”,有人出图是“克苏鲁”。差别不在人品,而在“数据→参数→验证→迭代”的闭环有没有跑通。把本文脚本全盘抄走,你至少能领先80%野生玩家;再把MLOps流程跑顺,你就能让老板相信:“AI画风定制”不是魔术,而是可排期、可上线、可回滚的普通需求。下一回,当你凌晨三点看到猫咪高清大图从网页里蹦出来,别忘了给显
Stable Diffusion微调实战:把AI画风调成你的专属滤镜,顺带踩坑埋
- Stable Diffusion微调实战:把AI画风调成你的专属滤镜,顺带踩坑埋雷全记录
-
- 为什么非得折腾微调?因为“默认脸”实在看吐了
- 主流微调方法全家桶:Dreambooth、LoRA、Textual Inversion、全量微调,谁才是你的菜?
- 第一步:数据准备——别急着跑代码,先给AI喂“干净饭”
- 第二步:环境搭好——一次装对,省得后面抓鬼
- 第三步:LoRA训练超细颗粒度拆解——参数怎么填、学习率踩雷实录
- 第四步:模型出炉,怎么知道“训好了”?
- 第五步:Dreambooth备选方案——显存够就上,效果确实更顶
- 第六步:前端如何把“微调成果”塞进网页按钮?
- 第七步:踩坑大全——从“ CUDA out of memory”到“猫怎么又变成狗”
- 第八步:效率翻倍的黑科技——数据清洗到云端部署一条龙
- 第九步:可复现、可协作、可上线的MLOps小作坊
- 写在最后:别只调模型,也调调自己的工作流
Stable Diffusion微调实战:把AI画风调成你的专属滤镜,顺带踩坑埋雷全记录
“我不想再抽到别人的老婆了,我要AI只画我的猫。”
——某位凌晨三点还在调学习率的前端同胞
如果你也曾对着Stable Diffusion生成的“千人一面”直翻白眼,恭喜你,已经摸到微调这扇大门的把手。本文不是论文,不是广告,更不是“三分钟包会”的短视频脚本,而是一篇来自加班狗的血泪笔记:从“为啥要微调”到“怎么把微调模型塞进React按钮”,顺带把路上踩过的坑挨个埋好,方便后来人直接平地起飞。全文一万字起步,代码管饱,注释管够,显卡风扇管转——请自备咖啡,我们慢慢聊。
为什么非得折腾微调?因为“默认脸”实在看吐了
Stable Diffusion开源社区里流传着一句黑话:
“不微调,你永远在抽卡;微调完,卡池里只剩你老婆。”
话糙理不糙。原生模型再强,也是用LAION-5B这种“大锅烩”练出来的,画风杂糅到像食堂大杂烩——偶尔好吃,经常翻车。
想要“只画我家橘猫”“只画我们品牌IP的二次元娘”“只画老板头像的Q版”,除了微调,没有第二条路。
更现实一点:
- 甲方爸爸要求“画风统一”,不能今天宫崎骏明天哥斯拉。
- 前端项目里要“实时出图”,不能让用户抽到奇行种。
- 显存只有12 G,全量微调直接OOM,得想办法“少吃多跑”。
于是,人人都在喊“我要微调”,可真正跑通pipeline还能把模型塞进Web页的,十不存一。下文就把“从0到线上可点按钮”拆成七步,每一步都给出可直接复制的脚本,以及“如果报错xxxx怎么办”的急救小纸条。
主流微调方法全家桶:Dreambooth、LoRA、Textual Inversion、全量微调,谁才是你的菜?
先放结论,再讲故事:
| 方案 | 显存占用 | 训练时长 | 画风记忆 | 提示词依赖 | 推荐指数(个人向) |
|---|---|---|---|---|---|
| Dreambooth | 16 G+ | 30 min~2 h | ★★★★★ | 低 | ⭐⭐⭐⭐☆ |
| LoRA | 8 G | 10~30 min | ★★★★☆ | 高 | ⭐⭐⭐⭐⭐ |
| Textual Inversion | 6 G | 5~15 min | ★★☆☆☆ | 极高 | ⭐⭐☆☆☆ |
| Full Fine-tune | 24 G+ | 半天~一天 | ★★★★★ | 低 | ⭐⭐☆☆☆(穷鬼劝退) |
Dreambooth:把“猫娘”概念硬塞进模型权重,效果最稳,但吃显存吃到哭。
LoRA:在注意力旁路插“小辫子”,冻结主干,只训旁路,省显存、省时间,还能多LoRA叠加,前端最爱。
Textual Inversion:只学一个新token Embedding,模型权重纹丝不动,训练最快,但也最容易“记不住细节”。
Full Fine-tune:土豪玩法,除了展示公司财力,一般不建议。
下文实战以“LoRA”为主 Dreambooth为辅,原因无他:
- 单人单卡2080Ti也能跑;
- 导出
.safetensors才几十兆,前端包体积友好; - 支持WebUI、ComfyUI、diffusers全生态,一把梭。
第一步:数据准备——别急着跑代码,先给AI喂“干净饭”
1.1 拍图 or 搜图?数量与质量黄金区间
经验值:
- 目标主体“橘猫”→30~50张即可,太多反而过拟合;
- 目标主体“二次元原创角色”→最好80+,角色三视图、表情包、服饰细节全覆盖;
- 分辨率统一512×512以上,PNG/JPG都行,别整webp,部分脚本会跪。
1.2 自动去重+裁剪一条龙脚本
# dedup_resize.py
from pathlib import Path
from PIL import Image, ImageFile
import imagehash, shutil
ImageFile.LOAD_TRUNCATED_IMAGES = True
def dedup_and_resize(src_folder: Path, dst_folder: Path, size=512):
dst_folder.mkdir(exist_ok=True)
hashes = set()
for img_path in src_folder.rglob("*"):
try:
img = Image.open(img_path).convert("RGB")
h = imagehash.average_hash(img, 8)
if h in hashes:
continue # 重复图直接扔
hashes.add(h)
img = img.resize((size, size), Image.LANCZOS)
img.save(dst_folder / img_path.name, quality=95)
except Exception as e:
print("❌ 无法处理", img_path, e)
if __name__ == "__main__":
dedup_and_resize(Path("raw_imgs"), Path("clean_imgs"))
跑完你会得到一份clean_imgs/文件夹,体积清爽,哈希唯一。
1.3 手动打标签——别嫌麻烦,这是控制提示词的灵魂
LoRA训练需要“图文对”。懒人做法:让BLIP自动标注,再人工过一遍。
# 安装koboldcpp-blip Caption工具
pip install koboldcpp-blip
python -m koboldcpp_blip --input clean_imgs --output captions
生成得到同名.txt文件,内容类似a orange cat sitting on grass。
接着人工批量替换:
- 把“orange cat”统一成触发词
ohmycat(触发词越独特越好,避免跟原模型token冲突); - 如果想让模型记住猫穿“小披风”,就在对应图里手动加
ohmycat wearing red cape。
小技巧:触发词+描述词长度≤75 token(SD1.x限制),留点余量给后面推理加装饰词。
第二步:环境搭好——一次装对,省得后面抓鬼
# 新建conda环境,Python3.10最稳
conda create -n lora python=3.10 -y
conda activate lora
# 核心三件套
pip install torch==2.0.1+cu118 torchvision==0.15.2+cu118 -f https://download.pytorch.org/whl/torch_stable.html
pip install transformers accelerate diffusers==0.18.2
pip install -U xformers # 显存省30%
# 训练仓库用kohya_ss,社区最活跃
git clone https://github.com/kohya-ss/sd-scripts.git
cd sd-scripts
pip install -r requirements.txt
装完跑一把python -m pytest tests/——全绿就继续,有红就对着GitHub issue搜,99%是CUDA版本没对齐。
第三步:LoRA训练超细颗粒度拆解——参数怎么填、学习率踩雷实录
3.1 目录结构(强迫症福音)
project/
├─ clean_imgs/ # 图片
├─ captions/ # 同名txt
├─ output/ # 训练产出
├─ reg/ # 可选正则化图(防止过拟合)
└─ train.sh # 一键启动
3.2 训练脚本(可直接抄,含中文注释)
# train.sh
export MODEL_NAME="runwayml/stable-diffusion-v1-5" # 基模
export IMG_ROOT="clean_imgs"
export CAPTION_ROOT="captions"
export OUTPUT_DIR="output/ohmycat_lora"
export RESOLUTION=512
export TRAIN_BATCH_SIZE=1
export GRADIENT_ACCUMULATION_STEPS=4 # 相当于batch=4
export MAX_TRAIN_STEPS=800
export LEARNING_RATE="1e-4"
export LR_SCHEDULER="cosine_with_restarts"
export LR_WARMUP_STEPS=100
export NETWORK_DIM=64 # LoRA rank,越高越能吃细节
export NETWORK_ALPHA=32 # 平滑系数,一半rank即可
export SAVE_STEPS=200 # 每200步存一次,方便回滚
export CAPTION_DROPOUT=0.05 # 随机丢弃caption,防过拟合
accelerate launch --num_cpu_threads_per_process 8 train_network.py \
--pretrained_model_name_or_path=$MODEL_NAME \
--train_data_dir=$IMG_ROOT \
--output_dir=$OUTPUT_DIR \
--resolution=$RESOLUTION \
--batch_size=$TRAIN_BATCH_SIZE \
--gradient_accumulation_steps=$GRADIENT_ACCUMULATION_STEPS \
--max_train_steps=$MAX_TRAIN_STEPS \
--learning_rate=$LEARNING_RATE \
--lr_scheduler=$LR_SCHEDULER \
--lr_warmup_steps=$LR_WARMUP_STEPS \
--use_8bit_adam \
--network_module=networks.lora \
--network_dim=$NETWORK_DIM \
--network_alpha=$NETWORK_ALPHA \
--save_steps=$SAVE_STEPS \
--caption_dropout_rate=$CAPTION_DROPOUT \
--mixed_precision="fp16" \
--xformers \
--cache_latents \
--gradient_checkpointing
3.3 常见翻车现场
| 症状 | 大概率原因 | 速效救心丸 |
|---|---|---|
| loss 降到0.03 突然NaN | 学习率太高 | 降到5e-5 |
| 显存依旧爆炸 | batch_size=1也炸?把resolution降到384先跑通,再开梯度检查点 | |
| 生成图全灰 | 正则化图缺失+步数太多 → 模型崩。加–num_reg_images=200,或干脆降步数到500 |
第四步:模型出炉,怎么知道“训好了”?
4.1 实时验证脚本(训练边跑边抽卡)
# validate.py
import torch
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16
)
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
# 加载最新LoRA
lora_path = "output/ohmycat_lora/ohmycat_lora-800.safetensors"
pipe.unet.load_attn_procs(lora_path)
prompt = "ohmycat wearing sunglasses, sitting on beach, sunset"
image = pipe(prompt, num_inference_steps=20, guidance_scale=7.5).images[0]
image.save("validate_800.jpg")
肉眼观察:
- 猫的品种、毛色跟你训练图是否一致?
- 触发词
ohmycat下,猫以外的元素(背景、道具)是否自由变化?
如果“猫固定、背景多变”,恭喜,模型已收敛;
如果“猫变成狗”→触发词冲突,换词重练;
如果“猫永远同一姿势”→过拟合,加正则化图、降步数、加caption_dropout。
第五步:Dreambooth备选方案——显存够就上,效果确实更顶
LoRA 800步能出80分效果,Dreambooth 1200步能冲95分,但16 G显存是门票。
前端同学如果公司报销A100,可直接冲。
关键差异(其余参数类似):
- 把
--train_text_encoder打开,让文本编码器也一起微调; - 学习率要更低(5e-6),不然文本编码器分分钟NaN;
- 产出不是一个
.safetensors,而是全新model_index.json整包,体积2~4 G。
前端集成时,Dreambooth模型走“整包替换”,LoRA可走“即插即拔”,后面会细讲。
第六步:前端如何把“微调成果”塞进网页按钮?
6.1 技术选型:全栈JavaScript也能玩推理
方案A:纯前端ONNX
- 把LoRA权重合并到UNET,转ONNX,再转TensorFlow.js/WebGPU;
- 优势:无服务器成本,离线跑;
- 劣势:第一次加载模型200 M,流量费感人;且WebGPU支持率≈Chrome亲儿子。
方案B:小水管后端+React按钮(推荐)
- 后端FastAPI加载diffusers,走GPU推理,前端纯调API;
- 优势:前端代码无CUDA依赖,部署简单;
- 劣势:需要一张GPU云服务器。
下文以方案B为例,因为——
“让用户的浏览器去跑Stable Diffusion,风扇声会吓退99%甲方。”
6.2 后端FastAPI(单文件,含缓存、队列、错误重试)
# main.py
import io, uuid, time
from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
import torch
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
app = FastAPI()
device = "cuda"
# 全局加载一次模型,省显存
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16,
safety_checker=None # 省显存,自己加过滤
)
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
# 挂载LoRA,可热插拔
LORA_PATH = "output/ohmycat_lora/ohmycat_lora-800.safetensors"
pipe.unet.load_attn_procs(LORA_PATH)
pipe = pipe.to(device)
class GenReq(BaseModel):
prompt: str
width: int = 512
height: int = 512
steps: int = 20
guidance: float = 7.5
seed: int = -1
@app.post("/txt2img")
def txt2img(req: GenReq):
if req.seed == -1:
req.seed = int(time.time())
generator = torch.Generator(device=device).manual_seed(req.seed)
try:
image = pipe(
req.prompt,
width=req.width,
height=req.height,
num_inference_steps=req.steps,
guidance_scale=req.guidance,
generator=generator
).images[0]
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
buf = io.BytesIO()
image.save(buf, format="PNG")
buf.seek(0)
return StreamingResponse(buf, media_type="image/png")
# 健康检查
@app.get("/hc")
def hc():
return {"status": "ok"}
启动:
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 1
6.3 React组件(Next.js示范,自带loading、错误提示)
// components/OhmycatGenerator.tsx
import { useState } from 'react'
export default function OhmycatGenerator() {
const [prompt, setPrompt] = useState('ohmycat wearing christmas hat')
const [loading, setLoading] = useState(false)
const [img, setImg] = useState('')
const [error, setError] = useState('')
const generate = async () => {
setLoading(true)
setError('')
try {
const res = await fetch('/api/txt2img', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt, width: 512, height: 512 })
})
if (!res.ok) throw new Error(await res.text())
const blob = await res.blob()
setImg(URL.createObjectURL(blob))
} catch (e: any) {
setError(e.message)
} finally {
setLoading(false)
}
}
return (
<div className="p-4">
<textarea
className="w-full textarea textarea-bordered"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
/>
<button
className="btn btn-primary mt-2"
onClick={generate}
disabled={loading}
>
{loading ? '生成中…' : '抽一只猫'}
</button>
{error && <div className="text-red-500 mt-2">{error}</div>}
{img && (
<div className="mt-4">
<img src={img} alt="result" className="rounded shadow" />
</div>
)}
</div>
)
}
前端页面秒级出图,后端单次512×512/20步≈2.5 s(RTX 3080)。
如果想再提速,把--num_inference_steps降到15,或者在后端加cache——相同prompt直接读缓存。
第七步:踩坑大全——从“ CUDA out of memory”到“猫怎么又变成狗”
| 坑位 | 症状 | 根因 | 急救 |
|---|---|---|---|
| OOM | 训练/推理直接炸 | batch_size、resolution、attention同时飙高 | 先降resolution→384,再开gradient_checkpointing,再开xformers,再不行换LoRA |
| NaN Loss | loss曲线突然升天 | 学习率太高/文本编码器炸 | 降LR到5e-5,关闭text_encoder训练,或加clip_grad_norm=1.0 |
| 过拟合 | 同一背景、同一姿势 | 训练图太少/步数太多/caption_dropout太少 | 加正则化图200张,步数砍半,caption_dropout提到0.1 |
| 概念漂移 | 狗混入猫包 | 触发词太普通,跟原模型token冲突 | 触发词加特殊前缀xyz_ohmycat,重新打标 |
| 灰图生成 | 全灰/全黑 | 训练崩/scheduler不兼容 | 换回DDIM试,确认vae是否损坏 |
| 推理慢 | 用户等到睡着 | 没开xformers,没缓存latent | 后端加cache,开xformers,换DPMSolver |
第八步:效率翻倍的黑科技——数据清洗到云端部署一条龙
8.1 数据清洗自动化(加Webhook,推送到Hugging Face Dataset)
# auto_upload.py
from huggingface_hub import HfApi
import shutil, datetime
api = HfApi()
zip_name = f"ohmycat_{datetime.date.today()}.zip"
shutil.make_archive(zip_name[:-4], 'zip', "clean_imgs")
api.upload_file(
path_or_fileobj=zip_name,
path_in_repo=f"data/{zip_name}",
repo_id="你的用户名/ohmycat-dataset",
repo_type="dataset",
token="你的HF Token"
)
每晚定时跑,数据版本可回溯,队友再也不用“谁动了我的图”吵架。
8.2 Hugging Face Spaces秒级部署体验版
把上面FastAPI代码塞到Spaces→Docker SDK模板,Dockerfile写两行:
FROM pytorch/pytorch:2.0.1-cuda11.8-cudnn8-runtime
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
push上去,HF送你免费GPU 16 G,30分钟就能让甲方在线体验。
(记得把模型文件放Git-LFS,不然仓库爆炸。)
8.3 ControlNet加持——让猫精准坐在画面左下角
LoRA管“画风”,ControlNet管“构图”。
把Canny边缘图喂给ControlNet,prompt再写ohmycat,就能让猫按你给的轮廓摆pose。
前端调用只需多传一张binary图片,后端把ControlNet条件并联进pipeline即可,推理时间+30%,精准度+200%。
第九步:可复现、可协作、可上线的MLOps小作坊
9.1 版本管理:不止代码,模型、数据、参数全要管
- 代码:git
- 模型:git-lfs or DVC
- 数据:HF Dataset + tag
- 参数:把
train.sh里所有变量写进config.yaml,训练时hydra自动读,保证每次commit都能复现。
9.2 实验记录:Notion+MLflow双保险
- MLflow跟踪loss、学习率、最终FID;
- Notion写“人话”结论:800步猫毛最蓬松,1200步猫变胖,甲方喜欢胖猫→上1200。
9.3 云端训练:从Colab到AWS SageMaker
- Colab Pro+:RTX V100 16 G,一晚5美元,适合demo;
- SageMaker:G5.xlarge 24 G,按需计费,CI/CD一键部署,适合生产。
把train.sh包成sagemaker-training-job,数据集从HF拉,训练完自动推HF Hub,全流程无人值守。
写在最后:别只调模型,也调调自己的工作流
微调Stable Diffusion不是玄学,胜似玄学:
同样的30张猫图,有人出图是“ins滤镜”,有人出图是“克苏鲁”。
差别不在人品,而在“数据→参数→验证→迭代”的闭环有没有跑通。
把本文脚本全盘抄走,你至少能领先80%野生玩家;
再把MLOps流程跑顺,你就能让老板相信:
“AI画风定制”不是魔术,而是可排期、可上线、可回滚的普通需求。
下一回,当你凌晨三点看到猫咪高清大图从网页里蹦出来,
别忘了给显卡风扇一个拥抱——
它才是这段深夜加班故事里,最拼命的打工猫。
(全文完,代码已开源,愿世间再无“千人一面”。)

更多推荐


所有评论(0)