本文档记录 Lykon/dreamshaper-7(Stable Diffusion 1.5 微调的文生图模型)在华为昇腾 NPU(Ascend910)上,基于 diffusers + torch_npu 的真实推理适配与验证结果。
StableDiffusionPipeline 组成为 UNet2DConditionModel(cross_attention_dim=768,参数 859,520,964)+ AutoencoderKL VAE(83,653,863)+ CLIPTextModel 文本编码器(123,060,480)+ DEISMultistepScheduler。float32(为正确性对照强制全精度,7.7 GB fp32 权重在 65 GB HBM 下峰值仅约 8.6 GB,绰绰有余),任务类型:text-to-image。num_inference_steps=25、guidance_scale=7.5(标准 SD1.5 多步 + CFG 用法)。适配要点:
trust_remote_code、无自定义算子;StableDiffusionPipeline.from_pretrained(dir, torch_dtype=torch.float32, safety_checker=None, requires_safety_checker=False) 后 pipe.to("npu:0") 即可整条管线上 NPU 真跑。safety_checker(safety_checker=None, requires_safety_checker=False)避免额外下载并获取未过滤输出用于评测。torch.Generator("npu").manual_seed(seed) 可用;注意 NPU 随机数流与 CPU 不同,此处固定 seed 只保证 NPU 内部逐次可复现(非跨设备一致)。同 prompt + 同 seed 25 步生成逐像素可复现(max|Δ|=0)。fp32;HF32(fp32-matmul 的 TF32-like 近似)默认关闭以保证 fp32 确定性,其偏差在一条真实 fp32 UNet 前向上另行实测(见 §9)。相关获取地址:
| 组件 | 版本 |
|---|---|
| NPU | Ascend910(65GB HBM) |
CANN | 8.5.1 |
torch | 2.9.0+cpu |
torch-npu | 2.9.0.post1 |
diffusers | 0.39.0 |
transformers | 4.57.6 |
Python | 3.11.14 |
# 依赖(diffusers 必须 --no-deps,否则会拉入新版 torch 覆盖 torch_npu)
pip install --no-deps diffusers # 0.39.0
# 其余运行期依赖多数已随 torch_npu 环境预置:transformers / huggingface_hub / safetensors / pillow / numpy
# 权重(约 7.7 GB,含 fp32 + fp16 两套 variant,本脚本用 fp32)。inference.py 内已自包含下载逻辑,缓存为空时自动拉取:
export HF_ENDPOINT=https://hf-mirror.com
python -c "from huggingface_hub import snapshot_download; \
snapshot_download('Lykon/dreamshaper-7', local_dir='$HOME/.cache/models/dreamshaper-7', \
ignore_patterns=['*.onnx','*.h5','*.msgpack','tf_model*','flax_model*'])"权重目录可用环境变量 dreamshaper_7_MODEL_DIR 或 MODEL_DIR 覆盖,默认 ~/.cache/models/dreamshaper-7。
昇腾单卡(本次为 card 0)独占运行,Ascend 编译缓存重定向到 /tmp(NFS 配额安全):
cd /opt/atomgit/deliverables/dreamshaper-7
flock -w 7200 /tmp/npu_card0.lock bash -c '
ASCEND_RT_VISIBLE_DEVICES=0 ASCEND_CACHE_PATH=/tmp/ascend_cache_dreamshaper-7 \
ASCEND_PROCESS_LOG_PATH=/tmp/ascend_log ASCEND_WORK_PATH=/tmp/ascend_work \
python inference.py --device npu 'ASCEND_RT_VISIBLE_DEVICES=0 下进程内该卡即 npu:0,故 inference.py 内统一使用 "npu:0"(可移植)。脚本仅支持 NPU,不提供 --device cpu。
固定输入:prompt="a photograph of an astronaut riding a horse",seed=12345,num_inference_steps=25,guidance_scale=7.5。
真实 stdout(节选):
=== [Gate 1] 逐子模型 output_loading_info(期望 0/0/0)===
unet: missing=0 unexpected=0 mismatched=0 params=859,520,964
vae: missing=0 unexpected=0 mismatched=0 params=83,653,863
text_encoder: missing=0 unexpected=0 mismatched=0 params=123,060,480
[Gate 1] PASS (all 0/0/0)
=== 加载 StableDiffusionPipeline (fp32) -> npu:0 ===
loaded+moved in 7.4s; scheduler=DEISMultistepScheduler
unet.device=npu:0 vae.device=npu:0 text_encoder.device=npu:0
=== [§5 Smoke] 固定 prompt+seed 多步生成 ===
generator on: npu
image size=(512, 512) mode=RGB shape=(512, 512, 3) dtype=uint8
pixel stats: mean=83.68 std=85.32 min=0 max=255
saved -> /tmp/dreamshaper-7_out.png
determinism (same seed x2): max|Δpixel|=0 mean|Δpixel|=0.0000| 验证项 | 结果 |
|---|---|
| 输出图像 | 512x512 RGB PNG(/tmp/dreamshaper-7_out.png),内容与 prompt 一致(宇航员骑马照片) |
| 25 步生成延迟 | ~2078 ms/image(稳态 p50,含 VAE 解码) |
| 复现性(同 seed 二次) | `max |
单图、25 步(num_inference_steps=25、guidance_scale=7.5、含 CFG 双前向),预热 5 次、正式计时 20 次:
| 指标 | 数值 |
|---|---|
| avg | 2078.3 ms |
| min | 2077.2 ms |
| max | 2081.7 ms |
p50 | 2078.0 ms |
p90 | 2079.3 ms |
p95 | 2081.7 ms |
吞吐(p50) | 0.481 img/s |
| 峰值 HBM | 8616 MB |
p95/p50 | 1.00(无双峰污染,测量极干净) |
文生图任务无 Ground-Truth 图像,采用「定性 + 复现性 + 结构度量」三证,并以随机初始化 UNet 对照组作为决定性功能证据。
prompt+同 seed 二次生成 max|Δpixel|=0(逐像素一致)。grad_mag」区分「自然照片 vs 噪声」——自然照片平滑、grad_mag 小;随机 UNet 输出高频彩噪、grad_mag 大。对随机对照跑 N=3 个不同权重种子,并要求预训练决定性低于每一个随机种子:| 对照 | grad_mag | 随机/预训练 |
|---|---|---|
| 预训练(正确加载) | 6.77 | — |
| 随机 UNet(wseed=0) | 17.78 | 2.63x |
| 随机 UNet(wseed=1) | 13.74 | 2.03x |
| 随机 UNet(wseed=2) | 13.93 | 2.06x |
| 随机均值 | 15.15 | 2.24x |
结论:预训练模型 grad_mag 决定性地低于全部随机种子(均值 2.24x,逐种子全胜),即预训练产出连贯结构、随机初始化产出噪声 → Gate 3 决定性通过。
注:本模型走多步
DEISMultistepScheduler,即使随机 UNet 的噪声预测也会被多步去噪部分平滑,故autocorr_lag1(邻域空间自相关)区分度不足(预训练0.979vs 随机≈0.955,差距小),因此用grad_mag(高频能量)作为主判别量。
三道加载正确性门禁全部通过:
| 门禁 | 结果 |
|---|---|
Gate 1(output_loading_info) | UNet / VAE / text_encoder 均 0 missing / 0 unexpected / 0 mismatched |
| Gate 2(config 落地) | cross_attention_dim=768、block_out_channels=[320,640,1280,1280]、attention_head_dim=8 等 9 项关键结构键全部生效一致;scheduler=DEISMultistepScheduler |
| Gate 3(随机对照) | grad_mag 预训练 6.77 vs 随机均值 15.15(2.24x,逐种子全胜)→ 决定性通过 |



最易踩的坑:ASCEND_CACHE_PATH 目录缺失会伪装成 HF32/ACL error 500001。
AclSetCompileopt ... error 500001。ASCEND_CACHE_PATH 指向的目录若不存在,GE/AOE 初始化失败会伪装成 HF32/ACL 报错,误导排查方向。os.makedirs(..., exist_ok=True) 预建。其余事项:
fp32 保正确性:config 提供 fp16/fp32 两套 variant,为正确性对照本脚本强制 torch_dtype=torch.float32(不传 variant,加载 model.safetensors);7.7 GB fp32 权重在 65 GB HBM 下峰值仅 8616 MB,无需回落 fp16。torch.Generator("npu").manual_seed(seed) 只保证 NPU 内部逐次可复现(同 seed 二次 max|Δpixel|=0),不保证与 CPU/GPU 跨设备逐像素一致;README 已如实标注。fp32,脚本默认关闭 HF32(torch.npu.matmul.allow_hf32=False、torch.npu.conv.allow_hf32=False)保证 fp32 确定性;在一条真实 fp32 UNet 前向上实测 HF32 on/off max|Δ(sample)| = 6.05e-04(相对 1.57e-4),量级极小。safety_checker 已关闭:脚本设 safety_checker=None, requires_safety_checker=False 以避免额外下载并获取未过滤输出用于评测;公开服务场景请自行开启。torch_dtype 弃用提示可忽略:diffusers 0.39.0 会打印「torch_dtype is deprecated! Use dtype instead!」,但模型级 from_pretrained 实际仍只接受 torch_dtype,忽略该提示即可。#NPU #Ascend