Add Keye vl 8b 1.5#41040
Conversation
|
@zucchini-nlp Please help take a look at this PR, thanks. |
|
Nice, just wondering if it is same or similar to #39292 in terms of architecture? If it is, then imo it makes more sense to push the prev PR and allow |
|
@zucchini-nlp |
|
To resolve the torch issue, you need to import The other CI issues seem related to np version used, probably you need to find the equivalent of |
|
I will review the PR shortly |
|
@zucchini-nlp |
zucchini-nlp
left a comment
There was a problem hiding this comment.
Thanks for the PR, I think we need to clean up a bit since most part of the model are identical to Qwen-VL series. Also, regarding the video processing, let's create a separate video processing class similar to Qwen2-VL
I reviewed most of it, will come back soon to finish reviewing
| @@ -0,0 +1,252 @@ | |||
| <!--Copyright 2025 The Kuai Keye Team and The HuggingFace Inc. team. All rights reserved. | |||
There was a problem hiding this comment.
just to make sure, is Kuai correct or should it be Kwai?
| # Keye-VL-Preview | ||
| [Keye-VL-Preview](https://huggingface.co/papers/2507.01949) is an 8-billion-parameter multimodal foundation model, excels in short-video understanding while maintaining robust general-purpose vision-language abilities through a comprehensive pre- and post-training process, including reinforcement learning and alignment. | ||
|
|
||
| The abstract from the paper is the following: | ||
|
|
||
| *While Multimodal Large Language Models (MLLMs) demonstrate remarkable capabilities on static images, they often fall short in comprehending dynamic, information-dense short-form videos, a dominant medium in today’s digital landscape. To bridge this gap, we introduce Kwai Keye-VL, an 8-billion-parameter multimodal foundation model engineered for leading-edge performance in short-video understanding while maintaining robust general-purpose vision-language abilities. The development of Keye-VL rests on two core pillars: a massive, high-quality dataset exceeding 600 billion tokens with a strong emphasis on video, and an innovative training recipe. This recipe features a four-stage pre-training process for solid vision-language alignment, followed by a meticulous two-phase post-training process. The first post-training stage enhances foundational capabilities like instruction following, while the second phase focuses on stimulating advanced reasoning. In this second phase, a key innovation is our five-mode “cold-start” data mixture, which includes “thinking”, “non-thinking”, “auto-think”, “think with image”, and high-quality video data. This mixture teaches the model to decide when and how to reason. Subsequent reinforcement learning (RL) and alignment steps further enhance these reasoning capabilities and correct abnormal model behaviors, such as repetitive outputs. To validate our approach, we conduct extensive evaluations, showing that Keye-VL achieves state-of-the-art results on public video benchmarks and remains highly competitive on general image-based tasks (Figure 1). Furthermore, we develop and release the KC-MMBench, a new benchmark tailored for real-world short-video scenarios, where Keye-VL shows a significant advantage. Comprehensive human evaluations also confirm that our model provides a superior user experience compared to other leading models of a similar scale. This paper details the architecture, data construction strategy, and training methodology of Keye-VL, offering valuable insights for building the next generation of MLLMs for the video era.* |
There was a problem hiding this comment.
preview is not available anymore, can delete
| "qwen2_5_vl", | ||
| "videollava", | ||
| "vipllava", | ||
| "keyevl1_5", |
There was a problem hiding this comment.
to delete as well, instead we should change the checkpoint keys before release. This list is reserved for models we cannot change because they are released in the past
| image_token_id: int = None, | ||
| video_token_id: int = None, | ||
| attention_bias: bool = False, |
There was a problem hiding this comment.
The special image/video tokens also go in KeyeVL1_5Config. Then we can copy the text config from any existing LM's and reduce LOC :)
| spatial_merge_size = config.vision_config.spatial_merge_size | ||
| self.merge_kernel_size = (spatial_merge_size, spatial_merge_size) | ||
|
|
||
| self.hidden_size = self.vision_config.hidden_size * self.merge_kernel_size[0] * self.merge_kernel_size[1] |
There was a problem hiding this comment.
isn't used in forward, we can keep as hidden_size = my-value-here
| m1, m2 = self.merge_kernel_size | ||
| h_kernel, w_kernel = self.merge_kernel_size |
| "InformerConfig", | ||
| "JukeboxPriorConfig", | ||
| "JukeboxTokenizer", | ||
| "KeyeImageProcessor", |
There was a problem hiding this comment.
has to be fixed, we can't skip image processor docs
| # text_config={ | ||
| # "vocab_size": 99, | ||
| # "hidden_size": 32, | ||
| # "intermediate_size": 37, | ||
| # "num_hidden_layers": 4, | ||
| # "num_attention_heads": 4, | ||
| # "num_key_value_heads": 2, | ||
| # "hidden_act": "silu", | ||
| # "max_position_embeddings": 512, |
| @@ -0,0 +1,533 @@ | |||
| # Copyright 2025 The Keye Team and The HuggingFace Inc. team. All rights reserved. | |||
There was a problem hiding this comment.
we need test for processing, image processing and video processing classes as well
| _checkpoint_conversion_mapping = { | ||
| "^visual": "model.visual", | ||
| "^mlp_AR": "model.mm_projector", | ||
| r"^model(?!\.(language_model|visual|mm_projector))": "model.language_model", | ||
| } |
| device = pixel_values.device | ||
| pixel_values = pixel_values.type(self.visual.dtype) | ||
| pixel_values = pixel_values.unsqueeze(0) | ||
| assert torch.all(image_grid_thw[:, 0] == 1) | ||
| image_grid_thw = image_grid_thw.to(device) | ||
|
|
||
| total_patches = image_grid_thw.prod(dim=1) | ||
| width = torch.repeat_interleave(image_grid_thw[:, 2], total_patches) | ||
| cu_seqlens = total_patches.cumsum(0) | ||
|
|
||
| arange = torch.arange(cu_seqlens[-1], dtype=torch.long, device=device) | ||
| image_position_ids = arange - torch.repeat_interleave(cu_seqlens.to(device) - total_patches, total_patches) | ||
|
|
||
| width_position_ids = torch.remainder(image_position_ids, width) | ||
| height_position_ids = torch.div(image_position_ids, width, rounding_mode="floor") | ||
| cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0).to(dtype=torch.int32, device=device) | ||
| width_position_ids = width_position_ids.to(device) | ||
| height_position_ids = height_position_ids.to(device) | ||
|
|
There was a problem hiding this comment.
we need to call self.get_image_features()
| device = pixel_values_videos.device | ||
| pixel_values_videos = pixel_values_videos.type(self.visual.dtype) | ||
| pixel_values_videos = pixel_values_videos.unsqueeze(0) | ||
| video_grid_thw = split_thw(video_grid_thw.squeeze(0)).to(device) | ||
|
|
||
| assert torch.all(video_grid_thw[:, 0] == 1) | ||
|
|
||
| total_patches = video_grid_thw.prod(dim=1) | ||
| width = torch.repeat_interleave(video_grid_thw[:, 2], total_patches) | ||
| cu_seqlens = total_patches.cumsum(0) | ||
| arange = torch.arange(cu_seqlens[-1], dtype=torch.long, device=device) | ||
| video_position_ids = arange - torch.repeat_interleave(cu_seqlens.to(device) - total_patches, total_patches) |
There was a problem hiding this comment.
same here, we need to call self.get_video_features()
| if input_ids is None: | ||
| image_mask = inputs_embeds == self.get_input_embeddings()( | ||
| torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device) | ||
| ) | ||
| image_mask = image_mask.all(-1) | ||
| else: | ||
| image_mask = input_ids == self.config.image_token_id | ||
|
|
There was a problem hiding this comment.
this is obtained from self.get_placeholder_mask similar to other VLMs (e,g. Qwen-VL)
| if input_ids is None: | ||
| video_mask = inputs_embeds == self.get_input_embeddings()( | ||
| torch.tensor(self.config.video_token_id, dtype=torch.long, device=inputs_embeds.device) | ||
| ) | ||
| video_mask = video_mask.all(-1) | ||
| else: | ||
| video_mask = input_ids == self.config.video_token_id | ||
|
|
||
| n_video_tokens = video_mask.sum().item() | ||
|
|
There was a problem hiding this comment.
same here, from self.get_placeholder_mask similar to other VLMs (e,g. Qwen-VL)
| assert torch.all(image_grid_thw[:, 0] == 1) | ||
| image_grid_thw = image_grid_thw.to(device) |
There was a problem hiding this comment.
we can delete these two lines
| total_patches = video_grid_thw.prod(dim=1) | ||
| width = torch.repeat_interleave(video_grid_thw[:, 2], total_patches) | ||
| cu_seqlens = total_patches.cumsum(0) | ||
| arange = torch.arange(cu_seqlens[-1], dtype=torch.long, device=device) | ||
| video_position_ids = arange - torch.repeat_interleave(cu_seqlens.to(device) - total_patches, total_patches) | ||
|
|
||
| width_position_ids = torch.remainder(video_position_ids, width) | ||
| height_position_ids = torch.div(video_position_ids, width, rounding_mode="floor") | ||
| cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0).to(dtype=torch.int32, device=device) | ||
| width_position_ids = width_position_ids.to(device) | ||
| height_position_ids = height_position_ids.to(device) | ||
|
|
There was a problem hiding this comment.
these lines are identical for video and image. I think we can move it under VisionModel.forward so that users can pass directly the image pixels and let the model handle the rest
| Examples: | ||
| Temporal (Time): 3 patches, representing different segments of the video in time. | ||
| Height: 2 patches, dividing each frame vertically. | ||
| Width: 2 patches, dividing each frame horizontally. | ||
| We also have some important parameters: | ||
| fps (Frames Per Second): The video's frame rate, set to 1. This means one frame is processed each second. | ||
| tokens_per_second: This is a crucial parameter. It dictates how many "time-steps" or "temporal tokens" are conceptually packed into a one-second interval of the video. In this case, we have 25 tokens per second. So each second of the video will be represented with 25 separate time points. It essentially defines the temporal granularity. | ||
| temporal_patch_size: The number of frames that compose one temporal patch. Here, it's 2 frames. | ||
| interval: The step size for the temporal position IDs, calculated as tokens_per_second * temporal_patch_size / fps. In this case, 25 * 2 / 1 = 50. This means that each temporal patch will be have a difference of 50 in the temporal position IDs. | ||
| input_ids: [V V V V V V V V V V V V T T T T T], here V is for vision. | ||
| vision temporal position_ids: [0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4] | ||
| vision height position_ids: [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5] | ||
| vision width position_ids: [0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5] | ||
| text temporal position_ids: [101, 102, 103, 104, 105] | ||
| text height position_ids: [101, 102, 103, 104, 105] | ||
| text width position_ids: [101, 102, 103, 104, 105] |
There was a problem hiding this comment.
can you comment what is the diff from qwen2-vl and update the docs?
|
Ping me again when it is ready for review, I will unsubscribe myself from notifications |
|
@zucchini-nlp |
|
[For maintainers] Suggested jobs to run (before merge) run-slow: auto, keye_vl_1_5 |
|
@Kwai-Keye oke, today or tomorrow I will try to take a look. I'll also be on vacation starting Thursday until November, so the review process will be a bit slower In the meantime, can you take a look at failing tests? |

Model Upgrade: Keye-VL-1.5-8B
Overview
This PR introduces an upgraded version of the visual-language model, transitioning from the previous
keye-previewtokeye-vl-1.5-8B. The update includes architectural refinements, documentation improvements, code optimizations, and style enhancements.Key Changes
1. Model Architecture
2. Documentation
3. Code Optimization
Impact
Usage
Refer to the updated documentation for detailed instructions on using the new model version.
Notes