Adding RT-DETRv2 for object detection (#34773)
* cookiecutter add rtdetrv2 * make modular working * working modelgit add . * working modelgit add . * finalize moduar inheritence * finalize moduar inheritence * Update src/transformers/models/rtdetrv2/modular_rtdetrv2.py Co-authored-by: Cyril Vallez <cyril.vallez@gmail.com> * update modular and add rename * remove output ckpt * define loss_kwargs * fix CamelCase naming * fix naming + files * fix modular and convert file * additional changes * fix modular * fix import error (switch to lazy) * fix autobackbone * make style * add * update testing * fix loss * remove old folder * fix testing for v2 * update docstring * fix docstring * add resnetv2 (with modular bug to fix) * remove resnetv2 backbone * fix changes * small fixes * remove rtdetrv2resnetconfig * add rtdetrv2 name to convert * make style * Update docs/source/en/model_doc/rt_detr_v2.md Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com> * Update src/transformers/models/rt_detr_v2/modular_rt_detr_v2.py Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com> * Update src/transformers/models/rt_detr_v2/modular_rt_detr_v2.py Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com> * fix modular typo after review * add reviewed changes * add final review changes * Update docs/source/en/model_doc/rt_detr_v2.md Co-authored-by: Cyril Vallez <cyril.vallez@gmail.com> * Update src/transformers/models/rt_detr_v2/__init__.py Co-authored-by: Cyril Vallez <cyril.vallez@gmail.com> * Update src/transformers/models/rt_detr_v2/convert_rt_detr_v2_weights_to_hf.py Co-authored-by: Cyril Vallez <cyril.vallez@gmail.com> * add review changes * remove rtdetrv2 resnet * removing this weird project change * change ckpt name from jadechoghari to author * implement review and update testing * update naming and remove wrong ckpt * name * make fix-copies * Fix RT-DETR loss * Add resources, fix name * Fix repo in docs * Fix table name --------- Co-authored-by: jadechoghari <jadechoghari@users.noreply.huggingface.co> Co-authored-by: Cyril Vallez <cyril.vallez@gmail.com> Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com> Co-authored-by: qubvel <qubvel@gmail.com>
This commit is contained in:
@@ -709,6 +709,8 @@
|
||||
title: ResNet
|
||||
- local: model_doc/rt_detr
|
||||
title: RT-DETR
|
||||
- local: model_doc/rt_detr_v2
|
||||
title: RT-DETRv2
|
||||
- local: model_doc/segformer
|
||||
title: SegFormer
|
||||
- local: model_doc/seggpt
|
||||
|
||||
@@ -305,6 +305,7 @@ Flax), PyTorch, and/or TensorFlow.
|
||||
| [RoFormer](model_doc/roformer) | ✅ | ✅ | ✅ |
|
||||
| [RT-DETR](model_doc/rt_detr) | ✅ | ❌ | ❌ |
|
||||
| [RT-DETR-ResNet](model_doc/rt_detr_resnet) | ✅ | ❌ | ❌ |
|
||||
| [RT-DETRv2](model_doc/rt_detr_v2) | ✅ | ❌ | ❌ |
|
||||
| [RWKV](model_doc/rwkv) | ✅ | ❌ | ❌ |
|
||||
| [SAM](model_doc/sam) | ✅ | ✅ | ❌ |
|
||||
| [SeamlessM4T](model_doc/seamless_m4t) | ✅ | ❌ | ❌ |
|
||||
|
||||
97
docs/source/en/model_doc/rt_detr_v2.md
Normal file
97
docs/source/en/model_doc/rt_detr_v2.md
Normal file
@@ -0,0 +1,97 @@
|
||||
<!--Copyright 2025 The HuggingFace Team. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
an "AS IS" BASIS, 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.
|
||||
|
||||
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
||||
rendered properly in your Markdown viewer.
|
||||
|
||||
-->
|
||||
|
||||
# RT-DETRv2
|
||||
|
||||
## Overview
|
||||
|
||||
The RT-DETRv2 model was proposed in [RT-DETRv2: Improved Baseline with Bag-of-Freebies for Real-Time Detection Transformer](https://arxiv.org/abs/2407.17140) by Wenyu Lv, Yian Zhao, Qinyao Chang, Kui Huang, Guanzhong Wang, Yi Liu.
|
||||
|
||||
RT-DETRv2 refines RT-DETR by introducing selective multi-scale feature extraction, a discrete sampling operator for broader deployment compatibility, and improved training strategies like dynamic data augmentation and scale-adaptive hyperparameters. These changes enhance flexibility and practicality while maintaining real-time performance.
|
||||
|
||||
The abstract from the paper is the following:
|
||||
|
||||
*In this report, we present RT-DETRv2, an improved Real-Time DEtection TRansformer (RT-DETR). RT-DETRv2 builds upon the previous state-of-the-art real-time detector, RT-DETR, and opens up a set of bag-of-freebies for flexibility and practicality, as well as optimizing the training strategy to achieve enhanced performance. To improve the flexibility, we suggest setting a distinct number of sampling points for features at different scales in the deformable attention to achieve selective multi-scale feature extraction by the decoder. To enhance practicality, we propose an optional discrete sampling operator to replace the grid_sample operator that is specific to RT-DETR compared to YOLOs. This removes the deployment constraints typically associated with DETRs. For the training strategy, we propose dynamic data augmentation and scale-adaptive hyperparameters customization to improve performance without loss of speed.*
|
||||
|
||||
This model was contributed by [jadechoghari](https://huggingface.co/jadechoghari).
|
||||
The original code can be found [here](https://github.com/lyuwenyu/RT-DETR).
|
||||
|
||||
## Usage tips
|
||||
|
||||
This second version of RT-DETR improves how the decoder finds objects in an image.
|
||||
|
||||
- **better sampling** – adjusts offsets so the model looks at the right areas
|
||||
- **flexible attention** – can use smooth (bilinear) or fixed (discrete) sampling
|
||||
- **optimized processing** – improves how attention weights mix information
|
||||
|
||||
```py
|
||||
>>> import torch
|
||||
>>> import requests
|
||||
|
||||
>>> from PIL import Image
|
||||
>>> from transformers import RTDetrV2ForObjectDetection, RTDetrImageProcessor
|
||||
|
||||
>>> url = 'http://images.cocodataset.org/val2017/000000039769.jpg'
|
||||
>>> image = Image.open(requests.get(url, stream=True).raw)
|
||||
|
||||
>>> image_processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_v2_r18vd")
|
||||
>>> model = RTDetrV2ForObjectDetection.from_pretrained("PekingU/rtdetr_v2_r18vd")
|
||||
|
||||
>>> inputs = image_processor(images=image, return_tensors="pt")
|
||||
|
||||
>>> with torch.no_grad():
|
||||
... outputs = model(**inputs)
|
||||
|
||||
>>> results = image_processor.post_process_object_detection(outputs, target_sizes=torch.tensor([(image.height, image.width)]), threshold=0.5)
|
||||
|
||||
>>> for result in results:
|
||||
... for score, label_id, box in zip(result["scores"], result["labels"], result["boxes"]):
|
||||
... score, label = score.item(), label_id.item()
|
||||
... box = [round(i, 2) for i in box.tolist()]
|
||||
... print(f"{model.config.id2label[label]}: {score:.2f} {box}")
|
||||
cat: 0.97 [341.14, 25.11, 639.98, 372.89]
|
||||
cat: 0.96 [12.78, 56.35, 317.67, 471.34]
|
||||
remote: 0.95 [39.96, 73.12, 175.65, 117.44]
|
||||
sofa: 0.86 [-0.11, 2.97, 639.89, 473.62]
|
||||
sofa: 0.82 [-0.12, 1.78, 639.87, 473.52]
|
||||
remote: 0.79 [333.65, 76.38, 370.69, 187.48]
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
A list of official Hugging Face and community (indicated by 🌎) resources to help you get started with RT-DETRv2.
|
||||
|
||||
<PipelineTag pipeline="object-detection"/>
|
||||
|
||||
- Scripts for finetuning [`RTDetrV2ForObjectDetection`] with [`Trainer`] or [Accelerate](https://huggingface.co/docs/accelerate/index) can be found [here](https://github.com/huggingface/transformers/tree/main/examples/pytorch/object-detection).
|
||||
- See also: [Object detection task guide](../tasks/object_detection).
|
||||
- Notebooks for [inference](https://github.com/qubvel/transformers-notebooks/blob/main/notebooks/RT_DETR_v2_inference.ipynb) and [fine-tuning](https://github.com/qubvel/transformers-notebooks/blob/main/notebooks/RT_DETR_v2_finetune_on_a_custom_dataset.ipynb) RT-DETRv2 on a custom dataset (🌎).
|
||||
|
||||
|
||||
## RTDetrV2Config
|
||||
|
||||
[[autodoc]] RTDetrV2Config
|
||||
|
||||
|
||||
## RTDetrV2Model
|
||||
|
||||
[[autodoc]] RTDetrV2Model
|
||||
- forward
|
||||
|
||||
## RTDetrV2ForObjectDetection
|
||||
|
||||
[[autodoc]] RTDetrV2ForObjectDetection
|
||||
- forward
|
||||
@@ -748,6 +748,7 @@ _import_structure = {
|
||||
"RoFormerTokenizer",
|
||||
],
|
||||
"models.rt_detr": ["RTDetrConfig", "RTDetrResNetConfig"],
|
||||
"models.rt_detr_v2": ["RTDetrV2Config"],
|
||||
"models.rwkv": ["RwkvConfig"],
|
||||
"models.sam": [
|
||||
"SamConfig",
|
||||
@@ -3454,6 +3455,9 @@ else:
|
||||
"RTDetrResNetPreTrainedModel",
|
||||
]
|
||||
)
|
||||
_import_structure["models.rt_detr_v2"].extend(
|
||||
["RTDetrV2ForObjectDetection", "RTDetrV2Model", "RTDetrV2PreTrainedModel"]
|
||||
)
|
||||
_import_structure["models.rwkv"].extend(
|
||||
[
|
||||
"RwkvForCausalLM",
|
||||
@@ -5875,6 +5879,7 @@ if TYPE_CHECKING:
|
||||
RTDetrConfig,
|
||||
RTDetrResNetConfig,
|
||||
)
|
||||
from .models.rt_detr_v2 import RTDetrV2Config
|
||||
from .models.rwkv import RwkvConfig
|
||||
from .models.sam import (
|
||||
SamConfig,
|
||||
@@ -8171,6 +8176,7 @@ if TYPE_CHECKING:
|
||||
RTDetrResNetBackbone,
|
||||
RTDetrResNetPreTrainedModel,
|
||||
)
|
||||
from .models.rt_detr_v2 import RTDetrV2ForObjectDetection, RTDetrV2Model, RTDetrV2PreTrainedModel
|
||||
from .models.rwkv import (
|
||||
RwkvForCausalLM,
|
||||
RwkvModel,
|
||||
|
||||
@@ -18,7 +18,6 @@ import torch.nn.functional as F
|
||||
|
||||
from ..utils import is_scipy_available, is_vision_available, requires_backends
|
||||
from .loss_for_object_detection import (
|
||||
_set_aux_loss,
|
||||
box_iou,
|
||||
dice_loss,
|
||||
generalized_box_iou,
|
||||
@@ -35,6 +34,15 @@ if is_vision_available():
|
||||
from transformers.image_transforms import center_to_corners_format
|
||||
|
||||
|
||||
# different for RT-DETR: not slicing the last element like in DETR one
|
||||
@torch.jit.unused
|
||||
def _set_aux_loss(outputs_class, outputs_coord):
|
||||
# this is a workaround to make torchscript happy, as torchscript
|
||||
# doesn't support dictionary with non-homogeneous values, such
|
||||
# as a dict having both a Tensor and a list.
|
||||
return [{"logits": a, "pred_boxes": b} for a, b in zip(outputs_class, outputs_coord)]
|
||||
|
||||
|
||||
class RTDetrHungarianMatcher(nn.Module):
|
||||
"""This class computes an assignment between the targets and the predictions of the network
|
||||
|
||||
|
||||
@@ -132,4 +132,5 @@ LOSS_MAPPING = {
|
||||
"GroundingDinoForObjectDetection": DeformableDetrForObjectDetectionLoss,
|
||||
"ConditionalDetrForSegmentation": DeformableDetrForSegmentationLoss,
|
||||
"RTDetrForObjectDetection": RTDetrForObjectDetectionLoss,
|
||||
"RTDetrV2ForObjectDetection": RTDetrForObjectDetectionLoss,
|
||||
}
|
||||
|
||||
@@ -233,6 +233,7 @@ from . import (
|
||||
roc_bert,
|
||||
roformer,
|
||||
rt_detr,
|
||||
rt_detr_v2,
|
||||
rwkv,
|
||||
sam,
|
||||
seamless_m4t,
|
||||
|
||||
@@ -259,6 +259,7 @@ CONFIG_MAPPING_NAMES = OrderedDict(
|
||||
("roformer", "RoFormerConfig"),
|
||||
("rt_detr", "RTDetrConfig"),
|
||||
("rt_detr_resnet", "RTDetrResNetConfig"),
|
||||
("rt_detr_v2", "RTDetrV2Config"),
|
||||
("rwkv", "RwkvConfig"),
|
||||
("sam", "SamConfig"),
|
||||
("seamless_m4t", "SeamlessM4TConfig"),
|
||||
@@ -600,6 +601,7 @@ MODEL_NAMES_MAPPING = OrderedDict(
|
||||
("roformer", "RoFormer"),
|
||||
("rt_detr", "RT-DETR"),
|
||||
("rt_detr_resnet", "RT-DETR-ResNet"),
|
||||
("rt_detr_v2", "RT-DETRv2"),
|
||||
("rwkv", "RWKV"),
|
||||
("sam", "SAM"),
|
||||
("seamless_m4t", "SeamlessM4T"),
|
||||
|
||||
@@ -238,6 +238,7 @@ MODEL_MAPPING_NAMES = OrderedDict(
|
||||
("roc_bert", "RoCBertModel"),
|
||||
("roformer", "RoFormerModel"),
|
||||
("rt_detr", "RTDetrModel"),
|
||||
("rt_detr_v2", "RTDetrV2Model"),
|
||||
("rwkv", "RwkvModel"),
|
||||
("sam", "SamModel"),
|
||||
("seamless_m4t", "SeamlessM4TModel"),
|
||||
@@ -897,6 +898,7 @@ MODEL_FOR_OBJECT_DETECTION_MAPPING_NAMES = OrderedDict(
|
||||
("deta", "DetaForObjectDetection"),
|
||||
("detr", "DetrForObjectDetection"),
|
||||
("rt_detr", "RTDetrForObjectDetection"),
|
||||
("rt_detr_v2", "RTDetrV2ForObjectDetection"),
|
||||
("table-transformer", "TableTransformerForObjectDetection"),
|
||||
("yolos", "YolosForObjectDetection"),
|
||||
]
|
||||
|
||||
@@ -2115,7 +2115,6 @@ class RTDetrForObjectDetection(RTDetrPreTrainedModel):
|
||||
|
||||
loss, loss_dict, auxiliary_outputs, enc_topk_logits, enc_topk_bboxes = None, None, None, None, None
|
||||
if labels is not None:
|
||||
if self.training and denoising_meta_values is not None:
|
||||
enc_topk_logits = outputs.enc_topk_logits if return_dict else outputs[-5]
|
||||
enc_topk_bboxes = outputs.enc_topk_bboxes if return_dict else outputs[-4]
|
||||
loss, loss_dict, auxiliary_outputs = self.loss_function(
|
||||
|
||||
29
src/transformers/models/rt_detr_v2/__init__.py
Normal file
29
src/transformers/models/rt_detr_v2/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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.
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ...utils import _LazyModule
|
||||
from ...utils.import_utils import define_import_structure
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .configuration_rt_detr_v2 import *
|
||||
from .modeling_rt_detr_v2 import *
|
||||
else:
|
||||
import sys
|
||||
|
||||
_file = globals()["__file__"]
|
||||
sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
|
||||
383
src/transformers/models/rt_detr_v2/configuration_rt_detr_v2.py
Normal file
383
src/transformers/models/rt_detr_v2/configuration_rt_detr_v2.py
Normal file
@@ -0,0 +1,383 @@
|
||||
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
||||
# This file was automatically generated from src/transformers/models/rt_detr_v2/modular_rt_detr_v2.py.
|
||||
# Do NOT edit this file manually as any edits will be overwritten by the generation of
|
||||
# the file from the modular. If any change should be done, please apply the change to the
|
||||
# modular_rt_detr_v2.py file directly. One of our CI enforces this.
|
||||
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
||||
# coding=utf-8
|
||||
# Copyright 2025 Baidu Inc and The HuggingFace Inc. team.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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.
|
||||
|
||||
from ...configuration_utils import PretrainedConfig
|
||||
from ...utils import logging
|
||||
from ...utils.backbone_utils import verify_backbone_config_arguments
|
||||
from ..auto import CONFIG_MAPPING
|
||||
|
||||
|
||||
logger = logging.get_logger(__name__)
|
||||
|
||||
|
||||
class RTDetrV2Config(PretrainedConfig):
|
||||
r"""
|
||||
This is the configuration class to store the configuration of a [`RTDetrV2Model`]. It is used to instantiate a
|
||||
RT-DETR model according to the specified arguments, defining the model architecture. Instantiating a configuration
|
||||
with the defaults will yield a similar configuration to that of the RT-DETR architecture.
|
||||
|
||||
e.g. [PekingU/rtdetr_r18vd](https://huggingface.co/PekingU/rtdetr_r18vd)
|
||||
|
||||
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
|
||||
documentation from [`PretrainedConfig`] for more information.
|
||||
|
||||
Args:
|
||||
initializer_range (`float`, *optional*, defaults to 0.01):
|
||||
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
|
||||
initializer_bias_prior_prob (`float`, *optional*):
|
||||
The prior probability used by the bias initializer to initialize biases for `enc_score_head` and `class_embed`.
|
||||
If `None`, `prior_prob` computed as `prior_prob = 1 / (num_labels + 1)` while initializing model weights.
|
||||
layer_norm_eps (`float`, *optional*, defaults to 1e-05):
|
||||
The epsilon used by the layer normalization layers.
|
||||
batch_norm_eps (`float`, *optional*, defaults to 1e-05):
|
||||
The epsilon used by the batch normalization layers.
|
||||
backbone_config (`Dict`, *optional*, defaults to `RTDetrV2ResNetConfig()`):
|
||||
The configuration of the backbone model.
|
||||
backbone (`str`, *optional*):
|
||||
Name of backbone to use when `backbone_config` is `None`. If `use_pretrained_backbone` is `True`, this
|
||||
will load the corresponding pretrained weights from the timm or transformers library. If `use_pretrained_backbone`
|
||||
is `False`, this loads the backbone's config and uses that to initialize the backbone with random weights.
|
||||
use_pretrained_backbone (`bool`, *optional*, defaults to `False`):
|
||||
Whether to use pretrained weights for the backbone.
|
||||
use_timm_backbone (`bool`, *optional*, defaults to `False`):
|
||||
Whether to load `backbone` from the timm library. If `False`, the backbone is loaded from the transformers
|
||||
library.
|
||||
freeze_backbone_batch_norms (`bool`, *optional*, defaults to `True`):
|
||||
Whether to freeze the batch normalization layers in the backbone.
|
||||
backbone_kwargs (`dict`, *optional*):
|
||||
Keyword arguments to be passed to AutoBackbone when loading from a checkpoint
|
||||
e.g. `{'out_indices': (0, 1, 2, 3)}`. Cannot be specified if `backbone_config` is set.
|
||||
encoder_hidden_dim (`int`, *optional*, defaults to 256):
|
||||
Dimension of the layers in hybrid encoder.
|
||||
encoder_in_channels (`list`, *optional*, defaults to `[512, 1024, 2048]`):
|
||||
Multi level features input for encoder.
|
||||
feat_strides (`List[int]`, *optional*, defaults to `[8, 16, 32]`):
|
||||
Strides used in each feature map.
|
||||
encoder_layers (`int`, *optional*, defaults to 1):
|
||||
Total of layers to be used by the encoder.
|
||||
encoder_ffn_dim (`int`, *optional*, defaults to 1024):
|
||||
Dimension of the "intermediate" (often named feed-forward) layer in decoder.
|
||||
encoder_attention_heads (`int`, *optional*, defaults to 8):
|
||||
Number of attention heads for each attention layer in the Transformer encoder.
|
||||
dropout (`float`, *optional*, defaults to 0.0):
|
||||
The ratio for all dropout layers.
|
||||
activation_dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout ratio for activations inside the fully connected layer.
|
||||
encode_proj_layers (`List[int]`, *optional*, defaults to `[2]`):
|
||||
Indexes of the projected layers to be used in the encoder.
|
||||
positional_encoding_temperature (`int`, *optional*, defaults to 10000):
|
||||
The temperature parameter used to create the positional encodings.
|
||||
encoder_activation_function (`str`, *optional*, defaults to `"gelu"`):
|
||||
The non-linear activation function (function or string) in the encoder and pooler. If string, `"gelu"`,
|
||||
`"relu"`, `"silu"` and `"gelu_new"` are supported.
|
||||
activation_function (`str`, *optional*, defaults to `"silu"`):
|
||||
The non-linear activation function (function or string) in the general layer. If string, `"gelu"`,
|
||||
`"relu"`, `"silu"` and `"gelu_new"` are supported.
|
||||
eval_size (`Tuple[int, int]`, *optional*):
|
||||
Height and width used to compute the effective height and width of the position embeddings after taking
|
||||
into account the stride.
|
||||
normalize_before (`bool`, *optional*, defaults to `False`):
|
||||
Determine whether to apply layer normalization in the transformer encoder layer before self-attention and
|
||||
feed-forward modules.
|
||||
hidden_expansion (`float`, *optional*, defaults to 1.0):
|
||||
Expansion ratio to enlarge the dimension size of RepVGGBlock and CSPRepLayer.
|
||||
d_model (`int`, *optional*, defaults to 256):
|
||||
Dimension of the layers exclude hybrid encoder.
|
||||
num_queries (`int`, *optional*, defaults to 300):
|
||||
Number of object queries.
|
||||
decoder_in_channels (`list`, *optional*, defaults to `[256, 256, 256]`):
|
||||
Multi level features dimension for decoder
|
||||
decoder_ffn_dim (`int`, *optional*, defaults to 1024):
|
||||
Dimension of the "intermediate" (often named feed-forward) layer in decoder.
|
||||
num_feature_levels (`int`, *optional*, defaults to 3):
|
||||
The number of input feature levels.
|
||||
decoder_n_points (`int`, *optional*, defaults to 4):
|
||||
The number of sampled keys in each feature level for each attention head in the decoder.
|
||||
decoder_layers (`int`, *optional*, defaults to 6):
|
||||
Number of decoder layers.
|
||||
decoder_attention_heads (`int`, *optional*, defaults to 8):
|
||||
Number of attention heads for each attention layer in the Transformer decoder.
|
||||
decoder_activation_function (`str`, *optional*, defaults to `"relu"`):
|
||||
The non-linear activation function (function or string) in the decoder. If string, `"gelu"`,
|
||||
`"relu"`, `"silu"` and `"gelu_new"` are supported.
|
||||
attention_dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout ratio for the attention probabilities.
|
||||
num_denoising (`int`, *optional*, defaults to 100):
|
||||
The total number of denoising tasks or queries to be used for contrastive denoising.
|
||||
label_noise_ratio (`float`, *optional*, defaults to 0.5):
|
||||
The fraction of denoising labels to which random noise should be added.
|
||||
box_noise_scale (`float`, *optional*, defaults to 1.0):
|
||||
Scale or magnitude of noise to be added to the bounding boxes.
|
||||
learn_initial_query (`bool`, *optional*, defaults to `False`):
|
||||
Indicates whether the initial query embeddings for the decoder should be learned during training
|
||||
anchor_image_size (`Tuple[int, int]`, *optional*):
|
||||
Height and width of the input image used during evaluation to generate the bounding box anchors. If None, automatic generate anchor is applied.
|
||||
disable_custom_kernels (`bool`, *optional*, defaults to `True`):
|
||||
Whether to disable custom kernels.
|
||||
with_box_refine (`bool`, *optional*, defaults to `True`):
|
||||
Whether to apply iterative bounding box refinement, where each decoder layer refines the bounding boxes
|
||||
based on the predictions from the previous layer.
|
||||
is_encoder_decoder (`bool`, *optional*, defaults to `True`):
|
||||
Whether the architecture has an encoder decoder structure.
|
||||
matcher_alpha (`float`, *optional*, defaults to 0.25):
|
||||
Parameter alpha used by the Hungarian Matcher.
|
||||
matcher_gamma (`float`, *optional*, defaults to 2.0):
|
||||
Parameter gamma used by the Hungarian Matcher.
|
||||
matcher_class_cost (`float`, *optional*, defaults to 2.0):
|
||||
The relative weight of the class loss used by the Hungarian Matcher.
|
||||
matcher_bbox_cost (`float`, *optional*, defaults to 5.0):
|
||||
The relative weight of the bounding box loss used by the Hungarian Matcher.
|
||||
matcher_giou_cost (`float`, *optional*, defaults to 2.0):
|
||||
The relative weight of the giou loss of used by the Hungarian Matcher.
|
||||
use_focal_loss (`bool`, *optional*, defaults to `True`):
|
||||
Parameter informing if focal loss should be used.
|
||||
auxiliary_loss (`bool`, *optional*, defaults to `True`):
|
||||
Whether auxiliary decoding losses (loss at each decoder layer) are to be used.
|
||||
focal_loss_alpha (`float`, *optional*, defaults to 0.75):
|
||||
Parameter alpha used to compute the focal loss.
|
||||
focal_loss_gamma (`float`, *optional*, defaults to 2.0):
|
||||
Parameter gamma used to compute the focal loss.
|
||||
weight_loss_vfl (`float`, *optional*, defaults to 1.0):
|
||||
Relative weight of the varifocal loss in the object detection loss.
|
||||
weight_loss_bbox (`float`, *optional*, defaults to 5.0):
|
||||
Relative weight of the L1 bounding box loss in the object detection loss.
|
||||
weight_loss_giou (`float`, *optional*, defaults to 2.0):
|
||||
Relative weight of the generalized IoU loss in the object detection loss.
|
||||
eos_coefficient (`float`, *optional*, defaults to 0.0001):
|
||||
Relative classification weight of the 'no-object' class in the object detection loss.
|
||||
decoder_n_levels (`int`, *optional*, defaults to 3):
|
||||
The number of feature levels used by the decoder.
|
||||
decoder_offset_scale (`float`, *optional*, defaults to 0.5):
|
||||
Scaling factor applied to the attention offsets in the decoder.
|
||||
decoder_method (`str`, *optional*, defaults to `"default"`):
|
||||
The method to use for the decoder: `"default"` or `"discrete"`.
|
||||
|
||||
Examples:
|
||||
|
||||
```python
|
||||
>>> from transformers import RTDetrV2Config, RTDetrV2Model
|
||||
|
||||
>>> # Initializing a RT-DETR configuration
|
||||
>>> configuration = RTDetrV2Config()
|
||||
|
||||
>>> # Initializing a model (with random weights) from the configuration
|
||||
>>> model = RTDetrV2Model(configuration)
|
||||
|
||||
>>> # Accessing the model configuration
|
||||
>>> configuration = model.config
|
||||
```
|
||||
"""
|
||||
|
||||
model_type = "rt_detr_v2"
|
||||
layer_types = ["basic", "bottleneck"]
|
||||
attribute_map = {
|
||||
"hidden_size": "d_model",
|
||||
"num_attention_heads": "encoder_attention_heads",
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
initializer_range=0.01,
|
||||
initializer_bias_prior_prob=None,
|
||||
layer_norm_eps=1e-5,
|
||||
batch_norm_eps=1e-5,
|
||||
# backbone
|
||||
backbone_config=None,
|
||||
backbone=None,
|
||||
use_pretrained_backbone=False,
|
||||
use_timm_backbone=False,
|
||||
freeze_backbone_batch_norms=True,
|
||||
backbone_kwargs=None,
|
||||
# encoder HybridEncoder
|
||||
encoder_hidden_dim=256,
|
||||
encoder_in_channels=[512, 1024, 2048],
|
||||
feat_strides=[8, 16, 32],
|
||||
encoder_layers=1,
|
||||
encoder_ffn_dim=1024,
|
||||
encoder_attention_heads=8,
|
||||
dropout=0.0,
|
||||
activation_dropout=0.0,
|
||||
encode_proj_layers=[2],
|
||||
positional_encoding_temperature=10000,
|
||||
encoder_activation_function="gelu",
|
||||
activation_function="silu",
|
||||
eval_size=None,
|
||||
normalize_before=False,
|
||||
hidden_expansion=1.0,
|
||||
# decoder RTDetrV2Transformer
|
||||
d_model=256,
|
||||
num_queries=300,
|
||||
decoder_in_channels=[256, 256, 256],
|
||||
decoder_ffn_dim=1024,
|
||||
num_feature_levels=3,
|
||||
decoder_n_points=4,
|
||||
decoder_layers=6,
|
||||
decoder_attention_heads=8,
|
||||
decoder_activation_function="relu",
|
||||
attention_dropout=0.0,
|
||||
num_denoising=100,
|
||||
label_noise_ratio=0.5,
|
||||
box_noise_scale=1.0,
|
||||
learn_initial_query=False,
|
||||
anchor_image_size=None,
|
||||
disable_custom_kernels=True,
|
||||
with_box_refine=True,
|
||||
is_encoder_decoder=True,
|
||||
# Loss
|
||||
matcher_alpha=0.25,
|
||||
matcher_gamma=2.0,
|
||||
matcher_class_cost=2.0,
|
||||
matcher_bbox_cost=5.0,
|
||||
matcher_giou_cost=2.0,
|
||||
use_focal_loss=True,
|
||||
auxiliary_loss=True,
|
||||
focal_loss_alpha=0.75,
|
||||
focal_loss_gamma=2.0,
|
||||
weight_loss_vfl=1.0,
|
||||
weight_loss_bbox=5.0,
|
||||
weight_loss_giou=2.0,
|
||||
eos_coefficient=1e-4,
|
||||
decoder_n_levels=3, # default value
|
||||
decoder_offset_scale=0.5, # default value
|
||||
decoder_method="default",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(is_encoder_decoder=is_encoder_decoder, **kwargs)
|
||||
self.initializer_range = initializer_range
|
||||
self.initializer_bias_prior_prob = initializer_bias_prior_prob
|
||||
self.layer_norm_eps = layer_norm_eps
|
||||
self.batch_norm_eps = batch_norm_eps
|
||||
# backbone
|
||||
if backbone_config is None and backbone is None:
|
||||
logger.info(
|
||||
"`backbone_config` and `backbone` are `None`. Initializing the config with the default `RTDetrV2-ResNet` backbone."
|
||||
)
|
||||
backbone_model_type = "rt_detr_resnet"
|
||||
config_class = CONFIG_MAPPING[backbone_model_type]
|
||||
# this will map it to RTDetrResNetConfig
|
||||
# note: we can instead create RTDetrV2ResNetConfig but it will be exactly the same as V1
|
||||
# and we would need to create RTDetrV2ResNetModel
|
||||
backbone_config = config_class(
|
||||
num_channels=3,
|
||||
embedding_size=64,
|
||||
hidden_sizes=[256, 512, 1024, 2048],
|
||||
depths=[3, 4, 6, 3],
|
||||
layer_type="bottleneck",
|
||||
hidden_act="relu",
|
||||
downsample_in_first_stage=False,
|
||||
downsample_in_bottleneck=False,
|
||||
out_features=None,
|
||||
out_indices=[2, 3, 4],
|
||||
)
|
||||
elif isinstance(backbone_config, dict):
|
||||
backbone_model_type = backbone_config.pop("model_type")
|
||||
config_class = CONFIG_MAPPING[backbone_model_type]
|
||||
backbone_config = config_class.from_dict(backbone_config)
|
||||
|
||||
verify_backbone_config_arguments(
|
||||
use_timm_backbone=use_timm_backbone,
|
||||
use_pretrained_backbone=use_pretrained_backbone,
|
||||
backbone=backbone,
|
||||
backbone_config=backbone_config,
|
||||
backbone_kwargs=backbone_kwargs,
|
||||
)
|
||||
|
||||
self.backbone_config = backbone_config
|
||||
self.backbone = backbone
|
||||
self.use_pretrained_backbone = use_pretrained_backbone
|
||||
self.use_timm_backbone = use_timm_backbone
|
||||
self.freeze_backbone_batch_norms = freeze_backbone_batch_norms
|
||||
self.backbone_kwargs = backbone_kwargs
|
||||
# encoder
|
||||
self.encoder_hidden_dim = encoder_hidden_dim
|
||||
self.encoder_in_channels = encoder_in_channels
|
||||
self.feat_strides = feat_strides
|
||||
self.encoder_ffn_dim = encoder_ffn_dim
|
||||
self.dropout = dropout
|
||||
self.activation_dropout = activation_dropout
|
||||
self.encode_proj_layers = encode_proj_layers
|
||||
self.encoder_layers = encoder_layers
|
||||
self.positional_encoding_temperature = positional_encoding_temperature
|
||||
self.eval_size = eval_size
|
||||
self.normalize_before = normalize_before
|
||||
self.encoder_activation_function = encoder_activation_function
|
||||
self.activation_function = activation_function
|
||||
self.hidden_expansion = hidden_expansion
|
||||
self.num_queries = num_queries
|
||||
self.decoder_ffn_dim = decoder_ffn_dim
|
||||
self.decoder_in_channels = decoder_in_channels
|
||||
self.num_feature_levels = num_feature_levels
|
||||
self.decoder_n_points = decoder_n_points
|
||||
self.decoder_layers = decoder_layers
|
||||
self.decoder_attention_heads = decoder_attention_heads
|
||||
self.decoder_activation_function = decoder_activation_function
|
||||
self.attention_dropout = attention_dropout
|
||||
self.num_denoising = num_denoising
|
||||
self.label_noise_ratio = label_noise_ratio
|
||||
self.box_noise_scale = box_noise_scale
|
||||
self.learn_initial_query = learn_initial_query
|
||||
self.anchor_image_size = anchor_image_size
|
||||
self.auxiliary_loss = auxiliary_loss
|
||||
self.disable_custom_kernels = disable_custom_kernels
|
||||
self.with_box_refine = with_box_refine
|
||||
# Loss
|
||||
self.matcher_alpha = matcher_alpha
|
||||
self.matcher_gamma = matcher_gamma
|
||||
self.matcher_class_cost = matcher_class_cost
|
||||
self.matcher_bbox_cost = matcher_bbox_cost
|
||||
self.matcher_giou_cost = matcher_giou_cost
|
||||
self.use_focal_loss = use_focal_loss
|
||||
self.focal_loss_alpha = focal_loss_alpha
|
||||
self.focal_loss_gamma = focal_loss_gamma
|
||||
self.weight_loss_vfl = weight_loss_vfl
|
||||
self.weight_loss_bbox = weight_loss_bbox
|
||||
self.weight_loss_giou = weight_loss_giou
|
||||
self.eos_coefficient = eos_coefficient
|
||||
|
||||
if not hasattr(self, "d_model"):
|
||||
self.d_model = d_model
|
||||
|
||||
if not hasattr(self, "encoder_attention_heads"):
|
||||
self.encoder_attention_heads = encoder_attention_heads
|
||||
# add the new attributes with the given values or defaults
|
||||
self.decoder_n_levels = decoder_n_levels
|
||||
self.decoder_offset_scale = decoder_offset_scale
|
||||
self.decoder_method = decoder_method
|
||||
|
||||
@classmethod
|
||||
def from_backbone_configs(cls, backbone_config: PretrainedConfig, **kwargs):
|
||||
"""Instantiate a [`RTDetrV2Config`] (or a derived class) from a pre-trained backbone model configuration and DETR model
|
||||
configuration.
|
||||
|
||||
Args:
|
||||
backbone_config ([`PretrainedConfig`]):
|
||||
The backbone configuration.
|
||||
|
||||
Returns:
|
||||
[`RTDetrV2Config`]: An instance of a configuration object
|
||||
"""
|
||||
return cls(
|
||||
backbone_config=backbone_config,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["RTDetrV2Config"]
|
||||
@@ -0,0 +1,363 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2025 The HuggingFace Inc. team.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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.
|
||||
"""Convert RT Detr V2 checkpoints with Timm backbone"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
import torch
|
||||
from huggingface_hub import hf_hub_download
|
||||
from PIL import Image
|
||||
from torchvision import transforms
|
||||
|
||||
from transformers import RTDetrImageProcessor, RTDetrV2Config, RTDetrV2ForObjectDetection
|
||||
from transformers.utils import logging
|
||||
|
||||
|
||||
logging.set_verbosity_info()
|
||||
logger = logging.get_logger(__name__)
|
||||
|
||||
|
||||
def get_rt_detr_v2_config(model_name: str) -> RTDetrV2Config:
|
||||
config = RTDetrV2Config()
|
||||
|
||||
config.num_labels = 80
|
||||
repo_id = "huggingface/label-files"
|
||||
filename = "coco-detection-mmdet-id2label.json"
|
||||
id2label = json.load(open(hf_hub_download(repo_id, filename, repo_type="dataset"), "r"))
|
||||
id2label = {int(k): v for k, v in id2label.items()}
|
||||
config.id2label = id2label
|
||||
config.label2id = {v: k for k, v in id2label.items()}
|
||||
|
||||
if model_name == "rtdetr_v2_r18vd":
|
||||
config.backbone_config.hidden_sizes = [64, 128, 256, 512]
|
||||
config.backbone_config.depths = [2, 2, 2, 2]
|
||||
config.backbone_config.layer_type = "basic"
|
||||
config.encoder_in_channels = [128, 256, 512]
|
||||
config.hidden_expansion = 0.5
|
||||
config.decoder_layers = 3
|
||||
elif model_name == "rtdetr_v2_r34vd":
|
||||
config.backbone_config.hidden_sizes = [64, 128, 256, 512]
|
||||
config.backbone_config.depths = [3, 4, 6, 3]
|
||||
config.backbone_config.layer_type = "basic"
|
||||
config.encoder_in_channels = [128, 256, 512]
|
||||
config.hidden_expansion = 0.5
|
||||
config.decoder_layers = 4
|
||||
# TODO: check this not working
|
||||
elif model_name == "rtdetr_v2_r50vd_m":
|
||||
config.hidden_expansion = 0.5
|
||||
elif model_name == "rtdetr_v2_r50vd":
|
||||
pass
|
||||
elif model_name == "rtdetr_v2_r101vd":
|
||||
config.backbone_config.depths = [3, 4, 23, 3]
|
||||
config.encoder_ffn_dim = 2048
|
||||
config.encoder_hidden_dim = 384
|
||||
config.decoder_in_channels = [384, 384, 384]
|
||||
|
||||
return config
|
||||
|
||||
|
||||
# Define a mapping from original keys to converted keys using regex
|
||||
ORIGINAL_TO_CONVERTED_KEY_MAPPING = {
|
||||
r"backbone.conv1.conv1_1.conv.weight": r"model.backbone.model.embedder.embedder.0.convolution.weight",
|
||||
r"backbone.conv1.conv1_1.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.embedder.embedder.0.normalization.\1",
|
||||
r"backbone.conv1.conv1_2.conv.weight": r"model.backbone.model.embedder.embedder.1.convolution.weight",
|
||||
r"backbone.conv1.conv1_2.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.embedder.embedder.1.normalization.\1",
|
||||
r"backbone.conv1.conv1_3.conv.weight": r"model.backbone.model.embedder.embedder.2.convolution.weight",
|
||||
r"backbone.conv1.conv1_3.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.embedder.embedder.2.normalization.\1",
|
||||
r"backbone.res_layers.(\d+).blocks.(\d+).branch2a.conv.weight": r"model.backbone.model.encoder.stages.\1.layers.\2.layer.0.convolution.weight",
|
||||
r"backbone.res_layers.(\d+).blocks.(\d+).branch2a.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.encoder.stages.\1.layers.\2.layer.0.normalization.\3",
|
||||
r"backbone.res_layers.(\d+).blocks.(\d+).branch2b.conv.weight": r"model.backbone.model.encoder.stages.\1.layers.\2.layer.1.convolution.weight",
|
||||
r"backbone.res_layers.(\d+).blocks.(\d+).branch2b.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.encoder.stages.\1.layers.\2.layer.1.normalization.\3",
|
||||
r"backbone.res_layers.(\d+).blocks.(\d+).branch2c.conv.weight": r"model.backbone.model.encoder.stages.\1.layers.\2.layer.2.convolution.weight",
|
||||
r"backbone.res_layers.(\d+).blocks.(\d+).branch2c.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.encoder.stages.\1.layers.\2.layer.2.normalization.\3",
|
||||
r"encoder.encoder.(\d+).layers.0.self_attn.out_proj.weight": r"model.encoder.encoder.\1.layers.0.self_attn.out_proj.weight",
|
||||
r"encoder.encoder.(\d+).layers.0.self_attn.out_proj.bias": r"model.encoder.encoder.\1.layers.0.self_attn.out_proj.bias",
|
||||
r"encoder.encoder.(\d+).layers.0.linear1.weight": r"model.encoder.encoder.\1.layers.0.fc1.weight",
|
||||
r"encoder.encoder.(\d+).layers.0.linear1.bias": r"model.encoder.encoder.\1.layers.0.fc1.bias",
|
||||
r"encoder.encoder.(\d+).layers.0.linear2.weight": r"model.encoder.encoder.\1.layers.0.fc2.weight",
|
||||
r"encoder.encoder.(\d+).layers.0.linear2.bias": r"model.encoder.encoder.\1.layers.0.fc2.bias",
|
||||
r"encoder.encoder.(\d+).layers.0.norm1.weight": r"model.encoder.encoder.\1.layers.0.self_attn_layer_norm.weight",
|
||||
r"encoder.encoder.(\d+).layers.0.norm1.bias": r"model.encoder.encoder.\1.layers.0.self_attn_layer_norm.bias",
|
||||
r"encoder.encoder.(\d+).layers.0.norm2.weight": r"model.encoder.encoder.\1.layers.0.final_layer_norm.weight",
|
||||
r"encoder.encoder.(\d+).layers.0.norm2.bias": r"model.encoder.encoder.\1.layers.0.final_layer_norm.bias",
|
||||
r"encoder.input_proj.(\d+).conv.weight": r"model.encoder_input_proj.\1.0.weight",
|
||||
r"encoder.input_proj.(\d+).norm.(.*)": r"model.encoder_input_proj.\1.1.\2",
|
||||
r"encoder.fpn_blocks.(\d+).conv(\d+).conv.weight": r"model.encoder.fpn_blocks.\1.conv\2.conv.weight",
|
||||
# r"encoder.fpn_blocks.(\d+).conv(\d+).norm.(.*)": r"model.encoder.fpn_blocks.\1.conv\2.norm.\3",
|
||||
r"encoder.fpn_blocks.(\d+).conv(\d+).norm.(weight|bias|running_mean|running_var)": r"model.encoder.fpn_blocks.\1.conv\2.norm.\3",
|
||||
r"encoder.lateral_convs.(\d+).conv.weight": r"model.encoder.lateral_convs.\1.conv.weight",
|
||||
r"encoder.lateral_convs.(\d+).norm.(.*)": r"model.encoder.lateral_convs.\1.norm.\2",
|
||||
r"encoder.fpn_blocks.(\d+).bottlenecks.(\d+).conv(\d+).conv.weight": r"model.encoder.fpn_blocks.\1.bottlenecks.\2.conv\3.conv.weight",
|
||||
r"encoder.fpn_blocks.(\d+).bottlenecks.(\d+).conv(\d+).norm.(\w+)": r"model.encoder.fpn_blocks.\1.bottlenecks.\2.conv\3.norm.\4",
|
||||
r"encoder.pan_blocks.(\d+).conv(\d+).conv.weight": r"model.encoder.pan_blocks.\1.conv\2.conv.weight",
|
||||
r"encoder.pan_blocks.(\d+).conv(\d+).norm.(weight|bias|running_mean|running_var)": r"model.encoder.pan_blocks.\1.conv\2.norm.\3",
|
||||
r"encoder.pan_blocks.(\d+).bottlenecks.(\d+).conv(\d+).conv.weight": r"model.encoder.pan_blocks.\1.bottlenecks.\2.conv\3.conv.weight",
|
||||
r"encoder.pan_blocks.(\d+).bottlenecks.(\d+).conv(\d+).norm.(weight|bias|running_mean|running_var)": r"model.encoder.pan_blocks.\1.bottlenecks.\2.conv\3.norm.\4",
|
||||
r"encoder.downsample_convs.(\d+).conv.weight": r"model.encoder.downsample_convs.\1.conv.weight",
|
||||
r"encoder.downsample_convs.(\d+).norm.(weight|bias|running_mean|running_var)": r"model.encoder.downsample_convs.\1.norm.\2",
|
||||
r"decoder.decoder.layers.(\d+).self_attn.out_proj.weight": r"model.decoder.layers.\1.self_attn.out_proj.weight",
|
||||
r"decoder.decoder.layers.(\d+).self_attn.out_proj.bias": r"model.decoder.layers.\1.self_attn.out_proj.bias",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.sampling_offsets.weight": r"model.decoder.layers.\1.encoder_attn.sampling_offsets.weight",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.sampling_offsets.bias": r"model.decoder.layers.\1.encoder_attn.sampling_offsets.bias",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.attention_weights.weight": r"model.decoder.layers.\1.encoder_attn.attention_weights.weight",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.attention_weights.bias": r"model.decoder.layers.\1.encoder_attn.attention_weights.bias",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.value_proj.weight": r"model.decoder.layers.\1.encoder_attn.value_proj.weight",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.value_proj.bias": r"model.decoder.layers.\1.encoder_attn.value_proj.bias",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.output_proj.weight": r"model.decoder.layers.\1.encoder_attn.output_proj.weight",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.output_proj.bias": r"model.decoder.layers.\1.encoder_attn.output_proj.bias",
|
||||
r"decoder.decoder.layers.(\d+).norm1.weight": r"model.decoder.layers.\1.self_attn_layer_norm.weight",
|
||||
r"decoder.decoder.layers.(\d+).norm1.bias": r"model.decoder.layers.\1.self_attn_layer_norm.bias",
|
||||
r"decoder.decoder.layers.(\d+).norm2.weight": r"model.decoder.layers.\1.encoder_attn_layer_norm.weight",
|
||||
r"decoder.decoder.layers.(\d+).norm2.bias": r"model.decoder.layers.\1.encoder_attn_layer_norm.bias",
|
||||
r"decoder.decoder.layers.(\d+).linear1.weight": r"model.decoder.layers.\1.fc1.weight",
|
||||
r"decoder.decoder.layers.(\d+).linear1.bias": r"model.decoder.layers.\1.fc1.bias",
|
||||
r"decoder.decoder.layers.(\d+).linear2.weight": r"model.decoder.layers.\1.fc2.weight",
|
||||
r"decoder.decoder.layers.(\d+).linear2.bias": r"model.decoder.layers.\1.fc2.bias",
|
||||
r"decoder.decoder.layers.(\d+).norm3.weight": r"model.decoder.layers.\1.final_layer_norm.weight",
|
||||
r"decoder.decoder.layers.(\d+).norm3.bias": r"model.decoder.layers.\1.final_layer_norm.bias",
|
||||
r"decoder.decoder.layers.(\d+).cross_attn.num_points_scale": r"model.decoder.layers.\1.encoder_attn.n_points_scale",
|
||||
r"decoder.dec_score_head.(\d+).weight": r"model.decoder.class_embed.\1.weight",
|
||||
r"decoder.dec_score_head.(\d+).bias": r"model.decoder.class_embed.\1.bias",
|
||||
r"decoder.dec_bbox_head.(\d+).layers.(\d+).(weight|bias)": r"model.decoder.bbox_embed.\1.layers.\2.\3",
|
||||
r"decoder.denoising_class_embed.weight": r"model.denoising_class_embed.weight",
|
||||
r"decoder.query_pos_head.layers.0.weight": r"model.decoder.query_pos_head.layers.0.weight",
|
||||
r"decoder.query_pos_head.layers.0.bias": r"model.decoder.query_pos_head.layers.0.bias",
|
||||
r"decoder.query_pos_head.layers.1.weight": r"model.decoder.query_pos_head.layers.1.weight",
|
||||
r"decoder.query_pos_head.layers.1.bias": r"model.decoder.query_pos_head.layers.1.bias",
|
||||
r"decoder.enc_output.proj.weight": r"model.enc_output.0.weight",
|
||||
r"decoder.enc_output.proj.bias": r"model.enc_output.0.bias",
|
||||
r"decoder.enc_output.norm.weight": r"model.enc_output.1.weight",
|
||||
r"decoder.enc_output.norm.bias": r"model.enc_output.1.bias",
|
||||
r"decoder.enc_score_head.weight": r"model.enc_score_head.weight",
|
||||
r"decoder.enc_score_head.bias": r"model.enc_score_head.bias",
|
||||
r"decoder.enc_bbox_head.layers.(\d+).(weight|bias)": r"model.enc_bbox_head.layers.\1.\2",
|
||||
r"backbone.res_layers.0.blocks.0.short.conv.weight": r"model.backbone.model.encoder.stages.0.layers.0.shortcut.convolution.weight",
|
||||
r"backbone.res_layers.0.blocks.0.short.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.encoder.stages.0.layers.0.shortcut.normalization.\1",
|
||||
r"backbone.res_layers.(\d+).blocks.0.short.conv.conv.weight": r"model.backbone.model.encoder.stages.\1.layers.0.shortcut.1.convolution.weight",
|
||||
r"backbone.res_layers.(\d+).blocks.0.short.conv.norm.(\w+)": r"model.backbone.model.encoder.stages.\1.layers.0.shortcut.1.normalization.\2",
|
||||
# Mapping for subsequent blocks in other stages
|
||||
r"backbone.res_layers.(\d+).blocks.0.short.conv.weight": r"model.backbone.model.encoder.stages.\1.layers.0.shortcut.1.convolution.weight",
|
||||
r"backbone.res_layers.(\d+).blocks.0.short.norm.(weight|bias|running_mean|running_var)": r"model.backbone.model.encoder.stages.\1.layers.0.shortcut.1.normalization.\2",
|
||||
r"decoder.input_proj.(\d+).conv.weight": r"model.decoder_input_proj.\1.0.weight",
|
||||
r"decoder.input_proj.(\d+).norm.(.*)": r"model.decoder_input_proj.\1.1.\2",
|
||||
}
|
||||
|
||||
|
||||
def convert_old_keys_to_new_keys(state_dict_keys: dict = None):
|
||||
# Use the mapping to rename keys
|
||||
for original_key, converted_key in ORIGINAL_TO_CONVERTED_KEY_MAPPING.items():
|
||||
for key in list(state_dict_keys.keys()):
|
||||
new_key = re.sub(original_key, converted_key, key)
|
||||
if new_key != key:
|
||||
state_dict_keys[new_key] = state_dict_keys.pop(key)
|
||||
|
||||
return state_dict_keys
|
||||
|
||||
|
||||
def read_in_q_k_v(state_dict, config):
|
||||
prefix = ""
|
||||
encoder_hidden_dim = config.encoder_hidden_dim
|
||||
|
||||
# first: transformer encoder
|
||||
for i in range(config.encoder_layers):
|
||||
# read in weights + bias of input projection layer (in PyTorch's MultiHeadAttention, this is a single matrix + bias)
|
||||
in_proj_weight = state_dict.pop(f"{prefix}encoder.encoder.{i}.layers.0.self_attn.in_proj_weight")
|
||||
in_proj_bias = state_dict.pop(f"{prefix}encoder.encoder.{i}.layers.0.self_attn.in_proj_bias")
|
||||
# next, add query, keys and values (in that order) to the state dict
|
||||
state_dict[f"model.encoder.encoder.{i}.layers.0.self_attn.q_proj.weight"] = in_proj_weight[
|
||||
:encoder_hidden_dim, :
|
||||
]
|
||||
state_dict[f"model.encoder.encoder.{i}.layers.0.self_attn.q_proj.bias"] = in_proj_bias[:encoder_hidden_dim]
|
||||
state_dict[f"model.encoder.encoder.{i}.layers.0.self_attn.k_proj.weight"] = in_proj_weight[
|
||||
encoder_hidden_dim : 2 * encoder_hidden_dim, :
|
||||
]
|
||||
state_dict[f"model.encoder.encoder.{i}.layers.0.self_attn.k_proj.bias"] = in_proj_bias[
|
||||
encoder_hidden_dim : 2 * encoder_hidden_dim
|
||||
]
|
||||
state_dict[f"model.encoder.encoder.{i}.layers.0.self_attn.v_proj.weight"] = in_proj_weight[
|
||||
-encoder_hidden_dim:, :
|
||||
]
|
||||
state_dict[f"model.encoder.encoder.{i}.layers.0.self_attn.v_proj.bias"] = in_proj_bias[-encoder_hidden_dim:]
|
||||
# next: transformer decoder (which is a bit more complex because it also includes cross-attention)
|
||||
for i in range(config.decoder_layers):
|
||||
# read in weights + bias of input projection layer of self-attention
|
||||
in_proj_weight = state_dict.pop(f"{prefix}decoder.decoder.layers.{i}.self_attn.in_proj_weight")
|
||||
in_proj_bias = state_dict.pop(f"{prefix}decoder.decoder.layers.{i}.self_attn.in_proj_bias")
|
||||
# next, add query, keys and values (in that order) to the state dict
|
||||
state_dict[f"model.decoder.layers.{i}.self_attn.q_proj.weight"] = in_proj_weight[:256, :]
|
||||
state_dict[f"model.decoder.layers.{i}.self_attn.q_proj.bias"] = in_proj_bias[:256]
|
||||
state_dict[f"model.decoder.layers.{i}.self_attn.k_proj.weight"] = in_proj_weight[256:512, :]
|
||||
state_dict[f"model.decoder.layers.{i}.self_attn.k_proj.bias"] = in_proj_bias[256:512]
|
||||
state_dict[f"model.decoder.layers.{i}.self_attn.v_proj.weight"] = in_proj_weight[-256:, :]
|
||||
state_dict[f"model.decoder.layers.{i}.self_attn.v_proj.bias"] = in_proj_bias[-256:]
|
||||
|
||||
|
||||
# We will verify our results on an image of cute cats
|
||||
def prepare_img():
|
||||
url = "http://images.cocodataset.org/val2017/000000039769.jpg"
|
||||
im = Image.open(requests.get(url, stream=True).raw)
|
||||
|
||||
return im
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def write_model_and_image_processor(model_name, output_dir, push_to_hub, repo_id):
|
||||
"""
|
||||
Copy/paste/tweak model's weights to our RTDETR structure.
|
||||
"""
|
||||
|
||||
# load default config
|
||||
config = get_rt_detr_v2_config(model_name)
|
||||
|
||||
# load original model from torch hub
|
||||
model_name_to_checkpoint_url = {
|
||||
"rtdetr_v2_r18vd": "https://github.com/lyuwenyu/storage/releases/download/v0.2/rtdetrv2_r18vd_120e_coco_rerun_48.1.pth",
|
||||
"rtdetr_v2_r34vd": "https://github.com/lyuwenyu/storage/releases/download/v0.1/rtdetrv2_r34vd_120e_coco_ema.pth",
|
||||
"rtdetr_v2_r50vd": "https://github.com/lyuwenyu/storage/releases/download/v0.1/rtdetrv2_r50vd_6x_coco_ema.pth",
|
||||
"rtdetr_v2_r101vd": "https://github.com/lyuwenyu/storage/releases/download/v0.1/rtdetrv2_r101vd_6x_coco_from_paddle.pth",
|
||||
}
|
||||
logger.info(f"Converting model {model_name}...")
|
||||
state_dict = torch.hub.load_state_dict_from_url(model_name_to_checkpoint_url[model_name], map_location="cpu")[
|
||||
"ema"
|
||||
]["module"]
|
||||
# rename keys
|
||||
state_dict = convert_old_keys_to_new_keys(state_dict)
|
||||
for key in state_dict.copy().keys():
|
||||
if key.endswith("num_batches_tracked"):
|
||||
del state_dict[key]
|
||||
# query, key and value matrices need special treatment
|
||||
read_in_q_k_v(state_dict, config)
|
||||
# important: we need to prepend a prefix to each of the base model keys as the head models use different attributes for them
|
||||
for key in state_dict.copy().keys():
|
||||
if key.endswith("num_batches_tracked"):
|
||||
del state_dict[key]
|
||||
# for two_stage
|
||||
if "bbox_embed" in key or ("class_embed" in key and "denoising_" not in key):
|
||||
state_dict[key.split("model.decoder.")[-1]] = state_dict[key]
|
||||
|
||||
# no need in ckpt
|
||||
del state_dict["decoder.anchors"]
|
||||
del state_dict["decoder.valid_mask"]
|
||||
# finally, create HuggingFace model and load state dict
|
||||
model = RTDetrV2ForObjectDetection(config)
|
||||
model.load_state_dict(state_dict)
|
||||
model.eval()
|
||||
|
||||
# load image processor
|
||||
image_processor = RTDetrImageProcessor()
|
||||
|
||||
# prepare image
|
||||
img = prepare_img()
|
||||
|
||||
# preprocess image
|
||||
transformations = transforms.Compose(
|
||||
[
|
||||
transforms.Resize([640, 640], interpolation=transforms.InterpolationMode.BILINEAR),
|
||||
transforms.ToTensor(),
|
||||
]
|
||||
)
|
||||
original_pixel_values = transformations(img).unsqueeze(0) # insert batch dimension
|
||||
|
||||
encoding = image_processor(images=img, return_tensors="pt")
|
||||
pixel_values = encoding["pixel_values"]
|
||||
|
||||
assert torch.allclose(original_pixel_values, pixel_values)
|
||||
|
||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||
model.to(device)
|
||||
pixel_values = pixel_values.to(device)
|
||||
|
||||
# Pass image by the model
|
||||
with torch.no_grad():
|
||||
outputs = model(pixel_values)
|
||||
|
||||
if model_name == "rtdetr_v2_r18vd":
|
||||
expected_slice_logits = torch.tensor(
|
||||
[[-3.7045, -5.1913, -6.1787], [-4.0106, -9.3450, -5.2043], [-4.1287, -4.7463, -5.8634]]
|
||||
)
|
||||
expected_slice_boxes = torch.tensor(
|
||||
[[0.2582, 0.5497, 0.4764], [0.1684, 0.1985, 0.2120], [0.7665, 0.4146, 0.4669]]
|
||||
)
|
||||
elif model_name == "rtdetr_v2_r34vd":
|
||||
expected_slice_logits = torch.tensor(
|
||||
[[-4.6108, -5.9453, -3.8505], [-3.8702, -6.1136, -5.5677], [-3.7790, -6.4538, -5.9449]]
|
||||
)
|
||||
expected_slice_boxes = torch.tensor(
|
||||
[[0.1691, 0.1984, 0.2118], [0.2594, 0.5506, 0.4736], [0.7669, 0.4136, 0.4654]]
|
||||
)
|
||||
elif model_name == "rtdetr_v2_r50vd":
|
||||
expected_slice_logits = torch.tensor(
|
||||
[[-4.7881, -4.6754, -6.1624], [-5.4441, -6.6486, -4.3840], [-3.5455, -4.9318, -6.3544]]
|
||||
)
|
||||
expected_slice_boxes = torch.tensor(
|
||||
[[0.2588, 0.5487, 0.4747], [0.5497, 0.2760, 0.0573], [0.7688, 0.4133, 0.4634]]
|
||||
)
|
||||
elif model_name == "rtdetr_v2_r101vd":
|
||||
expected_slice_logits = torch.tensor(
|
||||
[[-4.6162, -4.9189, -4.6656], [-4.4701, -4.4997, -4.9659], [-5.6641, -7.9000, -5.0725]]
|
||||
)
|
||||
expected_slice_boxes = torch.tensor(
|
||||
[[0.7707, 0.4124, 0.4585], [0.2589, 0.5492, 0.4735], [0.1688, 0.1993, 0.2108]]
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unknown rt_detr_v2_name: {model_name}")
|
||||
assert torch.allclose(outputs.logits[0, :3, :3], expected_slice_logits.to(outputs.logits.device), atol=1e-4)
|
||||
assert torch.allclose(outputs.pred_boxes[0, :3, :3], expected_slice_boxes.to(outputs.pred_boxes.device), atol=1e-3)
|
||||
|
||||
if output_dir is not None:
|
||||
Path(output_dir).mkdir(exist_ok=True)
|
||||
print(f"Saving model {model_name} to {output_dir}")
|
||||
model.save_pretrained(output_dir)
|
||||
print(f"Saving image processor to {output_dir}")
|
||||
image_processor.save_pretrained(output_dir)
|
||||
|
||||
if push_to_hub:
|
||||
# Upload model, image processor and config to the hub
|
||||
logger.info("Uploading PyTorch model and image processor to the hub...")
|
||||
config.push_to_hub(
|
||||
repo_id=repo_id,
|
||||
commit_message="Add config from convert_rt_detr_v2_original_pytorch_checkpoint_to_pytorch.py",
|
||||
)
|
||||
model.push_to_hub(
|
||||
repo_id=repo_id,
|
||||
commit_message="Add model from convert_rt_detr_v2_original_pytorch_checkpoint_to_pytorch.py",
|
||||
)
|
||||
image_processor.push_to_hub(
|
||||
repo_id=repo_id,
|
||||
commit_message="Add image processor from convert_rt_detr_v2_original_pytorch_checkpoint_to_pytorch.py",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--model_name",
|
||||
default="rtdetr_v2_r18vd",
|
||||
type=str,
|
||||
help="model_name of the checkpoint you'd like to convert.",
|
||||
)
|
||||
parser.add_argument("--output_dir", default=None, type=str, help="Location to write HF model and image processor")
|
||||
parser.add_argument("--push_to_hub", action="store_true", help="Whether to push the model to the hub or not.")
|
||||
parser.add_argument(
|
||||
"--repo_id",
|
||||
type=str,
|
||||
help="repo_id where the model will be pushed to.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
write_model_and_image_processor(args.model_name, args.output_dir, args.push_to_hub, args.repo_id)
|
||||
2122
src/transformers/models/rt_detr_v2/modeling_rt_detr_v2.py
Normal file
2122
src/transformers/models/rt_detr_v2/modeling_rt_detr_v2.py
Normal file
File diff suppressed because it is too large
Load Diff
607
src/transformers/models/rt_detr_v2/modular_rt_detr_v2.py
Normal file
607
src/transformers/models/rt_detr_v2/modular_rt_detr_v2.py
Normal file
@@ -0,0 +1,607 @@
|
||||
# coding=utf-8
|
||||
# Copyright 2025 Baidu Inc and The HuggingFace Inc. team.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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.
|
||||
from functools import partial
|
||||
from typing import List, Optional
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from torch import Tensor, nn
|
||||
|
||||
from ...configuration_utils import PretrainedConfig
|
||||
from ...utils import logging
|
||||
from ...utils.backbone_utils import (
|
||||
verify_backbone_config_arguments,
|
||||
)
|
||||
from ..auto import CONFIG_MAPPING
|
||||
from ..rt_detr.modeling_rt_detr import (
|
||||
RTDetrDecoder,
|
||||
RTDetrDecoderLayer,
|
||||
RTDetrForObjectDetection,
|
||||
RTDetrMLPPredictionHead,
|
||||
RTDetrModel,
|
||||
RTDetrMultiscaleDeformableAttention,
|
||||
RTDetrPreTrainedModel,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.get_logger(__name__)
|
||||
|
||||
|
||||
class RTDetrV2Config(PretrainedConfig):
|
||||
r"""
|
||||
This is the configuration class to store the configuration of a [`RTDetrV2Model`]. It is used to instantiate a
|
||||
RT-DETR model according to the specified arguments, defining the model architecture. Instantiating a configuration
|
||||
with the defaults will yield a similar configuration to that of the RT-DETR architecture.
|
||||
|
||||
e.g. [PekingU/rtdetr_r18vd](https://huggingface.co/PekingU/rtdetr_r18vd)
|
||||
|
||||
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
|
||||
documentation from [`PretrainedConfig`] for more information.
|
||||
|
||||
Args:
|
||||
initializer_range (`float`, *optional*, defaults to 0.01):
|
||||
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
|
||||
initializer_bias_prior_prob (`float`, *optional*):
|
||||
The prior probability used by the bias initializer to initialize biases for `enc_score_head` and `class_embed`.
|
||||
If `None`, `prior_prob` computed as `prior_prob = 1 / (num_labels + 1)` while initializing model weights.
|
||||
layer_norm_eps (`float`, *optional*, defaults to 1e-05):
|
||||
The epsilon used by the layer normalization layers.
|
||||
batch_norm_eps (`float`, *optional*, defaults to 1e-05):
|
||||
The epsilon used by the batch normalization layers.
|
||||
backbone_config (`Dict`, *optional*, defaults to `RTDetrV2ResNetConfig()`):
|
||||
The configuration of the backbone model.
|
||||
backbone (`str`, *optional*):
|
||||
Name of backbone to use when `backbone_config` is `None`. If `use_pretrained_backbone` is `True`, this
|
||||
will load the corresponding pretrained weights from the timm or transformers library. If `use_pretrained_backbone`
|
||||
is `False`, this loads the backbone's config and uses that to initialize the backbone with random weights.
|
||||
use_pretrained_backbone (`bool`, *optional*, defaults to `False`):
|
||||
Whether to use pretrained weights for the backbone.
|
||||
use_timm_backbone (`bool`, *optional*, defaults to `False`):
|
||||
Whether to load `backbone` from the timm library. If `False`, the backbone is loaded from the transformers
|
||||
library.
|
||||
freeze_backbone_batch_norms (`bool`, *optional*, defaults to `True`):
|
||||
Whether to freeze the batch normalization layers in the backbone.
|
||||
backbone_kwargs (`dict`, *optional*):
|
||||
Keyword arguments to be passed to AutoBackbone when loading from a checkpoint
|
||||
e.g. `{'out_indices': (0, 1, 2, 3)}`. Cannot be specified if `backbone_config` is set.
|
||||
encoder_hidden_dim (`int`, *optional*, defaults to 256):
|
||||
Dimension of the layers in hybrid encoder.
|
||||
encoder_in_channels (`list`, *optional*, defaults to `[512, 1024, 2048]`):
|
||||
Multi level features input for encoder.
|
||||
feat_strides (`List[int]`, *optional*, defaults to `[8, 16, 32]`):
|
||||
Strides used in each feature map.
|
||||
encoder_layers (`int`, *optional*, defaults to 1):
|
||||
Total of layers to be used by the encoder.
|
||||
encoder_ffn_dim (`int`, *optional*, defaults to 1024):
|
||||
Dimension of the "intermediate" (often named feed-forward) layer in decoder.
|
||||
encoder_attention_heads (`int`, *optional*, defaults to 8):
|
||||
Number of attention heads for each attention layer in the Transformer encoder.
|
||||
dropout (`float`, *optional*, defaults to 0.0):
|
||||
The ratio for all dropout layers.
|
||||
activation_dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout ratio for activations inside the fully connected layer.
|
||||
encode_proj_layers (`List[int]`, *optional*, defaults to `[2]`):
|
||||
Indexes of the projected layers to be used in the encoder.
|
||||
positional_encoding_temperature (`int`, *optional*, defaults to 10000):
|
||||
The temperature parameter used to create the positional encodings.
|
||||
encoder_activation_function (`str`, *optional*, defaults to `"gelu"`):
|
||||
The non-linear activation function (function or string) in the encoder and pooler. If string, `"gelu"`,
|
||||
`"relu"`, `"silu"` and `"gelu_new"` are supported.
|
||||
activation_function (`str`, *optional*, defaults to `"silu"`):
|
||||
The non-linear activation function (function or string) in the general layer. If string, `"gelu"`,
|
||||
`"relu"`, `"silu"` and `"gelu_new"` are supported.
|
||||
eval_size (`Tuple[int, int]`, *optional*):
|
||||
Height and width used to compute the effective height and width of the position embeddings after taking
|
||||
into account the stride.
|
||||
normalize_before (`bool`, *optional*, defaults to `False`):
|
||||
Determine whether to apply layer normalization in the transformer encoder layer before self-attention and
|
||||
feed-forward modules.
|
||||
hidden_expansion (`float`, *optional*, defaults to 1.0):
|
||||
Expansion ratio to enlarge the dimension size of RepVGGBlock and CSPRepLayer.
|
||||
d_model (`int`, *optional*, defaults to 256):
|
||||
Dimension of the layers exclude hybrid encoder.
|
||||
num_queries (`int`, *optional*, defaults to 300):
|
||||
Number of object queries.
|
||||
decoder_in_channels (`list`, *optional*, defaults to `[256, 256, 256]`):
|
||||
Multi level features dimension for decoder
|
||||
decoder_ffn_dim (`int`, *optional*, defaults to 1024):
|
||||
Dimension of the "intermediate" (often named feed-forward) layer in decoder.
|
||||
num_feature_levels (`int`, *optional*, defaults to 3):
|
||||
The number of input feature levels.
|
||||
decoder_n_points (`int`, *optional*, defaults to 4):
|
||||
The number of sampled keys in each feature level for each attention head in the decoder.
|
||||
decoder_layers (`int`, *optional*, defaults to 6):
|
||||
Number of decoder layers.
|
||||
decoder_attention_heads (`int`, *optional*, defaults to 8):
|
||||
Number of attention heads for each attention layer in the Transformer decoder.
|
||||
decoder_activation_function (`str`, *optional*, defaults to `"relu"`):
|
||||
The non-linear activation function (function or string) in the decoder. If string, `"gelu"`,
|
||||
`"relu"`, `"silu"` and `"gelu_new"` are supported.
|
||||
attention_dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout ratio for the attention probabilities.
|
||||
num_denoising (`int`, *optional*, defaults to 100):
|
||||
The total number of denoising tasks or queries to be used for contrastive denoising.
|
||||
label_noise_ratio (`float`, *optional*, defaults to 0.5):
|
||||
The fraction of denoising labels to which random noise should be added.
|
||||
box_noise_scale (`float`, *optional*, defaults to 1.0):
|
||||
Scale or magnitude of noise to be added to the bounding boxes.
|
||||
learn_initial_query (`bool`, *optional*, defaults to `False`):
|
||||
Indicates whether the initial query embeddings for the decoder should be learned during training
|
||||
anchor_image_size (`Tuple[int, int]`, *optional*):
|
||||
Height and width of the input image used during evaluation to generate the bounding box anchors. If None, automatic generate anchor is applied.
|
||||
disable_custom_kernels (`bool`, *optional*, defaults to `True`):
|
||||
Whether to disable custom kernels.
|
||||
with_box_refine (`bool`, *optional*, defaults to `True`):
|
||||
Whether to apply iterative bounding box refinement, where each decoder layer refines the bounding boxes
|
||||
based on the predictions from the previous layer.
|
||||
is_encoder_decoder (`bool`, *optional*, defaults to `True`):
|
||||
Whether the architecture has an encoder decoder structure.
|
||||
matcher_alpha (`float`, *optional*, defaults to 0.25):
|
||||
Parameter alpha used by the Hungarian Matcher.
|
||||
matcher_gamma (`float`, *optional*, defaults to 2.0):
|
||||
Parameter gamma used by the Hungarian Matcher.
|
||||
matcher_class_cost (`float`, *optional*, defaults to 2.0):
|
||||
The relative weight of the class loss used by the Hungarian Matcher.
|
||||
matcher_bbox_cost (`float`, *optional*, defaults to 5.0):
|
||||
The relative weight of the bounding box loss used by the Hungarian Matcher.
|
||||
matcher_giou_cost (`float`, *optional*, defaults to 2.0):
|
||||
The relative weight of the giou loss of used by the Hungarian Matcher.
|
||||
use_focal_loss (`bool`, *optional*, defaults to `True`):
|
||||
Parameter informing if focal loss should be used.
|
||||
auxiliary_loss (`bool`, *optional*, defaults to `True`):
|
||||
Whether auxiliary decoding losses (loss at each decoder layer) are to be used.
|
||||
focal_loss_alpha (`float`, *optional*, defaults to 0.75):
|
||||
Parameter alpha used to compute the focal loss.
|
||||
focal_loss_gamma (`float`, *optional*, defaults to 2.0):
|
||||
Parameter gamma used to compute the focal loss.
|
||||
weight_loss_vfl (`float`, *optional*, defaults to 1.0):
|
||||
Relative weight of the varifocal loss in the object detection loss.
|
||||
weight_loss_bbox (`float`, *optional*, defaults to 5.0):
|
||||
Relative weight of the L1 bounding box loss in the object detection loss.
|
||||
weight_loss_giou (`float`, *optional*, defaults to 2.0):
|
||||
Relative weight of the generalized IoU loss in the object detection loss.
|
||||
eos_coefficient (`float`, *optional*, defaults to 0.0001):
|
||||
Relative classification weight of the 'no-object' class in the object detection loss.
|
||||
decoder_n_levels (`int`, *optional*, defaults to 3):
|
||||
The number of feature levels used by the decoder.
|
||||
decoder_offset_scale (`float`, *optional*, defaults to 0.5):
|
||||
Scaling factor applied to the attention offsets in the decoder.
|
||||
decoder_method (`str`, *optional*, defaults to `"default"`):
|
||||
The method to use for the decoder: `"default"` or `"discrete"`.
|
||||
|
||||
Examples:
|
||||
|
||||
```python
|
||||
>>> from transformers import RTDetrV2Config, RTDetrV2Model
|
||||
|
||||
>>> # Initializing a RT-DETR configuration
|
||||
>>> configuration = RTDetrV2Config()
|
||||
|
||||
>>> # Initializing a model (with random weights) from the configuration
|
||||
>>> model = RTDetrV2Model(configuration)
|
||||
|
||||
>>> # Accessing the model configuration
|
||||
>>> configuration = model.config
|
||||
```
|
||||
"""
|
||||
|
||||
model_type = "rt_detr_v2"
|
||||
layer_types = ["basic", "bottleneck"]
|
||||
attribute_map = {
|
||||
"hidden_size": "d_model",
|
||||
"num_attention_heads": "encoder_attention_heads",
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
initializer_range=0.01,
|
||||
initializer_bias_prior_prob=None,
|
||||
layer_norm_eps=1e-5,
|
||||
batch_norm_eps=1e-5,
|
||||
# backbone
|
||||
backbone_config=None,
|
||||
backbone=None,
|
||||
use_pretrained_backbone=False,
|
||||
use_timm_backbone=False,
|
||||
freeze_backbone_batch_norms=True,
|
||||
backbone_kwargs=None,
|
||||
# encoder HybridEncoder
|
||||
encoder_hidden_dim=256,
|
||||
encoder_in_channels=[512, 1024, 2048],
|
||||
feat_strides=[8, 16, 32],
|
||||
encoder_layers=1,
|
||||
encoder_ffn_dim=1024,
|
||||
encoder_attention_heads=8,
|
||||
dropout=0.0,
|
||||
activation_dropout=0.0,
|
||||
encode_proj_layers=[2],
|
||||
positional_encoding_temperature=10000,
|
||||
encoder_activation_function="gelu",
|
||||
activation_function="silu",
|
||||
eval_size=None,
|
||||
normalize_before=False,
|
||||
hidden_expansion=1.0,
|
||||
# decoder RTDetrV2Transformer
|
||||
d_model=256,
|
||||
num_queries=300,
|
||||
decoder_in_channels=[256, 256, 256],
|
||||
decoder_ffn_dim=1024,
|
||||
num_feature_levels=3,
|
||||
decoder_n_points=4,
|
||||
decoder_layers=6,
|
||||
decoder_attention_heads=8,
|
||||
decoder_activation_function="relu",
|
||||
attention_dropout=0.0,
|
||||
num_denoising=100,
|
||||
label_noise_ratio=0.5,
|
||||
box_noise_scale=1.0,
|
||||
learn_initial_query=False,
|
||||
anchor_image_size=None,
|
||||
disable_custom_kernels=True,
|
||||
with_box_refine=True,
|
||||
is_encoder_decoder=True,
|
||||
# Loss
|
||||
matcher_alpha=0.25,
|
||||
matcher_gamma=2.0,
|
||||
matcher_class_cost=2.0,
|
||||
matcher_bbox_cost=5.0,
|
||||
matcher_giou_cost=2.0,
|
||||
use_focal_loss=True,
|
||||
auxiliary_loss=True,
|
||||
focal_loss_alpha=0.75,
|
||||
focal_loss_gamma=2.0,
|
||||
weight_loss_vfl=1.0,
|
||||
weight_loss_bbox=5.0,
|
||||
weight_loss_giou=2.0,
|
||||
eos_coefficient=1e-4,
|
||||
decoder_n_levels=3, # default value
|
||||
decoder_offset_scale=0.5, # default value
|
||||
decoder_method="default",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(is_encoder_decoder=is_encoder_decoder, **kwargs)
|
||||
self.initializer_range = initializer_range
|
||||
self.initializer_bias_prior_prob = initializer_bias_prior_prob
|
||||
self.layer_norm_eps = layer_norm_eps
|
||||
self.batch_norm_eps = batch_norm_eps
|
||||
# backbone
|
||||
if backbone_config is None and backbone is None:
|
||||
logger.info(
|
||||
"`backbone_config` and `backbone` are `None`. Initializing the config with the default `RTDetrV2-ResNet` backbone."
|
||||
)
|
||||
backbone_model_type = "rt_detr_resnet"
|
||||
config_class = CONFIG_MAPPING[backbone_model_type]
|
||||
# this will map it to RTDetrResNetConfig
|
||||
# note: we can instead create RTDetrV2ResNetConfig but it will be exactly the same as V1
|
||||
# and we would need to create RTDetrV2ResNetModel
|
||||
backbone_config = config_class(
|
||||
num_channels=3,
|
||||
embedding_size=64,
|
||||
hidden_sizes=[256, 512, 1024, 2048],
|
||||
depths=[3, 4, 6, 3],
|
||||
layer_type="bottleneck",
|
||||
hidden_act="relu",
|
||||
downsample_in_first_stage=False,
|
||||
downsample_in_bottleneck=False,
|
||||
out_features=None,
|
||||
out_indices=[2, 3, 4],
|
||||
)
|
||||
elif isinstance(backbone_config, dict):
|
||||
backbone_model_type = backbone_config.pop("model_type")
|
||||
config_class = CONFIG_MAPPING[backbone_model_type]
|
||||
backbone_config = config_class.from_dict(backbone_config)
|
||||
|
||||
verify_backbone_config_arguments(
|
||||
use_timm_backbone=use_timm_backbone,
|
||||
use_pretrained_backbone=use_pretrained_backbone,
|
||||
backbone=backbone,
|
||||
backbone_config=backbone_config,
|
||||
backbone_kwargs=backbone_kwargs,
|
||||
)
|
||||
|
||||
self.backbone_config = backbone_config
|
||||
self.backbone = backbone
|
||||
self.use_pretrained_backbone = use_pretrained_backbone
|
||||
self.use_timm_backbone = use_timm_backbone
|
||||
self.freeze_backbone_batch_norms = freeze_backbone_batch_norms
|
||||
self.backbone_kwargs = backbone_kwargs
|
||||
# encoder
|
||||
self.encoder_hidden_dim = encoder_hidden_dim
|
||||
self.encoder_in_channels = encoder_in_channels
|
||||
self.feat_strides = feat_strides
|
||||
self.encoder_ffn_dim = encoder_ffn_dim
|
||||
self.dropout = dropout
|
||||
self.activation_dropout = activation_dropout
|
||||
self.encode_proj_layers = encode_proj_layers
|
||||
self.encoder_layers = encoder_layers
|
||||
self.positional_encoding_temperature = positional_encoding_temperature
|
||||
self.eval_size = eval_size
|
||||
self.normalize_before = normalize_before
|
||||
self.encoder_activation_function = encoder_activation_function
|
||||
self.activation_function = activation_function
|
||||
self.hidden_expansion = hidden_expansion
|
||||
self.num_queries = num_queries
|
||||
self.decoder_ffn_dim = decoder_ffn_dim
|
||||
self.decoder_in_channels = decoder_in_channels
|
||||
self.num_feature_levels = num_feature_levels
|
||||
self.decoder_n_points = decoder_n_points
|
||||
self.decoder_layers = decoder_layers
|
||||
self.decoder_attention_heads = decoder_attention_heads
|
||||
self.decoder_activation_function = decoder_activation_function
|
||||
self.attention_dropout = attention_dropout
|
||||
self.num_denoising = num_denoising
|
||||
self.label_noise_ratio = label_noise_ratio
|
||||
self.box_noise_scale = box_noise_scale
|
||||
self.learn_initial_query = learn_initial_query
|
||||
self.anchor_image_size = anchor_image_size
|
||||
self.auxiliary_loss = auxiliary_loss
|
||||
self.disable_custom_kernels = disable_custom_kernels
|
||||
self.with_box_refine = with_box_refine
|
||||
# Loss
|
||||
self.matcher_alpha = matcher_alpha
|
||||
self.matcher_gamma = matcher_gamma
|
||||
self.matcher_class_cost = matcher_class_cost
|
||||
self.matcher_bbox_cost = matcher_bbox_cost
|
||||
self.matcher_giou_cost = matcher_giou_cost
|
||||
self.use_focal_loss = use_focal_loss
|
||||
self.focal_loss_alpha = focal_loss_alpha
|
||||
self.focal_loss_gamma = focal_loss_gamma
|
||||
self.weight_loss_vfl = weight_loss_vfl
|
||||
self.weight_loss_bbox = weight_loss_bbox
|
||||
self.weight_loss_giou = weight_loss_giou
|
||||
self.eos_coefficient = eos_coefficient
|
||||
|
||||
if not hasattr(self, "d_model"):
|
||||
self.d_model = d_model
|
||||
|
||||
if not hasattr(self, "encoder_attention_heads"):
|
||||
self.encoder_attention_heads = encoder_attention_heads
|
||||
# add the new attributes with the given values or defaults
|
||||
self.decoder_n_levels = decoder_n_levels
|
||||
self.decoder_offset_scale = decoder_offset_scale
|
||||
self.decoder_method = decoder_method
|
||||
|
||||
@classmethod
|
||||
def from_backbone_configs(cls, backbone_config: PretrainedConfig, **kwargs):
|
||||
"""Instantiate a [`RTDetrV2Config`] (or a derived class) from a pre-trained backbone model configuration and DETR model
|
||||
configuration.
|
||||
|
||||
Args:
|
||||
backbone_config ([`PretrainedConfig`]):
|
||||
The backbone configuration.
|
||||
|
||||
Returns:
|
||||
[`RTDetrV2Config`]: An instance of a configuration object
|
||||
"""
|
||||
return cls(
|
||||
backbone_config=backbone_config,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def multi_scale_deformable_attention_v2(
|
||||
value: Tensor,
|
||||
value_spatial_shapes: Tensor,
|
||||
sampling_locations: Tensor,
|
||||
attention_weights: Tensor,
|
||||
num_points_list: List[int],
|
||||
method="default",
|
||||
) -> Tensor:
|
||||
batch_size, _, num_heads, hidden_dim = value.shape
|
||||
_, num_queries, num_heads, num_levels, num_points = sampling_locations.shape
|
||||
value_list = (
|
||||
value.permute(0, 2, 3, 1)
|
||||
.flatten(0, 1)
|
||||
.split([height.item() * width.item() for height, width in value_spatial_shapes], dim=-1)
|
||||
)
|
||||
# sampling_offsets [8, 480, 8, 12, 2]
|
||||
if method == "default":
|
||||
sampling_grids = 2 * sampling_locations - 1
|
||||
elif method == "discrete":
|
||||
sampling_grids = sampling_locations
|
||||
sampling_grids = sampling_grids.permute(0, 2, 1, 3, 4).flatten(0, 1)
|
||||
sampling_grids = sampling_grids.split(num_points_list, dim=-2)
|
||||
sampling_value_list = []
|
||||
for level_id, (height, width) in enumerate(value_spatial_shapes):
|
||||
# batch_size, height*width, num_heads, hidden_dim
|
||||
# -> batch_size, height*width, num_heads*hidden_dim
|
||||
# -> batch_size, num_heads*hidden_dim, height*width
|
||||
# -> batch_size*num_heads, hidden_dim, height, width
|
||||
value_l_ = value_list[level_id].reshape(batch_size * num_heads, hidden_dim, height, width)
|
||||
# batch_size, num_queries, num_heads, num_points, 2
|
||||
# -> batch_size, num_heads, num_queries, num_points, 2
|
||||
# -> batch_size*num_heads, num_queries, num_points, 2
|
||||
sampling_grid_l_ = sampling_grids[level_id]
|
||||
# batch_size*num_heads, hidden_dim, num_queries, num_points
|
||||
if method == "default":
|
||||
sampling_value_l_ = nn.functional.grid_sample(
|
||||
value_l_, sampling_grid_l_, mode="bilinear", padding_mode="zeros", align_corners=False
|
||||
)
|
||||
elif method == "discrete":
|
||||
sampling_coord = (sampling_grid_l_ * torch.tensor([[width, height]], device=value.device) + 0.5).to(
|
||||
torch.int64
|
||||
)
|
||||
|
||||
# Separate clamping for x and y coordinates
|
||||
sampling_coord_x = sampling_coord[..., 0].clamp(0, width - 1)
|
||||
sampling_coord_y = sampling_coord[..., 1].clamp(0, height - 1)
|
||||
|
||||
# Combine the clamped coordinates
|
||||
sampling_coord = torch.stack([sampling_coord_x, sampling_coord_y], dim=-1)
|
||||
sampling_coord = sampling_coord.reshape(batch_size * num_heads, num_queries * num_points_list[level_id], 2)
|
||||
sampling_idx = (
|
||||
torch.arange(sampling_coord.shape[0], device=value.device)
|
||||
.unsqueeze(-1)
|
||||
.repeat(1, sampling_coord.shape[1])
|
||||
)
|
||||
sampling_value_l_ = value_l_[sampling_idx, :, sampling_coord[..., 1], sampling_coord[..., 0]]
|
||||
sampling_value_l_ = sampling_value_l_.permute(0, 2, 1).reshape(
|
||||
batch_size * num_heads, hidden_dim, num_queries, num_points_list[level_id]
|
||||
)
|
||||
sampling_value_list.append(sampling_value_l_)
|
||||
# (batch_size, num_queries, num_heads, num_levels, num_points)
|
||||
# -> (batch_size, num_heads, num_queries, num_levels, num_points)
|
||||
# -> (batch_size, num_heads, 1, num_queries, num_levels*num_points)
|
||||
attention_weights = attention_weights.permute(0, 2, 1, 3).reshape(
|
||||
batch_size * num_heads, 1, num_queries, sum(num_points_list)
|
||||
)
|
||||
output = (
|
||||
(torch.concat(sampling_value_list, dim=-1) * attention_weights)
|
||||
.sum(-1)
|
||||
.view(batch_size, num_heads * hidden_dim, num_queries)
|
||||
)
|
||||
return output.transpose(1, 2).contiguous()
|
||||
|
||||
|
||||
# the main change
|
||||
class RTDetrV2MultiscaleDeformableAttention(RTDetrMultiscaleDeformableAttention):
|
||||
"""
|
||||
RTDetrV2 version of multiscale deformable attention, extending the base implementation
|
||||
with improved offset handling and initialization.
|
||||
"""
|
||||
|
||||
def __init__(self, config: RTDetrV2Config):
|
||||
num_heads = config.decoder_attention_heads
|
||||
n_points = config.decoder_n_points
|
||||
# Initialize parent class with config parameters
|
||||
super().__init__(config=config, num_heads=num_heads, n_points=n_points)
|
||||
|
||||
# V2-specific attributes
|
||||
self.n_levels = config.decoder_n_levels
|
||||
self.offset_scale = config.decoder_offset_scale
|
||||
self.method = config.decoder_method
|
||||
# Initialize n_points list and scale
|
||||
n_points_list = [self.n_points for _ in range(self.n_levels)]
|
||||
self.n_points_list = n_points_list
|
||||
n_points_scale = [1 / n for n in n_points_list for _ in range(n)]
|
||||
self.register_buffer("n_points_scale", torch.tensor(n_points_scale, dtype=torch.float32))
|
||||
|
||||
def forward(
|
||||
self,
|
||||
hidden_states: torch.Tensor,
|
||||
attention_mask: Optional[torch.Tensor] = None,
|
||||
encoder_hidden_states=None,
|
||||
encoder_attention_mask=None,
|
||||
position_embeddings: Optional[torch.Tensor] = None,
|
||||
reference_points=None,
|
||||
spatial_shapes=None,
|
||||
level_start_index=None,
|
||||
output_attentions: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
# Process inputs up to sampling locations calculation using parent class logic
|
||||
if position_embeddings is not None:
|
||||
hidden_states = self.with_pos_embed(hidden_states, position_embeddings)
|
||||
|
||||
batch_size, num_queries, _ = hidden_states.shape
|
||||
batch_size, sequence_length, _ = encoder_hidden_states.shape
|
||||
if (spatial_shapes[:, 0] * spatial_shapes[:, 1]).sum() != sequence_length:
|
||||
raise ValueError(
|
||||
"Make sure to align the spatial shapes with the sequence length of the encoder hidden states"
|
||||
)
|
||||
|
||||
value = self.value_proj(encoder_hidden_states)
|
||||
if attention_mask is not None:
|
||||
value = value.masked_fill(~attention_mask[..., None], float(0))
|
||||
value = value.view(batch_size, sequence_length, self.n_heads, self.d_model // self.n_heads)
|
||||
|
||||
# V2-specific sampling offsets shape
|
||||
sampling_offsets = self.sampling_offsets(hidden_states).view(
|
||||
batch_size, num_queries, self.n_heads, self.n_levels * self.n_points, 2
|
||||
)
|
||||
|
||||
attention_weights = self.attention_weights(hidden_states).view(
|
||||
batch_size, num_queries, self.n_heads, self.n_levels * self.n_points
|
||||
)
|
||||
attention_weights = F.softmax(attention_weights, -1)
|
||||
|
||||
# V2-specific sampling locations calculation
|
||||
if reference_points.shape[-1] == 2:
|
||||
offset_normalizer = torch.stack([spatial_shapes[..., 1], spatial_shapes[..., 0]], -1)
|
||||
sampling_locations = (
|
||||
reference_points[:, :, None, :, None, :]
|
||||
+ sampling_offsets / offset_normalizer[None, None, None, :, None, :]
|
||||
)
|
||||
elif reference_points.shape[-1] == 4:
|
||||
n_points_scale = self.n_points_scale.to(dtype=hidden_states.dtype).unsqueeze(-1)
|
||||
offset = sampling_offsets * n_points_scale * reference_points[:, :, None, :, 2:] * self.offset_scale
|
||||
sampling_locations = reference_points[:, :, None, :, :2] + offset
|
||||
else:
|
||||
raise ValueError(f"Last dim of reference_points must be 2 or 4, but got {reference_points.shape[-1]}")
|
||||
|
||||
# V2-specific attention implementation choice
|
||||
output = multi_scale_deformable_attention_v2(
|
||||
value, spatial_shapes, sampling_locations, attention_weights, self.n_points_list, self.method
|
||||
)
|
||||
|
||||
output = self.output_proj(output)
|
||||
return output, attention_weights
|
||||
|
||||
|
||||
class RTDetrV2DecoderLayer(RTDetrDecoderLayer):
|
||||
def __init__(self, config: RTDetrV2Config):
|
||||
# initialize parent class
|
||||
super().__init__(config)
|
||||
# override only the encoder attention module with v2 version
|
||||
self.encoder_attn = RTDetrV2MultiscaleDeformableAttention(config)
|
||||
|
||||
|
||||
class RTDetrV2PreTrainedModel(RTDetrPreTrainedModel):
|
||||
pass
|
||||
|
||||
|
||||
class RTDetrV2Decoder(RTDetrDecoder):
|
||||
def __init__(self, config: RTDetrV2Config):
|
||||
super().__init__(config)
|
||||
self.layers = nn.ModuleList([RTDetrV2DecoderLayer(config) for _ in range(config.decoder_layers)])
|
||||
|
||||
|
||||
class RTDetrV2Model(RTDetrModel):
|
||||
def __init__(self, config: RTDetrV2Config):
|
||||
super().__init__(config)
|
||||
# decoder
|
||||
self.decoder = RTDetrV2Decoder(config)
|
||||
|
||||
|
||||
class RTDetrV2MLPPredictionHead(RTDetrMLPPredictionHead):
|
||||
pass
|
||||
|
||||
|
||||
class RTDetrV2ForObjectDetection(RTDetrForObjectDetection, RTDetrV2PreTrainedModel):
|
||||
def __init__(self, config: RTDetrV2Config):
|
||||
RTDetrV2PreTrainedModel.__init__(config)
|
||||
# RTDETR encoder-decoder model
|
||||
self.model = RTDetrV2Model(config)
|
||||
|
||||
# Detection heads on top
|
||||
class_embed = partial(nn.Linear, config.d_model, config.num_labels)
|
||||
bbox_embed = partial(RTDetrV2MLPPredictionHead, config, config.d_model, config.d_model, 4, num_layers=3)
|
||||
|
||||
self.class_embed = nn.ModuleList([class_embed() for _ in range(config.decoder_layers)])
|
||||
self.bbox_embed = nn.ModuleList([bbox_embed() for _ in range(config.decoder_layers)])
|
||||
|
||||
self.model.decoder.class_embed = self.class_embed
|
||||
self.model.decoder.bbox_embed = self.bbox_embed
|
||||
|
||||
# Initialize weights and apply final processing
|
||||
self.post_init()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"RTDetrV2Config",
|
||||
"RTDetrV2Model",
|
||||
"RTDetrV2PreTrainedModel",
|
||||
"RTDetrV2ForObjectDetection",
|
||||
]
|
||||
@@ -8492,6 +8492,27 @@ class RTDetrResNetPreTrainedModel(metaclass=DummyObject):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class RTDetrV2ForObjectDetection(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class RTDetrV2Model(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class RTDetrV2PreTrainedModel(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
requires_backends(self, ["torch"])
|
||||
|
||||
|
||||
class RwkvForCausalLM(metaclass=DummyObject):
|
||||
_backends = ["torch"]
|
||||
|
||||
|
||||
0
tests/models/rt_detr_v2/__init__.py
Normal file
0
tests/models/rt_detr_v2/__init__.py
Normal file
769
tests/models/rt_detr_v2/test_modeling_rt_detr_v2.py
Normal file
769
tests/models/rt_detr_v2/test_modeling_rt_detr_v2.py
Normal file
@@ -0,0 +1,769 @@
|
||||
# coding = utf-8
|
||||
# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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.
|
||||
"""Testing suite for the PyTorch RT_DETR_V2 model."""
|
||||
|
||||
import inspect
|
||||
import math
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from parameterized import parameterized
|
||||
|
||||
from transformers import (
|
||||
RTDetrResNetConfig,
|
||||
RTDetrV2Config,
|
||||
is_torch_available,
|
||||
is_vision_available,
|
||||
)
|
||||
from transformers.testing_utils import require_torch, require_torch_gpu, require_vision, slow, torch_device
|
||||
from transformers.utils import cached_property
|
||||
|
||||
from ...test_configuration_common import ConfigTester
|
||||
from ...test_modeling_common import ModelTesterMixin, _config_zero_init, floats_tensor
|
||||
from ...test_pipeline_mixin import PipelineTesterMixin
|
||||
|
||||
|
||||
if is_torch_available():
|
||||
import torch
|
||||
|
||||
from transformers import RTDetrV2ForObjectDetection, RTDetrV2Model
|
||||
|
||||
if is_vision_available():
|
||||
from PIL import Image
|
||||
|
||||
from transformers import RTDetrImageProcessor
|
||||
|
||||
|
||||
CHECKPOINT = "PekingU/rtdetr_v2_r18vd"
|
||||
|
||||
|
||||
class RTDetrV2ModelTester:
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
batch_size=3,
|
||||
is_training=True,
|
||||
use_labels=True,
|
||||
n_targets=3,
|
||||
num_labels=10,
|
||||
initializer_range=0.02,
|
||||
layer_norm_eps=1e-5,
|
||||
batch_norm_eps=1e-5,
|
||||
# backbone
|
||||
backbone_config=None,
|
||||
# encoder HybridEncoder
|
||||
encoder_hidden_dim=32,
|
||||
encoder_in_channels=[128, 256, 512],
|
||||
feat_strides=[8, 16, 32],
|
||||
encoder_layers=1,
|
||||
encoder_ffn_dim=64,
|
||||
encoder_attention_heads=2,
|
||||
dropout=0.0,
|
||||
activation_dropout=0.0,
|
||||
encode_proj_layers=[2],
|
||||
positional_encoding_temperature=10000,
|
||||
encoder_activation_function="gelu",
|
||||
activation_function="silu",
|
||||
eval_size=None,
|
||||
normalize_before=False,
|
||||
# decoder RTDetrV2Transformer
|
||||
d_model=32,
|
||||
num_queries=30,
|
||||
decoder_in_channels=[32, 32, 32],
|
||||
decoder_ffn_dim=64,
|
||||
num_feature_levels=3,
|
||||
decoder_n_points=4,
|
||||
decoder_n_levels=3,
|
||||
decoder_layers=2,
|
||||
decoder_attention_heads=2,
|
||||
decoder_activation_function="relu",
|
||||
attention_dropout=0.0,
|
||||
num_denoising=0,
|
||||
label_noise_ratio=0.5,
|
||||
box_noise_scale=1.0,
|
||||
learn_initial_query=False,
|
||||
anchor_image_size=None,
|
||||
image_size=64,
|
||||
disable_custom_kernels=True,
|
||||
with_box_refine=True,
|
||||
):
|
||||
self.parent = parent
|
||||
self.batch_size = batch_size
|
||||
self.num_channels = 3
|
||||
self.is_training = is_training
|
||||
self.use_labels = use_labels
|
||||
self.n_targets = n_targets
|
||||
self.num_labels = num_labels
|
||||
self.initializer_range = initializer_range
|
||||
self.layer_norm_eps = layer_norm_eps
|
||||
self.batch_norm_eps = batch_norm_eps
|
||||
self.backbone_config = backbone_config
|
||||
self.encoder_hidden_dim = encoder_hidden_dim
|
||||
self.encoder_in_channels = encoder_in_channels
|
||||
self.feat_strides = feat_strides
|
||||
self.encoder_layers = encoder_layers
|
||||
self.encoder_ffn_dim = encoder_ffn_dim
|
||||
self.encoder_attention_heads = encoder_attention_heads
|
||||
self.dropout = dropout
|
||||
self.activation_dropout = activation_dropout
|
||||
self.encode_proj_layers = encode_proj_layers
|
||||
self.positional_encoding_temperature = positional_encoding_temperature
|
||||
self.encoder_activation_function = encoder_activation_function
|
||||
self.activation_function = activation_function
|
||||
self.eval_size = eval_size
|
||||
self.normalize_before = normalize_before
|
||||
self.d_model = d_model
|
||||
self.num_queries = num_queries
|
||||
self.decoder_in_channels = decoder_in_channels
|
||||
self.decoder_ffn_dim = decoder_ffn_dim
|
||||
self.num_feature_levels = num_feature_levels
|
||||
self.decoder_n_points = decoder_n_points
|
||||
self.decoder_n_levels = decoder_n_levels
|
||||
self.decoder_layers = decoder_layers
|
||||
self.decoder_attention_heads = decoder_attention_heads
|
||||
self.decoder_activation_function = decoder_activation_function
|
||||
self.attention_dropout = attention_dropout
|
||||
self.num_denoising = num_denoising
|
||||
self.label_noise_ratio = label_noise_ratio
|
||||
self.box_noise_scale = box_noise_scale
|
||||
self.learn_initial_query = learn_initial_query
|
||||
self.anchor_image_size = anchor_image_size
|
||||
self.image_size = image_size
|
||||
self.disable_custom_kernels = disable_custom_kernels
|
||||
self.with_box_refine = with_box_refine
|
||||
|
||||
self.encoder_seq_length = math.ceil(self.image_size / 32) * math.ceil(self.image_size / 32)
|
||||
|
||||
def prepare_config_and_inputs(self):
|
||||
pixel_values = floats_tensor([self.batch_size, self.num_channels, self.image_size, self.image_size])
|
||||
|
||||
pixel_mask = torch.ones([self.batch_size, self.image_size, self.image_size], device=torch_device)
|
||||
|
||||
labels = None
|
||||
if self.use_labels:
|
||||
# labels is a list of Dict (each Dict being the labels for a given example in the batch)
|
||||
labels = []
|
||||
for i in range(self.batch_size):
|
||||
target = {}
|
||||
target["class_labels"] = torch.randint(
|
||||
high=self.num_labels, size=(self.n_targets,), device=torch_device
|
||||
)
|
||||
target["boxes"] = torch.rand(self.n_targets, 4, device=torch_device)
|
||||
labels.append(target)
|
||||
|
||||
config = self.get_config()
|
||||
config.num_labels = self.num_labels
|
||||
return config, pixel_values, pixel_mask, labels
|
||||
|
||||
def get_config(self):
|
||||
hidden_sizes = [10, 20, 30, 40]
|
||||
backbone_config = RTDetrResNetConfig(
|
||||
embeddings_size=10,
|
||||
hidden_sizes=hidden_sizes,
|
||||
depths=[1, 1, 2, 1],
|
||||
out_features=["stage2", "stage3", "stage4"],
|
||||
out_indices=[2, 3, 4],
|
||||
)
|
||||
return RTDetrV2Config.from_backbone_configs(
|
||||
backbone_config=backbone_config,
|
||||
encoder_hidden_dim=self.encoder_hidden_dim,
|
||||
encoder_in_channels=hidden_sizes[1:],
|
||||
feat_strides=self.feat_strides,
|
||||
encoder_layers=self.encoder_layers,
|
||||
encoder_ffn_dim=self.encoder_ffn_dim,
|
||||
encoder_attention_heads=self.encoder_attention_heads,
|
||||
dropout=self.dropout,
|
||||
activation_dropout=self.activation_dropout,
|
||||
encode_proj_layers=self.encode_proj_layers,
|
||||
positional_encoding_temperature=self.positional_encoding_temperature,
|
||||
encoder_activation_function=self.encoder_activation_function,
|
||||
activation_function=self.activation_function,
|
||||
eval_size=self.eval_size,
|
||||
normalize_before=self.normalize_before,
|
||||
d_model=self.d_model,
|
||||
num_queries=self.num_queries,
|
||||
decoder_in_channels=self.decoder_in_channels,
|
||||
decoder_ffn_dim=self.decoder_ffn_dim,
|
||||
num_feature_levels=self.num_feature_levels,
|
||||
decoder_n_points=self.decoder_n_points,
|
||||
decoder_n_levels=self.decoder_n_levels,
|
||||
decoder_layers=self.decoder_layers,
|
||||
decoder_attention_heads=self.decoder_attention_heads,
|
||||
decoder_activation_function=self.decoder_activation_function,
|
||||
attention_dropout=self.attention_dropout,
|
||||
num_denoising=self.num_denoising,
|
||||
label_noise_ratio=self.label_noise_ratio,
|
||||
box_noise_scale=self.box_noise_scale,
|
||||
learn_initial_query=self.learn_initial_query,
|
||||
anchor_image_size=self.anchor_image_size,
|
||||
image_size=self.image_size,
|
||||
disable_custom_kernels=self.disable_custom_kernels,
|
||||
with_box_refine=self.with_box_refine,
|
||||
)
|
||||
|
||||
def prepare_config_and_inputs_for_common(self):
|
||||
config, pixel_values, pixel_mask, labels = self.prepare_config_and_inputs()
|
||||
inputs_dict = {"pixel_values": pixel_values}
|
||||
return config, inputs_dict
|
||||
|
||||
def create_and_check_rt_detr_model(self, config, pixel_values, pixel_mask, labels):
|
||||
model = RTDetrV2Model(config=config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
|
||||
result = model(pixel_values=pixel_values, pixel_mask=pixel_mask)
|
||||
result = model(pixel_values)
|
||||
|
||||
self.parent.assertEqual(result.last_hidden_state.shape, (self.batch_size, self.num_queries, self.d_model))
|
||||
|
||||
def create_and_check_rt_detr_object_detection_head_model(self, config, pixel_values, pixel_mask, labels):
|
||||
model = RTDetrV2ForObjectDetection(config=config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
|
||||
result = model(pixel_values=pixel_values, pixel_mask=pixel_mask)
|
||||
result = model(pixel_values)
|
||||
|
||||
self.parent.assertEqual(result.logits.shape, (self.batch_size, self.num_queries, self.num_labels))
|
||||
self.parent.assertEqual(result.pred_boxes.shape, (self.batch_size, self.num_queries, 4))
|
||||
|
||||
result = model(pixel_values=pixel_values, pixel_mask=pixel_mask, labels=labels)
|
||||
|
||||
self.parent.assertEqual(result.loss.shape, ())
|
||||
self.parent.assertEqual(result.logits.shape, (self.batch_size, self.num_queries, self.num_labels))
|
||||
self.parent.assertEqual(result.pred_boxes.shape, (self.batch_size, self.num_queries, 4))
|
||||
|
||||
|
||||
@require_torch
|
||||
class RTDetrV2ModelTest(ModelTesterMixin, PipelineTesterMixin, unittest.TestCase):
|
||||
all_model_classes = (RTDetrV2Model, RTDetrV2ForObjectDetection) if is_torch_available() else ()
|
||||
pipeline_model_mapping = (
|
||||
{"image-feature-extraction": RTDetrV2Model, "object-detection": RTDetrV2ForObjectDetection}
|
||||
if is_torch_available()
|
||||
else {}
|
||||
)
|
||||
is_encoder_decoder = True
|
||||
test_torchscript = False
|
||||
test_pruning = False
|
||||
test_head_masking = False
|
||||
test_missing_keys = False
|
||||
|
||||
# special case for head models
|
||||
def _prepare_for_class(self, inputs_dict, model_class, return_labels=False):
|
||||
inputs_dict = super()._prepare_for_class(inputs_dict, model_class, return_labels=return_labels)
|
||||
|
||||
if return_labels:
|
||||
if model_class.__name__ == "RTDetrV2ForObjectDetection":
|
||||
labels = []
|
||||
for i in range(self.model_tester.batch_size):
|
||||
target = {}
|
||||
target["class_labels"] = torch.ones(
|
||||
size=(self.model_tester.n_targets,), device=torch_device, dtype=torch.long
|
||||
)
|
||||
target["boxes"] = torch.ones(
|
||||
self.model_tester.n_targets, 4, device=torch_device, dtype=torch.float
|
||||
)
|
||||
labels.append(target)
|
||||
inputs_dict["labels"] = labels
|
||||
|
||||
return inputs_dict
|
||||
|
||||
def setUp(self):
|
||||
self.model_tester = RTDetrV2ModelTester(self)
|
||||
self.config_tester = ConfigTester(
|
||||
self,
|
||||
config_class=RTDetrV2Config,
|
||||
has_text_modality=False,
|
||||
common_properties=["hidden_size", "num_attention_heads"],
|
||||
)
|
||||
|
||||
def test_config(self):
|
||||
self.config_tester.run_common_tests()
|
||||
|
||||
def test_rt_detr_model(self):
|
||||
config_and_inputs = self.model_tester.prepare_config_and_inputs()
|
||||
self.model_tester.create_and_check_rt_detr_model(*config_and_inputs)
|
||||
|
||||
def test_rt_detr_object_detection_head_model(self):
|
||||
config_and_inputs = self.model_tester.prepare_config_and_inputs()
|
||||
self.model_tester.create_and_check_rt_detr_object_detection_head_model(*config_and_inputs)
|
||||
|
||||
@unittest.skip(reason="RTDetrV2 does not use inputs_embeds")
|
||||
def test_inputs_embeds(self):
|
||||
pass
|
||||
|
||||
@unittest.skip(reason="RTDetrV2 does not use test_inputs_embeds_matches_input_ids")
|
||||
def test_inputs_embeds_matches_input_ids(self):
|
||||
pass
|
||||
|
||||
@unittest.skip(reason="RTDetrV2 does not support input and output embeddings")
|
||||
def test_model_get_set_embeddings(self):
|
||||
pass
|
||||
|
||||
@unittest.skip(reason="RTDetrV2 does not support input and output embeddings")
|
||||
def test_model_common_attributes(self):
|
||||
pass
|
||||
|
||||
@unittest.skip(reason="RTDetrV2 does not use token embeddings")
|
||||
def test_resize_tokens_embeddings(self):
|
||||
pass
|
||||
|
||||
@unittest.skip(reason="Feed forward chunking is not implemented")
|
||||
def test_feed_forward_chunking(self):
|
||||
pass
|
||||
|
||||
def test_attention_outputs(self):
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
config.return_dict = True
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
inputs_dict["output_attentions"] = True
|
||||
inputs_dict["output_hidden_states"] = False
|
||||
config.return_dict = True
|
||||
model = model_class(config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
outputs = model(**self._prepare_for_class(inputs_dict, model_class))
|
||||
attentions = outputs.encoder_attentions
|
||||
self.assertEqual(len(attentions), self.model_tester.encoder_layers)
|
||||
|
||||
# check that output_attentions also work using config
|
||||
del inputs_dict["output_attentions"]
|
||||
config.output_attentions = True
|
||||
model = model_class(config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
outputs = model(**self._prepare_for_class(inputs_dict, model_class))
|
||||
attentions = outputs.encoder_attentions
|
||||
self.assertEqual(len(attentions), self.model_tester.encoder_layers)
|
||||
|
||||
self.assertListEqual(
|
||||
list(attentions[0].shape[-3:]),
|
||||
[
|
||||
self.model_tester.encoder_attention_heads,
|
||||
self.model_tester.encoder_seq_length,
|
||||
self.model_tester.encoder_seq_length,
|
||||
],
|
||||
)
|
||||
out_len = len(outputs)
|
||||
|
||||
correct_outlen = 13
|
||||
|
||||
# loss is at first position
|
||||
if "labels" in inputs_dict:
|
||||
correct_outlen += 1 # loss is added to beginning
|
||||
# Object Detection model returns pred_logits and pred_boxes
|
||||
if model_class.__name__ == "RTDetrV2ForObjectDetection":
|
||||
correct_outlen += 2
|
||||
|
||||
self.assertEqual(out_len, correct_outlen)
|
||||
|
||||
# decoder attentions
|
||||
decoder_attentions = outputs.decoder_attentions
|
||||
self.assertIsInstance(decoder_attentions, (list, tuple))
|
||||
self.assertEqual(len(decoder_attentions), self.model_tester.decoder_layers)
|
||||
self.assertListEqual(
|
||||
list(decoder_attentions[0].shape[-3:]),
|
||||
[
|
||||
self.model_tester.decoder_attention_heads,
|
||||
self.model_tester.num_queries,
|
||||
self.model_tester.num_queries,
|
||||
],
|
||||
)
|
||||
|
||||
# cross attentions
|
||||
cross_attentions = outputs.cross_attentions
|
||||
self.assertIsInstance(cross_attentions, (list, tuple))
|
||||
self.assertEqual(len(cross_attentions), self.model_tester.decoder_layers)
|
||||
self.assertListEqual(
|
||||
list(cross_attentions[0].shape[-3:]),
|
||||
[
|
||||
self.model_tester.num_queries,
|
||||
self.model_tester.decoder_attention_heads,
|
||||
self.model_tester.decoder_n_levels * self.model_tester.decoder_n_points,
|
||||
],
|
||||
)
|
||||
|
||||
# Check attention is always last and order is fine
|
||||
inputs_dict["output_attentions"] = True
|
||||
inputs_dict["output_hidden_states"] = True
|
||||
model = model_class(config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
outputs = model(**self._prepare_for_class(inputs_dict, model_class))
|
||||
|
||||
if hasattr(self.model_tester, "num_hidden_states_types"):
|
||||
added_hidden_states = self.model_tester.num_hidden_states_types
|
||||
else:
|
||||
# RTDetrV2 should maintin encoder_hidden_states output
|
||||
added_hidden_states = 2
|
||||
self.assertEqual(out_len + added_hidden_states, len(outputs))
|
||||
|
||||
self_attentions = outputs.encoder_attentions
|
||||
|
||||
self.assertEqual(len(self_attentions), self.model_tester.encoder_layers)
|
||||
self.assertListEqual(
|
||||
list(self_attentions[0].shape[-3:]),
|
||||
[
|
||||
self.model_tester.encoder_attention_heads,
|
||||
self.model_tester.encoder_seq_length,
|
||||
self.model_tester.encoder_seq_length,
|
||||
],
|
||||
)
|
||||
|
||||
def test_hidden_states_output(self):
|
||||
def check_hidden_states_output(inputs_dict, config, model_class):
|
||||
model = model_class(config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
|
||||
with torch.no_grad():
|
||||
outputs = model(**self._prepare_for_class(inputs_dict, model_class))
|
||||
|
||||
hidden_states = outputs.encoder_hidden_states if config.is_encoder_decoder else outputs.hidden_states
|
||||
|
||||
expected_num_layers = getattr(
|
||||
self.model_tester, "expected_num_hidden_layers", len(self.model_tester.encoder_in_channels) - 1
|
||||
)
|
||||
self.assertEqual(len(hidden_states), expected_num_layers)
|
||||
|
||||
self.assertListEqual(
|
||||
list(hidden_states[1].shape[-2:]),
|
||||
[
|
||||
self.model_tester.image_size // self.model_tester.feat_strides[-1],
|
||||
self.model_tester.image_size // self.model_tester.feat_strides[-1],
|
||||
],
|
||||
)
|
||||
|
||||
if config.is_encoder_decoder:
|
||||
hidden_states = outputs.decoder_hidden_states
|
||||
|
||||
expected_num_layers = getattr(
|
||||
self.model_tester, "expected_num_hidden_layers", self.model_tester.decoder_layers + 1
|
||||
)
|
||||
|
||||
self.assertIsInstance(hidden_states, (list, tuple))
|
||||
self.assertEqual(len(hidden_states), expected_num_layers)
|
||||
|
||||
self.assertListEqual(
|
||||
list(hidden_states[0].shape[-2:]),
|
||||
[self.model_tester.num_queries, self.model_tester.d_model],
|
||||
)
|
||||
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
inputs_dict["output_hidden_states"] = True
|
||||
check_hidden_states_output(inputs_dict, config, model_class)
|
||||
|
||||
# check that output_hidden_states also work using config
|
||||
del inputs_dict["output_hidden_states"]
|
||||
config.output_hidden_states = True
|
||||
|
||||
check_hidden_states_output(inputs_dict, config, model_class)
|
||||
|
||||
def test_retain_grad_hidden_states_attentions(self):
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
config.output_hidden_states = True
|
||||
config.output_attentions = True
|
||||
|
||||
model_class = self.all_model_classes[0]
|
||||
model = model_class(config)
|
||||
model.to(torch_device)
|
||||
|
||||
inputs = self._prepare_for_class(inputs_dict, model_class)
|
||||
|
||||
outputs = model(**inputs)
|
||||
|
||||
# we take the first output since last_hidden_state is the first item
|
||||
output = outputs[0]
|
||||
|
||||
encoder_hidden_states = outputs.encoder_hidden_states[0]
|
||||
encoder_attentions = outputs.encoder_attentions[0]
|
||||
encoder_hidden_states.retain_grad()
|
||||
encoder_attentions.retain_grad()
|
||||
|
||||
decoder_attentions = outputs.decoder_attentions[0]
|
||||
decoder_attentions.retain_grad()
|
||||
|
||||
cross_attentions = outputs.cross_attentions[0]
|
||||
cross_attentions.retain_grad()
|
||||
|
||||
output.flatten()[0].backward(retain_graph=True)
|
||||
|
||||
self.assertIsNotNone(encoder_hidden_states.grad)
|
||||
self.assertIsNotNone(encoder_attentions.grad)
|
||||
self.assertIsNotNone(decoder_attentions.grad)
|
||||
self.assertIsNotNone(cross_attentions.grad)
|
||||
|
||||
def test_forward_signature(self):
|
||||
config, _ = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
model = model_class(config)
|
||||
signature = inspect.signature(model.forward)
|
||||
arg_names = [*signature.parameters.keys()]
|
||||
expected_arg_names = ["pixel_values"]
|
||||
self.assertListEqual(arg_names[:1], expected_arg_names)
|
||||
|
||||
def test_different_timm_backbone(self):
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
|
||||
# let's pick a random timm backbone
|
||||
config.backbone = "tf_mobilenetv3_small_075"
|
||||
config.backbone_config = None
|
||||
config.use_timm_backbone = True
|
||||
config.backbone_kwargs = {"out_indices": [2, 3, 4]}
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
model = model_class(config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
outputs = model(**self._prepare_for_class(inputs_dict, model_class))
|
||||
|
||||
if model_class.__name__ == "RTDetrV2ForObjectDetection":
|
||||
expected_shape = (
|
||||
self.model_tester.batch_size,
|
||||
self.model_tester.num_queries,
|
||||
self.model_tester.num_labels,
|
||||
)
|
||||
self.assertEqual(outputs.logits.shape, expected_shape)
|
||||
# Confirm out_indices was propogated to backbone
|
||||
self.assertEqual(len(model.model.backbone.intermediate_channel_sizes), 3)
|
||||
else:
|
||||
# Confirm out_indices was propogated to backbone
|
||||
self.assertEqual(len(model.backbone.intermediate_channel_sizes), 3)
|
||||
|
||||
self.assertTrue(outputs)
|
||||
|
||||
def test_hf_backbone(self):
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
|
||||
# Load a pretrained HF checkpoint as backbone
|
||||
config.backbone = "microsoft/resnet-18"
|
||||
config.backbone_config = None
|
||||
config.use_timm_backbone = False
|
||||
config.use_pretrained_backbone = True
|
||||
config.backbone_kwargs = {"out_indices": [2, 3, 4]}
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
model = model_class(config)
|
||||
model.to(torch_device)
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
outputs = model(**self._prepare_for_class(inputs_dict, model_class))
|
||||
|
||||
if model_class.__name__ == "RTDetrV2ForObjectDetection":
|
||||
expected_shape = (
|
||||
self.model_tester.batch_size,
|
||||
self.model_tester.num_queries,
|
||||
self.model_tester.num_labels,
|
||||
)
|
||||
self.assertEqual(outputs.logits.shape, expected_shape)
|
||||
# Confirm out_indices was propogated to backbone
|
||||
self.assertEqual(len(model.model.backbone.intermediate_channel_sizes), 3)
|
||||
else:
|
||||
# Confirm out_indices was propogated to backbone
|
||||
self.assertEqual(len(model.backbone.intermediate_channel_sizes), 3)
|
||||
|
||||
self.assertTrue(outputs)
|
||||
|
||||
def test_initialization(self):
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
|
||||
configs_no_init = _config_zero_init(config)
|
||||
configs_no_init.initializer_bias_prior_prob = 0.2
|
||||
bias_value = -1.3863 # log_e ((1 - 0.2) / 0.2)
|
||||
|
||||
failed_cases = []
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
model = model_class(config=configs_no_init)
|
||||
# Skip the check for the backbone
|
||||
for name, module in model.named_modules():
|
||||
if module.__class__.__name__ == "RTDetrV2ConvEncoder":
|
||||
backbone_params = [f"{name}.{key}" for key in module.state_dict().keys()]
|
||||
break
|
||||
|
||||
for name, param in model.named_parameters():
|
||||
if param.requires_grad:
|
||||
if ("class_embed" in name and "bias" in name) or "enc_score_head.bias" in name:
|
||||
bias_tensor = torch.full_like(param.data, bias_value)
|
||||
if not torch.allclose(param.data, bias_tensor, atol=1e-4):
|
||||
failed_cases.append(
|
||||
f"Parameter {name} of model {model_class} seems not properly initialized. "
|
||||
f"Biases should be initialized to {bias_value}, got {param.data}"
|
||||
)
|
||||
elif (
|
||||
"level_embed" in name
|
||||
or "sampling_offsets.bias" in name
|
||||
or "value_proj" in name
|
||||
or "output_proj" in name
|
||||
or "reference_points" in name
|
||||
or "enc_score_head.weight" in name
|
||||
or ("class_embed" in name and "weight" in name)
|
||||
or name in backbone_params
|
||||
):
|
||||
continue
|
||||
else:
|
||||
mean = param.data.mean()
|
||||
round_mean = (mean * 1e9).round() / 1e9
|
||||
round_mean = round_mean.item()
|
||||
if round_mean not in [0.0, 1.0]:
|
||||
failed_cases.append(
|
||||
f"Parameter {name} of model {model_class} seems not properly initialized. "
|
||||
f"Mean is {round_mean}, but should be in [0, 1]"
|
||||
)
|
||||
|
||||
message = "\n" + "\n".join(failed_cases)
|
||||
self.assertTrue(not failed_cases, message)
|
||||
|
||||
@parameterized.expand(["float32", "float16", "bfloat16"])
|
||||
@require_torch_gpu
|
||||
@slow
|
||||
def test_inference_with_different_dtypes(self, torch_dtype_str):
|
||||
torch_dtype = {
|
||||
"float32": torch.float32,
|
||||
"float16": torch.float16,
|
||||
"bfloat16": torch.bfloat16,
|
||||
}[torch_dtype_str]
|
||||
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
model = model_class(config)
|
||||
model.to(torch_device).to(torch_dtype)
|
||||
model.eval()
|
||||
for key, tensor in inputs_dict.items():
|
||||
if tensor.dtype == torch.float32:
|
||||
inputs_dict[key] = tensor.to(torch_dtype)
|
||||
with torch.no_grad():
|
||||
_ = model(**self._prepare_for_class(inputs_dict, model_class))
|
||||
|
||||
@parameterized.expand(["float32", "float16", "bfloat16"])
|
||||
@require_torch_gpu
|
||||
@slow
|
||||
def test_inference_equivalence_for_static_and_dynamic_anchors(self, torch_dtype_str):
|
||||
torch_dtype = {
|
||||
"float32": torch.float32,
|
||||
"float16": torch.float16,
|
||||
"bfloat16": torch.bfloat16,
|
||||
}[torch_dtype_str]
|
||||
|
||||
config, inputs_dict = self.model_tester.prepare_config_and_inputs_for_common()
|
||||
h, w = inputs_dict["pixel_values"].shape[-2:]
|
||||
|
||||
# convert inputs to the desired dtype
|
||||
for key, tensor in inputs_dict.items():
|
||||
if tensor.dtype == torch.float32:
|
||||
inputs_dict[key] = tensor.to(torch_dtype)
|
||||
|
||||
for model_class in self.all_model_classes:
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
model_class(config).save_pretrained(tmpdirname)
|
||||
model_static = model_class.from_pretrained(
|
||||
tmpdirname, anchor_image_size=[h, w], device_map=torch_device, torch_dtype=torch_dtype
|
||||
).eval()
|
||||
model_dynamic = model_class.from_pretrained(
|
||||
tmpdirname, anchor_image_size=None, device_map=torch_device, torch_dtype=torch_dtype
|
||||
).eval()
|
||||
|
||||
self.assertIsNotNone(model_static.config.anchor_image_size)
|
||||
self.assertIsNone(model_dynamic.config.anchor_image_size)
|
||||
|
||||
with torch.no_grad():
|
||||
outputs_static = model_static(**self._prepare_for_class(inputs_dict, model_class))
|
||||
outputs_dynamic = model_dynamic(**self._prepare_for_class(inputs_dict, model_class))
|
||||
|
||||
self.assertTrue(
|
||||
torch.allclose(
|
||||
outputs_static.last_hidden_state, outputs_dynamic.last_hidden_state, rtol=1e-4, atol=1e-4
|
||||
),
|
||||
f"Max diff: {(outputs_static.last_hidden_state - outputs_dynamic.last_hidden_state).abs().max()}",
|
||||
)
|
||||
|
||||
|
||||
TOLERANCE = 1e-4
|
||||
|
||||
|
||||
# We will verify our results on an image of cute cats
|
||||
def prepare_img():
|
||||
image = Image.open("./tests/fixtures/tests_samples/COCO/000000039769.png")
|
||||
return image
|
||||
|
||||
|
||||
@require_torch
|
||||
@require_vision
|
||||
class RTDetrV2ModelIntegrationTest(unittest.TestCase):
|
||||
@cached_property
|
||||
def default_image_processor(self):
|
||||
return RTDetrImageProcessor.from_pretrained(CHECKPOINT) if is_vision_available() else None
|
||||
|
||||
def test_inference_object_detection_head(self):
|
||||
model = RTDetrV2ForObjectDetection.from_pretrained(CHECKPOINT).to(torch_device)
|
||||
|
||||
image_processor = self.default_image_processor
|
||||
image = prepare_img()
|
||||
inputs = image_processor(images=image, return_tensors="pt").to(torch_device)
|
||||
|
||||
with torch.no_grad():
|
||||
outputs = model(**inputs)
|
||||
|
||||
expected_shape_logits = torch.Size((1, 300, model.config.num_labels))
|
||||
self.assertEqual(outputs.logits.shape, expected_shape_logits)
|
||||
|
||||
expected_logits = torch.tensor(
|
||||
[
|
||||
[-3.7047, -5.1914, -6.1787],
|
||||
[-4.0108, -9.3449, -5.2047],
|
||||
[-4.1287, -4.7461, -5.8633],
|
||||
]
|
||||
).to(torch_device)
|
||||
expected_boxes = torch.tensor(
|
||||
[
|
||||
[0.2582, 0.5497, 0.4764],
|
||||
[0.1684, 0.1985, 0.2120],
|
||||
[0.7665, 0.4146, 0.4669],
|
||||
]
|
||||
).to(torch_device)
|
||||
|
||||
torch.testing.assert_close(outputs.logits[0, :3, :3], expected_logits, atol=1e-4, rtol=1e-4)
|
||||
|
||||
expected_shape_boxes = torch.Size((1, 300, 4))
|
||||
self.assertEqual(outputs.pred_boxes.shape, expected_shape_boxes)
|
||||
torch.testing.assert_close(outputs.pred_boxes[0, :3, :3], expected_boxes, atol=1e-4, rtol=1e-4)
|
||||
|
||||
# verify postprocessing
|
||||
results = image_processor.post_process_object_detection(
|
||||
outputs, threshold=0.0, target_sizes=[image.size[::-1]]
|
||||
)[0]
|
||||
expected_scores = torch.tensor([0.9652, 0.9599, 0.9462, 0.8613], device=torch_device)
|
||||
expected_labels = [15, 15, 65, 57]
|
||||
expected_slice_boxes = torch.tensor(
|
||||
[
|
||||
[3.4114e02, 2.5111e01, 6.3998e02, 3.7289e02],
|
||||
[1.2780e01, 5.6346e01, 3.1767e02, 4.7134e02],
|
||||
[3.9959e01, 7.3117e01, 1.7565e02, 1.1744e02],
|
||||
[-1.0521e-01, 2.9717e00, 6.3989e02, 4.7362e02],
|
||||
],
|
||||
device=torch_device,
|
||||
)
|
||||
self.assertTrue(torch.allclose(results["scores"][:4], expected_scores, atol=1e-3, rtol=1e-4))
|
||||
self.assertSequenceEqual(results["labels"][:4].tolist(), expected_labels)
|
||||
torch.testing.assert_close(results["boxes"][:4], expected_slice_boxes, atol=1e-3, rtol=1e-4)
|
||||
@@ -203,6 +203,20 @@ SPECIAL_CASES_TO_ALLOW = {
|
||||
"weight_loss_giou",
|
||||
"weight_loss_vfl",
|
||||
],
|
||||
"RTDetrV2Config": [
|
||||
"eos_coefficient",
|
||||
"focal_loss_alpha",
|
||||
"focal_loss_gamma",
|
||||
"matcher_alpha",
|
||||
"matcher_bbox_cost",
|
||||
"matcher_class_cost",
|
||||
"matcher_gamma",
|
||||
"matcher_giou_cost",
|
||||
"use_focal_loss",
|
||||
"weight_loss_bbox",
|
||||
"weight_loss_giou",
|
||||
"weight_loss_vfl",
|
||||
],
|
||||
"YolosConfig": [
|
||||
"bbox_cost",
|
||||
"bbox_loss_coefficient",
|
||||
|
||||
Reference in New Issue
Block a user