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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

## Latest News 🗞️🚀

* 08/25/2026 7.4.0-dev `main`: ✨ Added Tencent `HunyuanOCR` quantization support.
* 08/25/2026 7.4.0-dev `main`: ✨ Added `lm_head` and embedding quantization lifecycle.
* 08/24/2026 7.4.0-dev `main`: ✨ Added Baidu `Unlimited-OCR` quantization support.
* 08/20/2026 7.4.0-dev `main`: ✨ Added `deepseek_v32` / DeepSeek V3.2; `muse_glimmer` / Muse Glimmer multimodal; `mage_vl` / Mage-VL; Cohere `North Micro Vision` (`cohere_compass`); `axk2` (A.X-K2) model support.
Expand Down Expand Up @@ -269,7 +270,7 @@ Selected public references where teams or companies explicitly mention GPT-QMode
| MiniMax M2/M3 | ✅ | AfMoE | ✅ | Bailing-MoE | ✅ | LFM2 / LFM2-VL / LFM2-MoE | ✅ | Marin | ✅ |
| InternVL Chat | ✅ | Laguna | ✅ | Mimo / Mimo V2 | ✅ | Zamba / Zamba2 | ✅ | Intern S1 / S2 Preview | ✅ |
| HunYuan V1 Dense / MoE | ✅ | HY-V3 | ✅ | Inkling | ✅ | Solar Open / Open 2 | ✅ | North Micro Vision | ✅ |
| Mage-VL | ✅ | Unlimited-OCR | ✅ | | | | | | |
| Mage-VL | ✅ | Unlimited-OCR | ✅ | HunyuanOCR | ✅ | | | | |
| Muse Glimmer | ✅ | | | | | | | | |
| SmolLM3 | ✅ | | | | | | | | |

Expand Down
2 changes: 2 additions & 0 deletions gptqmodel/models/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
from .definitions.hrm_text import HrmTextQModel # noqa: E402
from .definitions.hunyuan_v1_dense import HunYuanDenseV1QModel # noqa: E402
from .definitions.hunyuan_v1_moe import HunYuanMoEV1QModel # noqa: E402
from .definitions.hunyuan_vl import HunYuanVLQModel # noqa: E402
from .definitions.hy_v3 import HYV3QModel # noqa: E402
from .definitions.hymba import HymbaQModel # noqa: E402
from .definitions.instella import InstellaQModel # noqa: E402
Expand Down Expand Up @@ -257,6 +258,7 @@
"hrm_text": HrmTextQModel,
"hunyuan_v1_dense": HunYuanDenseV1QModel,
"hunyuan_v1_moe": HunYuanMoEV1QModel,
"hunyuan_vl": HunYuanVLQModel,
"hy_v3": HYV3QModel,
"qwen": QwenQModel,
"mistral": LlamaQModel, # 100% llama clone
Expand Down
1 change: 1 addition & 0 deletions gptqmodel/models/definitions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from .hrm_text import HrmTextQModel
from .hunyuan_v1_dense import HunYuanDenseV1QModel
from .hunyuan_v1_moe import HunYuanMoEV1QModel
from .hunyuan_vl import HunYuanVLQModel
from .hy_v3 import HYV3QModel
from .hymba import HymbaQModel
from .instella import InstellaQModel
Expand Down
134 changes: 134 additions & 0 deletions gptqmodel/models/definitions/hunyuan_vl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# SPDX-FileCopyrightText: 2026 ModelCloud.ai
# SPDX-FileCopyrightText: 2026 qubitium@modelcloud.ai
# SPDX-License-Identifier: Apache-2.0
# Contact: qubitium@modelcloud.ai, x.com/qubitium

from typing import Any, Dict

from transformers import AutoModelForImageTextToText, AutoProcessor, ProcessorMixin

from ...utils.calibration import batched
from ...utils.looper_helpers import normalize_device_like
from ...utils.model import MODALITY, move_to
from ...utils.offload import offload_to_disk
from .._const import CPU
from ..base import BaseQModel


class HunYuanVLQModel(BaseQModel):
"""Quantization definition for native Transformers Hunyuan-VL checkpoints."""

