[VLM] Optimize GLM4.5-V-style video processing to only decode necessary frames (#24161)
Signed-off-by: Isotr0py <mozf@mail2.sysu.edu.cn>
This commit is contained in:
@@ -1023,6 +1023,43 @@ class Glm4vProcessingInfo(BaseProcessingInfo):
|
||||
selected_timestamps.append(timestamps_list[idx])
|
||||
return selected_timestamps
|
||||
|
||||
def _construct_video_placeholder(
|
||||
self,
|
||||
video_array: np.ndarray,
|
||||
metadata: dict[str, Any],
|
||||
grid_thw: torch.Tensor,
|
||||
) -> str:
|
||||
hf_processor = self.get_hf_processor()
|
||||
tokenizer = self.get_tokenizer()
|
||||
image_processor = hf_processor.image_processor
|
||||
|
||||
hf_config = self.get_hf_config()
|
||||
boi_token_id = hf_config.image_start_token_id
|
||||
eoi_token_id = hf_config.image_end_token_id
|
||||
bov_token_id = hf_config.video_start_token_id
|
||||
eov_token_id = hf_config.video_end_token_id
|
||||
merge_length = image_processor.merge_size**2
|
||||
|
||||
assert isinstance(grid_thw, torch.Tensor)
|
||||
timestamps = self._get_video_second_idx(metadata, len(video_array))
|
||||
frames_idx_token = [
|
||||
tokenizer.encode(str(i), add_special_tokens=False)
|
||||
for i in timestamps
|
||||
]
|
||||
T, H, W = grid_thw
|
||||
num_tokens_per_frame = int(H * W) // merge_length
|
||||
placeholder = []
|
||||
placeholder.append(bov_token_id)
|
||||
for frame_idx in frames_idx_token:
|
||||
placeholder.append(boi_token_id)
|
||||
placeholder.extend([hf_processor.video_token_id] *
|
||||
num_tokens_per_frame)
|
||||
placeholder.append(eoi_token_id)
|
||||
placeholder.extend(frame_idx)
|
||||
placeholder.append(eov_token_id)
|
||||
|
||||
return placeholder
|
||||
|
||||
|
||||
class Glm4vDummyInputsBuilder(BaseDummyInputsBuilder[Glm4vProcessingInfo]):
|
||||
|
||||
@@ -1118,17 +1155,10 @@ class Glm4vMultiModalProcessor(BaseMultiModalProcessor[Glm4vProcessingInfo]):
|
||||
for item in mm_data.pop("videos", []):
|
||||
video_array, metadata = item
|
||||
|
||||
# FIXME(Isotr0py): Activate the below logic after we can disable
|
||||
# resampling from video loader backend.
|
||||
# assert metadata["total_num_frames"] == len(video_array), (
|
||||
# f"Total frames {metadata['total_num_frames']} does not "
|
||||
# f"match the length of video array {len(video_array)}.")
|
||||
if metadata["video_backend"] == "opencv_dynamic":
|
||||
mm_kwargs["do_sample_frames"] = False
|
||||
|
||||
# NOTE: Temporary workaround for resampled videos.
|
||||
# this can cause a divergence with HF implementation if
|
||||
# the input video is resampled in advance.
|
||||
|
||||
if metadata["total_num_frames"] != len(video_array):
|
||||
elif metadata["total_num_frames"] != len(video_array):
|
||||
logger.warning(
|
||||
"Total frames in metadata "
|
||||
"(%s) does not match the length of "
|
||||
@@ -1140,11 +1170,10 @@ class Glm4vMultiModalProcessor(BaseMultiModalProcessor[Glm4vProcessingInfo]):
|
||||
len(video_array),
|
||||
)
|
||||
metadata["total_num_frames"] = len(video_array)
|
||||
metadata = VideoMetadata(**metadata)
|
||||
|
||||
video_mm_data = dict()
|
||||
video_mm_data["videos"] = [[video_array]]
|
||||
video_mm_data["video_metadata"] = [[metadata]]
|
||||
video_mm_data["video_metadata"] = [[VideoMetadata(**metadata)]]
|
||||
|
||||
video_outputs = super()._call_hf_processor(
|
||||
prompt="<|begin_of_video|><|video|><|end_of_video|>",
|
||||
@@ -1152,11 +1181,23 @@ class Glm4vMultiModalProcessor(BaseMultiModalProcessor[Glm4vProcessingInfo]):
|
||||
mm_kwargs=mm_kwargs,
|
||||
tok_kwargs=tok_kwargs,
|
||||
)
|
||||
input_ids = video_outputs.pop("input_ids")
|
||||
input_ids[input_ids == processor.image_token_id] = (
|
||||
processor.video_token_id)
|
||||
video_placeholder = processor.tokenizer.batch_decode(
|
||||
input_ids)[0]
|
||||
if "do_sample_frames" in mm_kwargs and not mm_kwargs[
|
||||
"do_sample_frames"]:
|
||||
# Transformers v4.55 has incorrect timestamps issue for
|
||||
# skip sampling. We construct the placeholder manually to
|
||||
# get placeholders with correct timestamps.
|
||||
placeholder = self.info._construct_video_placeholder(
|
||||
video_array,
|
||||
metadata,
|
||||
video_outputs["video_grid_thw"].squeeze(0),
|
||||
)
|
||||
video_placeholder = processor.tokenizer.decode(placeholder)
|
||||
else:
|
||||
input_ids = video_outputs.pop("input_ids")
|
||||
input_ids[input_ids == processor.image_token_id] = (
|
||||
processor.video_token_id)
|
||||
video_placeholder = processor.tokenizer.batch_decode(
|
||||
input_ids)[0]
|
||||
prompt = prompt.replace(
|
||||
"<|begin_of_video|><|video|><|end_of_video|>",
|
||||
video_placeholder,
|
||||
@@ -1202,14 +1243,6 @@ class Glm4vMultiModalProcessor(BaseMultiModalProcessor[Glm4vProcessingInfo]):
|
||||
hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
|
||||
image_processor = self.info.get_image_processor(
|
||||
**hf_processor_mm_kwargs)
|
||||
tokenizer = self.info.get_tokenizer()
|
||||
hf_config = self.info.get_hf_config()
|
||||
|
||||
boi_token_id = hf_config.image_start_token_id
|
||||
eoi_token_id = hf_config.image_end_token_id
|
||||
|
||||
bov_token_id = hf_config.video_start_token_id
|
||||
eov_token_id = hf_config.video_end_token_id
|
||||
|
||||
merge_length = image_processor.merge_size**2
|
||||
|
||||
@@ -1227,21 +1260,8 @@ class Glm4vMultiModalProcessor(BaseMultiModalProcessor[Glm4vProcessingInfo]):
|
||||
assert isinstance(grid_thw, torch.Tensor)
|
||||
|
||||
video, metadata = mm_items["video"][item_idx]
|
||||
timestamps = self.info._get_video_second_idx(metadata, len(video))
|
||||
frames_idx_token = [
|
||||
tokenizer.encode(str(i), add_special_tokens=False)
|
||||
for i in timestamps
|
||||
]
|
||||
num_tokens_per_frame = int(grid_thw[1:].prod()) // merge_length
|
||||
placeholder = []
|
||||
placeholder.append(bov_token_id)
|
||||
for frame_idx in frames_idx_token:
|
||||
placeholder.append(boi_token_id)
|
||||
placeholder.extend([hf_processor.video_token_id] *
|
||||
num_tokens_per_frame)
|
||||
placeholder.append(eoi_token_id)
|
||||
placeholder.extend(frame_idx)
|
||||
placeholder.append(eov_token_id)
|
||||
placeholder = self.info._construct_video_placeholder(
|
||||
video, metadata, grid_thw)
|
||||
return PromptUpdateDetails.select_token_id(
|
||||
placeholder,
|
||||
embed_token_id=hf_processor.video_token_id,
|
||||
|
||||
Reference in New Issue
Block a user