返回 JoyAI-Echo
schedulers.py
根目录 / ltx-core / src / ltx_core / components / schedulers.py
1 import math
2 from functools import lru_cache
3
4 import numpy
5 import scipy
6 import torch
7
8 from ltx_core.components.protocols import SchedulerProtocol
9
10 BASE_SHIFT_ANCHOR = 1024
11 MAX_SHIFT_ANCHOR = 4096
12
13
14 class LTX2Scheduler(SchedulerProtocol):
15 """
16 Default scheduler for LTX-2 diffusion sampling.
17 Generates a sigma schedule with token-count-dependent shifting and optional
18 stretching to a terminal value.
19 """
20
21 def execute(
22 self,
23 steps: int,
24 latent: torch.Tensor | None = None,
25 max_shift: float = 2.05,
26 base_shift: float = 0.95,
27 stretch: bool = True,
28 terminal: float = 0.1,
29 default_number_of_tokens: int = MAX_SHIFT_ANCHOR,
30 **_kwargs,
31 ) -> torch.FloatTensor:
32 tokens = math.prod(latent.shape[2:]) if latent is not None else default_number_of_tokens
33 sigmas = torch.linspace(1.0, 0.0, steps + 1)
34
35 x1 = BASE_SHIFT_ANCHOR
36 x2 = MAX_SHIFT_ANCHOR
37 mm = (max_shift - base_shift) / (x2 - x1)
38 b = base_shift - mm * x1
39 sigma_shift = (tokens) * mm + b
40
41 power = 1
42 sigmas = torch.where(
43 sigmas != 0,
44 math.exp(sigma_shift) / (math.exp(sigma_shift) + (1 / sigmas - 1) ** power),
45 0,
46 )
47
48 # Stretch sigmas so that its final value matches the given terminal value.
49 if stretch:
50 non_zero_mask = sigmas != 0
51 non_zero_sigmas = sigmas[non_zero_mask]
52 one_minus_z = 1.0 - non_zero_sigmas
53 scale_factor = one_minus_z[-1] / (1.0 - terminal)
54 stretched = 1.0 - (one_minus_z / scale_factor)
55 sigmas[non_zero_mask] = stretched
56
57 return sigmas.to(torch.float32)
58
59
60 class LinearQuadraticScheduler(SchedulerProtocol):
61 """
62 Scheduler with linear steps followed by quadratic steps.
63 Produces a sigma schedule that transitions linearly up to a threshold,
64 then follows a quadratic curve for the remaining steps.
65 """
66
67 def execute(
68 self, steps: int, threshold_noise: float = 0.025, linear_steps: int | None = None, **_kwargs
69 ) -> torch.FloatTensor:
70 if steps == 1:
71 return torch.FloatTensor([1.0, 0.0])
72
73 if linear_steps is None:
74 linear_steps = steps // 2
75 linear_sigma_schedule = [i * threshold_noise / linear_steps for i in range(linear_steps)]
76 threshold_noise_step_diff = linear_steps - threshold_noise * steps
77 quadratic_steps = steps - linear_steps
78 quadratic_sigma_schedule = []
79 if quadratic_steps > 0:
80 quadratic_coef = threshold_noise_step_diff / (linear_steps * quadratic_steps**2)
81 linear_coef = threshold_noise / linear_steps - 2 * threshold_noise_step_diff / (quadratic_steps**2)
82 const = quadratic_coef * (linear_steps**2)
83 quadratic_sigma_schedule = [
84 quadratic_coef * (i**2) + linear_coef * i + const for i in range(linear_steps, steps)
85 ]
86 sigma_schedule = linear_sigma_schedule + quadratic_sigma_schedule + [1.0]
87 sigma_schedule = [1.0 - x for x in sigma_schedule]
88 return torch.FloatTensor(sigma_schedule)
89
90
91 class BetaScheduler(SchedulerProtocol):
92 """
93 Scheduler using a beta distribution to sample timesteps.
94 Based on: https://arxiv.org/abs/2407.12173
95 """
96
97 shift = 2.37
98 timesteps_length = 10000
99
100 def execute(self, steps: int, alpha: float = 0.6, beta: float = 0.6) -> torch.FloatTensor:
101 """
102 Execute the beta scheduler.
103 Args:
104 steps: The number of steps to execute the scheduler for.
105 alpha: The alpha parameter for the beta distribution.
106 beta: The beta parameter for the beta distribution.
107 Warnings:
108 The number of steps within `sigmas` theoretically might be less than `steps+1`,
109 because of the deduplication of the identical timesteps
110 Returns:
111 A tensor of sigmas.
112 """
113 model_sampling_sigmas = _precalculate_model_sampling_sigmas(self.shift, self.timesteps_length)
114 total_timesteps = len(model_sampling_sigmas) - 1
115 ts = 1 - numpy.linspace(0, 1, steps, endpoint=False)
116 ts = numpy.rint(scipy.stats.beta.ppf(ts, alpha, beta) * total_timesteps).tolist()
117 ts = list(dict.fromkeys(ts))
118
119 sigmas = [float(model_sampling_sigmas[int(t)]) for t in ts] + [0.0]
120 return torch.FloatTensor(sigmas)
121
122
123 @lru_cache(maxsize=5)
124 def _precalculate_model_sampling_sigmas(shift: float, timesteps_length: int) -> torch.Tensor:
125 timesteps = torch.arange(1, timesteps_length + 1, 1) / timesteps_length
126 return torch.Tensor([flux_time_shift(shift, 1.0, t) for t in timesteps])
127
128
129 def flux_time_shift(mu: float, sigma: float, t: float) -> float:
130 return math.exp(mu) / (math.exp(mu) + (1 / t - 1) ** sigma)
131
131 lines PYTHON