Files
wehub-resource-sync a203934033
Lint test / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:34:58 +08:00

629 lines
28 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (c) ModelScope Contributors. All rights reserved.
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from dataclasses import dataclass, field
from PIL import Image, ImageOps
from transformers.dynamic_module_utils import get_class_from_dynamic_module
from typing import Any, Dict, List, Optional
from swift.utils import get_env_args
from ..base import Template
from ..constant import LLMTemplateType, MLLMTemplateType
from ..register import TemplateMeta, register_template
from ..template_inputs import StdTemplateInputs
from ..utils import Prompt, findall
@dataclass
class DeepseekTemplateMeta(TemplateMeta):
prefix: Prompt = field(default_factory=lambda: [['bos_token_id']])
prompt: Prompt = field(default_factory=lambda: ['User: {{QUERY}}\n\nAssistant:'])
chat_sep: Optional[Prompt] = field(default_factory=lambda: [['eos_token_id']])
suffix: Prompt = field(default_factory=lambda: [['eos_token_id']])
system_prefix: Optional[Prompt] = field(default_factory=lambda: [['bos_token_id'], '{{SYSTEM}}\n\n'])
register_template(DeepseekTemplateMeta(LLMTemplateType.deepseek, ))
register_template(
TemplateMeta(
LLMTemplateType.deepseek_coder,
prefix=['{{SYSTEM}}'],
prompt=['### Instruction:\n{{QUERY}}\n### Response:\n'],
chat_sep=['\n<|EOT|>\n'],
suffix=['\n<|EOT|>'],
stop_words=['<|EOT|>'],
default_system=('You are an AI programming assistant, utilizing the Deepseek Coder model, '
'developed by Deepseek Company, and you only answer questions related to computer science. '
'For politically sensitive questions, security and privacy issues, '
'and other non-computer science questions, you will refuse to answer\n')))
class DeepseekVLTemplate(Template):
image_placeholder = ['<image_placeholder>']
skip_prompt = False
use_model = True
placeholder_tokens = ['<image_placeholder>']
image_token_num_per_image: int = 576
def _encode(self, inputs: StdTemplateInputs) -> Dict[str, Any]:
is_janus = getattr(self, 'is_janus', False)
encoded = super()._encode(inputs)
images = inputs.images
processor = self.processor
input_ids, labels = encoded['input_ids'], encoded['labels']
if not inputs.generate_mode: # understanding task
idx_list = findall(input_ids, processor.image_id) # '<image_placeholder>'
new_input_ids, new_labels = [], []
lo = 0
for hi in idx_list:
new_input_ids += input_ids[lo:hi]
if labels is not None:
new_labels += labels[lo:hi]
image_tokens = [processor.image_id] * processor.num_image_tokens
if is_janus:
image_tokens = [processor.image_start_id] + image_tokens + [processor.image_end_id]
new_input_ids += image_tokens
new_labels += [-100] * len(image_tokens)
lo = hi + 1
new_input_ids += input_ids[lo:]
if labels is not None:
new_labels += labels[lo:]
else:
new_labels = None
if is_janus:
from janus.models.processing_vlm import VLChatProcessorOutput
else:
from deepseek_vl.models.processing_vlm import VLChatProcessorOutput
images_outputs = processor.image_processor(images, return_tensors='pt')
output = VLChatProcessorOutput(
sft_format=None,
input_ids=torch.tensor(new_input_ids),
pixel_values=images_outputs.pixel_values,
num_image_tokens=torch.tensor([processor.num_image_tokens] * len(idx_list)))
encoded = {'output': output, 'input_ids': new_input_ids, 'labels': new_labels}
return encoded
else: # image generation task
if self.is_training:
raise NotImplementedError('Only support the inference of generation of Janus series models.')
sft_format = self.tokenizer.decode(input_ids)
prompt = sft_format + processor.image_start_tag
input_ids = processor.tokenizer.encode(prompt)
input_ids = torch.LongTensor(input_ids)
encoded = {'input_ids': input_ids, 'labels': labels, 'generate_mode': inputs.generate_mode}
return encoded
def _post_encode(self, model: nn.Module, inputs: Dict[str, Any]) -> Dict[str, Any]:
if not inputs.get('generate_mode'):
inputs['pixel_values'] = inputs['pixel_values'].to(dtype=self.model_info.torch_dtype)
inputs_embeds = model.prepare_inputs_embeds(**inputs)
return {'inputs_embeds': inputs_embeds}
else:
return inputs
def _data_collator(self, batch: List[Dict[str, Any]], *, padding_to: Optional[int] = None) -> Dict[str, Any]:
gene_img_list = [b.get('generate_mode') for b in batch]
if all(gene_img_list):
generate_mode = True
elif not any(gene_img_list):
generate_mode = False
else:
raise NotImplementedError('Do not support understanding and image generation tasks in one batch.')
if not generate_mode:
output = self.fetch_inputs(batch, ['output'])['output']
batched_output = dict(self.processor.batchify(output))
res = super()._data_collator(batch, padding_to=padding_to)
return {**batched_output, **res}
else:
res = super()._data_collator(batch, padding_to=padding_to)
res['generate_mode'] = generate_mode
return res
def generate(self, model, *args, **kwargs):
if not kwargs.get('generate_mode'):
return super().generate(model, *args, **kwargs)
else:
# generate how many number of images for each prompt, it is named parallel_size in the author's code
parallel_size = kwargs['generation_config'].num_return_sequences
temperature = kwargs['generation_config'].temperature
cfg_weight = get_env_args('cfg_weight', float, 5.0)
input_ids = kwargs['input_ids'] # [bsz, max_input_token_num]
bsz, max_input_token_num = input_ids.shape
tokens = torch.zeros((bsz, parallel_size * 2, max_input_token_num),
dtype=torch.int).cuda() # [bsz, parallel_size*2, max_input_token_num]
for i in range(parallel_size * 2):
tokens[:, i, :] = input_ids
if i % 2 != 0:
tokens[:, i, 1:-1] = self.processor.pad_id
inputs_embeds = model.language_model.get_input_embeddings()(
tokens) # [bsz, parallel_size*2, max_input_token_num, 2048]
generated_tokens = torch.zeros(
(bsz, parallel_size, self.image_token_num_per_image),
dtype=torch.int).cuda() # [bsz, 16, image_token_num_per_image] placeholder for the generated tokens
# set the first two dimensions into one dimension for batch size
inputs_embeds = inputs_embeds.reshape(bsz * parallel_size * 2, max_input_token_num, -1)
generated_tokens = generated_tokens.reshape(bsz * parallel_size, self.image_token_num_per_image)
for i in range(self.image_token_num_per_image): # generate the tokens of image in a auto-regression way
outputs = model.language_model.model(
inputs_embeds=inputs_embeds,
use_cache=True,
past_key_values=outputs.past_key_values if i != 0 else None)
hidden_states = outputs.last_hidden_state
logits = self.model.gen_head(hidden_states[:, -1, :])
logit_cond = logits[0::2, :]
logit_uncond = logits[1::2, :]
logits = logit_uncond + cfg_weight * (logit_cond - logit_uncond)
probs = torch.softmax(logits / temperature, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
generated_tokens[:, i] = next_token.squeeze(dim=-1) # [parallel_size, self.image_token_num_per_image]
next_token = torch.cat([next_token.unsqueeze(dim=1), next_token.unsqueeze(dim=1)], dim=1).view(-1)
img_embeds = model.prepare_gen_img_embeds(next_token) # [parallel_size * 2, 2048]
inputs_embeds = img_embeds.unsqueeze(dim=1) # [parallel_size * 2, 1, 2048]
# no need to reset the original first two dimensions, waiting for the update of the upper layer
# inputs_embeds = inputs_embeds.reshape(bsz, parallel_size*2, -1)
# generated_tokens = generated_tokens.reshape(bsz, parallel_size, self.image_token_num_per_image)
return {'sequences': generated_tokens}
def decode_generate_ids(self, generate_ids: List[int], **kwargs) -> Any:
if 'template_inputs' not in kwargs or not kwargs['template_inputs'].generate_mode:
return super().decode_generate_ids(generate_ids, **kwargs)
else:
img_size = get_env_args('img_size', int, 384)
patch_size = 16
num_to_decode = 1 # for now, generate_ids is a 1D list
generate_ids = torch.tensor(generate_ids).unsqueeze(0) # [num_to_decode=1, self.image_token_num_per_image]
dec = self.model.gen_vision_model.decode_code(
generate_ids.to(dtype=torch.int),
shape=[num_to_decode, 8, img_size // patch_size, img_size // patch_size])
dec = dec.to(torch.float32).cpu().numpy().transpose(0, 2, 3, 1) # [num_to_decode, H, W, ch=3]
dec = np.clip((dec + 1) / 2 * 255, 0, 255)
visual_img = np.zeros((num_to_decode, img_size, img_size, 3), dtype=np.uint8)
visual_img[:, :, :] = dec
img_list = []
for i in range(num_to_decode):
cur_img = Image.fromarray(visual_img[i])
img_list.append({'type': 'image', 'image': cur_img})
return img_list
@dataclass
class DeepseekVLTemplateMeta(DeepseekTemplateMeta):
default_system: Optional[str] = ('You are a helpful language and vision assistant. '
'You are able to understand the visual content that the user provides, '
'and assist the user with a variety of tasks using natural language.')
register_template(DeepseekVLTemplateMeta(
MLLMTemplateType.deepseek_vl,
template_cls=DeepseekVLTemplate,
))
class DeepseekJanus(DeepseekVLTemplate):
is_janus = True
image_placeholder = ['<image_placeholder>\n']
register_template(DeepseekVLTemplateMeta(MLLMTemplateType.deepseek_janus, template_cls=DeepseekJanus))
class DeepseekOCR(Template):
version = 'v1'
image_placeholder = ['<image>\n']
def init_env_args(self):
# Delay loading dynamic modules that require specific transformers versions
# These will be loaded lazily in _preprocess_image when actually needed
# This avoids triggering transformers version compatibility issues for vllm backend
super().init_env_args()
self._BasicImageTransform = None
self._dynamic_preprocess = None
self.crop_mode = get_env_args('crop_mode', bool, True)
self.base_size = get_env_args('base_size', int, 1024)
# image_size will be set after detecting version (v1: 640, v2: 768)
self._image_size_override = get_env_args('image_size', int, None)
@property
def image_size(self):
if self._image_size_override is not None:
return self._image_size_override
return 768 if self.version == 'v2' else 640
@property
def crop_threshold(self):
# v1: 640, v2: 768
return 768 if self.version == 'v2' else 640
def _load_dynamic_modules(self):
"""Lazily load dynamic modules from model repository."""
if self._BasicImageTransform is None:
model_dir = self.model_info.model_dir
model_type_name = 'deepseekocr2' if self.version == 'v2' else 'deepseekocr'
self._BasicImageTransform = get_class_from_dynamic_module(f'modeling_{model_type_name}.BasicImageTransform',
model_dir)
self._dynamic_preprocess = get_class_from_dynamic_module(f'modeling_{model_type_name}.dynamic_preprocess',
model_dir)
@property
def BasicImageTransform(self):
self._load_dynamic_modules()
return self._BasicImageTransform
@property
def dynamic_preprocess(self):
self._load_dynamic_modules()
return self._dynamic_preprocess
def _preprocess_image(self, images, image_token_id):
# Code borrowed from
# https://modelscope.cn/models/deepseek-ai/DeepSeek-OCR/file/view/master/modeling_deepseekocr.py?status=1
# https://modelscope.cn/models/deepseek-ai/DeepSeek-OCR-2/file/view/master/modeling_deepseekocr2.py?status=1
crop_mode = self.crop_mode
patch_size = 16
downsample_ratio = 4
valid_img_tokens = 0
w, h = images[0].size
ratio = 1 - ((max(w, h) - min(w, h)) / (max(w, h)))
crop_threshold = self.crop_threshold
image_size = self.image_size
image_transform = self.BasicImageTransform(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5), normalize=True)
images_list, images_crop_list = [], []
tokenized_str = []
images_spatial_crop = []
for image in images:
if crop_mode:
if image.size[0] <= crop_threshold and image.size[1] <= crop_threshold:
crop_ratio = [1, 1]
else:
if crop_mode:
images_crop_raw, crop_ratio = self.dynamic_preprocess(image)
else:
crop_ratio = [1, 1]
"""process the global view"""
global_view = ImageOps.pad(
image, (self.base_size, self.base_size), color=tuple(int(x * 255) for x in image_transform.mean))
if self.base_size == 1024:
valid_img_tokens += int(256 * ratio)
elif self.base_size == 1280:
valid_img_tokens += int(400 * ratio)
images_list.append(image_transform(global_view).to(torch.bfloat16))
width_crop_num, height_crop_num = crop_ratio
images_spatial_crop.append([width_crop_num, height_crop_num])
if width_crop_num > 1 or height_crop_num > 1:
"""process the local views"""
for i in range(len(images_crop_raw)):
images_crop_list.append(image_transform(images_crop_raw[i]).to(torch.bfloat16))
if image_size == 640:
valid_img_tokens += len(images_crop_list) * 100
elif image_size == 768:
valid_img_tokens += len(images_crop_list) * 144
num_queries = math.ceil((image_size // patch_size) / downsample_ratio)
num_queries_base = math.ceil((self.base_size // patch_size) / downsample_ratio)
"""add image tokens"""
# v1: adds newline token after each row, v2: no newline tokens in rows
if self.version == 'v2':
tokenized_image = ([image_token_id] * num_queries_base) * num_queries_base
tokenized_image += [image_token_id]
if width_crop_num > 1 or height_crop_num > 1:
tokenized_image += ([image_token_id] * (num_queries * width_crop_num)) * (
num_queries * height_crop_num)
else:
tokenized_image = ([image_token_id] * num_queries_base + [image_token_id]) * num_queries_base
tokenized_image += [image_token_id]
if width_crop_num > 1 or height_crop_num > 1:
tokenized_image += ([image_token_id] * (num_queries * width_crop_num) + [image_token_id]) * (
num_queries * height_crop_num)
tokenized_str.append(tokenized_image)
else:
"""process the global view"""
if image_size <= crop_threshold:
image = image.resize((image_size, image_size))
global_view = ImageOps.pad(
image, (image_size, image_size), color=tuple(int(x * 255) for x in image_transform.mean))
images_list.append(image_transform(global_view).to(torch.bfloat16))
if self.base_size == 1024:
valid_img_tokens += int(256 * ratio)
elif self.base_size == 1280:
valid_img_tokens += int(400 * ratio)
elif self.base_size == 640:
valid_img_tokens += int(100 * 1)
elif self.base_size == 512:
valid_img_tokens += int(64 * 1)
elif self.base_size == 768:
valid_img_tokens += int(144 * 1)
width_crop_num, height_crop_num = 1, 1
images_spatial_crop.append([width_crop_num, height_crop_num])
"""add image tokens"""
num_queries = math.ceil((image_size // patch_size) / downsample_ratio)
# v1: adds newline token after each row, v2: no newline tokens in rows
if self.version == 'v2':
tokenized_image = ([image_token_id] * num_queries) * num_queries
tokenized_image += [image_token_id]
else:
tokenized_image = ([image_token_id] * num_queries + [image_token_id]) * num_queries
tokenized_image += [image_token_id]
tokenized_str.append(tokenized_image)
if len(images_list) == 0:
images_ori = torch.zeros((1, 3, self.image_size, self.image_size))
images_spatial_crop = torch.zeros((1, 2), dtype=torch.long)
images_crop = torch.zeros((1, 3, self.base_size, self.base_size))
else:
images_ori = torch.stack(images_list, dim=0)
images_spatial_crop = torch.tensor(images_spatial_crop, dtype=torch.long)
if images_crop_list:
images_crop = torch.stack(images_crop_list, dim=0)
else:
images_crop = torch.zeros((1, 3, self.base_size, self.base_size))
return tokenized_str, images_ori, images_crop, images_spatial_crop
def _encode(self, inputs: StdTemplateInputs) -> Dict[str, Any]:
encoded = super()._encode(inputs)
input_ids = encoded['input_ids']
labels = encoded['labels']
loss_scale = encoded.get('loss_scale', None)
image_token = self._tokenize('<image>')
idx_list = findall(input_ids, image_token)
if idx_list:
tokenized_str, images_ori, images_crop, images_spatial_crop = self._preprocess_image(
inputs.images, image_token[0])
input_ids, labels, loss_scale = self._extend_tokens(input_ids, labels, loss_scale, idx_list,
lambda i: tokenized_str[i])
encoded['input_ids'] = input_ids
encoded['labels'] = labels
encoded['loss_scale'] = loss_scale
encoded['images'] = [(images_crop, images_ori)]
encoded['images_seq_mask'] = (torch.tensor(input_ids) == image_token[0])[None]
encoded['images_spatial_crop'] = images_spatial_crop
return encoded
def _data_collator_mm_data(self, batch: List[Dict[str, Any]]) -> Dict[str, Any]:
res = super()._data_collator_mm_data(batch)
images = self.gather_list(batch, 'images')
if images:
res['images'] = images
images_seq_mask = [x['images_seq_mask'] for x in batch if x.get('images_seq_mask') is not None]
images_spatial_crop = self.concat_tensor(batch, 'images_spatial_crop', 0)
padding_side = self.padding_side if self.is_training else 'left'
if images_seq_mask:
max_len = max([x.shape[1] for x in images_seq_mask])
res['images_seq_mask'] = torch.concat([
F.pad(x, (0, max_len - x.shape[1]) if padding_side == 'right' else (max_len - x.shape[1], 0))
for x in images_seq_mask
])
if images_spatial_crop is not None:
res['images_spatial_crop'] = images_spatial_crop
return res
register_template(
TemplateMeta(
MLLMTemplateType.deepseek_ocr,
prefix=['<begin▁of▁sentence>'],
prompt=['{{QUERY}}'],
chat_sep=None,
template_cls=DeepseekOCR))
class DeepseekOCR2(DeepseekOCR):
version = 'v2'
register_template(
TemplateMeta(
MLLMTemplateType.deepseek_ocr2,
prefix=['<begin▁of▁sentence>'],
prompt=['{{QUERY}}'],
chat_sep=None,
template_cls=DeepseekOCR2))
class UnlimitedOCR(DeepseekOCR):
image_placeholder = ['<image>'] # Remove trailing newline; override the parent class default
def init_env_args(self):
super().init_env_args()
self._device_fixed = False # Instance variable; avoid sharing state across multiple instances.
def _fix_device(self):
if not self._device_fixed and self.model is not None:
try:
vision_device = next(self.model.model.vision_model.parameters()).device
self.model.model.image_newline.data = self.model.model.image_newline.data.to(vision_device)
self.model.model.view_seperator.data = self.model.model.view_seperator.data.to(vision_device)
self._device_fixed = True
except Exception:
pass
def _encode(self, inputs: StdTemplateInputs) -> Dict[str, Any]:
self._fix_device()
return super()._encode(inputs)
def _load_dynamic_modules(self):
if self._BasicImageTransform is None:
model_dir = self.model_info.model_dir
self._BasicImageTransform = get_class_from_dynamic_module('modeling_unlimitedocr.BasicImageTransform',
model_dir)
self._dynamic_preprocess = get_class_from_dynamic_module('modeling_unlimitedocr.dynamic_preprocess',
model_dir)
register_template(
TemplateMeta(
MLLMTemplateType.unlimited_ocr,
prefix=[['bos_token_id']],
prompt=['{{QUERY}}'],
chat_sep=None,
template_cls=UnlimitedOCR,
))
@dataclass
class DeepseekV2_5TemplateMeta(TemplateMeta):
prefix: Prompt = field(default_factory=lambda: ['<begin▁of▁sentence>{{SYSTEM}}'])
prompt: Prompt = field(default_factory=lambda: ['<User>{{QUERY}}<Assistant>'])
chat_sep: Optional[Prompt] = field(default_factory=lambda: ['<end▁of▁sentence>'])
suffix: Prompt = field(default_factory=lambda: ['<end▁of▁sentence>'])
register_template(DeepseekV2_5TemplateMeta(LLMTemplateType.deepseek_v2_5))
register_template(DeepseekV2_5TemplateMeta(LLMTemplateType.deepseek_r1, is_thinking=True, thinking_prefix='<think>\n'))
class DeepseekV3_1Template(Template):
jinja_enable_thinking_key = 'thinking'
non_thinking_prefix_only_after_user = True
register_template(
DeepseekV2_5TemplateMeta(
LLMTemplateType.deepseek_v3_1,
agent_template='deepseek_v3_1',
is_thinking=True,
template_cls=DeepseekV3_1Template,
thinking_prefix='<think>',
non_thinking_prefix='</think>',
history_thinking_prefix='</think>'))
REASONING_EFFORT_MAX = (
'Reasoning Effort: Absolute maximum with no shortcuts permitted.\n'
'You MUST be very thorough in your thinking and comprehensively decompose the problem to resolve '
'the root cause, rigorously stress-testing your logic against all potential paths, edge cases, '
'and adversarial scenarios.\n'
'Explicitly write out your entire deliberation process, documenting every intermediate step, '
'considered alternative, and rejected hypothesis to ensure absolutely no assumption is left unchecked.\n\n')
class DeepseekV4Template(DeepseekV3_1Template):
def init_env_args(self):
super().init_env_args()
# reasoning_effort: "max", "high", or None
self.reasoning_effort = get_env_args('reasoning_effort', str, None)
if self.reasoning_effort is None:
self.reasoning_effort = 'high' if self.enable_thinking else None
self.enable_thinking = self.reasoning_effort in ('max', 'high')
self.chat_template_kwargs['reasoning_effort'] = self.reasoning_effort
def _get_enable_thinking(self, inputs=None):
reasoning_effort = None if inputs is None else inputs.chat_template_kwargs.get('reasoning_effort')
if reasoning_effort is not None:
return reasoning_effort in ('max', 'high')
return super()._get_enable_thinking(inputs)
def _get_system(self, inputs):
system = super()._get_system(inputs)
reasoning_effort = inputs.chat_template_kwargs.get('reasoning_effort')
if reasoning_effort is None:
reasoning_effort = self.reasoning_effort
if reasoning_effort == 'max' and self._get_enable_thinking(inputs):
system = REASONING_EFFORT_MAX + (system or '')
return system
register_template(
DeepseekV2_5TemplateMeta(
LLMTemplateType.deepseek_v4,
agent_template='deepseek_v4',
is_thinking=True,
template_cls=DeepseekV4Template,
thinking_prefix='<think>',
non_thinking_prefix='</think>',
history_thinking_prefix='</think>'))
class DeepseekVL2Template(DeepseekVLTemplate):
image_placeholder = ['<image>\n']
placeholder_tokens = ['<image>']
def _encode(self, inputs: StdTemplateInputs) -> Dict[str, Any]:
from deepseek_vl2.models.processing_deepseek_vl_v2 import VLChatProcessorOutput
encoded = Template._encode(self, inputs)
images = inputs.images
processor = self.processor
input_ids, labels = encoded['input_ids'], encoded['labels']
images_seq_mask = [False] * len(input_ids)
idx_list = findall(input_ids, processor.image_token_id) # '<image>'
_, images_list, _, images_spatial_crop, num_image_tokens = processor.tokenize_with_images(
'<image>' * len(images), images, cropping=len(images) <= 2)
new_num_tokens = 0
for idx, n_image_tokens in zip(idx_list, num_image_tokens):
image_tokens = [processor.image_token_id] * n_image_tokens
input_ids = input_ids[:idx] + image_tokens + input_ids[idx + 1:]
if labels is not None:
labels = labels[:idx] + [-100] * n_image_tokens + labels[idx + 1:]
images_seq_mask = images_seq_mask[:idx] + [True] * n_image_tokens + images_seq_mask[idx + 1:]
new_num_tokens += n_image_tokens - 1
output = VLChatProcessorOutput(
sft_format=None,
input_ids=torch.tensor(input_ids),
target_ids=torch.tensor(input_ids),
images=torch.stack(images_list) if images_list else torch.zeros((0, 3, 384, 384)),
images_seq_mask=torch.tensor(images_seq_mask),
images_spatial_crop=torch.tensor(images_spatial_crop),
num_image_tokens=num_image_tokens)
output.images = output.images.to(dtype=self.model_info.torch_dtype)
encoded = {'output': output, 'input_ids': input_ids, 'labels': labels}
return encoded
def _post_encode(self, model: nn.Module, inputs: Dict[str, Any]) -> Dict[str, Any]:
inputs['images_seq_mask'] = inputs['images_seq_mask'].to(torch.bool)
inputs['images_spatial_crop'] = inputs['images_spatial_crop'].to(torch.long)
inputs_embeds = model.prepare_inputs_embeds(**inputs)
return {'inputs_embeds': inputs_embeds}
register_template(
DeepseekV2_5TemplateMeta(
MLLMTemplateType.deepseek_vl2,
prompt=['<|User|>: {{QUERY}}\n\n<|Assistant|>:'],
template_cls=DeepseekVL2Template,
))
register_template(
DeepseekVLTemplateMeta(
MLLMTemplateType.deepseek_janus_pro,
prompt=['<|User|>: {{QUERY}}\n\n<|Assistant|>:'],
template_cls=DeepseekJanus))