m0_63918125/controlnet-inpaint-endpoint
模型介绍
文件和版本
Pull Requests
讨论
分析

controlnet-inpaint-endpoint on Ascend NPU (torch_npu 2.9.0.post1)

1. 简介

本文档记录 OrderAndChaos/controlnet-inpaint-endpoint(ControlNet v1.1 inpaint 权重,等价于 control_v11p_sd15_inpaint)在华为昇腾 NPU(npu:0)上的适配与真机验证结果。

  • 模型结构:ControlNetModel(361,279,120 参数,cross_attention_dim=768, block_out_channels=[320,640,1280,1280],in_channels=4),配合 Stable Diffusion 1.5 base 组成 StableDiffusionControlNetPipeline。
  • 任务类型:image-to-image / ControlNet inpainting(图像局部重绘)。
  • dtype:全程 float32(正确性对照,未用 fp16)。
  • 该仓库只提供 ControlNet 权重(diffusion_pytorch_model.safetensors + config.json), base 管线需另取 SD1.5。本次 base 使用非 gated 的 stable-diffusion-v1-5/stable-diffusion-v1-5 (即原 runwayml/stable-diffusion-v1-5 的同权重社区再上传)。

适配要点:

  • 无需 trust_remote_code;ControlNetModel.from_pretrained + StableDiffusionControlNetPipeline.from_pretrained(SD15, controlnet=...) 后 .to("npu:0") 即可。
  • 条件构造遵循官方 handler.py:把 mask 覆盖(待重绘)区域的像素置为 -1.0,得到 inpaint 控制图, 再传给 StableDiffusionControlNetPipeline(非 Inpaint 专用管线)。
  • safety_checker=None、feature_extractor=None(研究评测,跳过可选组件;本地未取其配置)。
  • scheduler 用 UniPCMultistepScheduler(官方 handler 一致)。

相关获取地址:

  • 权重下载地址(HuggingFace):https://huggingface.co/OrderAndChaos/controlnet-inpaint-endpoint
  • 权重下载地址(GitCode 镜像):https://ai.gitcode.com/hf_mirrors/OrderAndChaos/controlnet-inpaint-endpoint
  • SD1.5 base(本次实际使用):https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5

2. 验证环境

组件版本
CANN8.5.1
torch2.9.0
torch-npu2.9.0.post1
diffusers0.39.0
transformers4.57.6
numpy1.26.4
Python3.11.14
  • NPU:Ascend910,逻辑卡 npu:0(物理卡 1,ASCEND_RT_VISIBLE_DEVICES=1,65536 MB HBM)
  • 推理引擎:torch_npu + diffusers
  • 权重路径:ControlNet ~/.cache/models/controlnet-inpaint-endpoint(约 1.4 GB), SD1.5 base ~/.cache/models/sd15-base(约 4 GB)
  • 任务类型:image-to-image(controlnet inpainting)

3. 环境准备与权重获取

export PATH=/usr/local/python3.11.14/bin:$PATH
pip install -r requirements.txt          # diffusers 需 --no-deps,见文件注释
python -c "import torch,torch_npu; assert torch.npu.is_available(); print('NPU OK')"

权重自动获取:inference.py 顶部自包含,缺权重时经 HF_ENDPOINT=https://hf-mirror.com snapshot_download 拉取 ControlNet 权重与 SD1.5 base(只取 safetensors + 必要 config, 忽略 .bin/TF/Flax/ONNX 重复件)。实测经 hf-mirror + Xet 直连可稳定下载(大文件偶发超时会自动续传)。

4. 推理运行

已验证通过的命令(套卡锁 + 固定物理卡 + 编译缓存进 /tmp):

flock -w 7200 /tmp/npu_card1.lock bash -c '
  export PATH=/usr/local/python3.11.14/bin:$PATH
  ASCEND_RT_VISIBLE_DEVICES=1 ASCEND_CACHE_PATH=/tmp/ascend_cache_controlnet-inpaint-endpoint \
  python inference.py --device npu'

脚本一次跑完三道加载门禁、Smoke、Gate-3 对照、HF32 实测与性能采集,结果写 /tmp/controlnet-inpaint-endpoint_npu_results.json,重绘输出图存 /tmp/controlnet-inpaint-endpoint_out.png。

5. Smoke 验证

python inference.py --device npu 在昇腾 NPU 上的真实输出(节选):

=== [Gate 1] ControlNet output_loading_info(期望 0/0/0)===
  controlnet: missing=0 unexpected=0 mismatched=0 params=361,279,120
  [Gate 1] PASS (0/0/0)

=== [Gate 2] ControlNet config 落地审计(原始 config.json vs 生效值)===
  cross_attention_dim: file=768 effective=768 OK
  block_out_channels: file=[320, 640, 1280, 1280] effective=[320, 640, 1280, 1280] OK
  in_channels: file=4 effective=4 OK
  conditioning_embedding_out_channels: file=[16, 32, 96, 256] effective=[16, 32, 96, 256] OK
  [Gate 2] PASS

=== [§5 Smoke] 合成图+矩形mask inpaint ===
  generator on: npu
  image size=(512, 512) mode=RGB shape=(512, 512, 3) dtype=uint8
  pixel stats: mean=101.60 std=60.72 min=1 max=241
  unmasked-region mean|Δ vs orig|=16.59 (lower=structure preserved)
  determinism (same seed x2): max|Δpixel|=0 mean|Δpixel|=0.0000
  pretrained unmasked-region corr vs orig=0.8758

