diff --git a/modules/lora/network_pdd.py b/modules/lora/network_pdd.py index 24818829d..b90e20cbf 100644 --- a/modules/lora/network_pdd.py +++ b/modules/lora/network_pdd.py @@ -285,7 +285,7 @@ def reconcile(pipe, loaded, components): def pin(p, model): - """Hold a generation on the distilled step count and shipped schedule while heads are installed; returns the pinned step count or None.""" + """Hold a generation on the distilled evaluation count and shipped schedule while heads are installed; returns the scheduler step argument or None.""" state = getattr(model, 'sdnext_pdd', None) if state is None: return None @@ -297,13 +297,13 @@ def pin(p, model): scheduler.set_shift(scheduler.config['shift']) shifts[name] = scheduler.config['shift'] requested = p.steps - p.steps = state.steps + p.steps = state.heads.nfe # what the user-facing step count means: transformer evaluations if getattr(p, 'task_args', None) is not None: - p.task_args['num_inference_steps'] = state.steps + p.task_args['num_inference_steps'] = state.steps # the scheduler argument that yields that many grid intervals if getattr(model, 'num_timesteps', None) is not None: model.num_timesteps = state.heads.nfe # the progress total counts transformer evaluations extra = getattr(p, 'extra_generation_params', None) if extra is not None: extra.update({state.spec.shift_keys[name]: shift for name, shift in shifts.items() if name in state.spec.shift_keys}) - log.info(f'Network: type=PDD name="{state.name}" steps={state.steps} requested={requested} nfe={state.heads.nfe} shift={shifts}') + log.info(f'Network: type=PDD name="{state.name}" steps={state.heads.nfe} requested={requested} grid_steps={state.steps} shift={shifts}') return state.steps diff --git a/modules/minimax/minimax_ui.py b/modules/minimax/minimax_ui.py index aee62c526..69890ceeb 100644 --- a/modules/minimax/minimax_ui.py +++ b/modules/minimax/minimax_ui.py @@ -26,7 +26,7 @@ def create_ui(prompt, _negative, styles, overrides, script_inputs, mp4_fps, mp4_ width, height = ui_sections.create_resolution_inputs('minimax', default_width=1024, default_height=576, step=32) btn_detect_image_size = ToolButton(value=ui_symbols.detect, elem_id="minimax_resize_detect_size") with gr.Row(): - steps = gr.Slider(minimum=2, maximum=100, step=1, label="MiniMax steps", elem_id='minimax_steps', value=30) + steps = gr.Slider(minimum=1, maximum=100, step=1, label="MiniMax steps", elem_id='minimax_steps', value=30) frames = gr.Slider(label='MiniMax frames', minimum=22, maximum=362, step=17, value=124, elem_id='minimax_frames') with gr.Row(): video_shift = gr.Slider(minimum=0.5, maximum=20.0, step=0.1, value=12.0, label="MiniMax video shift", elem_id='minimax_video_shift') diff --git a/modules/video_models/video_minimax.py b/modules/video_models/video_minimax.py index 1b5065764..6841ce0f1 100644 --- a/modules/video_models/video_minimax.py +++ b/modules/video_models/video_minimax.py @@ -29,9 +29,9 @@ def apply_overrides(p, pipe, still: bool = False, audio: bool = True, preview: b log.debug(f'Pipeline: cls={pipe.__class__.__name__} frames requested={getattr(p, "frames", None)} aligned={frames}') p.frames = frames p.task_args['num_frames'] = frames - p.steps = max(2, p.steps) - p.task_args['num_inference_steps'] = p.steps - pipe.num_timesteps = p.steps - 1 # sigma grid includes the terminal point; feeds the progress total + p.steps = max(1, p.steps) # transformer evaluations, as on every other model + p.task_args['num_inference_steps'] = p.steps + 1 # the scheduler counts the terminal sigma as a grid point + pipe.num_timesteps = p.steps # feeds the progress total if p.sampler_name not in ('None', 'Default'): log.warning(f'Pipeline: cls={pipe.__class__.__name__} sampler={p.sampler_name} unsupported: using model default') p.sampler_name = 'Default' # the model default is the bespoke scheduler pair, which discrete samplers must not replace diff --git a/test/test-minimax-shift.py b/test/test-minimax-shift.py index f81e023c3..3af160c00 100644 --- a/test/test-minimax-shift.py +++ b/test/test-minimax-shift.py @@ -6,7 +6,7 @@ Offline unit tests for the MiniMax schedule shift in modules.video_models.video_ - ``set_sampler_shift`` writes scheduler, default_scheduler and audio_scheduler and keys the result for infotext - a request without values resets what the previous request set - the Default sampler restore, a deepcopy of default_scheduler, carries the shift into the sigma grid -- ``apply_overrides`` records the applied values on the processing object +- ``apply_overrides`` records the applied values on the processing object and hands the scheduler one grid point more than the step count No running server required. @@ -188,6 +188,7 @@ def test_apply_overrides_records_the_applied_values(): assert p.extra_generation_params == {'Video shift': 6.0, 'Audio shift': AUDIO_SHIFT}, f'recorded={p.extra_generation_params}' assert shifts(pipe) == (6.0, 6.0, AUDIO_SHIFT), f'shifts={shifts(pipe)}' assert p.sampler_name == 'Default' + assert p.steps == STEPS and p.task_args['num_inference_steps'] == STEPS + 1 and pipe.num_timesteps == STEPS, f'steps={p.steps} grid_steps={p.task_args["num_inference_steps"]} total={pipe.num_timesteps}' def test_apply_overrides_without_values_uses_the_shipped_schedule(): diff --git a/test/test-pdd.py b/test/test-pdd.py index 2849ba384..d285a0dd8 100644 --- a/test/test-pdd.py +++ b/test/test-pdd.py @@ -335,7 +335,7 @@ def test_pin_overrides_steps_and_shift(): pipe.audio_scheduler.set_shift(2.0) p = types.SimpleNamespace(steps=30, task_args={'num_inference_steps': 30}, extra_generation_params={'Video shift': 4.0, 'Audio shift': 2.0}) assert network_pdd.pin(p, pipe) == 9 - assert p.steps == 9 and p.task_args['num_inference_steps'] == 9 + assert p.steps == 8 and p.task_args['num_inference_steps'] == 9, f'steps={p.steps} grid_steps={p.task_args["num_inference_steps"]}' assert pipe.num_timesteps == 8 assert pipe.scheduler.shift == 12.0 and pipe.audio_scheduler.shift == 3.0 assert p.extra_generation_params == {'Video shift': 12.0, 'Audio shift': 3.0}, f'recorded={p.extra_generation_params}' diff --git a/ui/locale/locale_en.json b/ui/locale/locale_en.json index b9b884e47..fb2be6fd9 100644 --- a/ui/locale/locale_en.json +++ b/ui/locale/locale_en.json @@ -949,6 +949,7 @@ {"id":"minimax_video_shift","label":"MiniMax video shift","localized":"","hint":"Exponential shift of the video sigma schedule, sigma' = s*sigma / (1 + (s-1)*sigma). Values above 1 move the step grid toward full noise, values below 1 toward the clean end. The value is absolute and does not scale with the step count.