loader = AutoModelForImageTextToText

require_load_processor = True

modality = [MODALITY.TEXT, MODALITY.IMAGE_TO_TEXT]

pre_lm_head_norm_module = "model.language_model.norm"
rotary_embedding = "model.language_model.rotary_emb"

module_tree = [
"model",
"language_model",
"layers",
"#",
{
"input_layernorm": ("input_layernorm:!",),
"self_attn": (
"query_layernorm:!",
"q_proj:0",
"key_layernorm:!",
"k_proj:0",
"v_proj:0",
"o_proj:1",
),
"post_attention_layernorm": ("post_attention_layernorm:!",),
"mlp": ("gate_proj:0", "up_proj:0", "down_proj:1"),
},
]

def _materialize_module(self, parent, name: str, module_path: str):
module = getattr(parent, name)
target_device = normalize_device_like(self.quantize_config.device) or CPU
setattr(
parent,
name,
self.shell_module_materialize(
module,
target_device,
module_path=module_path,
),
)

def pre_quantize_generate_hook_start(self):
core_model = self.model.model
language_model = core_model.language_model
self._materialize_module(
language_model, "embed_tokens", "model.language_model.embed_tokens"
)
self._materialize_module(language_model, "norm", "model.language_model.norm")
self._materialize_module(
language_model, "rotary_emb", "model.language_model.rotary_emb"
)
self._materialize_module(core_model, "vision_tower", "model.vision_tower")

def pre_quantize_generate_hook_end(self):
core_model = self.model.model
language_model = core_model.language_model
modules = (
(language_model, "embed_tokens"),
(language_model, "norm"),
(language_model, "rotary_emb"),
(core_model, "vision_tower"),
)

if self.quantize_config.offload_to_disk:
for parent, name in modules:
offload_to_disk(
model=parent,
module=getattr(parent, name),
disk_path=self.quantize_config.offload_to_disk_path,
)
return

for parent, name in modules:
setattr(parent, name, move_to(getattr(parent, name), device=CPU))

def preprocess_dataset(self, sample: Dict) -> Dict:
return sample

def load_processor(self) -> ProcessorMixin:
return AutoProcessor.from_pretrained(
self.model_local_path,
trust_remote_code=False,
backend="pil",
)

def prepare_dataset(
self,
calibration_dataset,
batch_size: int = 1,
**kwargs,
) -> list[Dict[str, Any]]:
del kwargs
processor = self.load_processor()
calibration_data = []
for batch in batched(
calibration_dataset,
batch_size,
process_func=self.preprocess_dataset,
):
inputs = processor.apply_chat_template(
batch,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt",
processor_kwargs={"padding": True},
)
calibration_data.append(inputs)
del processor
return calibration_data


__all__ = ["HunYuanVLQModel"]
11 changes: 11 additions & 0 deletions gptqmodel/utils/hf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,17 @@ def _normalize_chatglm_remote_code_config_compat(config: Any) -> None:


def _normalize_rope_parameters_config_compat(config: Any) -> None:
# HunYuanVL is a composite config: RoPE belongs exclusively to its text
# sub-config. Adding a synthetic top-level rope_parameters value is not
# harmless here because HunYuanVLConfig treats flat text fields as legacy
# overrides when the config is serialized and loaded again. That would
# replace the nested multimodal mrope_section with a default RoPE config.
if getattr(config, "model_type", None) == "hunyuan_vl":
text_config = getattr(config, "text_config", None)
if text_config is not None:
_normalize_rope_parameters_config_compat(text_config)
return

rope_parameters = getattr(config, "rope_parameters", None)
if (
isinstance(rope_parameters, dict)
Expand Down
27 changes: 27 additions & 0 deletions tests/models/ovis/image_to_test_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from gptqmodel.models.definitions.deepseek_vl import DeepSeekVLQModel
from gptqmodel.models.definitions.deepseek_vl_v2 import DeepSeekVLV2QModel
from gptqmodel.models.definitions.ernie4_5_vl_moe import Ernie4_5_VLMoeQModel
from gptqmodel.models.definitions.hunyuan_vl import HunYuanVLQModel
from gptqmodel.models.definitions.intern_s2_preview import InternS2PreviewQModel
from gptqmodel.models.definitions.inkling import InklingMMQModel
from gptqmodel.models.definitions.interns1 import InternS1QModel
Expand Down Expand Up @@ -112,6 +113,25 @@ def format_unlimited_ocr_dataset(image, assistant):
}


