[Bugfix] Restore GLM-Image unshifted timesteps after set_timesteps (#7953)
Signed-off-by: sinksilk <785976238@qq.com>
This commit is contained in:
1 parent
03ccce9a99
commit
f1057b1e0d
2 files changed
+75
No files matched your search
@@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""CPU tests for GLM-Image timestep handoff through retrieve_timesteps.
|
||||
|
||||
GLM-Image integrates with resolution-shifted sigmas but conditions the DiT on
|
||||
unshifted timesteps. diffusers >= 0.40 FlowMatchEulerDiscreteScheduler.set_timesteps
|
||||
overwrites caller-supplied timesteps with `sigmas * num_train_timesteps`;
|
||||
retrieve_timesteps must restore the provided schedule.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
import torch
|
||||
from diffusers import FlowMatchEulerDiscreteScheduler
|
||||
|
||||
from vllm_omni.diffusion.models.glm_image.pipeline_glm_image import (
|
||||
calculate_shift,
|
||||
retrieve_timesteps,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu, pytest.mark.diffusion]
|
||||
|
||||
|
||||
def _glm_image_scheduler() -> FlowMatchEulerDiscreteScheduler:
|
||||
# Match scheduler/scheduler_config.json shipped with zai-org/GLM-Image.
|
||||
return FlowMatchEulerDiscreteScheduler(
|
||||
num_train_timesteps=1000,
|
||||
use_dynamic_shifting=True,
|
||||
time_shift_type="linear",
|
||||
base_shift=0.25,
|
||||
max_shift=0.75,
|
||||
)
|
||||
|
||||
|
||||
def test_retrieve_timesteps_preserves_caller_unshifted_timesteps():
|
||||
"""Caller timesteps must survive set_timesteps when both args are passed."""
|
||||
num_inference_steps = 50
|
||||
# 1024x1024 -> image_seq_len = ((1024/8)*(1024/8)) / (2**2) = 4096
|
||||
image_seq_len = 4096
|
||||
scheduler = _glm_image_scheduler()
|
||||
|
||||
timesteps_array = np.linspace(scheduler.config.num_train_timesteps, 1.0, num_inference_steps + 1)[:-1]
|
||||
timesteps_array = timesteps_array.astype(np.int64).astype(np.float32)
|
||||
sigmas = timesteps_array / scheduler.config.num_train_timesteps
|
||||
mu = calculate_shift(
|
||||
image_seq_len,
|
||||
scheduler.config.get("base_image_seq_len", 256),
|
||||
scheduler.config.get("base_shift", 0.25),
|
||||
scheduler.config.get("max_shift", 0.75),
|
||||
)
|
||||
assert mu == pytest.approx(3.25)
|
||||
|
||||
provided = timesteps_array.tolist()
|
||||
got, n_steps = retrieve_timesteps(
|
||||
scheduler,
|
||||
num_inference_steps,
|
||||
"cpu",
|
||||
provided,
|
||||
sigmas.tolist(),
|
||||
mu=mu,
|
||||
)
|
||||
|
||||
expected = torch.as_tensor(provided, dtype=torch.float32)
|
||||
assert n_steps == num_inference_steps
|
||||
assert got.shape == expected.shape
|
||||
torch.testing.assert_close(got.cpu(), expected, rtol=0, atol=0)
|
||||
torch.testing.assert_close(scheduler.timesteps.cpu(), expected, rtol=0, atol=0)
|
||||
|
||||
# Integration still uses the resolution-shifted sigma schedule.
|
||||
shifted = torch.as_tensor(sigmas, dtype=torch.float32)
|
||||
assert not torch.allclose(scheduler.sigmas[:-1].cpu(), shifted, rtol=0, atol=0)
|
||||
@@ -193,6 +193,10 @@ def retrieve_timesteps(
|
||||
f" timestep or sigma schedules. Please check whether you are using the correct scheduler."
|
||||
)
|
||||
scheduler.set_timesteps(timesteps=timesteps, sigmas=sigmas, device=device, **kwargs)
|
||||
# GLM-Image conditions the DiT on the *unshifted* timesteps while integrating with the
|
||||
# resolution-shifted sigmas. FlowMatchEulerDiscreteScheduler.set_timesteps (diffusers >= 0.40)
|
||||
# overwrites provided timesteps with `sigmas * num_train_timesteps`, so restore them here.
|
||||
scheduler.timesteps = torch.as_tensor(timesteps, dtype=torch.float32, device=scheduler.timesteps.device)
|
||||
timesteps = scheduler.timesteps
|
||||
num_inference_steps = len(timesteps)
|
||||
elif timesteps is not None:
|
||||
|
||||
Reference in new issue
Block a user