Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1142,15 +1142,17 @@ def get_vision_position_ids(
grid_thw[2].item() // spatial_merge_size,
)

image_seq_length = llm_grid_h * llm_grid_w * llm_grid_t

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix repo from qwen2-vl, here and after this

position_width = torch.arange(start_position, start_position + llm_grid_w, device=device).repeat(
llm_grid_h * llm_grid_t
)
position_height = torch.arange(start_position, start_position + llm_grid_h, device=device).repeat_interleave(
llm_grid_w * llm_grid_t
)
position_temporal = torch.full((image_seq_length,), start_position, device=device, dtype=torch.long)
position_temporal = position_temporal * time_interval
# Add `start_position` after arange for compile
position_temporal = torch.arange(llm_grid_t, device=device) * time_interval
position_width = torch.arange(llm_grid_w, device=device) + start_position
position_height = torch.arange(llm_grid_h, device=device) + start_position

# Repeat the positions per each grid and per video frame. Repeat patterns are important
# do not modify without checking values!
position_width = position_width.repeat(llm_grid_h * llm_grid_t)
position_height = position_height.repeat_interleave(llm_grid_w).repeat(llm_grid_t)
# Important: add `start_positions` after applying `time_interval`, order matters
position_temporal = position_temporal.repeat_interleave(llm_grid_h * llm_grid_w) + start_position
vision_position_ids = torch.stack([position_temporal, position_height, position_width], dim=0)

return vision_position_ids
Expand Down
62 changes: 20 additions & 42 deletions src/transformers/models/glm46v/modeling_glm46v.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,17 @@ def get_vision_position_ids(
grid_thw[2].item() // spatial_merge_size,
)

image_seq_length = llm_grid_h * llm_grid_w * llm_grid_t
position_width = torch.arange(start_position, start_position + llm_grid_w, device=device).repeat(
llm_grid_h * llm_grid_t
)
position_height = torch.arange(start_position, start_position + llm_grid_h, device=device).repeat_interleave(
llm_grid_w * llm_grid_t
)
position_temporal = torch.full((image_seq_length,), start_position, device=device, dtype=torch.long)
position_temporal = position_temporal * time_interval
# Add `start_position` after arange for compile
position_temporal = torch.arange(llm_grid_t, device=device) * time_interval
position_width = torch.arange(llm_grid_w, device=device) + start_position
position_height = torch.arange(llm_grid_h, device=device) + start_position

# Repeat the positions per each grid and per video frame. Repeat patterns are important
# do not modify without checking values!
position_width = position_width.repeat(llm_grid_h * llm_grid_t)
position_height = position_height.repeat_interleave(llm_grid_w).repeat(llm_grid_t)
# Important: add `start_positions` after applying `time_interval`, order matters
position_temporal = position_temporal.repeat_interleave(llm_grid_h * llm_grid_w) + start_position
vision_position_ids = torch.stack([position_temporal, position_height, position_width], dim=0)

return vision_position_ids
Expand All @@ -169,24 +171,8 @@ def get_rope_index(
**kwargs,
) -> tuple[torch.Tensor, torch.Tensor]:
"""
Calculate the 3D rope index based on image and video's sizes. The utility expects a `vision + text`
sequence and will error out otherwise. For pure text sequence, please rely on model's auto-inferred
position ids. In a mixed vision + text sequence, vision tokens use 3D RoPE (temporal, height, width)
while text tokens use standard 1D RoPE.

Example:
Temporal patches: 3; Height patches: 2; Width patches: 2
Each vision input results in (temporal x height × width) positions. Here: 3 x 2 × 2 = 12 positions total.

Temporal position IDs are spaced by:
`interval = tokens_per_second * temporal_patch_size / fps`

If fps = 1; tokens_per_second = 25; temporal_patch_size = 2, temporal IDs increase by 50 for each temporal patch:
`[0, 0, 0, 0, 50, 50, 50, 50, 100, 100, 100, 100]`

Height IDs repeat per row: `[0, 0, 1, 1, ...]`
Width IDs alternate per column: `[0, 1, 0, 1, ...]`
Text tokens follow standard 1D RoPE and the position IDs grow consequently with a step of `1`
Difference from Qwen2VL/Qwen2.5VL's get_rope_index:
- GLM46V uses timestamps to seperate each video frame, so the video_grid_thw should also be split too.

Args:
input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
Expand All @@ -208,6 +194,11 @@ def get_rope_index(
position_ids (`torch.LongTensor` of shape `(3, batch_size, sequence_length)`)
mrope_position_deltas (`torch.Tensor` of shape `(batch_size)`)
"""