=== [Gate 3] 随机初始化 ControlNet 对照(N=3 seeds)===
  [A structure-control] pretrained unmasked_corr=0.8758 vs random seeds=[-0.0857, -0.0857, -0.0857] mean=-0.0857 -> beats_all(+0.1)=True
  [B determinism] same-seed max|Δpixel|=0 (<=5 -> True)
  [C sane range] min=1 max=241 std=60.72 -> True
  [Gate 3] DECISIVE — pretrained ControlNet conditions output on control image; random-init destroys structure

=== ALL STAGES DONE (Gate1/Gate2/Gate3 PASS, smoke OK, perf OK) ===

验证结果:

  • Gate 1:ControlNet 权重加载 missing=0 unexpected=0 mismatched=0,361,279,120 参数。
  • Gate 2:原始 config.json 关键结构键(cross_attention_dim/block_out_channels/ conditioning_embedding_out_channels 等)全部落到 model.config。
  • Smoke:合成砖墙图 + 中央矩形 mask + prompt,512x512 重绘输出,像素范围合理(1~241)。
  • 完全确定性:同 seed 两次生成 max|Δpixel|=0。
  • 结构受控:未 mask 区域与原图相关性 0.876(预训练 ControlNet 保留原图结构、只在 mask 内生成)。

6. 性能参考

测试条件:单图、25 步、512x512、guidance_scale=7.5、CFG(batch=2)、float32; 预热 5 次、正式 20 次、每次前后 torch.npu.synchronize()。

指标数值
avg_ms2956.9 ms
min_ms / max_ms2946.6 / 2996.7 ms
p50_ms / p90_ms / p95_ms2951.5 / 2994.7 / 2996.7 ms
throughput(p50)0.339 img/s
peak_HBM6762 MB
p95/p501.02(稳态平滑,无明显尖刺)

7. 精度评测

生成任务无参照 GT,采用「预训练 ControlNet 受控生成 vs 随机初始化 ControlNet」三证判别:

指标数值
评测方式未 mask 区域与原图像素相关性 + 决定性 + 数值合理性
预训练 未mask区域相关性0.8758
随机初始化 未mask区域相关性-0.0857(N=3 seed 均值)
判别beats_all(+0.1)=True → 决定性胜出
决定性同 seed×2 `max
数值范围min=1 max=241 std=60.72(sane)

结论:预训练 ControlNet 令输出受控于控制图(保留原图非重绘区结构、仅在 mask 内合成内容), 随机初始化 ControlNet 破坏图像结构(相关性趋近 0)。三证均通过,Gate 3 决定性 PASS。

HF32 说明:管线 float32;对 ControlNet 一次真实 fp32 前向测 HF32 on/off 偏差, mid_block max|Δ| = 9.07e-03(相对 1.76e-03)。评测默认关闭 HF32(torch.npu.conv.allow_hf32=False、 torch.npu.matmul.allow_hf32=False)以保证确定性。

8. 适配截图

  • agent workflow
  • npu device call
  • model result

9. 注意事项

该仓库只含 ControlNet 权重,SD1.5 base 需另取,且 base 的 model_index.json 声明了可选的 feature_extractor/safety_checker 组件——只下载核心子目录时会因缺 preprocessor_config.json 加载失败。

实际失败特征如下:

  • 现象:StableDiffusionControlNetPipeline.from_pretrained 抛出 OSError: Can't load image processor。
  • 关键报错:Can't load image processor for '.../sd15-base' ... make sure ... containing a preprocessor_config.json file(其上层为 IndexError: list index out of range)。
  • 位置:transformers/image_processing_base.py: get_image_processor_dict。

原因不是权重缺失或路径错误,而是 base 管线的可选组件(feature_extractor 的 CLIPImageProcessor 配置)未随核心 safetensors 一起下载。处理方式:加载时显式跳过可选组件—— from_pretrained(..., safety_checker=None, feature_extractor=None, requires_safety_checker=False)。

其余注意事项:

  1. 首帧编译耗时:首次前向触发 CANN/TBE 算子编译,单次可达数十秒;性能采集务必预热 >=5 次 剔除编译,正式段 p95/p50=1.10 稳态平滑。
  2. ASCEND_CACHE_PATH 必须 import 前预建:缺该目录会让 GE/AOE 初始化失败, 常伪装成 ACL error 500001。脚本已在顶部 os.makedirs(...)。
  3. inpaint 条件构造:控制图需把待重绘(mask 白区)像素置 -1.0,其余保留归一化像素; 这是 control_v11p_sd15_inpaint 的正确用法(配 StableDiffusionControlNetPipeline,非 Inpaint 管线)。
  4. 进程要完全 detach:长时间 fp32 多次生成期间,若父 shell 被回收会出现 TBE Subprocess ... main process disappeared;用 setsid/nohup 完全脱离终端再跑。
  5. base 权重来源:runwayml/stable-diffusion-v1-5 已从 HF 下架,使用同权重的 stable-diffusion-v1-5/stable-diffusion-v1-5(非 gated)替代。

10. 标签

#NPU #Ascend