SeFi-Image 显存吃紧 / 先降分辨率再调精度与采样步数

文章导读
SeFi-Image 显存吃紧时,最不容易白折腾的顺序是:先把分辨率降下来,再动精度相关配置,最后才减少采样步数。原因是分辨率直接决定中间特征图和注意力的张量规模,改动通常只落在配置里,回滚成本最低;精度配置省下的是权重与激活的占用,但会带来画质和算子兼容的取舍;采样步数主要换的是时间,对显存峰值的削减通常有限,却会直接改变画面细节。判断有没有真的省下显存,不能靠“跑通了没有”,要看同一张图的峰值
📋 目录
  1. 壹 读出当前推理的显存占用与峰值
  2. 贰 只在配置里改分辨率并重跑一张
  3. 叁 再改精度相关配置项并对比显存
  4. 肆 最后减少采样步数并观察质量变化
  5. 伍 把三组结果记录成表,确认哪一步真正腾出了显存
A A

SeFi-Image 显存吃紧时,最不容易白折腾的顺序是:先把分辨率降下来,再动精度相关配置,最后才减少采样步数。原因是分辨率直接决定中间特征图和注意力的张量规模,改动通常只落在配置里,回滚成本最低;精度配置省下的是权重与激活的占用,但会带来画质和算子兼容的取舍;采样步数主要换的是时间,对显存峰值的削减通常有限,却会直接改变画面细节。判断有没有真的省下显存,不能靠“跑通了没有”,要看同一张图的峰值显存和耗时。

显存报错后先降分辨率,是把最直接的杠杆先动掉:它影响中间特征图和注意力开销,改动位置少、回滚成本低。分辨率调到不报错后,再动精度配置,最后才减少采样步数,因为步数主要换时间、省显存有限,且直接改变画面细节。每一步都应记录峰值显存与耗时,用同一提示词、同一随机种子复跑一张对照,确认是真的腾出显存,而不是把压力挪到别的阶段。

读出当前推理的显存占用与峰值

调整前先建立基线。没有基线,后面每一步“省了多少”都无从判断。建议固定同一提示词、同一尺寸、同一种子,只跑一张,避免批量推理把峰值掩盖过去。

调整的优先级顺序,可以先按下面这张表排:

顺序改动项典型配置位置主要影响回滚成本
1输出分辨率推理配置 / CLI 的 height、width特征图与注意力张量规模低,改回数值即可
2精度(dtype / precision)推理配置、CLI 参数或 pipeline 构造处的 torch_dtype权重与激活占用、算子支持中,需确认设备与算子支持
3采样步数推理配置 / CLI 的 steps、num_inference_steps耗时为主,峰值削减有限低,但画质变化明显

监控分两层。进程外看整卡,用下面这类命令持续采样,建议把输出重定向到文件,方便事后比对:

# 每秒刷新一次整卡占用
nvidia-smi `--query-gpu`=timestamp,memory.used,memory.total,utilization.gpu \
  `--format`=csv -l 1 | tee gpu_mem_baseline.csv

# 只想盯进程
watch -n 1 nvidia-smi

进程内看峰值,在推理脚本首尾各加一次统计,得到的是这一步真正申请到的显存:

SeFi-Image 显存吃紧 / 先降分辨率再调精度与采样步数
import torch
torch.cuda.reset_peak_memory_stats()
# ... 模型加载 + 单张推理 ...
print("peak_allocated_GB", torch.cuda.max_memory_allocated() / 1024**3)
print("peak_reserved_GB", torch.cuda.max_memory_reserved() / 1024**3)

峰值通常出现在四个阶段:模型权重上卡时、首次前向(含文本编码器或 VAE 加载)时、去噪循环中张量最大的一步、以及 VAE 解码与后处理放大时。如果日志里看不到阶段划分,可以在脚本里打点计时,把阶段名和当次 nvidia-smi 读数写在一起。记录格式建议统一成:

时间 | 阶段 | 分辨率 | dtype | steps | 进程内峰值 | 整卡占用 | 单张耗时 | 是否OOM

只在配置里改分辨率并重跑一张

这一步只改分辨率,精度和步数保持不动,否则无法判断是谁在起作用。字段名按实际脚本替换,常见写法是长宽分开,也有的用边长加比例:

# 推理配置片段(字段名按实际脚本替换)
inference:
  height: 1024   # 改前 1024 -> 改后 768 或 640
  width: 1024
  steps: 30
  precision: fp16
  seed: 1234
  prompt: "固定的那一句提示词"