# Separate video grid thw into multiple grids because timestamps are used to seperate videos.
if video_grid_thw is not None:
video_grid_thw = torch.repeat_interleave(video_grid_thw, video_grid_thw[:, 0], dim=0)
video_grid_thw[:, 0] = 1
spatial_merge_size = self.config.vision_config.spatial_merge_size

mrope_position_deltas = []
Expand Down Expand Up @@ -237,7 +228,6 @@ def get_rope_index(
input_type_group.append((key, start_index, end_index))

current_pos = 0
video_group_index = 0
llm_pos_ids_list = []
for modality_type, start_idx, end_idx in input_type_group:
# text == 0
Expand All @@ -249,21 +239,9 @@ def get_rope_index(
current_pos += text_len
# image == 1, video == 2
else:
# GLM46V splits video into segments per frame but there's only one `grid_thw`
# per whole video. We can't exhaus the iterator and have to re-use the grid
# while processing the same video!
if modality_type == 2:
if video_group_index == 0:
grid_thw = next(grid_iters[modality_type])
video_group_index += 1
video_group_index = 0 if video_group_index >= grid_thw[0] else video_group_index
else:
grid_thw = next(grid_iters[modality_type])

# Videos are processed per frame separately, each temporal grid is always `1`
temp_merge_size = grid_thw[0]
grid_thw = next(grid_iters[modality_type])
vision_position_ids = self.get_vision_position_ids(
current_pos, grid_thw, temp_merge_size, spatial_merge_size, device=input_ids.device
current_pos, grid_thw, 1, spatial_merge_size, device=input_ids.device
)
llm_pos_ids_list.append(vision_position_ids)
current_pos += max(grid_thw[1], grid_thw[2]) // spatial_merge_size
Expand Down
62 changes: 20 additions & 42 deletions src/transformers/models/glm4v/modeling_glm4v.py
Original file line number Diff line number Diff line change
Expand Up @@ -989,15 +989,17 @@ def get_vision_position_ids(
grid_thw[2].item() // spatial_merge_size,
)

image_seq_length = llm_grid_h * llm_grid_w * llm_grid_t
position_width = torch.arange(start_position, start_position + llm_grid_w, device=device).repeat(
llm_grid_h * llm_grid_t
)
position_height = torch.arange(start_position, start_position + llm_grid_h, device=device).repeat_interleave(
llm_grid_w * llm_grid_t
)
position_temporal = torch.full((image_seq_length,), start_position, device=device, dtype=torch.long)
position_temporal = position_temporal * time_interval
# Add `start_position` after arange for compile
position_temporal = torch.arange(llm_grid_t, device=device) * time_interval
position_width = torch.arange(llm_grid_w, device=device) + start_position
position_height = torch.arange(llm_grid_h, device=device) + start_position

# Repeat the positions per each grid and per video frame. Repeat patterns are important
# do not modify without checking values!
position_width = position_width.repeat(llm_grid_h * llm_grid_t)
position_height = position_height.repeat_interleave(llm_grid_w).repeat(llm_grid_t)
# Important: add `start_positions` after applying `time_interval`, order matters
position_temporal = position_temporal.repeat_interleave(llm_grid_h * llm_grid_w) + start_position
vision_position_ids = torch.stack([position_temporal, position_height, position_width], dim=0)

return vision_position_ids
Expand All @@ -1012,24 +1014,8 @@ def get_rope_index(
**kwargs,
) -> tuple[torch.Tensor, torch.Tensor]:
"""
Calculate the 3D rope index based on image and video's sizes. The utility expects a `vision + text`
sequence and will error out otherwise. For pure text sequence, please rely on model's auto-inferred
position ids. In a mixed vision + text sequence, vision tokens use 3D RoPE (temporal, height, width)
while text tokens use standard 1D RoPE.

Example:
Temporal patches: 3; Height patches: 2; Width patches: 2
Each vision input results in (temporal x height × width) positions. Here: 3 x 2 × 2 = 12 positions total.

Temporal position IDs are spaced by:
`interval = tokens_per_second * temporal_patch_size / fps`

If fps = 1; tokens_per_second = 25; temporal_patch_size = 2, temporal IDs increase by 50 for each temporal patch:
`[0, 0, 0, 0, 50, 50, 50, 50, 100, 100, 100, 100]`

Height IDs repeat per row: `[0, 0, 1, 1, ...]`
Width IDs alternate per column: `[0, 1, 0, 1, ...]`
Text tokens follow standard 1D RoPE and the position IDs grow consequently with a step of `1`
Difference from Qwen2VL/Qwen2.5VL's get_rope_index:
- GLM4V uses timestamps to seperate each video frame, so the video_grid_thw should also be split too.

Args:
input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
Expand All @@ -1051,6 +1037,11 @@ def get_rope_index(
position_ids (`torch.LongTensor` of shape `(3, batch_size, sequence_length)`)
mrope_position_deltas (`torch.Tensor` of shape `(batch_size)`)
"""

# Separate video grid thw into multiple grids because timestamps are used to seperate videos.
if video_grid_thw is not None:
video_grid_thw = torch.repeat_interleave(video_grid_thw, video_grid_thw[:, 0], dim=0)
video_grid_thw[:, 0] = 1
spatial_merge_size = self.config.vision_config.spatial_merge_size

mrope_position_deltas = []
Expand Down Expand Up @@ -1080,7 +1071,6 @@ def get_rope_index(
input_type_group.append((key, start_index, end_index))

current_pos = 0
video_group_index = 0
llm_pos_ids_list = []
for modality_type, start_idx, end_idx in input_type_group:
# text == 0
Expand All @@ -1092,21 +1082,9 @@ def get_rope_index(
current_pos += text_len
# image == 1, video == 2
else:
# GLM4V splits video into segments per frame but there's only one `grid_thw`
# per whole video. We can't exhaus the iterator and have to re-use the grid
# while processing the same video!
if modality_type == 2:
if video_group_index == 0:
grid_thw = next(grid_iters[modality_type])
video_group_index += 1
video_group_index = 0 if video_group_index >= grid_thw[0] else video_group_index
else:
grid_thw = next(grid_iters[modality_type])

# Videos are processed per frame separately, each temporal grid is always `1`
temp_merge_size = grid_thw[0]
grid_thw = next(grid_iters[modality_type])
vision_position_ids = self.get_vision_position_ids(
current_pos, grid_thw, temp_merge_size, spatial_merge_size, device=input_ids.device
current_pos, grid_thw, 1, spatial_merge_size, device=input_ids.device
)
llm_pos_ids_list.append(vision_position_ids)
current_pos += max(grid_thw[1], grid_thw[2]) // spatial_merge_size
Expand Down
99 changes: 9 additions & 90 deletions src/transformers/models/glm4v/modular_glm4v.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import itertools
from collections.abc import Callable

import numpy as np
Expand Down Expand Up @@ -880,32 +879,12 @@ def get_placeholder_mask(

def get_rope_index(
self,
input_ids: torch.LongTensor,
mm_token_type_ids: torch.IntTensor,
Comment on lines 880 to -884

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing, just a bit shorter and easier to follow. Copied from 'qwen3-vl'

image_grid_thw: torch.LongTensor | None = None,
video_grid_thw: torch.LongTensor | None = None,
attention_mask: torch.Tensor | None = None,
**kwargs,
**super_kwargs,
) -> tuple[torch.Tensor, torch.Tensor]:
"""
Calculate the 3D rope index based on image and video's sizes. The utility expects a `vision + text`
sequence and will error out otherwise. For pure text sequence, please rely on model's auto-inferred
position ids. In a mixed vision + text sequence, vision tokens use 3D RoPE (temporal, height, width)
while text tokens use standard 1D RoPE.

Example:
Temporal patches: 3; Height patches: 2; Width patches: 2
Each vision input results in (temporal x height × width) positions. Here: 3 x 2 × 2 = 12 positions total.

Temporal position IDs are spaced by:
`interval = tokens_per_second * temporal_patch_size / fps`

If fps = 1; tokens_per_second = 25; temporal_patch_size = 2, temporal IDs increase by 50 for each temporal patch:
`[0, 0, 0, 0, 50, 50, 50, 50, 100, 100, 100, 100]`

Height IDs repeat per row: `[0, 0, 1, 1, ...]`
Width IDs alternate per column: `[0, 1, 0, 1, ...]`
Text tokens follow standard 1D RoPE and the position IDs grow consequently with a step of `1`
Difference from Qwen2VL/Qwen2.5VL's get_rope_index:
- GLM4V uses timestamps to seperate each video frame, so the video_grid_thw should also be split too.

Args:
input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
Expand All @@ -927,73 +906,13 @@ def get_rope_index(
position_ids (`torch.LongTensor` of shape `(3, batch_size, sequence_length)`)
mrope_position_deltas (`torch.Tensor` of shape `(batch_size)`)
"""
spatial_merge_size = self.config.vision_config.spatial_merge_size

mrope_position_deltas = []
position_ids = torch.zeros(
3,
input_ids.shape[0],
input_ids.shape[1],
dtype=input_ids.dtype,
device=input_ids.device,
)
grid_iters = {
1: iter(image_grid_thw) if image_grid_thw is not None else None,
2: iter(video_grid_thw) if video_grid_thw is not None else None,
}

for batch_idx, current_input_ids in enumerate(input_ids):
input_token_type = mm_token_type_ids[batch_idx]
if attention_mask is not None:
current_input_ids = current_input_ids[attention_mask[batch_idx].bool()]
input_token_type = input_token_type[attention_mask[batch_idx].bool()]

input_type_group = []
for key, group in itertools.groupby(enumerate(input_token_type.tolist()), lambda x: x[1]):
group = list(group)
start_index = group[0][0]
end_index = group[-1][0] + 1
input_type_group.append((key, start_index, end_index))

current_pos = 0
video_group_index = 0
llm_pos_ids_list = []
for modality_type, start_idx, end_idx in input_type_group:
# text == 0
if modality_type == 0:
text_len = end_idx - start_idx
llm_pos_ids_list.append(
torch.arange(text_len, device=input_ids.device).view(1, -1).expand(3, -1) + current_pos
)
current_pos += text_len
# image == 1, video == 2
else:
# GLM4V splits video into segments per frame but there's only one `grid_thw`
# per whole video. We can't exhaus the iterator and have to re-use the grid
# while processing the same video!
if modality_type == 2:
if video_group_index == 0:
grid_thw = next(grid_iters[modality_type])
video_group_index += 1
video_group_index = 0 if video_group_index >= grid_thw[0] else video_group_index
else:
grid_thw = next(grid_iters[modality_type])

# Videos are processed per frame separately, each temporal grid is always `1`
temp_merge_size = grid_thw[0]
vision_position_ids = self.get_vision_position_ids(
current_pos, grid_thw, temp_merge_size, spatial_merge_size, device=input_ids.device
)
llm_pos_ids_list.append(vision_position_ids)
current_pos += max(grid_thw[1], grid_thw[2]) // spatial_merge_size
llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
if attention_mask is not None:
position_ids[:, batch_idx, attention_mask[batch_idx].bool()] = llm_positions.to(position_ids.device)
else:
position_ids[:, batch_idx] = llm_positions.to(position_ids.device)
mrope_position_deltas.append(llm_positions.max() + 1 - len(current_input_ids))
mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1)
return position_ids, mrope_position_deltas
# Separate video grid thw into multiple grids because timestamps are used to seperate videos.
if video_grid_thw is not None:
video_grid_thw = torch.repeat_interleave(video_grid_thw, video_grid_thw[:, 0], dim=0)
video_grid_thw[:, 0] = 1

return super().get_rope_index(video_grid_thw=video_grid_thw, **super_kwargs)

@auto_docstring
@can_return_tuple
Expand Down
Loading
Loading