Add Qwen tail and chunk ablations
This commit is contained in:
+734
-12
@@ -1,12 +1,16 @@
|
||||
"""Omni-only causal turn-taking features at 5 Hz.
|
||||
|
||||
This module implements three inference paths for Qwen2.5-Omni-3B:
|
||||
This module implements five inference paths for Qwen2.5-Omni-3B:
|
||||
|
||||
1. ``causal-prefix``: recompute a causal AV window ending at every 200 ms.
|
||||
This is deliberately slow, but is the simplest correctness baseline.
|
||||
2. ``open-stream-replay``: cache completed AV blocks inside one
|
||||
still-open user/media turn; closure and readout tokens remain temporary.
|
||||
3. ``overlap-working-memory``: a frozen ablation that serializes cached
|
||||
3. ``completed-block-hold``: discard the incomplete native block and hold the
|
||||
most recent completed-block state on the 5 Hz output grid.
|
||||
4. ``micro-chunk-200ms``: commit each 200-ms interval independently as an
|
||||
explicitly non-native chunk-size stress test.
|
||||
5. ``overlap-working-memory``: a frozen ablation that serializes cached
|
||||
complete blocks and an overlapping ``[t-2,t]`` recent recap.
|
||||
|
||||
Neither path uses an RNN. A small head maps one Thinker state to the same
|
||||
@@ -37,7 +41,7 @@ import logging
|
||||
import math
|
||||
import subprocess
|
||||
from collections import OrderedDict
|
||||
from dataclasses import asdict, dataclass
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal
|
||||
|
||||
@@ -63,6 +67,8 @@ from transformers.cache_utils import DynamicCache
|
||||
Mode = Literal[
|
||||
"causal-prefix",
|
||||
"open-stream-replay",
|
||||
"completed-block-hold",
|
||||
"micro-chunk-200ms",
|
||||
"overlap-working-memory",
|
||||
]
|
||||
BackboneLoader = Literal["qwen", "roma"]
|
||||
@@ -389,6 +395,7 @@ class ExtractionResult:
|
||||
times: torch.Tensor # [N], seconds in the original session
|
||||
states: torch.Tensor # [N,D] or preserved query layers [N,L,D]
|
||||
verification: list[dict[str, Any]]
|
||||
diagnostics: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -621,6 +628,29 @@ def _time_grid(start: float, end: float, step: float) -> list[float]:
|
||||
return [start + i * step for i in range(1, count + 1)]
|
||||
|
||||
|
||||
def _periodic_boundary_mask(
|
||||
times: list[float],
|
||||
*,
|
||||
origin: float,
|
||||
period: float,
|
||||
) -> torch.BoolTensor:
|
||||
"""Mark right edges aligned to a positive multiple of ``period``."""
|
||||
|
||||
values = []
|
||||
for current_time in times:
|
||||
ratio = (current_time - origin) / period
|
||||
values.append(
|
||||
ratio > 0
|
||||
and math.isclose(
|
||||
ratio,
|
||||
round(ratio),
|
||||
rel_tol=0.0,
|
||||
abs_tol=1e-6,
|
||||
)
|
||||
)
|
||||
return torch.tensor(values, dtype=torch.bool)
|
||||
|
||||
|
||||
def _media_content(
|
||||
*,
|
||||
video_path: str | Path | None,
|
||||
@@ -1076,6 +1106,31 @@ def _tmrope_positions_per_chunk(
|
||||
return int(rounded)
|
||||
|
||||
|
||||
def _tmrope_positions_per_step(
|
||||
model: Qwen2_5OmniThinkerForConditionalGeneration,
|
||||
step_seconds: float,
|
||||
) -> int:
|
||||
"""Return the native TMRoPE advance for one micro-chunk step."""
|
||||
|
||||
positions_per_second = float(model.config.position_id_per_seconds)
|
||||
value = positions_per_second * step_seconds
|
||||
rounded = round(value)
|
||||
if not math.isclose(value, rounded, rel_tol=0.0, abs_tol=1e-6):
|
||||
raise ValueError(f"Non-integral TMRoPE positions per step: {value}")
|
||||
steps_per_chunk = _require_integer_ratio(
|
||||
float(model.config.seconds_per_chunk),
|
||||
step_seconds,
|
||||
"model native chunk / micro step",
|
||||
)
|
||||
positions_per_chunk = _tmrope_positions_per_chunk(model)
|
||||
if rounded * steps_per_chunk != positions_per_chunk:
|
||||
raise ValueError(
|
||||
"Micro-step TMRoPE advances do not tile one native chunk: "
|
||||
f"{rounded} * {steps_per_chunk} != {positions_per_chunk}"
|
||||
)
|
||||
return int(rounded)
|
||||
|
||||
|
||||
def _cache_length(cache: Any | None) -> int:
|
||||
if cache is None:
|
||||
return 0
|
||||
@@ -1906,10 +1961,634 @@ class OpenStreamReplayExtractor:
|
||||
global_step_offset=0,
|
||||
progress=progress,
|
||||
)
|
||||
steps_per_block = _require_integer_ratio(
|
||||
self.config.native_block_seconds,
|
||||
self.config.step_seconds,
|
||||
"native block / step",
|
||||
)
|
||||
return ExtractionResult(
|
||||
times=torch.tensor(all_times, dtype=torch.float64),
|
||||
states=torch.cat(all_states, dim=0),
|
||||
verification=all_checks,
|
||||
diagnostics={
|
||||
"production_processor_calls": len(all_times),
|
||||
"production_prepared_media_seconds": float(
|
||||
sum(
|
||||
((index % steps_per_block) + 1)
|
||||
* self.config.step_seconds
|
||||
for index in range(len(all_times))
|
||||
)
|
||||
),
|
||||
"state_source_times": torch.tensor(
|
||||
all_times, dtype=torch.float64
|
||||
),
|
||||
"initialization_mask": torch.zeros(
|
||||
len(all_times), dtype=torch.bool
|
||||
),
|
||||
"state_update_mask": torch.ones(
|
||||
len(all_times), dtype=torch.bool
|
||||
),
|
||||
"payload_commit_mask": _periodic_boundary_mask(
|
||||
all_times,
|
||||
origin=start,
|
||||
period=self.config.native_block_seconds,
|
||||
),
|
||||
"native_boundary_mask": _periodic_boundary_mask(
|
||||
all_times,
|
||||
origin=start,
|
||||
period=self.config.native_block_seconds,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class CompletedBlockHoldExtractor(OpenStreamReplayExtractor):
|
||||
"""Commit native 2-s blocks and causally hold the last emitted state.
|
||||
|
||||
This is the deliberately stale control for partial-tail processing. It
|
||||
never constructs an incomplete AV interval and never backfills a state
|
||||
into rows before that block was fully observed.
|
||||
"""
|
||||
|
||||
def _zero_state(self) -> torch.Tensor:
|
||||
hidden_size = _hidden_size(self.model)
|
||||
dtype = getattr(self.model, "dtype", torch.float32)
|
||||
if self.config.readout_layer_fusion == "stack":
|
||||
shape = (1, self.config.readout_last_n_layers, hidden_size)
|
||||
else:
|
||||
shape = (1, hidden_size)
|
||||
return torch.zeros(shape, dtype=dtype, device="cpu")
|
||||
|
||||
def extract(
|
||||
self,
|
||||
*,
|
||||
video_path: str | Path | None,
|
||||
audio_path: str | Path | None,
|
||||
start: float,
|
||||
end: float,
|
||||
use_audio_in_video: bool,
|
||||
verify_every: int = 0,
|
||||
verify_continuous_every: int = 0,
|
||||
show_progress: bool = True,
|
||||
) -> ExtractionResult:
|
||||
if verify_every < 0 or verify_continuous_every < 0:
|
||||
raise ValueError("verification intervals must be >= 0")
|
||||
times = _time_grid(start, end, self.config.step_seconds)
|
||||
committed_cache = None
|
||||
block_start = start
|
||||
history_start = start
|
||||
payload_base = None
|
||||
positions_per_chunk = _tmrope_positions_per_chunk(self.model)
|
||||
latest = self._zero_state()
|
||||
source_time = start
|
||||
states: list[torch.Tensor] = []
|
||||
source_times: list[float] = []
|
||||
updates: list[bool] = []
|
||||
initialization: list[bool] = []
|
||||
checks: list[dict[str, Any]] = []
|
||||
processor_calls = 0
|
||||
prepared_seconds = 0.0
|
||||
replay_block_count = max(
|
||||
0,
|
||||
_require_integer_ratio(
|
||||
self.config.memory_seconds,
|
||||
self.config.native_block_seconds,
|
||||
"memory / native block",
|
||||
)
|
||||
- 1,
|
||||
)
|
||||
needs_rolling_rebuild = (
|
||||
self.config.memory_seconds < end - start - 1e-6
|
||||
)
|
||||
completed_blocks = CompletedBlockPieceCache(
|
||||
origin=start,
|
||||
block_seconds=self.config.native_block_seconds,
|
||||
max_blocks=replay_block_count if needs_rolling_rebuild else 0,
|
||||
)
|
||||
|
||||
with tqdm(
|
||||
total=len(times),
|
||||
desc="completed-block-hold",
|
||||
unit="state",
|
||||
dynamic_ncols=True,
|
||||
mininterval=0.0,
|
||||
miniters=1,
|
||||
disable=not show_progress,
|
||||
) as progress:
|
||||
for global_step, current_time in enumerate(times, start=1):
|
||||
block_end = block_start + self.config.native_block_seconds
|
||||
update = math.isclose(
|
||||
current_time,
|
||||
block_end,
|
||||
rel_tol=0.0,
|
||||
abs_tol=1e-6,
|
||||
)
|
||||
if update:
|
||||
if (
|
||||
committed_cache is not None
|
||||
and block_start - history_start
|
||||
>= self.config.memory_seconds - 1e-6
|
||||
):
|
||||
history_start = block_start - (
|
||||
self.config.memory_seconds
|
||||
- self.config.native_block_seconds
|
||||
)
|
||||
history_intervals = _block_intervals(
|
||||
history_start,
|
||||
block_start,
|
||||
self.config.native_block_seconds,
|
||||
)
|
||||
if history_intervals:
|
||||
prepared_history = completed_blocks.get_many(
|
||||
history_intervals
|
||||
)
|
||||
(
|
||||
committed_cache,
|
||||
_,
|
||||
payload_base,
|
||||
_,
|
||||
_,
|
||||
) = self._cache_from_intervals(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
intervals=history_intervals,
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
prepared_blocks=prepared_history,
|
||||
)
|
||||
else:
|
||||
committed_cache = None
|
||||
payload_base = None
|
||||
|
||||
pieces = self._prepare_pieces(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
interval=(block_start, block_end),
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
processor_calls += 1
|
||||
prepared_seconds += block_end - block_start
|
||||
if payload_base is None:
|
||||
payload_base = int(
|
||||
pieces.payload_position_ids.min().item()
|
||||
)
|
||||
block_index = round(
|
||||
(block_start - history_start)
|
||||
/ self.config.native_block_seconds
|
||||
)
|
||||
payload_offset = (
|
||||
payload_base + block_index * positions_per_chunk
|
||||
)
|
||||
(
|
||||
committed_cache,
|
||||
_,
|
||||
closure_position_ids,
|
||||
) = _append_open_pieces(
|
||||
self.model,
|
||||
pieces,
|
||||
past_key_values=committed_cache,
|
||||
rope_offset=payload_offset,
|
||||
include_prefix=committed_cache is None,
|
||||
)
|
||||
latest = _read_open_state(
|
||||
self.model,
|
||||
committed_cache,
|
||||
closure_ids=pieces.closure_ids,
|
||||
closure_position_ids=closure_position_ids,
|
||||
query_ids=self.query_ids,
|
||||
last_n_layers=self.config.readout_last_n_layers,
|
||||
layer_fusion=self.config.readout_layer_fusion,
|
||||
).cpu()
|
||||
source_time = current_time
|
||||
completed_blocks.put(
|
||||
CachedOpenMediaBlock(
|
||||
start=block_start,
|
||||
end=block_end,
|
||||
pieces=pieces,
|
||||
)
|
||||
)
|
||||
|
||||
if verify_every > 0 and global_step % verify_every == 0:
|
||||
exact = self.state_from_scratch(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
intervals=_block_intervals(
|
||||
history_start,
|
||||
current_time,
|
||||
self.config.native_block_seconds,
|
||||
),
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
checks.append(
|
||||
_compare_states(
|
||||
current_time,
|
||||
latest,
|
||||
exact,
|
||||
kind="completed_hold_cache_replay",
|
||||
)
|
||||
)
|
||||
if (
|
||||
verify_continuous_every > 0
|
||||
and global_step % verify_continuous_every == 0
|
||||
):
|
||||
continuous = self.continuous_reference.state_for_intervals(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
intervals=[(history_start, current_time)],
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
checks.append(
|
||||
_compare_states(
|
||||
current_time,
|
||||
latest,
|
||||
continuous,
|
||||
kind="completed_hold_continuous_semantics",
|
||||
)
|
||||
)
|
||||
block_start = block_end
|
||||
|
||||
states.append(latest)
|
||||
source_times.append(source_time)
|
||||
updates.append(update)
|
||||
initialization.append(source_time <= start + 1e-6)
|
||||
progress.set_postfix_str(
|
||||
f"source={source_time:.1f}s", refresh=False
|
||||
)
|
||||
progress.update(1)
|
||||
|
||||
return ExtractionResult(
|
||||
times=torch.tensor(times, dtype=torch.float64),
|
||||
states=torch.cat(states, dim=0),
|
||||
verification=checks,
|
||||
diagnostics={
|
||||
"production_processor_calls": processor_calls,
|
||||
"production_prepared_media_seconds": float(prepared_seconds),
|
||||
"state_source_times": torch.tensor(
|
||||
source_times, dtype=torch.float64
|
||||
),
|
||||
"initialization_mask": torch.tensor(
|
||||
initialization, dtype=torch.bool
|
||||
),
|
||||
"state_update_mask": torch.tensor(updates, dtype=torch.bool),
|
||||
"payload_commit_mask": torch.tensor(updates, dtype=torch.bool),
|
||||
"native_boundary_mask": torch.tensor(updates, dtype=torch.bool),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class MicroChunkReplayExtractor(OpenStreamReplayExtractor):
|
||||
"""Commit each new 200-ms payload as a non-native streaming stress test."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model: Qwen2_5OmniThinkerForConditionalGeneration,
|
||||
processor: Qwen2_5OmniProcessor,
|
||||
config: StreamConfig,
|
||||
fixed_grid_av: FixedGridAV | None = None,
|
||||
) -> None:
|
||||
super().__init__(model, processor, config, fixed_grid_av=fixed_grid_av)
|
||||
self.positions_per_step = _tmrope_positions_per_step(
|
||||
model, config.step_seconds
|
||||
)
|
||||
|
||||
def _prepare_micro_pieces(
|
||||
self,
|
||||
*,
|
||||
video_path: str | Path | None,
|
||||
audio_path: str | Path | None,
|
||||
interval: tuple[float, float],
|
||||
use_audio_in_video: bool,
|
||||
) -> OpenMediaPieces:
|
||||
duration = interval[1] - interval[0]
|
||||
if not math.isclose(
|
||||
duration,
|
||||
self.config.step_seconds,
|
||||
rel_tol=0.0,
|
||||
abs_tol=1e-6,
|
||||
):
|
||||
raise ValueError(
|
||||
f"Micro interval is {duration:g}s, expected "
|
||||
f"{self.config.step_seconds:g}s"
|
||||
)
|
||||
pieces = self._prepare_pieces(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
interval=interval,
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
temporal_positions = pieces.payload_position_ids[0]
|
||||
temporal_span = int(
|
||||
temporal_positions.max().item()
|
||||
- temporal_positions.min().item()
|
||||
)
|
||||
if temporal_span >= self.positions_per_step:
|
||||
raise RuntimeError(
|
||||
"A 200-ms payload does not fit its native TMRoPE step: "
|
||||
f"interval={interval}, temporal_span={temporal_span}, "
|
||||
f"step_positions={self.positions_per_step}"
|
||||
)
|
||||
return pieces
|
||||
|
||||
def _micro_cache_from_intervals(
|
||||
self,
|
||||
*,
|
||||
video_path: str | Path | None,
|
||||
audio_path: str | Path | None,
|
||||
intervals: list[tuple[float, float]],
|
||||
use_audio_in_video: bool,
|
||||
prepared_blocks: list[CachedOpenMediaBlock] | None = None,
|
||||
) -> tuple[Any, int, torch.LongTensor, torch.LongTensor]:
|
||||
if not intervals:
|
||||
raise ValueError("At least one micro interval is required")
|
||||
if prepared_blocks is not None and len(prepared_blocks) != len(intervals):
|
||||
raise ValueError("prepared micro-block count does not match intervals")
|
||||
media_cache = None
|
||||
payload_base = None
|
||||
closure_ids = None
|
||||
closure_position_ids = None
|
||||
for index, interval in enumerate(intervals):
|
||||
if prepared_blocks is None:
|
||||
pieces = self._prepare_micro_pieces(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
interval=interval,
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
else:
|
||||
prepared = prepared_blocks[index]
|
||||
if not (
|
||||
math.isclose(
|
||||
prepared.start,
|
||||
interval[0],
|
||||
rel_tol=0.0,
|
||||
abs_tol=1e-6,
|
||||
)
|
||||
and math.isclose(
|
||||
prepared.end,
|
||||
interval[1],
|
||||
rel_tol=0.0,
|
||||
abs_tol=1e-6,
|
||||
)
|
||||
):
|
||||
raise ValueError("prepared micro-block interval mismatch")
|
||||
pieces = prepared.pieces
|
||||
if payload_base is None:
|
||||
payload_base = int(pieces.payload_position_ids.min().item())
|
||||
payload_offset = payload_base + index * self.positions_per_step
|
||||
(
|
||||
media_cache,
|
||||
_,
|
||||
closure_position_ids,
|
||||
) = _append_open_pieces(
|
||||
self.model,
|
||||
pieces,
|
||||
past_key_values=media_cache,
|
||||
rope_offset=payload_offset,
|
||||
include_prefix=index == 0,
|
||||
)
|
||||
closure_ids = pieces.closure_ids
|
||||
if media_cache is None or payload_base is None:
|
||||
raise RuntimeError("Micro-chunk replay produced no cache")
|
||||
if closure_ids is None or closure_position_ids is None:
|
||||
raise RuntimeError("Micro-chunk replay produced no closure")
|
||||
return media_cache, payload_base, closure_ids, closure_position_ids
|
||||
|
||||
def state_from_micro_scratch(
|
||||
self,
|
||||
*,
|
||||
video_path: str | Path | None,
|
||||
audio_path: str | Path | None,
|
||||
intervals: list[tuple[float, float]],
|
||||
use_audio_in_video: bool,
|
||||
) -> torch.Tensor:
|
||||
cache, _, closure_ids, closure_positions = (
|
||||
self._micro_cache_from_intervals(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
intervals=intervals,
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
)
|
||||
return _read_open_state(
|
||||
self.model,
|
||||
cache,
|
||||
closure_ids=closure_ids,
|
||||
closure_position_ids=closure_positions,
|
||||
query_ids=self.query_ids,
|
||||
last_n_layers=self.config.readout_last_n_layers,
|
||||
layer_fusion=self.config.readout_layer_fusion,
|
||||
)
|
||||
|
||||
def extract(
|
||||
self,
|
||||
*,
|
||||
video_path: str | Path | None,
|
||||
audio_path: str | Path | None,
|
||||
start: float,
|
||||
end: float,
|
||||
use_audio_in_video: bool,
|
||||
verify_every: int = 0,
|
||||
verify_continuous_every: int = 0,
|
||||
show_progress: bool = True,
|
||||
) -> ExtractionResult:
|
||||
if verify_every < 0 or verify_continuous_every < 0:
|
||||
raise ValueError("verification intervals must be >= 0")
|
||||
times = _time_grid(start, end, self.config.step_seconds)
|
||||
committed_cache = None
|
||||
block_start = start
|
||||
history_start = start
|
||||
payload_base = None
|
||||
states: list[torch.Tensor] = []
|
||||
checks: list[dict[str, Any]] = []
|
||||
prepared_seconds = 0.0
|
||||
needs_rolling_rebuild = (
|
||||
self.config.memory_seconds < end - start - 1e-6
|
||||
)
|
||||
replay_step_count = max(
|
||||
0,
|
||||
_require_integer_ratio(
|
||||
self.config.memory_seconds - self.config.native_block_seconds,
|
||||
self.config.step_seconds,
|
||||
"micro replay memory / step",
|
||||
),
|
||||
)
|
||||
prepared_cache = CompletedBlockPieceCache(
|
||||
origin=start,
|
||||
block_seconds=self.config.step_seconds,
|
||||
max_blocks=replay_step_count if needs_rolling_rebuild else 0,
|
||||
)
|
||||
|
||||
with tqdm(
|
||||
total=len(times),
|
||||
desc="micro-chunk-200ms",
|
||||
unit="state",
|
||||
dynamic_ncols=True,
|
||||
mininterval=0.0,
|
||||
miniters=1,
|
||||
disable=not show_progress,
|
||||
) as progress:
|
||||
for global_step, current_time in enumerate(times, start=1):
|
||||
while (
|
||||
current_time
|
||||
> block_start + self.config.native_block_seconds + 1e-6
|
||||
):
|
||||
block_start += self.config.native_block_seconds
|
||||
|
||||
if (
|
||||
committed_cache is not None
|
||||
and block_start - history_start
|
||||
>= self.config.memory_seconds - 1e-6
|
||||
):
|
||||
history_start = block_start - (
|
||||
self.config.memory_seconds
|
||||
- self.config.native_block_seconds
|
||||
)
|
||||
history_intervals = _block_intervals(
|
||||
history_start,
|
||||
block_start,
|
||||
self.config.step_seconds,
|
||||
)
|
||||
if history_intervals:
|
||||
prepared_history = prepared_cache.get_many(
|
||||
history_intervals
|
||||
)
|
||||
(
|
||||
committed_cache,
|
||||
payload_base,
|
||||
_,
|
||||
_,
|
||||
) = self._micro_cache_from_intervals(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
intervals=history_intervals,
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
prepared_blocks=prepared_history,
|
||||
)
|
||||
else:
|
||||
committed_cache = None
|
||||
payload_base = None
|
||||
|
||||
interval_start = current_time - self.config.step_seconds
|
||||
pieces = self._prepare_micro_pieces(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
interval=(interval_start, current_time),
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
prepared_seconds += current_time - interval_start
|
||||
if payload_base is None:
|
||||
payload_base = int(pieces.payload_position_ids.min().item())
|
||||
micro_index = round(
|
||||
(interval_start - history_start)
|
||||
/ self.config.step_seconds
|
||||
)
|
||||
payload_offset = (
|
||||
payload_base + micro_index * self.positions_per_step
|
||||
)
|
||||
(
|
||||
committed_cache,
|
||||
_,
|
||||
closure_positions,
|
||||
) = _append_open_pieces(
|
||||
self.model,
|
||||
pieces,
|
||||
past_key_values=committed_cache,
|
||||
rope_offset=payload_offset,
|
||||
include_prefix=committed_cache is None,
|
||||
)
|
||||
state = _read_open_state(
|
||||
self.model,
|
||||
committed_cache,
|
||||
closure_ids=pieces.closure_ids,
|
||||
closure_position_ids=closure_positions,
|
||||
query_ids=self.query_ids,
|
||||
last_n_layers=self.config.readout_last_n_layers,
|
||||
layer_fusion=self.config.readout_layer_fusion,
|
||||
)
|
||||
states.append(state.cpu())
|
||||
prepared_cache.put(
|
||||
CachedOpenMediaBlock(
|
||||
start=interval_start,
|
||||
end=current_time,
|
||||
pieces=pieces,
|
||||
)
|
||||
)
|
||||
|
||||
if verify_every > 0 and global_step % verify_every == 0:
|
||||
intervals = _block_intervals(
|
||||
history_start,
|
||||
current_time,
|
||||
self.config.step_seconds,
|
||||
)
|
||||
exact = self.state_from_micro_scratch(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
intervals=intervals,
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
checks.append(
|
||||
_compare_states(
|
||||
current_time,
|
||||
state,
|
||||
exact,
|
||||
kind="micro_chunk_cache_replay",
|
||||
)
|
||||
)
|
||||
if (
|
||||
verify_continuous_every > 0
|
||||
and global_step % verify_continuous_every == 0
|
||||
):
|
||||
continuous = self.continuous_reference.state_for_intervals(
|
||||
video_path=video_path,
|
||||
audio_path=audio_path,
|
||||
intervals=[(history_start, current_time)],
|
||||
use_audio_in_video=use_audio_in_video,
|
||||
)
|
||||
checks.append(
|
||||
_compare_states(
|
||||
current_time,
|
||||
state,
|
||||
continuous,
|
||||
kind="micro_chunk_vs_continuous_semantics",
|
||||
)
|
||||
)
|
||||
|
||||
if math.isclose(
|
||||
current_time,
|
||||
block_start + self.config.native_block_seconds,
|
||||
rel_tol=0.0,
|
||||
abs_tol=1e-6,
|
||||
):
|
||||
block_start = current_time
|
||||
progress.set_postfix_str(
|
||||
f"media={current_time:.1f}s", refresh=False
|
||||
)
|
||||
progress.update(1)
|
||||
|
||||
output_times = torch.tensor(times, dtype=torch.float64)
|
||||
return ExtractionResult(
|
||||
times=output_times,
|
||||
states=torch.cat(states, dim=0),
|
||||
verification=checks,
|
||||
diagnostics={
|
||||
"production_processor_calls": len(times),
|
||||
"production_prepared_media_seconds": float(prepared_seconds),
|
||||
"state_source_times": output_times.clone(),
|
||||
"initialization_mask": torch.zeros(
|
||||
len(times), dtype=torch.bool
|
||||
),
|
||||
"state_update_mask": torch.ones(
|
||||
len(times), dtype=torch.bool
|
||||
),
|
||||
"payload_commit_mask": torch.ones(
|
||||
len(times), dtype=torch.bool
|
||||
),
|
||||
"native_boundary_mask": _periodic_boundary_mask(
|
||||
times,
|
||||
origin=start,
|
||||
period=self.config.native_block_seconds,
|
||||
),
|
||||
"tmrope_positions_per_micro_step": self.positions_per_step,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -2240,8 +2919,8 @@ def _compare_states(
|
||||
*,
|
||||
kind: str,
|
||||
) -> dict[str, Any]:
|
||||
cached_f = cached.float()
|
||||
exact_f = exact.float()
|
||||
cached_f = cached.detach().float()
|
||||
exact_f = exact.detach().to(device=cached_f.device, dtype=torch.float32)
|
||||
cached_flat = cached_f.reshape(1, -1)
|
||||
exact_flat = exact_f.reshape(1, -1)
|
||||
return {
|
||||
@@ -2394,7 +3073,12 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument(
|
||||
"--mode",
|
||||
choices=("causal-prefix", "open-stream-replay"),
|
||||
choices=(
|
||||
"causal-prefix",
|
||||
"open-stream-replay",
|
||||
"completed-block-hold",
|
||||
"micro-chunk-200ms",
|
||||
),
|
||||
default="open-stream-replay",
|
||||
)
|
||||
parser.add_argument("--model-id", default=MODEL_ID)
|
||||
@@ -2554,7 +3238,12 @@ def main() -> None:
|
||||
show_progress=not args.no_progress,
|
||||
)
|
||||
else:
|
||||
extractor = OpenStreamReplayExtractor(
|
||||
extractor_class = {
|
||||
"open-stream-replay": OpenStreamReplayExtractor,
|
||||
"completed-block-hold": CompletedBlockHoldExtractor,
|
||||
"micro-chunk-200ms": MicroChunkReplayExtractor,
|
||||
}[args.mode]
|
||||
extractor = extractor_class(
|
||||
model, processor, config, fixed_grid_av=fixed_grid_av
|
||||
)
|
||||
result = extractor.extract(
|
||||
@@ -2573,11 +3262,16 @@ def main() -> None:
|
||||
"backbone_loader": args.backbone_loader,
|
||||
"backbone_metadata": backbone_metadata(model),
|
||||
"mode": args.mode,
|
||||
"stream_representation": (
|
||||
"single_causal_window"
|
||||
if args.mode == "causal-prefix"
|
||||
else "single_open_media_turn_with_2s_payload_commits"
|
||||
),
|
||||
"stream_representation": {
|
||||
"causal-prefix": "single_causal_window",
|
||||
"open-stream-replay": (
|
||||
"native_2s_completed_blocks_plus_temporary_partial_tail"
|
||||
),
|
||||
"completed-block-hold": (
|
||||
"native_2s_completed_blocks_forward_held_to_5hz"
|
||||
),
|
||||
"micro-chunk-200ms": "non_native_200ms_payload_commits",
|
||||
}[args.mode],
|
||||
"config": asdict(config),
|
||||
"start": args.start,
|
||||
"end": end,
|
||||
@@ -2614,9 +3308,37 @@ def main() -> None:
|
||||
if args.mode == "open-stream-replay"
|
||||
else None
|
||||
),
|
||||
"chunk_ablation_policy": (
|
||||
{
|
||||
"kind": "native_completed_block_hold",
|
||||
"native_chunk_seconds": config.native_block_seconds,
|
||||
"output_step_seconds": config.step_seconds,
|
||||
"tail_policy": "discard_incomplete_tail_and_hold_last_state",
|
||||
"initial_state": "zeros_until_first_complete_native_block",
|
||||
"fresh_at_every_5hz_step": False,
|
||||
"backbone": "frozen",
|
||||
}
|
||||
if args.mode == "completed-block-hold"
|
||||
else (
|
||||
{
|
||||
"kind": "non_native_micro_chunk_stress_test",
|
||||
"micro_chunk_seconds": config.step_seconds,
|
||||
"native_chunk_seconds": config.native_block_seconds,
|
||||
"tmrope_positions_per_micro_step": (
|
||||
_tmrope_positions_per_step(model, config.step_seconds)
|
||||
),
|
||||
"tail_policy": "commit_each_5hz_interval_as_a_payload",
|
||||
"fresh_at_every_5hz_step": True,
|
||||
"backbone": "frozen",
|
||||
}
|
||||
if args.mode == "micro-chunk-200ms"
|
||||
else None
|
||||
)
|
||||
),
|
||||
"times": result.times,
|
||||
"states": result.states,
|
||||
"verification": result.verification,
|
||||
"extraction_diagnostics": result.diagnostics,
|
||||
}
|
||||
if args.head_checkpoint is not None:
|
||||
head, head_loss_name = _load_head(
|
||||
|
||||
Reference in New Issue
Block a user