mirror of
https://github.com/huggingface/diffusers.git
synced 2026-10-24 20:59:27 +03:00
* Stable Audio 3 First Version Commit, require model dict loading on GPUs * Fixing bugs for sa3 VAE * Make quality fix * Fix the unit test failure * Fix SA3 pipeline noise: learned text-padding embedding + no cross-attn mask The diffusers Stable Audio 3 pipeline produced noise instead of music because the cross-attention conditioning was built differently from the reference: - The reference text conditioner uses padding_mode="learned": padded text positions (~245 of 256 for a short prompt) are filled with a trained `padding_embedding`, and the DiT attends to *all* positions (its cross-attention mask is intentionally disabled). Our pipeline instead zeroed padded positions and masked them out, wiping ~95% of the conditioning signal. Changes: - transformer_stable_audio3: add learned `prompt_padding_embedding`; in forward, replace masked text positions with it (in cond_token_dim space, before to_cond_embed) then attend to the full context, matching the reference. - pipeline_stable_audio_3: stop zeroing padded positions (the DiT now handles them); default `silence_padding_duration` to 6.0 (reference headroom default). - convert_..._to_diffusers: convert `conditioner.conditioners.prompt.padding_embedding` into the DiT. - scheduling_ping_pong: pin schedule endpoints (sigmas[0]=1.0, sigmas[-1]=0.0) to match the reference LogSNRShift endpoint preservation. - run_..._inference: coerce float16->float32 on CPU. - tests: update transformer state-dict expectations (523 tensors, add prompt_padding_embedding) and ping-pong sigma endpoint assertions. Verified against the reference with identical noise + identical conditioning: full 8-step ping-pong trajectories agree to 4e-6 (final latent). Removed the dev-only parity scripts (verify_*_dit_parity, verify_*_vae_parity) from the tree. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Style fix * Autopipeline available * Adding document description for testing examples. * Solving fix for serge review bot suggestion * Design review comments fix * CI/CD Fix for ping pong scheduler md file * Fix doc build: remove autodoc for nonexistent StableAudio3EulerSchedulerOutput * Fix doc build: qualify non-exported autodoc references with module paths * Fix details in scheduler and some nits * suggestion fix * fix-copies: propagate prepare_latents dtype fix to audio2audio/inpaint pipelines --------- Co-authored-by: Buffett Liu <jengliu@linkedin.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: dg845 <58458699+dg845@users.noreply.github.com>
170 lines
6.1 KiB
Python
170 lines
6.1 KiB
Python
# Copyright 2025 HuggingFace Inc.
|
||
#
|
||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
# You may obtain a copy of the License at
|
||
#
|
||
# http://www.apache.org/licenses/LICENSE-2.0
|
||
#
|
||
# Unless required by applicable law or agreed to in writing, software
|
||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
|
||
"""
|
||
Unit tests for AutoencoderSAME.
|
||
|
||
The generic model test mixins (`ModelTesterMixin`, `TrainingTesterMixin`, `MemoryTesterMixin`) cover
|
||
save/load, determinism, dtype casting, and memory optimizations via a shared `forward()`. This file also
|
||
covers what those mixins don't reach: the `encode()` / `decode()` methods' own `return_dict` handling, and
|
||
`downsampling_ratio` arithmetic (including the production SAME-S preset).
|
||
"""
|
||
|
||
import unittest
|
||
|
||
import pytest
|
||
import torch
|
||
|
||
from diffusers import AutoencoderSAME
|
||
from diffusers.utils.torch_utils import randn_tensor
|
||
|
||
from ...testing_utils import require_torch, torch_device
|
||
from ..testing_utils import BaseModelTesterConfig, MemoryTesterMixin, ModelTesterMixin, TrainingTesterMixin
|
||
|
||
|
||
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
||
# Tiny config used for fast unit testing (not the real SAME-S / SAME-L sizes)
|
||
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
||
|
||
TINY_CFG = {
|
||
"audio_channels": 2,
|
||
"patch_size": 4,
|
||
"encoder_channels": 16,
|
||
"encoder_c_mults": [1, 2],
|
||
"encoder_strides": [2, 2],
|
||
"encoder_transformer_depths": [1, 1],
|
||
"latent_dim": 8,
|
||
"use_differential_attention": False, # saves memory in tests
|
||
"dim_heads": 8,
|
||
"ff_mult": 2,
|
||
"sampling_rate": 44100,
|
||
}
|
||
|
||
# Matches SAME-S architecture (single TRB, stride=16) with reduced depth for speed.
|
||
SAME_S_SMALL_CFG = {
|
||
"audio_channels": 2,
|
||
"patch_size": 256,
|
||
"encoder_channels": 128,
|
||
"encoder_c_mults": [6],
|
||
"encoder_strides": [16],
|
||
"encoder_transformer_depths": [2], # 6 in production; reduced for test speed
|
||
"latent_dim": 256,
|
||
"use_differential_attention": False,
|
||
"dim_heads": 64,
|
||
"ff_mult": 3,
|
||
"sampling_rate": 44100,
|
||
}
|
||
|
||
# 128 audio samples, TINY_CFG's downsampling_ratio = patch_size(4) * strides(2*2) = 16 β evenly divides 128,
|
||
# so decode(encode(x)) reproduces x's exact shape (no padding-derived length mismatch).
|
||
BATCH_SIZE = 2
|
||
T_AUDIO = 128
|
||
|
||
|
||
class AutoencoderSAMETesterConfig(BaseModelTesterConfig):
|
||
@property
|
||
def model_class(self):
|
||
return AutoencoderSAME
|
||
|
||
@property
|
||
def main_input_name(self) -> str:
|
||
return "sample"
|
||
|
||
@property
|
||
def output_shape(self) -> tuple:
|
||
return (TINY_CFG["audio_channels"], T_AUDIO)
|
||
|
||
@property
|
||
def generator(self):
|
||
return torch.Generator("cpu").manual_seed(0)
|
||
|
||
def get_init_dict(self) -> dict:
|
||
return dict(TINY_CFG)
|
||
|
||
def get_dummy_inputs(self) -> dict:
|
||
audio = randn_tensor(
|
||
(BATCH_SIZE, TINY_CFG["audio_channels"], T_AUDIO), generator=self.generator, device=torch_device
|
||
)
|
||
return {"sample": audio}
|
||
|
||
|
||
class TestAutoencoderSAME(AutoencoderSAMETesterConfig, ModelTesterMixin):
|
||
pass
|
||
|
||
|
||
class TestAutoencoderSAMETraining(AutoencoderSAMETesterConfig, TrainingTesterMixin):
|
||
"""Training tests for AutoencoderSAME."""
|
||
|
||
|
||
class TestAutoencoderSAMEMemory(AutoencoderSAMETesterConfig, MemoryTesterMixin):
|
||
"""Memory optimization tests for AutoencoderSAME."""
|
||
|
||
@pytest.mark.skip(
|
||
"Test not supported because of 'weight_norm_fwd_first_dim_kernel' not implemented for 'Float8_e4m3fn'"
|
||
)
|
||
def test_layerwise_casting_training(self):
|
||
super().test_layerwise_casting_training()
|
||
|
||
@pytest.mark.skip(
|
||
"The TRB `mapping` convs of AutoencoderSAME are wrapped with torch.nn.utils.weight_norm. This causes the "
|
||
"hook's pre_forward to not cast the module weights to compute_dtype."
|
||
)
|
||
def test_layerwise_casting_memory(self):
|
||
super().test_layerwise_casting_memory()
|
||
|
||
|
||
@require_torch
|
||
class TestAutoencoderSAMEBehavior(unittest.TestCase):
|
||
"""Coverage beyond the generic mixins: `encode()`/`decode()`'s own `return_dict` handling,
|
||
`downsampling_ratio` arithmetic, and the SAME-S production-shape preset."""
|
||
|
||
def setUp(self):
|
||
torch.manual_seed(0)
|
||
self.model = AutoencoderSAME(**TINY_CFG).eval()
|
||
self.audio = torch.randn(BATCH_SIZE, TINY_CFG["audio_channels"], T_AUDIO)
|
||
|
||
def test_downsampling_ratio(self):
|
||
expected = TINY_CFG["patch_size"]
|
||
for s in TINY_CFG["encoder_strides"]:
|
||
expected *= s
|
||
self.assertEqual(self.model.downsampling_ratio, expected)
|
||
|
||
def test_roundtrip_shape_consistency(self):
|
||
latents = self.model.encode(self.audio).latents
|
||
decoded = self.model.decode(latents).sample
|
||
re_encoded = self.model.encode(decoded).latents
|
||
self.assertEqual(latents.shape, re_encoded.shape)
|
||
|
||
def test_encode_no_dict(self):
|
||
out_tuple = self.model.encode(self.audio, return_dict=False)
|
||
self.assertIsInstance(out_tuple, tuple)
|
||
self.assertEqual(len(out_tuple), 1)
|
||
self.assertIsInstance(out_tuple[0], torch.Tensor)
|
||
|
||
def test_decode_no_dict(self):
|
||
latents = self.model.encode(self.audio).latents
|
||
out_tuple = self.model.decode(latents, return_dict=False)
|
||
self.assertIsInstance(out_tuple, tuple)
|
||
self.assertEqual(len(out_tuple), 1)
|
||
|
||
def test_same_s_preset_downsampling_ratio(self):
|
||
"""Model with SAME-S production defaults should give 4096x ratio."""
|
||
model = AutoencoderSAME(**SAME_S_SMALL_CFG)
|
||
# patch_size=256, strides=[16] β 256Γ16 = 4096
|
||
self.assertEqual(model.downsampling_ratio, 4096)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|