def format_hunyuan_ocr_dataset(image, assistant):
del assistant
return [
{
"role": "user",
"content": [
{"type": "image", "image": image},
{
"type": "text",
"text": (
"提取文档图片中正文的所有信息用markdown格式表示,其中页眉、页脚部分忽略,"
"表格用html格式表达,文档中公式用latex格式表示,按照阅读顺序组织进行解析。"
),
},
],
}
]


def format_qwen2_5_omni_dataset(image, assistant):
return [
{
Expand Down Expand Up @@ -160,6 +180,10 @@ def prepare_unlimited_ocr_dataset(n_sample: int = 20) -> list[dict]:
return prepare_dataset(format_unlimited_ocr_dataset, n_sample=n_sample)


def prepare_hunyuan_ocr_dataset(n_sample: int = 20) -> list[list[dict]]:
return prepare_dataset(format_hunyuan_ocr_dataset, n_sample=n_sample)


def get_calib_dataset(model):
if isinstance(model, OvisQModel):
return prepare_dataset(format_ovis_dataset, n_sample=20)
Expand Down Expand Up @@ -204,4 +228,7 @@ def get_calib_dataset(model):
if isinstance(model, UnlimitedOCRQModel):
return prepare_unlimited_ocr_dataset(n_sample=20)

if isinstance(model, HunYuanVLQModel):
return prepare_hunyuan_ocr_dataset(n_sample=20)

raise NotImplementedError(f"Unsupported MODEL: {model.__class__}")
63 changes: 63 additions & 0 deletions tests/models/test_hunyuan_ocr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# SPDX-FileCopyrightText: 2026 ModelCloud.ai
# SPDX-FileCopyrightText: 2026 qubitium@modelcloud.ai
# SPDX-License-Identifier: Apache-2.0
# Contact: qubitium@modelcloud.ai, x.com/qubitium

from gptqmodel import BACKEND
from model_test import ModelTest
from ovis import image_to_test_dataset


def test_prepare_hunyuan_ocr_dataset_reuses_shared_dataset(monkeypatch):
calls = {}

def fake_prepare_dataset(format_func, n_sample):
calls["format_func"] = format_func
calls["n_sample"] = n_sample
return [format_func("image-url", "caption")]

monkeypatch.setattr(image_to_test_dataset, "prepare_dataset", fake_prepare_dataset)

dataset = image_to_test_dataset.prepare_hunyuan_ocr_dataset(n_sample=3)

assert calls == {
"format_func": image_to_test_dataset.format_hunyuan_ocr_dataset,
"n_sample": 3,
}
assert dataset == [
[
{
"role": "user",
"content": [
{"type": "image", "image": "image-url"},
{
"type": "text",
"text": (
"提取文档图片中正文的所有信息用markdown格式表示,其中页眉、页脚部分忽略,"
"表格用html格式表达,文档中公式用latex格式表示,按照阅读顺序组织进行解析。"
),
},
],
}
]
]


class TestHunyuanOCR(ModelTest):
NATIVE_MODEL_ID = "tencent/HunyuanOCR"
TRUST_REMOTE_CODE = False
USE_FLASH_ATTN = False
LOAD_BACKEND = BACKEND.AUTO
EVAL_BATCH_SIZE = 16
EVAL_TASKS_SLOW = {
"arc_challenge": {
"chat_template": True,
"acc": {"value": 0.2696245733788396, "floor_pct": 0.04},
"acc_norm": {"value": 0.30716723549488056, "floor_pct": 0.04},
},
}
EVAL_TASKS_FAST = ModelTest.derive_fast_eval_tasks(EVAL_TASKS_SLOW)
MODEL_COMPAT_FAST_LAYER_POSITION = "first"

def test_hunyuan_ocr(self):
self.quantize_and_evaluate()
Loading