如果是命令行入口,通常是 `--height`、`--width` 或 `--resolution` 这类参数,注意有些实现会强制取 8 或 64 的整数倍,改完先看配置回显或启动日志里打印的实际尺寸,确认没有被内部对齐规则改写。

改前改后各记录一次,重点看三列:整卡占用、进程内峰值、单张耗时。分辨率下降后,通常观察到的是峰值随尺寸下降,但下降幅度不是线性的,同时耗时也会同步减少。如果显存峰值几乎没动,说明瓶颈不在特征图,可能卡在权重加载或某个缓存未被释放,这时继续降分辨率属于白折腾,应该回头查常驻占用。

SeFi-Image 显存吃紧 / 先降分辨率再调精度与采样步数

再改精度相关配置项并对比显存

分辨率降到不报错之后,如果还想拿回画质,可以试着把精度往下调。配置位置一般在三处:推理配置文件里的 precision / dtype 字段;CLI 的 `--precision` 或 `--dtype` 参数;以及脚本里构造 pipeline 时的 torch_dtype=torch.float16 这类写法。三处如果同时存在,以实际生效的那一处日志为准,不要只看配置文件。

轮次分辨率dtype / precision进程内峰值整卡占用单张耗时是否OOM
A调整后尺寸fp32记录值记录值记录值—
B同上fp16 或 bf16记录值记录值记录值—

画面变化要盯几个固定观察点:高光与暗部的色带和断层、头发和织物这类细纹理、线条与文字的边缘锐度、整体色偏。建议把两张图并排放在同一查看器里放大到同一倍率看,而不是凭记忆判断。需要提醒的是,低精度在部分算子或设备上可能不被支持,出现报错或结果异常时应退回上一档,必要时确认当前设备对该精度类型的支持情况。

最后减少采样步数并观察质量变化

步数是最后动的一项。它对显存峰值的削减通常不如前两项明显,主要是省时间,代价落在细节和收敛程度上。取值建议做成梯度,而不是一次砍到底,比如在当前值附近取几档:当前值、当前值的三分之二左右、一半左右,具体档位按实际脚本支持的步数范围和调度器行为确定。

SeFi-Image 显存吃紧 / 先降分辨率再调精度与采样步数
# 步数梯度示例(数值按实际脚本替换)
steps: 30  -> 24  -> 20  -> 16

对照方式建议固定三件事:同一提示词、同一种子、同一分辨率与精度。每次只改步数,记录单张耗时和进程内峰值,同时把生成结果按步数命名归档,例如 seed1234_steps30.png。观察时按步数从高到低依次对比,留意结构是否散、细节是否糊、边缘是否出现明显噪点。如果某档开始出现结构性错误,说明已经低于这张图能接受的下限,应停在该档之上。

把三组结果记录成表,确认哪一步真正腾出了显存

三组结果放在一张表里,结论才站得住。汇总表建议至少包含这些字段:轮次、分辨率、精度、步数、进程内峰值、整卡占用、单张耗时、是否OOM、画面观察结论。填写时保持同一提示词和种子,只允许一个变量变化。

轮次 | 分辨率 | 精度 | 步数 | 进程内峰值 | 整卡占用 | 耗时 | OOM | 画面结论
基线 |        |      |      |            |          |      |     |
降分辨率 |     |      |      |            |          |      |     |
改精度 |       |      |      |            |          |      |     |
减步数 |       |      |      |            |          |      |     |

判断依据是行与行之间的差值,而不是单行的绝对值。如果降分辨率那一行的峰值下降最明显,说明特征图规模是主要矛盾;如果改精度降幅更大,说明权重与激活占用更值得处理;如果减步数几乎不改变峰值、只改变耗时,就不要指望用步数救显存。

恢复成功的验证方法有两条:一是日志里不再出现 OOM 回溯,且完整走完去噪与解码阶段并输出文件;二是进程内峰值和整卡占用都在设置的上限之内,并留有可接受的余量,用同样的命令再跑一张结果稳定。这里的顺序结论有适用边界:它针对的是单张推理、显存主要由中间张量决定的场景;如果瓶颈来自常驻权重、并发请求或未释放的缓存,先降分辨率未必有效,需要先定位常驻占用再决定改哪一项。具体取舍还要结合所用模型、设备显存和后端对低精度的支持情况确认。