Files
face-lora-qwen-image/docs/云端训练完整操作文档.md

17 KiB
Raw Permalink Blame History

脸部 LoRA 云端训练 · 完整操作文档(v2 复盘 + v3 方案)

建立:2026-08-09 性质:训练全流程唯一权威参考(含上次全部踩坑记录) 铁律:训练期间不要手动干预;监控全自动;用户睡觉期间 AI 不执行任何未经文档确认的操作

本次训练实况(v3 最终版,2026-08-09

pod id k1nzrqh8lfj7jgRTX 4090 24GB$0.74/h
IP/SSH 103.196.86.68 / 50155
镜像 runpod/pytorch:2.4.0-py3.11-cuda12.4.1-devel-ubuntu22.04
blocks_to_swap 84090 24GB,与 v2 一致)
训练 dim32/lr1e-4/120ep/32张=3840步,实测 2.5s/步ETA ~2.5h
预缓存 已验证 128 文件(32 img + 32 txt + 32 VAE _qie + 32 TE _qie_te
监控 monitor_v3 + 计划任务 FaceLoRA-Monitor-v3(每5分钟)
下载 https://k1nzrqh8lfj7jg-8888.proxy.runpod.net/HTTP 200 已验证)
预算 max_hours 4$0.74/h × 4 = $2.96 封顶

⚠️ GPU 选择结论(踩坑记录,2026-08-09)

  • RTX 5090Blackwell sm_120)不可用——镜像 PyTorch 2.4 只支持到 sm_90VAE 预缓存 GPU 编码直接失败(sm_120 not compatible),浪费 55GB 下载 + 烧钱
  • A40 可用但慢——实测 5.33s/步(算力 ~37 TFLOPS,是 4090 一半)
  • RTX 4090 最优——实测 2.5s/步,且是 v2 完整验证过的(上次跑完 2400 步)
  • 教训:换 GPU 前必须确认「镜像 PyTorch 的 CUDA capability ≤ GPU 架构」!5090=sm_120、4090/A40=sm_86/89PyTorch 2.4 支持 sm_50-90

⚠️ 预缓存验证方法(踩坑记录)Qwen 的缓存文件是 .safetensorsimg_xxx_..._qie.safetensors + _qie_te.safetensors),不是 .npz!判断缓存成功要看 _qie.safetensors 数量 = 素材数,别查 .npz(会误判为 0 导致误删/重复折腾)。


一、项目目标与当前状态

目标:用老莫(用户)老婆的照片训练脸部 LoRA,叠加 Qwen-Rapid-AIO-NSFW-v23 checkpoint 出图。

训练底模Qwen-Image-Edit-2511 bf16 官方底模(不能用 FP8 融合模型训练)。

当前素材状态2026-08-09):

  • 已选 32 张(特写 21 / 半身 8 / 全身 3),无冗余(已删重复)
  • 大笑 3 张(稀缺)、表情 6 种、光照多样
  • 训练集已生成:K:\AI\training\ldf\singled\5. autohandling_v6\train_dataset\img_001..032 + txt
  • dataset.zip 已打包:cloud/dataset.zip20.8MB32 img + 32 txt = 64 文件)
  • 余额:$10 已充值(预计本次花费 $3-4,足够)

二、训练参数(最终定稿)

参数 说明
network_dim 32 v1(dim16) 验证"不像"→ 调大;v2 未验证效果
learning_rate 1e-4 与 dim32 配套
max_train_epochs 120 32 张 × 120 = 3840 步
blocks_to_swap 8 4090 24GB + 62GB RAM 够用
fp8_base/scaled/vl on 省显存
seed 42 可复现
save_every_n_epochs 10 每 10 epoch 存 checkpoint12 个)
sample_every_n_epochs 10 每 10 epoch 出 4 张样本图
分辨率 1024

云端 GPURTX 4090 Secure Cloud $0.74/h(A40 无容量,最后用 4090 成功) 预计时长3840 步 × 3.3s/步 ≈ 3.5-4 小时,花费 **$3**