Default is 12, the value the model ships with. Distilled LoRAs run at the shift they were trained with: 12 for the 544p lightx2v files, 6 for their 768p files. Parallel decoding (PDD) LoRAs pin the shipped value.

Recorded in the output metadata as Video shift.","ui":"video"}, {"id":"minimax_audio_shift","label":"MiniMax audio shift","localized":"","hint":"Exponential shift of the audio sigma schedule. The audio rows are denoised on this schedule inside the joint pass, so the value applies with audio output disabled too.

Default is 3, the value the model ships with; the published turbo LoRAs keep it. Parallel decoding (PDD) LoRAs pin the shipped value.

Recorded in the output metadata as Audio shift.","ui":"video"}, {"id":"","label":"MiniMax Frames","localized":"","hint":"MiniMax is optimized to generate 5-15sec videos at 24 FPS","ui":"video"}, + {"id":"minimax_steps","label":"MiniMax steps","localized":"","hint":"Number of transformer evaluations, counted the same way as on every other model. Distilled LoRAs run at the count in their name: a 4-step file at 4, an 8-step file at 8. Parallel decoding (PDD) LoRAs pin their own count.

Default is 30.","ui":"video"}, {"id":"","label":"Model Info","localized":"","hint":"","ui":"component-8779"}, {"id":"","label":"Model pipeline","localized":"","hint":"If autodetect does not detect model automatically, select model type before loading a model","ui":"settings_sd"}, {"id":"","label":"Model auto-load on start","localized":"","hint":"","ui":"settings_sd"},