Skip to content

Commit 93bed93

Browse files
vasquzucchini-nlpairlsyn
authored
fixing more typos (#45689)
* squash commit * tmp * maybe no * use height first format! * more clean-up * and even more * fix(minicpmv4_6): respect _is_hf_initialized in ViTWindowAttentionMerger and cast target_sizes to int * fix(processing): handle batched video paths from apply_chat_template * no post init * fix: remove extra merge_kernel_size arg passed to DownsampleMLP * fix(minicpmv4_6): fix image processor output format and processor placeholder expansion * fix(minicpmv4_6): sync image processor with Option B and add skip_tensor_conversion * fix: remove empty fallbacks * fix: adjust tokenizer config * fix: forget import init * fix: tested video support and fix multimodal pipeline - Add video_token_id to config and model forward for video token masking - Add _process_visual_features helper to handle NaViT repacking and beam search expansion for both image and video inputs - Cast pixel_values to vision tower dtype to fix float32/bfloat16 mismatch - Fix video placeholder in processor: compute per-frame tokens correctly (patches_per_frame × num_frames) and restore slice_start/slice_end structure - Video processor now outputs grids_videos and num_frames metadata - Pass pixel_values_videos/target_sizes_videos through generate pipeline * fix(minicpmv4_6): default image_token_id to None and skip metadata tensor conversion * fix: replace video_start/end_token with image_start/end_token (align training) * fix(processor): derive token divisor from downsample_mode at call time * fix: align video processing with legacy semantics - Fix concat_frames_as_image canvas color from white (255) to black (0) to match the legacy _concat_images line_color=(0,0,0) separator lines - Add missing `self` parameter to concat_frames_as_image - Fix refine_size return order (height, width) in resize_and_split_patches - Remove unused imports (group_videos_by_shape, reorder_videos) - Rework _preprocess_video to process each visual unit independently with per-frame grids and target sizes instead of a single shared grid - Update processor placeholder generation to iterate per-frame with individual grid/token counts via num_patches_per_frame * fix(video_processor): align sub-frame stacking with sample_frames semantics Reconstruct sub_timestamps using the same double-loop as sample_frames and apply identical np.linspace downsampling, then group by second via cursor-based loop (matching legacy _group_stacked_by_second) instead of fixed-stride slicing — ensures composite-per-second interleaving is faithful to the original pipeline for all video durations. * refactor(minicpmv4_6): add get_video_features public API, remove _process_visual_features * refactor(minicpmv4_6): move beam expansion handling to _expand_inputs_for_generation * fix: remove redundant conversion * fix: Infer num_beams from pixel tensor shapes instead of passing via kwargs * add comment in code * Fix MiniCPMV4_6 test, image/processor batching and video sampling tests * fix: consolidate MiniCPMV4_6 video metadata validation * fix: address MiniCPM-V 4.6 review cleanups * vision attention as in qwen * skip the test, it will block merge otherwise * move around * . * return new! * frogot to delete from sig * last fixes * fix * fix repo * hmm * fix * come on * aya --------- Co-authored-by: raushan <raushan@huggingface.co> Co-authored-by: airlsyn <airlsyn.ai@gmail.com>
1 parent a29e967 commit 93bed93

25 files changed

Lines changed: 4638 additions & 6 deletions

docs/source/en/_toctree.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,8 @@
12871287
title: MetaCLIP 2
12881288
- local: model_doc/mgp-str
12891289
title: MGP-STR
1290+
- local: model_doc/minicpmv4_6
1291+
title: MiniCPM-V
12901292
- local: model_doc/mistral3
12911293
title: Mistral3
12921294
- local: model_doc/mistral4
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<!--Copyright 2026 OpenBMB and the HuggingFace Inc. team. All rights reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4+
the License. You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
9+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
specific language governing permissions and limitations under the License.
11+
12+
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
13+
rendered properly in your Markdown viewer.
14+
15+
-->
16+
*This model was released on 2025-09-16 and added to Hugging Face Transformers on 2026-04-28.*
17+
18+
<div style="float: right;">
19+
<div class="flex flex-wrap space-x-1">
20+
<img alt="PyTorch" src="https://img.shields.io/badge/PyTorch-DE3412?style=flat&logo=pytorch&logoColor=white">
21+
<img alt="SDPA" src="https://img.shields.io/badge/SDPA-DE3412?style=flat&logo=pytorch&logoColor=white">
22+
<img alt="FlashAttention" src="https://img.shields.io/badge/%E2%9A%A1%EF%B8%8E%20FlashAttention-eae0c8?style=flat">
23+
</div>
24+
</div>
25+
26+
# MiniCPM-V
27+
28+
[MiniCPM-V](https://huggingface.co/papers/2509.18154) is a series of efficient multimodal large language models developed by [OpenBMB](https://github.com/OpenBMB). The MiniCPM-V 4.6 architecture uses a [SigLIP](siglip) vision encoder with a window-attention merger and a [Qwen3.5](qwen3_5) language model backbone, supporting both 4x and 16x visual downsampling modes.
29+
30+
This model was contributed by [OpenBMB](https://huggingface.co/openbmb).
31+
The original code can be found [here](https://github.com/OpenBMB/MiniCPM-V).
32+
33+
## Usage example
34+
35+
### Inference with Pipeline
36+
37+
```python
38+
from transformers import pipeline
39+
40+
messages = [
41+
{
42+
"role": "user",
43+
"content": [
44+
{
45+
"type": "image",
46+
"image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg",
47+
},
48+
{"type": "text", "text": "Describe this image."},
49+
],
50+
},
51+
]
52+
53+
pipe = pipeline("image-text-to-text", model="openbmb/MiniCPM-V-4_6")
54+
outputs = pipe(text=messages, max_new_tokens=50, return_full_text=False)
55+
outputs[0]["generated_text"]
56+
```
57+
58+
### Inference on a single image
59+
60+
> [!NOTE]
61+
> The model has been trained with a specific prompt format for chatting. Use `processor.apply_chat_template(my_conversation_dict)` to correctly format your prompts.
62+
63+
```python
64+
from transformers import AutoProcessor, AutoModelForImageTextToText
65+
66+
model_checkpoint = "openbmb/MiniCPM-V-4_6"
67+
processor = AutoProcessor.from_pretrained(model_checkpoint)
68+
model = AutoModelForImageTextToText.from_pretrained(model_checkpoint, device_map="auto")
69+
70+
messages = [
71+
{
72+
"role": "user",
73+
"content": [
74+
{"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"},
75+
{"type": "text", "text": "Describe this image."},
76+
],
77+
}
78+
]
79+
80+
inputs = processor.apply_chat_template(
81+
messages, add_generation_prompt=True, tokenize=True,
82+
return_dict=True, return_tensors="pt",
83+
).to(model.device, dtype=model.dtype)
84+
85+
output = model.generate(**inputs, max_new_tokens=100)
86+
decoded_output = processor.decode(output[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True)
87+
print(decoded_output)
88+
```
89+
90+
### Downsampling mode
91+
92+
MiniCPM-V 4.6 supports two visual downsampling modes:
93+
94+
- **16x** (default): More aggressive downsampling, fewer visual tokens, faster inference.
95+
- **4x**: Less downsampling, more visual tokens, better for detail-rich tasks.
96+
97+
You can change the downsampling mode at runtime by passing `downsample_mode` via `processor_kwargs` and to `model.generate`:
98+
99+
```python
100+
inputs = processor.apply_chat_template(
101+
messages, add_generation_prompt=True, tokenize=True,
102+
return_dict=True, return_tensors="pt",
103+
processor_kwargs={"downsample_mode": "4x"},
104+
).to(model.device, dtype=model.dtype)
105+
106+
output = model.generate(**inputs, max_new_tokens=100, downsample_mode="4x")
107+
```
108+
109+
### Thinking mode
110+
111+
The model supports a thinking mode controlled by `enable_thinking` in the chat template. When enabled, the model generates internal reasoning before providing the final answer:
112+
113+
```python
114+
inputs = processor.apply_chat_template(
115+
messages, add_generation_prompt=True, tokenize=True,
116+
return_dict=True, return_tensors="pt",
117+
enable_thinking=True,
118+
).to(model.device, dtype=model.dtype)
119+
120+
output = model.generate(**inputs, max_new_tokens=1024)
121+
```
122+
123+
To disable thinking (default for evaluation):
124+
125+
```python
126+
inputs = processor.apply_chat_template(
127+
messages, add_generation_prompt=True, tokenize=True,
128+
return_dict=True, return_tensors="pt",
129+
enable_thinking=False,
130+
).to(model.device, dtype=model.dtype)
131+
```
132+
133+
### Image processing backend
134+
135+
MiniCPM-V 4.6 provides two image processing backends:
136+
137+
- **torchvision** (default): Uses `torchvision.transforms` for image resizing.
138+
- **pil**: Uses `PIL.Image.resize`, matching the original implementation.
139+
140+
To use the PIL backend:
141+
142+
```python
143+
from transformers import AutoProcessor, AutoImageProcessor
144+
145+
processor = AutoProcessor.from_pretrained(model_checkpoint)
146+
processor.image_processor = AutoImageProcessor.from_pretrained(model_checkpoint, backend="pil")
147+
```
148+
149+
### Video inference
150+
151+
MiniCPM-V 4.6 supports video understanding.
152+
153+
```python
154+
messages = [
155+
{
156+
"role": "user",
157+
"content": [
158+
{"type": "video", "video": "path/to/video.mp4"},
159+
{"type": "text", "text": "Describe what happens in this video."},
160+
],
161+
}
162+
]
163+
164+
inputs = processor.apply_chat_template(
165+
messages, add_generation_prompt=True, tokenize=True,
166+
return_dict=True, return_tensors="pt",
167+
).to(model.device, dtype=model.dtype)
168+
169+
output = model.generate(**inputs, max_new_tokens=200)
170+
decoded_output = processor.decode(output[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True)
171+
print(decoded_output)
172+
```
173+
174+
If you already have the rendered prompt string, you can call `processor(text=..., videos=[...])` directly instead.
175+
176+
## MiniCPMV4_6Config
177+
178+
[[autodoc]] MiniCPMV4_6Config
179+
180+
## MiniCPMV4_6VisionConfig
181+
182+
[[autodoc]] MiniCPMV4_6VisionConfig
183+
184+
## MiniCPMV4_6Model
185+
186+
[[autodoc]] MiniCPMV4_6Model
187+
- forward
188+
- get_image_features
189+
190+
## MiniCPMV4_6ForConditionalGeneration
191+
192+
[[autodoc]] MiniCPMV4_6ForConditionalGeneration
193+
- forward
194+
- get_image_features
195+
196+
## MiniCPMV4_6Processor
197+
198+
[[autodoc]] MiniCPMV4_6Processor
199+
- __call__
200+
201+
## MiniCPMV4_6ImageProcessor
202+
203+
[[autodoc]] MiniCPMV4_6ImageProcessor
204+
- preprocess
205+
206+
## MiniCPMV4_6ImageProcessorPil
207+
208+
[[autodoc]] MiniCPMV4_6ImageProcessorPil
209+
- preprocess
210+
211+
## MiniCPMV4_6VideoProcessor
212+
213+
[[autodoc]] MiniCPMV4_6VideoProcessor
214+
- preprocess

src/transformers/activations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ def get_activation(activation_string):
354354
gelu_new = get_activation("gelu_new")
355355
gelu = get_activation("gelu")
356356
gelu_fast = get_activation("gelu_fast")
357+
gelu_pytorch_tanh = get_activation("gelu_pytorch_tanh")
357358
quick_gelu = get_activation("quick_gelu")
358359
silu = get_activation("silu")
359360
mish = get_activation("mish")

src/transformers/image_transforms.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -837,24 +837,26 @@ def split_to_tiles(images: "torch.Tensor", num_tiles_height: int, num_tiles_widt
837837

838838

839839
def divide_to_patches(
840-
image: Union[np.ndarray, "torch.Tensor"], patch_size: int
840+
image: Union[np.ndarray, "torch.Tensor"], patch_size: int | tuple[int, int]
841841
) -> list[Union[np.ndarray, "torch.Tensor"]]:
842842
"""
843843
Divides an image into patches of a specified size.
844844
845845
Args:
846846
image (`np.array | "torch.Tensor"`):
847847
The input image.
848-
patch_size (`int`):
849-
The size of each patch.
848+
patch_size (`int` or `tuple[int, int]`):
849+
The size of each patch. If an int, patches are square. If a tuple,
850+
it is interpreted as `(patch_height, patch_width)`.
850851
Returns:
851852
list: A list of `np.array | "torch.Tensor"` representing the patches.
852853
"""
854+
patch_h, patch_w = (patch_size, patch_size) if isinstance(patch_size, int) else patch_size
853855
patches = []
854856
height, width = get_image_size(image, channel_dim=ChannelDimension.FIRST)
855-
for i in range(0, height, patch_size):
856-
for j in range(0, width, patch_size):
857-
patch = image[:, i : i + patch_size, j : j + patch_size]
857+
for i in range(0, height, patch_h):
858+
for j in range(0, width, patch_w):
859+
patch = image[..., i : i + patch_h, j : j + patch_w]
858860
patches.append(patch)
859861

860862
return patches

src/transformers/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@
250250
from .metaclip_2 import *
251251
from .mgp_str import *
252252
from .mimi import *
253+
from .minicpmv4_6 import *
253254
from .minimax import *
254255
from .minimax_m2 import *
255256
from .ministral import *

src/transformers/models/auto/auto_mappings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@
331331
("metaclip_2_vision_model", "MetaClip2VisionConfig"),
332332
("mgp-str", "MgpstrConfig"),
333333
("mimi", "MimiConfig"),
334+
("minicpmv4_6", "MiniCPMV4_6Config"),
335+
("minicpmv4_6_vision", "MiniCPMV4_6VisionConfig"),
334336
("minimax", "MiniMaxConfig"),
335337
("minimax_m2", "MiniMaxM2Config"),
336338
("ministral", "MinistralConfig"),
@@ -740,6 +742,7 @@
740742
("metaclip_2_text_model", "metaclip_2"),
741743
("metaclip_2_vision_model", "metaclip_2"),
742744
("mgp-str", "mgp_str"),
745+
("minicpmv4_6_vision", "minicpmv4_6"),
743746
("mlcd_vision_model", "mlcd"),
744747
("mllama_text_model", "mllama"),
745748
("mllama_vision_model", "mllama"),
@@ -901,6 +904,7 @@
901904
("llava_onevision", {"pil": "LlavaOnevisionImageProcessorPil", "torchvision": "LlavaOnevisionImageProcessor"}),
902905
("mask2former", {"pil": "Mask2FormerImageProcessorPil", "torchvision": "Mask2FormerImageProcessor"}),
903906
("maskformer", {"pil": "MaskFormerImageProcessorPil", "torchvision": "MaskFormerImageProcessor"}),
907+
("minicpmv4_6", {"pil": "MiniCPMV4_6ImageProcessorPil", "torchvision": "MiniCPMV4_6ImageProcessor"}),
904908
("mllama", {"pil": "MllamaImageProcessorPil", "torchvision": "MllamaImageProcessor"}),
905909
("mobilenet_v1", {"pil": "MobileNetV1ImageProcessorPil", "torchvision": "MobileNetV1ImageProcessor"}),
906910
("mobilenet_v2", {"pil": "MobileNetV2ImageProcessorPil", "torchvision": "MobileNetV2ImageProcessor"}),
@@ -967,6 +971,7 @@
967971
("internvl", "InternVLVideoProcessor"),
968972
("llava_next_video", "LlavaNextVideoVideoProcessor"),
969973
("llava_onevision", "LlavaOnevisionVideoProcessor"),
974+
("minicpmv4_6", "MiniCPMV4_6VideoProcessor"),
970975
("pe_video", "PeVideoVideoProcessor"),
971976
("perception_lm", "PerceptionLMVideoProcessor"),
972977
("qwen2_vl", "Qwen2VLVideoProcessor"),

src/transformers/models/auto/modeling_auto.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ class _BaseModelWithGenerate(PreTrainedModel, GenerationMixin):
285285
("metaclip_2", "MetaClip2Model"),
286286
("mgp-str", "MgpstrForSceneTextRecognition"),
287287
("mimi", "MimiModel"),
288+
("minicpmv4_6", "MiniCPMV4_6Model"),
288289
("minimax", "MiniMaxModel"),
289290
("minimax_m2", "MiniMaxM2Model"),
290291
("ministral", "MinistralModel"),
@@ -1008,6 +1009,7 @@ class _BaseModelWithGenerate(PreTrainedModel, GenerationMixin):
10081009
("llava_next", "LlavaNextForConditionalGeneration"),
10091010
("llava_next_video", "LlavaNextVideoForConditionalGeneration"),
10101011
("llava_onevision", "LlavaOnevisionForConditionalGeneration"),
1012+
("minicpmv4_6", "MiniCPMV4_6ForConditionalGeneration"),
10111013
("mistral3", "Mistral3ForConditionalGeneration"),
10121014
("mistral4", "Mistral4ForCausalLM"),
10131015
("mllama", "MllamaForConditionalGeneration"),

src/transformers/models/auto/processing_auto.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
("markuplm", "MarkupLMProcessor"),
120120
("metaclip_2", "CLIPProcessor"),
121121
("mgp-str", "MgpstrProcessor"),
122+
("minicpmv4_6", "MiniCPMV4_6Processor"),
122123
("mistral3", "PixtralProcessor"),
123124
("mllama", "MllamaProcessor"),
124125
("mm-grounding-dino", "GroundingDinoProcessor"),

src/transformers/models/auto/tokenization_auto.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
("megatron-bert", "BertTokenizer" if is_tokenizers_available() else None),
187187
("metaclip_2", "XLMRobertaTokenizer" if is_tokenizers_available() else None),
188188
("mgp-str", "MgpstrTokenizer"),
189+
("minicpmv4_6", "TokenizersBackend" if is_tokenizers_available() else None),
189190
(
190191
"ministral",
191192
"MistralCommonBackend"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2026 OpenBMB and the HuggingFace Inc. team. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from typing import TYPE_CHECKING
17+
18+
from ...utils import _LazyModule
19+
from ...utils.import_utils import define_import_structure
20+
21+
22+
if TYPE_CHECKING:
23+
from .configuration_minicpmv4_6 import *
24+
from .image_processing_minicpmv4_6 import *
25+
from .image_processing_pil_minicpmv4_6 import *
26+
from .modeling_minicpmv4_6 import *
27+
from .processing_minicpmv4_6 import *
28+
from .video_processing_minicpmv4_6 import *
29+
else:
30+
import sys
31+
32+
_file = globals()["__file__"]
33+
sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)

0 commit comments

Comments
 (0)