三、云端 pod 全流程(v2 实测验证,脚本化)

3.1 创建 podRunPod REST API

$proj = "D:\F\NewI\opencode\daily-workspace\projects\脸部LoRA训练-Qwen-Image"
$key = Get-Content "$proj\.runpod_api_key"
$body = @{
    name = "face-lora-train-v3"
    imageName = "runpod/pytorch:2.4.0-py3.11-cuda12.4.1-devel-ubuntu22.04"
    gpuTypeIds = @("NVIDIA GeForce RTX 4090")
    gpuCount = 1
    cloudType = "SECURE"          # 铁律:只用 Secure,禁 Community
    containerDiskInGb = 100
    volumeInGb = 0
} | ConvertTo-Json
$r = Invoke-RestMethod -Uri "https://rest.runpod.io/v1/pods" -Method Post `
  -Headers @{Authorization = "Bearer $key"; "Content-Type" = "application/json"} -Body $body -TimeoutSec 60
"pod id: $($r.id) | cost/h: $($r.costPerHr)"

关键点

  • API key 位置$proj\.runpod_api_keyC:\Users\hmo\.runpod_api_key 不存在,别用错)
  • A40 曾无容量stuck provisioning)→ 4090 是最终验证可行的
  • 创建后轮询 pod 状态直到 runtime 非空(拿到 IP + SSH 端口 + Jupyter 端口 + 密码)

3.2 等待就绪 + 拿连接信息

Start-Sleep 60  # 后台等待(铁律:禁止前台 sleep 轮询,分两步)
$r = Invoke-RestMethod -Uri "https://rest.runpod.io/v1/pods/$PODID" -Headers @{Authorization = "Bearer $key"} -TimeoutSec 30
if ($r.runtime) {
  "publicIp: $($r.runtime.publicIp)"
  $r.runtime.ports | ForEach-Object { "private:$($_.privatePort) -> public:$($_.publicPort)" }
  "jupyterPass: $($r.runtime.jupyterPassword)"
}

3.3 ⚠️ SSH key 修复(每次新 pod 必做,最大的坑)

背景RunPod Settings 里注册的 key 是错误的(指纹 I2i//,与本地私钥 7Eep5Qz 不匹配)。新 pod 的 PUBLIC_KEY 环境变量注入的是错误 key → SSH 永远 Permission denied。

修复(需用户在 RunPod Web Terminal 手动执行一次):

curl -sk -u xiaoxiao:fXmRReiHMnY3AVB "https://git.yoin.fun/api/v1/repos/xiaoxiao/face-lora-qwen-image/raw/cloud/correct_key.pub" -o /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys && service ssh restart && ssh-keygen -lf /root/.ssh/authorized_keys

成功标志:指纹显示 7Eep5QzBfPDzvTgHfNMOC5TPOmW+zSbhHYLctKmkX5Y(与本地 id_rsa.pub 一致)。

正确 key 已存cloud/correct_key.pubGitea 仓库里也有,可 curl 拉取)。

3.4 上传 4 个文件 + 解压 + 启动

# 本地 scp 上传(注意:目标路径含中文会坑,用 ASCII 中转或分步)
scp -i $KEY -P $PORT cloud/dataset.zip cloud/bootstrap.sh cloud/train.sh cloud/sample_prompts.txt root@$IP:/workspace/

# SSH 进 pod 后:
cd /workspace
unzip dataset.zip -d train_dataset   # pod 上可能没有 unzip → 用 python -m zipfile -e 代替
bash bootstrap.sh                    # 装 musubi + 下 55G 模型 + 预缓存,10-15 分钟
nohup bash train.sh > /workspace/train.log 2>&1 &   # 后台训练

⚠️ SSH 后台任务坑nohup ... & 直接跑会因 SSH session 挂起。正确:setsid nohup bash train.sh > /workspace/train.log 2>&1 < /dev/null &(三流全重定向)。

3.5 checkpoint 下载(HTTP 代理,不用 scp)

# pod 上启动 http.server(替代 Jupyter 占用的 8888
python3 -m http.server 8888 --directory /workspace/ckpt

# 本地 aria2c 多线程下载(验证过:aria2c 1.37.0 可用)
aria2c -x16 -s16 -k1M -d "local_dir" -o "myface_lora-000050.safetensors" "https://$PODID-8888.proxy.runpod.net/myface_lora-000050.safetensors"

下载 URL 格式https://<podid>-8888.proxy.runpod.net/<filename> checkpoint 大小~563MB 每个(v2 实测) 校验:完整文件 = 590153736 字节(562.8MB),小于此值 = 不完整,删了重下。

3.6 收尾:删除 pod

Invoke-RestMethod -Uri "https://rest.runpod.io/v1/pods/$PODID" -Method Delete -Headers @{Authorization = "Bearer $key"} -TimeoutSec 30

四、⚠️ 上次 v2 训练完整翻车复盘(必须吸取)

时间线(8/6 23:00 - 8/7 07:55,用户睡觉期间)

  1. 23:59 创建 pod f0fhlwz7rrhma64090 Secure $0.74/h
  2. 00:02 用户 Web Terminal 修 key → SSH 连上 → 上传 → bootstrap
  3. 00:10 训练启动(2400 步,3.3s/步,预计 2.2h
  4. 00:23 用户睡觉。我启动 monitor_v2.ps1SSH 轮询)
  5. 00:26 训练正常:261/2400loss 0.0601
  6. 07:54 用户醒来质问:"你在干什么?"
    • SSH 连不上banner exchange refused
    • pod 没了0 pods)→ 余额耗尽,pod 被 RunPod 强制终止
    • monitor 日志 steps=? 一直解析失败bug)→ 没检测到余额耗尽,没抢救
  7. 08:00 确认:训练其实完整跑完了samples 48 张 = e010-e120 全有)!但 checkpoint 一个没下载monitor 只等 e120 后下载,且下载逻辑没触发)

根因(三条,全部是 monitor 脚本 bug

# Bug 后果
1 tail -2 抓 tqdm 进度 → 正则失败,steps 永远 ? 进度不可见,异常检测失效
2 下载只在 e120 出现后一次性执行,且用 scp(中文路径坑) 中途不下载,崩溃时抢救也失败
3 无余额监控 余额耗尽 pod 被删,checkpoint 全丢

其他踩坑(8/7 全天)

  • scp 目标路径含中文projects\脸部LoRA训练-Qwen-Image)→ 文件写入失败。解决:用 HTTP 代理下载
  • ssh 后台任务挂起 sessionnohup & 不够,要 setsid nohup ... </dev/null >log 2>&1 &
  • image_to_text.pySenseNova)用 musubi venv python 跑(系统 python PATH 坏了)
  • OpenCV 读不了中文路径的 ONNX 模型 → 拷贝到 %TEMP% ASCII 路径
  • GUI 每次改代码要升版本号(页面标题 + 终端都显示,验证重启生效)
  • 前后台铁律:禁止前台 sleep/轮询;长期任务必须后台

五、v3 改进方案(本次要用,monitor_v3.ps1 已写好)

核心变化:从"SSH 轮询"改为"HTTP 文件轮询 + 状态持久化 + 计划任务"

v2(失败) v3(本次)
调度 手动 while 循环 Windows 计划任务每 5 分钟 + PID 锁
进度信号 SSH 解析 tqdm(失败) 云端 checkpoint 文件出现HTTP HEAD
下载 只等 e120 一次性 增量下载:每个 epoch 出现立即下
余额 时长止损:超预算自动删 pod
崩溃 依赖进度解析(失效) checkpoint mtime 超 45 分钟未更新 → 抢救
状态 monitor_state.txt 持久化(已下载清单)
通知 只写日志(用户明确不要微信/通知)

监控脚本(正式版)

projects/脸部LoRA训练-Qwen-Image/tools/monitor_train_v3.ps1(计划任务指向此文件)

  • temp/train_env.jsonpod_id/dl_url/ssh_host/ssh_port/max_hours/total_steps
  • PID 锁防重(计划任务重复触发安全)
  • 每次运行做一轮:列云端 checkpoint → 增量下载 → 完成判定 → 崩溃判定 → 超时止损 → 写状态
  • 所有输出在纯 ASCII 目录 C:\Users\hmo\AppData\Local\Temp\opencode\face_lora\monitor_v3.log / train_events.log / monitor_state.txt / checkpoints_v3/

⚠️ PS 5.1 血泪教训(2026-08-09 调试 40 分钟才定位)

  1. PowerShell 变量名不区分大小写! $STATE(路径)会被 $statehashtable覆盖 → 状态从未真正持久化 → 超时止损/崩溃检测/已下载清单全部静默失效。路径变量必须用独特名(如 $STATEFILE)。
  2. 中文路径下 Add-Content 写新 .json 文件不可靠Test-Path 返回 True 但文件实际不存在)→ 输出目录用纯 ASCII,状态用纯文本 .txt
  3. train_env.json 读取失败必须退出(曾因 $PROJ 未定义导致配置全空 → TIMEOUT: 0h>budget 差点误删 pod)。已加安全阀:关键字段为空即 exit。
  4. 脚本必须存 GBK 编码PowerShell 5.1 用 ANSI 读),且不能含非 GBK 字符⚠️ \u26a0 等会崩)。
  5. Event 日志train_events.log)记录关键动作(下载/完成/止损/删pod),未来 session 恢复上下文的依据。
  6. ⚠️ 完成判定必须用 final 文件(myface_lora.safetensors)作为信号,不能等 e120!训练在 e110 后直接存 final,没有 e120 checkpoint。本次 monitor 用 hasE120 && hasFinal 判定 → 永远不触发 → pod 多烧 12 小时 ~$9(血泪教训,2026-08-09)。正确:if ($hasFinal) 即完成。
  7. ⚠️ checkpoint 文件名是 6 位零填充 {0:D6}myface_lora-000010.safetensors),不是 000$e(00010)——否则探测全部 404 下载不到。
  8. ⚠️ 下载循环每轮只下 1 个 + 跳过本地已完整 + aria2c --continue:否则串行下多个 1.18GB 超时导致 persist 来不及执行、每轮重复下载。

需要准备 train_env.json(创建 pod 后填入)

{
  "pod_id": "<创建后从API拿>",
  "dl_url": "https://<podid>-8888.proxy.runpod.net",
  "ssh_host": "<publicIp>",
  "ssh_port": <ssh public port>,
  "max_hours": 4.0,
  "total_steps": 3840
}

六、执行清单(本次训练,按顺序)

  • 1. 用户确认:批准启动云端训练(涉及花钱 $3-4
  • 2. 用 §3.1 API 创建 pod4090 Secure
  • 3. 轮询拿到 IP/SSH 端口/8888 端口
  • 4. 用户 Web Terminal 执行一次 §3.3 的 key 修复命令
  • 5. 上传 4 文件 → 解压 → bootstrapnohup 后台)
  • 6. bootstrap 完成 → 启动 train.shnohup 后台)
  • 7. pod 上启动 http.server 8888 服务 ckpt 目录
  • 8. 填好 train_env.json → 注册 Windows 计划任务(每 5 分钟跑 monitor_v3.ps1
  • 9. 用户睡觉。监控全自动:增量下载 checkpoint → 完成/崩溃/超时自动处理
  • 10. 早上验证:本地 checkpoints_v3 目录应有一批 563MB 完整 checkpoint
  • 11. checkpoint 复制到 WebUI models\Lora\qwen\ → Rapid-AIO 出图验证

七、关键文件位置

文件 路径
训练集 K:\AI\training\ldf\singled\5. autohandling_v6\train_dataset\
数据集 zip cloud/dataset.zip
云端 bootstrap cloud/bootstrap.sh
云端训练 cloud/train.sh(已加崩溃自动重启 ×20
正确 SSH key cloud/correct_key.pub
API key projects\脸部LoRA训练-Qwen-Image\.runpod_api_key
监控 v3 temp/monitor_v3.ps1
监控配置 temp/train_env.json(待创建 pod 后填)
监控日志 temp/monitor_v3.log
本地 checkpoint temp/checkpoints_v3/

八、铁律(每次训练必须遵守)

  1. 只用 Secure Cloud,禁 Community(用户明确禁止)
  2. 创建 pod = 花钱,必须用户批准后才能执行
  3. 训练期间零手动干预,一切自动(用户睡觉)
  4. 任何新操作先查本文档,不记得就问,不猜
  5. 改代码必须升版本号(页面+终端可见)
  6. 禁止前台 sleep/轮询;长期任务后台启动
  7. scp 不用中文路径;下载用 HTTP 代理 + aria2c
  8. checkpoint 增量下载,任何时刻断线已下载的都是安全的
  9. 余额/时长超预算自动止损,不裸奔
  10. 训练完成后自动删 pod,停计费
  11. ⚠️ 换 GPU 前先查架构兼容性:镜像 PyTorch 2.4 支持 CUDA sm_50-905090(sm_120) 不可用,4090(sm_89)/A40(sm_86) 可用
  12. ⚠️ 预缓存验证看 _qie.safetensors(数量=素材数),不是 .npz;确认缓存齐全再启动训练,否则报 "No training items found"
  13. 文档更新不用问用户,改完直接写;涉及关键决策(换 GPU/删 pod)才需要确认
  14. 每次换 pod 都要用户修 SSH keyRunPod 注册 key 是坏的 I2i//)——除非找到一次性注入正确 key 的方法

本次 v4 训练实况(2026-08-10,续炼)

pod id 74oru7saetzjo9RTX 4090 Secure0.74/h
类型 续炼(非从头)
续炼起点 myface_lora-000060.safetensorsv3 成果,1.1GB
数据集 v732 图 + 全面重调 caption,3 张白色长手套标注)
训练 dim32/lr1e-4/50ep/32张=1600步,实测 ~3.2s/步,~1h15m
预缓存 云端 bootstrap 内完成(Vae + TextEncoder
输出 myface_v7000010-000040 + final,各 1125MB
完成 final 下载 → 自动删 pod06:15:35),花费 ~2h ≈ .5
成果位置 WebUI models\Lora\qwen\ivy\v7\5 文件,全 1125MB 完整)

v4 新增经验

  1. 续炼流程:本地打包 v7 dataset.zip → train.sh 加 --network_weights 指旧 checkpoint → 上传旧 checkpoint 到 /workspace/ → bootstrap 里 [3.5] 步骤自动移到 /workspace/ckpt/
  2. 运行新 pod 后查连接信息RunPod API 的 untime 字段为空是正常的!连接信息在顶层字段(publicIp/portMappings/ports),别等 untime 就绪(会误判卡住)
  3. SSH key:本次 env.PUBLIC_KEY 显示正确 keyhmo@daily-workspace),但 SSH 仍 Permission denied → 仍需用户 Web Terminal 执行 §3.3 修复命令(指纹 7Eep5Qz
  4. 训练完成无最后中间档50ep 训练最终只有 000010-000040 + finalepoch 50 直接存 final,无 000050)——monitor 别等最后中间档,final 出现即完成(与 v3 的 e110 模式一致)
  5. monitor 状态残留坑:旧训练 monitor_state.txt 的 done=True 会让新监控直接跳过 → 每次新训练前必须重置状态文件
  6. monitor 变量名坑:不能用 $envPS 内置 provider 前缀)当普通变量,会导致状态持久化静默失效
  7. 本地 run_cache.py 是死代码:训练走云端,本地预缓存脚本无意义(曾误花时间修它)

成果对比建议

  • myface_v7.safetensorsfinal)为主力
  • 与 myface_lora-000100v3 用户最满意版)对比:重点看手套是否正常(本次补了 3 张白色长手套标注)