Diffusers 混合推理 API 参考:remote_encode与remote_decode远程 VAE 编解码实战指南
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
混合推理(Hybrid Inference)是 Diffusers 提供的一项实验性能力,它把 VAE 编码、解码过程卸载到远程推理端点执行,从而让本地仅需加载扩散模型主体即可完成完整推理流程。本文以 api_reference.md 为骨架,结合 remote_utils.py 的源码 docstring 与 tests/remote 下的测试用例,系统讲解remote_encode与remote_decode两个公开 API 的完整参数语义、底层实现与实战用法,读完即可在低显存环境下跑通"编码 → 生成 → 解码"的完整链路。
一、混合推理 API 概述
1.1 为什么需要混合推理
扩散模型推理的显存压力通常来自两部分:UNet/Transformer 主干与 VAE。混合推理将占用可观显存、且与主干计算解耦的 VAE 部分转移到远程端点,本地只需要承担文本编码器与 UNet/Transformer 的推理负载,显著降低硬件门槛。其核心优势(见 overview.md)包括:
- 降低要求:无需昂贵硬件即可访问强大模型;
- 质量无损:解码、编码仍由对应模型的官方 VAE 完成,不牺牲输出质量;
- 成本友好:当前以免费 Pilot 形式提供;
- 开发者友好:只需一次简单的 HTTP 请求即可获得响应。
整个功能由 Hugging Face Inference Endpoints 支撑,对应端点常量集中定义在 constants.py。
1.2 API 入口
api_reference.md通过 autodoc 指令从源码 docstring 自动生成参考文档,公开 API 位于diffusers.utils.remote_utils模块(模块导出处),共两个函数:
| 函数 | 作用 | 返回值 |
|---|---|---|
remote_encode | 将图像/视频远程编码为潜在表示(latent) | torch.Tensor |
remote_decode | 将潜在表示远程解码为图像/视频 | Image.Image/list[Image.Image]/bytes/torch.Tensor |
调用方式:
from diffusers.utils.remote_utils import remote_decode, remote_encode二、remote_encode:远程 VAE 编码
remote_encode适用于训练、图生图、图生视频等场景——把图像或视频转换为潜在表示。其函数签名为:
def remote_encode( endpoint: str, image: "torch.Tensor" | Image.Image, scaling_factor: float | None = None, shift_factor: float | None = None, ) -> "torch.Tensor":2.1 参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
endpoint | str | 是 | 远程编码端点的 URL。不同模型对应不同端点,见 constants.py 中的ENCODE_ENDPOINT_*常量 |
image | torch.Tensor或PIL.Image.Image | 是 | 待编码的图像。传入torch.Tensor时需为float16/bfloat16等内存布局连续的张量;传入 PIL 图像时会自动序列化为 PNG 字节流发送 |
scaling_factor | float | 否 | 缩放因子。传入后端点会在编码过程中自动应用缩放(等价于latents * scaling_factor的逆操作)。若为None,则输入必须已由调用方完成缩放 |
shift_factor | float | 否 | 平移因子。传入后端点自动应用平移(等价于latents - shift_factor的逆操作)。若为None,则输入必须已由调用方完成平移 |
2.2 基本用法示例
以 Flux 模型为例,将一张图像远程编码为潜在表示:
from diffusers import FluxPipeline from diffusers.utils import load_image from diffusers.utils.remote_utils import remote_encode pipeline = FluxPipeline.from_pretrained( "black-forest-labs/FLUX.1-schnell", dtype=torch.float16, vae=None, # 关键:不加载本地 VAE device_map="cuda", # 也可用 "mps"、"xpu"、"cpu" ) init_image = load_image("path/to/astronaut.jpg") init_image = init_image.resize((768, 512)) init_latent = remote_encode( endpoint="https://ptccx55jz97f9zgo.us-east-1.aws.endpoints.huggingface.cloud/", image=init_image, scaling_factor=0.3611, # Flux 的缩放因子 shift_factor=0.1159, # Flux 的平移因子 )提示:
remote_encode使用的 Flux 编码端点与解码端点不同,编码端点在 constants.py 中定义为ENCODE_ENDPOINT_FLUX。
三、remote_decode:远程 VAE 解码
remote_decode将扩散模型输出的潜在表示转换回图像或视频,是混合推理最常用的 API。其函数签名为:
def remote_decode( endpoint: str, tensor: "torch.Tensor", processor: "VaeImageProcessor" | "VideoProcessor" | None = None, do_scaling: bool = True, scaling_factor: float | None = None, shift_factor: float | None = None, output_type: Literal["mp4", "pil", "pt"] = "pil", return_type: Literal["mp4", "pil", "pt"] = "pil", image_format: Literal["png", "jpg"] = "jpg", partial_postprocess: bool = False, input_tensor_type: Literal["binary"] = "binary", output_tensor_type: Literal["binary"] = "binary", height: int | None = None, width: int | None = None, ) -> Image.Image | list[Image.Image] | bytes | "torch.Tensor":3.1 核心参数详解
输入相关
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
endpoint | str | — | 远程解码端点 URL,见 constants.py 的DECODE_ENDPOINT_*常量 |
tensor | torch.Tensor | — | 待解码的潜在表示张量。序列化在本地 CPU 完成,本地设备不影响结果 |
processor | VaeImageProcessor/VideoProcessor | None | 图像/视频后处理器。当return_type="pt"且需要图像输出、或视频模型返回pil时需要传入 |
height/width | int | None | 仅packed latents(如 Flux 打包格式)必须显式传入,用于还原空间尺寸;普通[1, C, H, W]布局无需传入 |
缩放与平移(Scaling / Shift)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
do_scaling | bool | True | 已弃用(计划在 1.0.0 移除)。应改用显式传入scaling_factor/shift_factor。为True时缩放(如latents / vae.config.scaling_factor)在远端执行;为False时输入必须已应用缩放 |
scaling_factor | float | None | 传入后在远端自动应用缩放。若为None,输入必须已由调用方完成缩放 |
shift_factor | float | None | 传入后在远端自动应用平移(如latents + vae.config.shift_factor)。若为None,输入必须已由调用方完成平移 |
输出控制
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
output_type | "mp4"/"pil"/"pt" | "pil" | 端点的输出类型:"mp4"仅视频模型支持,端点返回视频bytes;"pil"图像模型返回image_format编码的图像字节,视频模型返回已部分后处理的torch.Tensor;"pt"图像与视频均支持,端点返回torch.Tensor |
return_type | "mp4"/"pil"/"pt" | "pil" | 函数的返回类型:"mp4"返回视频bytes;"pil"返回PIL.Image.Image;"pt"返回torch.Tensor |
image_format | "png"/"jpg" | "jpg" | 仅output_type="pil"时生效,指定端点返回 jpg 还是 png |
partial_postprocess | bool | False | 仅output_type="pt"时生效:False时返回未反归一化的float16/bfloat16张量;True时返回已反归一化的uint8张量 |
input_tensor_type/output_tensor_type | "binary" | "binary" | 张量传输格式。"base64"已被弃用,统一使用"binary" |
3.2output_type与return_type的四种组合
两个参数独立控制"端点侧输出"与"本地侧返回",组合后可覆盖几乎所有使用场景:
output_type="pil"+return_type="pil":端点返回图片字节,本地直接打开为 PIL 图像(无需processor);output_type="pt"+return_type="pil":端点返回张量,本地用processor后处理为 PIL 图像(partial_postprocess=True时可不传processor);output_type="pt"+return_type="pt":端到端张量传输,适合对接第三方后处理代码(不要求processor);output_type="mp4"+return_type="mp4":视频字节流直通,本地写文件即可。
3.3 官方推荐
源码 docstring 中给出了三条传输方案建议:
"pt"+partial_postprocess=True:最小传输体积下保持完整质量(uint8 已反归一化);"pt"+partial_postprocess=False:与第三方代码兼容性最好(保留浮点张量);"pil"+image_format="jpg":整体传输体积最小。
3.4 解码实战示例(Flux)
Flux 的 latent 是打包(packed)布局,解码时必须显式传入height与width:
from diffusers import FluxPipeline from diffusers.utils.remote_utils import remote_decode pipeline = FluxPipeline.from_pretrained( "black-forest-labs/FLUX.1-schnell", dtype=torch.bfloat16, vae=None, device_map="cuda", ) prompt = "A photorealistic Apollo-era photograph of a cat astronaut on the Moon..." latent = pipeline( prompt=prompt, guidance_scale=0.0, num_inference_steps=4, output_type="latent", # 关键:管线输出 latent 而非图像 ).images image = remote_decode( endpoint="https://whhx50ex1aryqvw6.us-east-1.aws.endpoints.huggingface.cloud/", tensor=latent, height=1024, width=1024, scaling_factor=0.3611, shift_factor=0.1159, ) image.save("image.jpg")3.5 视频解码实战示例(HunyuanVideo)
视频模型的远端解码支持output_type="mp4",直接获得视频字节:
import torch from diffusers import HunyuanVideoPipeline, HunyuanVideoTransformer3DModel from diffusers.utils.remote_utils import remote_decode transformer = HunyuanVideoTransformer3DModel.from_pretrained( "hunyuanvideo-community/HunyuanVideo", subfolder="transformer", dtype=torch.bfloat16 ) pipeline = HunyuanVideoPipeline.from_pretrained( "hunyuanvideo-community/HunyuanVideo", transformer=transformer, vae=None, dtype=torch.float16, device_map="cuda", ) latent = pipeline( prompt="A cat walks on the grass, realistic", height=320, width=512, num_frames=61, num_inference_steps=30, output_type="latent", ).frames video = remote_decode( endpoint="https://o7ywnmrahorts457.us-east-1.aws.endpoints.huggingface.cloud/", tensor=latent, output_type="mp4", ) if isinstance(video, bytes): with open("video.mp4", "wb") as f: f.write(video)四、模型缩放/平移因子速查
scaling_factor与shift_factor因模型而异,源码 docstring 与 tests/remote/test_remote_decode.py 中给出了各模型的权威取值:
| 模型 | scaling_factor | shift_factor | 解码端点常量 | 备注 |
|---|---|---|---|---|
| Stable Diffusion v1 | 0.18215 | — | DECODE_ENDPOINT_SD_V1 | 参考stabilityai/sd-vae-ft-mse |
| Stable Diffusion XL | 0.13025 | — | DECODE_ENDPOINT_SD_XL | 参考madebyollin/sdxl-vae-fp16-fix |
| Flux | 0.3611 | 0.1159 | DECODE_ENDPOINT_FLUX | latent 为 packed 布局,解码需传height/width;测试中 dtype 为bfloat16 |
| HunyuanVideo | 0.476986 | — | DECODE_ENDPOINT_HUNYUAN_VIDEO | 仅支持解码;dtype 为float16 |
注意:以上数值必须与所选端点对应的 VAE 模型严格匹配。若
scaling_factor/shift_factor传None,则调用方必须在发送前自行完成缩放与平移(测试用例test_no_scaling正是先本地tensor / scaling_factor、tensor + shift_factor再调用)。
五、底层实现剖析
remote_utils.py的每个公开函数都由"输入校验 → 请求准备 → HTTP 传输 → 响应后处理"四个阶段组成,理解这一链路有助于排查问题。
5.1 编码链路(prepare_encode→postprocess_encode)
check_inputs_encode:参数校验占位(当前为空实现);prepare_encode:若输入是torch.Tensor,通过safetensors.torch._to_ndarray(image.contiguous())取出底层 numpy 数组再转字节,同时把shape与dtype写入请求参数;若输入是 PIL 图像,则保存为 PNG 字节流。缩放/平移因子也会随请求参数一并发送;- HTTP 传输:
requests.post(endpoint, **kwargs),端点异常时抛出RuntimeError(response.json()); postprocess_encode:从响应头读取shape与dtype,用torch.frombuffer在本地零拷贝重建torch.Tensor。
5.2 解码链路(prepare_decode→postprocess_decode)
解码链路多了内容协商逻辑:
prepare_decode依据参数组合设置Content-Type: tensor/binary与Accept头——"pil"+ jpg 时为image/jpeg,png 时为image/png,"mp4"时为text/plain,其余为tensor/binary;shape、dtype、scaling_factor、shift_factor等随请求发送;postprocess_decode按output_type分支处理响应:"pt":从字节流重建张量;partial_postprocess=False且传processor时,经由processor.postprocess/postprocess_video得到 PIL 图像;"pil"且无processor:Image.open(io.BytesIO(...))直接打开端点返回的图片字节,并通过 detect_image_type(依据 JPEG/PNG/GIF/BMP 魔数)还原图片格式;"pil"且有processor:将float张量permute后乘以 255 转为uint8图像数组;"mp4":原样返回视频字节。
5.3 张量 dtype 映射
端到端传输使用 DTYPE_MAP 在字符串与torch.dtype之间映射,覆盖float16、float32、bfloat16、uint8四种,这也是output_type="pt"时能无损还原张量的关键。
六、测试验证与限制说明
6.1 测试覆盖
仓库在 tests/remote/test_remote_decode.py 与 tests/remote/test_remote_encode.py 中对上述 API 进行了系统性验证:
- 组合覆盖:
test_output_type_pt、test_output_type_pil、test_output_type_pt_partial_postprocess、test_output_type_pt_return_type_pt、test_output_type_mp4等用例逐一验证各输出组合; - 无缩放路径:
test_no_scaling验证调用方本地预缩放后传入do_scaling=False的兼容路径; - 弃用告警:
test_do_scaling_deprecation、test_input_tensor_type_base64_deprecation验证do_scaling与base64传输的FutureWarning; - 多分辨率:
test_multi_res覆盖 320~2048 共 12 档分辨率下的编码→解码往返; - 确定性:测试在 CPU 上以固定种子(
manual_seed(13))生成 latent 再搬运到目标设备,以保证参考切片可复现。
这些测试均标记为@slow,命中真实 Inference Endpoints,不属于常规 CI 契约。
6.2 使用限制与注意事项
- 实验性功能:混合推理当前处于实验阶段,接口与端点随时可能调整,反馈可通过项目 Issue 提交;
- 端点可用性:以仓库当前状态为准,编码端点(
ENCODE_ENDPOINT_*)曾出现下架并返回404 NOT_FOUND的情况(test_remote_encode.py 中的xfail标记即为此记录),实际使用时请以端点当前状态为准; - packed latents 必传尺寸:解码 Flux 等打包格式 latent 时,漏传
height/width会直接触发ValueError: 'height' and 'width' required for packed latents'(见 check_inputs_decode); - 管线侧配合:使用混合推理时,管线需设置
vae=None且生成时传output_type="latent",本地不再执行 VAE 相关计算。
七、完整链路示例:图生图
综合编码与解码,一个完整的"远程编码 → 本地生成 → 远程解码"流程如下(对应 vae_encode.md 中的生成示例):
import torch from diffusers import StableDiffusionImg2ImgPipeline from diffusers.utils import load_image from diffusers.utils.remote_utils import remote_decode, remote_encode pipe = StableDiffusionImg2ImgPipeline.from_pretrained( "stable-diffusion-v1-5/stable-diffusion-v1-5", dtype=torch.float16, variant="fp16", vae=None, ).to("cuda") init_image = load_image("path/to/sketch-mountains-input.jpg") init_image = init_image.resize((768, 512)) # 1. 远程编码:图像 → latent init_latent = remote_encode( endpoint="https://qc6479g0aac6qwy9.us-east-1.aws.endpoints.huggingface.cloud/", image=init_image, scaling_factor=0.18215, ) # 2. 本地生成:latent → 新的 latent prompt = "A fantasy landscape, trending on artstation" latent = pipe( prompt=prompt, image=init_latent, strength=0.75, output_type="latent", ).images # 3. 远程解码:latent → 图像 image = remote_decode( endpoint="https://q1bj3bpq6kzilnsu.us-east-1.aws.endpoints.huggingface.cloud/", tensor=latent, scaling_factor=0.18215, ) image.save("fantasy_landscape.jpg")八、性能参考(编码显存对比)
混合推理的价值在显存对比中最为直观。根据 vae_encode.md 中记录的 SD v1 编码实测数据(VAE 本地运行),2048×2048 分辨率在 RTX 3070 上会占用约 96% 显存、RTX 3080 上约 86.7%,而采用分块(tiled)编码后内存可压至约 8.5%~10.7%——但分块会增加耗时并影响质量。混合推理把 VAE 完全移出本地,等效于将这部分显存占用归零,从而为文本编码器与 UNet/Transformer 释放空间,这正是"无需昂贵硬件即可运行大模型"的机制基础。
对于更进一步的批量解码吞吐优化,可参考 overview.md 中基于queue.Queue+ 后台线程的解码队列模式:在解码当前 latent 的同时排队下一个 prompt 的生成请求,实现生成与解码流水线化。
延伸阅读:混合推理的整体概念与模型支持见 overview.md,编码入门与内存基准见 vae_encode.md,完整实现源码见 remote_utils.py,端点常量见 constants.py。
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考