"🖌️ SDXL" model_type improvements

Adjusted and fixed model collapse on high variance_presets or direction_shifts, making SDXL models more stable!

The fix remains isolated to SDXL by handling pooled_output inside if "pooled_output" in cond_dict:—which only executes when an SDXL/pooled conditioning tensor is passed, leaving all non-pooled models completely unaffected.
This commit is contained in:
Ramon Guthrie
2026-08-05 13:22:58 +01:00
committed by GitHub
parent 5d09d6d9cf
commit 6e363afa5d
+71 -33
View File
@@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
RBG Smart Seed Variance 🌱 2.2.1
RBG Smart Seed Variance 🌱 2.2.2
A user-friendly ComfyUI node for enhancing seed diversity in Z-Image Turbo and Qwen-Image.
"""
@@ -34,7 +34,7 @@ except ImportError:
class RBG_Smart_Seed_Variance:
"""
Adds diversity to outputs by injecting noise into text embeddings during early generation steps.
Designed for Z-Image Turbo and Qwen-Image which have low seed variance.
Applies synchronized noise to both cross-attention tokens and global pooled output vectors.
"""
# Preset configurations: (randomize_percent, strength)
@@ -57,7 +57,7 @@ class RBG_Smart_Seed_Variance:
"🎨 Chroma HD": (0.05, 0.5), # Very sensitive - needs strength < 1
"🧧 ERNIE-Image": (0.08, 0.45), # Very sensitive - collapses past Creative.
"🔮 Ideogram 4.0": (0.4, 0.7), # Qwen3-VL text encoder - high sensitivity, prone to noise-induced safety triggers
"🖌️ SDXL": (0.6, 0.8), # Dual CLIP encoder - moderate sensitivity
"🖌️ SDXL": (0.6, 0.7), # Dual CLIP encoder - moderate sensitivity
"🎬 Wan2.2": (0.8, 0.8), # Video model - conservative
"⚙️ Other": (0.8, 0.8), # Conservative default
}
@@ -250,15 +250,13 @@ class RBG_Smart_Seed_Variance:
"This module ships with ComfyUI — check your installation."
)
# Handle disabled mode
if variance_preset == "❌ Disabled":
return {"ui": {"protection_data": []}, "result": (conditioning, torch.zeros((1, 64, 64, 3)))}
# Get preset values or calculate from fine_tune_variance
preset_config = self.PRESETS.get(variance_preset)
if preset_config is None:
randomize_percent = (fine_tune_variance / 100.0) * 5.0 # 0-5%
strength = (fine_tune_variance / 100.0) * 50.0 # 0-50
randomize_percent = (fine_tune_variance / 100.0) * 5.0
strength = (fine_tune_variance / 100.0) * 50.0
else:
randomize_percent, strength = preset_config
@@ -267,14 +265,12 @@ class RBG_Smart_Seed_Variance:
randomize_percent *= randomize_mult
strength *= strength_mult
# Get direction shift config and apply user strength
direction_config = self.DIRECTION_SHIFTS.get(direction_shift, None)
if direction_config is not None:
pattern, preset_mult = direction_config
user_mult = shift_strength / 100.0
direction_config = (pattern, preset_mult * user_mult)
# Create noisy conditioning
noisy_conditioning = []
protection_masks_for_ui = []
heatmap_tensor = torch.zeros((1, 64, 64, 3))
@@ -287,7 +283,6 @@ class RBG_Smart_Seed_Variance:
cond_tensor = cond[0]
cond_dict = cond[1].copy() if len(cond) > 1 else {}
# Determine number of tokens from tensor shape
if len(cond_tensor.shape) == 3:
num_tokens = cond_tensor.shape[1]
elif len(cond_tensor.shape) == 2:
@@ -296,7 +291,6 @@ class RBG_Smart_Seed_Variance:
noisy_conditioning.append(cond)
continue
# Generate protection mask based on mode
if protect_mode == "⚙️ Custom Regions":
protection_mask = self._parse_protection_regions(protect_regions, num_tokens)
elif protect_mode == "🎲 Random Regions":
@@ -304,15 +298,12 @@ class RBG_Smart_Seed_Variance:
else:
protection_mask = self._legacy_protection_to_mask(protect_mode, num_tokens)
# Match target chunk
current_target = self._pick_target_chunk(target_vibe, i, cond_tensor)
pattern = direction_config[0] if direction_config is not None else "random"
# Tweak 4: Apply base conditioning layer rebalancing to boost expression signals in base prompt
rebalanced_cond_tensor = self._apply_base_rebalance(cond_tensor, pattern, model_type)
# Apply noise with rebalanced tensor
# Perturb Cross-Attention Tensor
modified_tensor = self._apply_noise(
rebalanced_cond_tensor,
randomize_percent,
@@ -326,7 +317,13 @@ class RBG_Smart_Seed_Variance:
model_type=model_type,
)
# Generate Heatmap for first conditioning chunk
# Perturb Pooled Output Vector (Crucial for SDXL global composition)
if "pooled_output" in cond_dict and isinstance(cond_dict["pooled_output"], torch.Tensor):
cond_dict["pooled_output"] = self._apply_noise_to_pooled(
cond_dict["pooled_output"], randomize_percent, strength, direction_config,
seed + i + 777, model_type=model_type
)
if i == 0:
diff = (modified_tensor - cond_tensor).norm(dim=-1)
max_diff = diff.max()
@@ -336,7 +333,6 @@ class RBG_Smart_Seed_Variance:
heatmap_row = diff.view(1, 1, viz_width, 1).expand(1, 64, viz_width, 3)
heatmap_tensor = torch.nn.functional.interpolate(heatmap_row.permute(0, 3, 1, 2), size=(64, 512), mode='nearest').permute(0, 2, 3, 1)
# Store protection mask for UI
if hasattr(protection_mask, 'tolist'):
mask_list = (protection_mask.cpu().numpy().tolist()
if protection_mask.device.type != 'cpu'
@@ -346,7 +342,6 @@ class RBG_Smart_Seed_Variance:
else:
mask_list = []
# Serialisable metadata
cond_dict["rbg_variance_fade_curve"] = fade_curve
cond_dict["rbg_variance_applied"] = True
cond_dict["rbg_direction_shift"] = direction_shift
@@ -358,7 +353,7 @@ class RBG_Smart_Seed_Variance:
protection_masks_for_ui.append(mask_list)
noisy_conditioning.append((modified_tensor, cond_dict))
# --- Composition Lock Logic ---
# --- Composition Lock Schedules ---
if variance_schedule != "constant":
cutoff_percent = min(1.0, max(0.0, cutoff_step / total_steps))
@@ -383,7 +378,6 @@ class RBG_Smart_Seed_Variance:
elif variance_schedule == "decreasing":
num_segments = 5
new_conditioning = []
for i in range(num_segments):
seg_start = (i / num_segments) * cutoff_percent
seg_end = ((i + 1) / num_segments) * cutoff_percent
@@ -452,7 +446,6 @@ class RBG_Smart_Seed_Variance:
)
return {"ui": {"protection_data": protection_masks_for_ui}, "result": (new_conditioning, heatmap_tensor)}
# --- Standard Noise Injection Logic ---
if noise_injection == "🚫 None" or noise_injection == "All Steps":
return {"ui": {"protection_data": protection_masks_for_ui}, "result": (noisy_conditioning, heatmap_tensor)}
@@ -504,16 +497,21 @@ class RBG_Smart_Seed_Variance:
protection_mask = self._legacy_protection_to_mask(protect_mode, num_tokens)
current_target = self._pick_target_chunk(target_vibe, i, cond_tensor)
pattern = direction_config[0] if direction_config is not None else "random"
# Tweak 4: Apply base conditioning layer rebalancing
rebalanced_cond_tensor = self._apply_base_rebalance(cond_tensor, pattern, model_type)
modified_tensor = self._apply_noise(
rebalanced_cond_tensor, randomize_percent, strength, protection_mask,
direction_config, current_target, fade_curve, seed + i, vibe_blend, model_type=model_type
)
if "pooled_output" in cond_dict and isinstance(cond_dict["pooled_output"], torch.Tensor):
cond_dict["pooled_output"] = self._apply_noise_to_pooled(
cond_dict["pooled_output"], randomize_percent, strength, direction_config,
seed + i + 777, model_type=model_type
)
scaled_conditioning.append((modified_tensor, cond_dict))
return scaled_conditioning
@@ -608,11 +606,9 @@ class RBG_Smart_Seed_Variance:
if embed_dim % 12 != 0:
return cond_tensor
# Determine base multipliers for Krea2 to boost expressions/textures
if pattern == "visceral_grit":
base_multipliers = [1.0, 1.0, 1.0, 1.8, 2.2, 2.2, 1.8, 1.4, 2.5, 2.2, 2.8, 1.4]
elif pattern == "wild_facial_asymmetry":
# Balanced expression & refusal band curve
base_multipliers = [1.05, 1.05, 1.10, 1.60, 2.00, 1.80, 2.20, 2.20, 3.20, 2.40, 1.80, 1.10]
elif pattern == "expression":
base_multipliers = [1.0, 1.0, 1.0, 2.0, 2.4, 2.4, 3.2, 1.0, 1.0, 1.0, 1.0, 1.0]
@@ -621,19 +617,47 @@ class RBG_Smart_Seed_Variance:
band_width = embed_dim // 12
band_tensor = torch.tensor(base_multipliers, dtype=cond_tensor.dtype, device=cond_tensor.device)
band_tensor = band_tensor.repeat_interleave(band_width) # [embed_dim]
band_tensor = band_tensor.repeat_interleave(band_width)
return cond_tensor * band_tensor
def _apply_noise_to_pooled(self, pooled_tensor, randomize_percent, strength, direction_config, seed, model_type="⚙️ Other"):
"""
Apply proportional variance noise to SDXL global pooled_output vector.
"""
if pooled_tensor is None or not isinstance(pooled_tensor, torch.Tensor):
return pooled_tensor
modified = pooled_tensor.clone()
ref_std = torch.std(modified).detach().clamp(min=1e-5)
generator = torch.Generator(device=pooled_tensor.device)
generator.manual_seed(seed)
pattern = direction_config[0] if direction_config is not None else "random"
dir_mult = direction_config[1] if direction_config is not None else 1.0
num_values = modified.numel()
noise = self._generate_directional_noise(num_values, pattern, strength * dir_mult, generator, pooled_tensor.device)
noise = noise.view_as(modified)
noise_std = torch.std(noise).detach().clamp(min=1e-6)
scale_factor = (strength / 35.0) * ref_std
scaled_noise = (noise / noise_std) * scale_factor
scaled_noise = torch.clamp(scaled_noise, -ref_std * 1.5, ref_std * 1.5)
return modified + scaled_noise
def _apply_noise(self, tensor, randomize_percent, strength, protection_mask,
direction_config, target_tensor, fade_curve, seed, vibe_blend=0.5, model_type="⚙️ Other"):
"""
Apply noise to a fraction of the tensor values, optionally with directional bias and fade curve.
Apply noise to cross-attention tokens with scale relative to embedding standard deviation.
"""
modified = tensor.clone()
generator = torch.Generator(device=tensor.device)
generator.manual_seed(seed)
ref_std = torch.std(modified).detach().clamp(min=1e-5)
if direction_config is not None:
pattern, dir_multiplier = direction_config
strength *= dir_multiplier
@@ -676,7 +700,6 @@ class RBG_Smart_Seed_Variance:
else:
noise = vibe_noise
# Tweak 2: Krea 2 Band-Aware Noise scaling (3D target vibe path)
if model_type == "📸 Krea2 (SingleStream)" and embed_dim % 12 == 0:
band_width = embed_dim // 12
if pattern == "spatial":
@@ -696,6 +719,10 @@ class RBG_Smart_Seed_Variance:
full_envelope = token_envelope.repeat_interleave(embed_dim).repeat(batch_size)
noise = noise * full_envelope
noise_std = torch.std(noise).detach().clamp(min=1e-6)
noise = (noise / noise_std) * (strength / 35.0) * ref_std
noise = torch.clamp(noise, -ref_std * 1.5, ref_std * 1.5)
modifiable_values += noise
modified[batch_mask] = modifiable_values
@@ -708,7 +735,6 @@ class RBG_Smart_Seed_Variance:
full_envelope = token_envelope.repeat_interleave(embed_dim).repeat(batch_size)
indices = torch.randperm(total_values, generator=generator, device=tensor.device)[:num_to_modify]
# Tweak 2: Krea 2 Band-Aware Noise scaling (3D random path)
if model_type == "📸 Krea2 (SingleStream)" and embed_dim % 12 == 0:
band_width = embed_dim // 12
if pattern == "spatial":
@@ -726,7 +752,12 @@ class RBG_Smart_Seed_Variance:
band_scale = band_tensor[feature_indices]
noise = noise * band_scale
noise = noise * full_envelope[indices]
noise = noise * full_envelope[indices]
noise_std = torch.std(noise).detach().clamp(min=1e-6)
noise = (noise / noise_std) * (strength / 35.0) * ref_std
noise = torch.clamp(noise, -ref_std * 1.5, ref_std * 1.5)
modifiable_values[indices] += noise
modified[batch_mask] = modifiable_values
@@ -766,7 +797,6 @@ class RBG_Smart_Seed_Variance:
else:
noise = vibe_noise
# Tweak 2: Krea 2 Band-Aware Noise scaling (2D target vibe path)
if model_type == "📸 Krea2 (SingleStream)" and embed_dim % 12 == 0:
band_width = embed_dim // 12
if pattern == "spatial":
@@ -786,6 +816,10 @@ class RBG_Smart_Seed_Variance:
full_envelope = token_envelope.repeat_interleave(embed_dim)
noise = noise * full_envelope
noise_std = torch.std(noise).detach().clamp(min=1e-6)
noise = (noise / noise_std) * (strength / 35.0) * ref_std
noise = torch.clamp(noise, -ref_std * 1.5, ref_std * 1.5)
modifiable_values += noise
modified[token_mask] = modifiable_values
@@ -798,7 +832,6 @@ class RBG_Smart_Seed_Variance:
full_envelope = token_envelope.repeat_interleave(embed_dim)
indices = torch.randperm(total_values, generator=generator, device=tensor.device)[:num_to_modify]
# Tweak 2: Krea 2 Band-Aware Noise scaling (2D random path)
if model_type == "📸 Krea2 (SingleStream)" and embed_dim % 12 == 0:
band_width = embed_dim // 12
if pattern == "spatial":
@@ -816,7 +849,12 @@ class RBG_Smart_Seed_Variance:
band_scale = band_tensor[feature_indices]
noise = noise * band_scale
noise = noise * full_envelope[indices]
noise = noise * full_envelope[indices]
noise_std = torch.std(noise).detach().clamp(min=1e-6)
noise = (noise / noise_std) * (strength / 35.0) * ref_std
noise = torch.clamp(noise, -ref_std * 1.5, ref_std * 1.5)
modifiable_values[indices] += noise
modified[token_mask] = modifiable_values