BiliSakura's picture
Upload SoftCon transformers checkpoints with model card metadata
c45eaf7 verified
Raw
History Blame Contribute Delete
6.68 kB
# Copyright 2024 The SoftCon Authors and The HuggingFace Inc. team.
"""Self-contained SoftCon model and config for trust_remote_code loading."""
from typing import Optional
import torch
import torch.nn as nn
from torchvision import models
from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
from transformers.modeling_utils import PreTrainedModel
from transformers.models.dinov2.configuration_dinov2 import Dinov2Config
from transformers.models.dinov2.modeling_dinov2 import Dinov2Model
from transformers.processing_utils import Unpack
from transformers.utils import TransformersKwargs, logging
logger = logging.get_logger(__name__)
class SoftConConfig(PreTrainedConfig):
model_type = "softcon"
def __init__(
self,
backbone="resnet50",
num_channels=13,
modality="s2c",
hidden_size=2048,
image_size=224,
patch_size=14,
init_values=1e-5,
num_hidden_layers=None,
num_attention_heads=None,
num_register_tokens=0,
block_chunks=0,
num_labels=0,
**kwargs,
):
super().__init__(**kwargs)
self.backbone = backbone
self.num_channels = num_channels
self.modality = modality
self.hidden_size = hidden_size
self.image_size = image_size
self.patch_size = patch_size
self.init_values = init_values
self.num_hidden_layers = num_hidden_layers
self.num_attention_heads = num_attention_heads
self.num_register_tokens = num_register_tokens
self.block_chunks = block_chunks
self.num_labels = num_labels
def get_dinov2_config(self) -> Dinov2Config:
if self.backbone not in {"vit_small", "vit_base"}:
raise ValueError(f"Backbone '{self.backbone}' is not a ViT encoder.")
if self.num_hidden_layers is None or self.num_attention_heads is None:
raise ValueError(
"ViT models require `num_hidden_layers` and `num_attention_heads` in the model config."
)
return Dinov2Config(
hidden_size=self.hidden_size,
num_hidden_layers=self.num_hidden_layers,
num_attention_heads=self.num_attention_heads,
image_size=self.image_size,
patch_size=self.patch_size,
num_channels=self.num_channels,
layerscale_value=self.init_values,
use_mask_token=False,
)
class SoftConPreTrainedModel(PreTrainedModel):
config: SoftConConfig
base_model_prefix = "softcon"
main_input_name = "pixel_values"
input_modalities = ("image",)
supports_gradient_checkpointing = False
def _build_resnet_encoder(config: SoftConConfig) -> nn.Module:
backbone = models.resnet50(weights=None)
if config.num_channels != 3:
backbone.conv1 = nn.Conv2d(config.num_channels, 64, kernel_size=7, stride=2, padding=3, bias=False)
backbone.fc = nn.Identity()
return backbone
def _build_vit_encoder(config: SoftConConfig) -> Dinov2Model:
return Dinov2Model(config.get_dinov2_config())
def _build_encoder(config: SoftConConfig) -> nn.Module:
if config.backbone == "resnet50":
return _build_resnet_encoder(config)
if config.backbone in {"vit_small", "vit_base"}:
return _build_vit_encoder(config)
raise ValueError(f"Unsupported backbone '{config.backbone}'")
class SoftConModel(SoftConPreTrainedModel):
def __init__(self, config: SoftConConfig):
super().__init__(config)
self.encoder = _build_encoder(config)
self.post_init()
def _forward_resnet(self, pixel_values: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
x = self.encoder.conv1(pixel_values)
x = self.encoder.bn1(x)
x = self.encoder.relu(x)
x = self.encoder.maxpool(x)
x = self.encoder.layer1(x)
x = self.encoder.layer2(x)
x = self.encoder.layer3(x)
x = self.encoder.layer4(x)
last_hidden_state = x.flatten(2).transpose(1, 2)
pooler_output = self.encoder.avgpool(x).flatten(1)
return last_hidden_state, pooler_output
def _forward_vit(self, pixel_values: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
outputs = self.encoder(pixel_values=pixel_values, return_dict=True)
return outputs.last_hidden_state, outputs.pooler_output
def forward(
self,
pixel_values: Optional[torch.Tensor] = None,
return_dict: Optional[bool] = None,
**kwargs: Unpack[TransformersKwargs],
) -> BaseModelOutputWithPooling:
if pixel_values is None:
raise ValueError("You must specify `pixel_values`")
pixel_values = pixel_values.to(dtype=self.dtype)
if return_dict is None:
return_dict = self.config.use_return_dict
if self.config.backbone == "resnet50":
last_hidden_state, pooler_output = self._forward_resnet(pixel_values)
else:
last_hidden_state, pooler_output = self._forward_vit(pixel_values)
if not return_dict:
return (last_hidden_state, pooler_output)
return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooler_output)
class SoftConForImageClassification(SoftConPreTrainedModel):
def __init__(self, config: SoftConConfig):
super().__init__(config)
self.softcon = SoftConModel(config)
self.classifier = (
nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
)
self.post_init()
def forward(
self,
pixel_values: Optional[torch.Tensor] = None,
labels: Optional[torch.Tensor] = None,
return_dict: Optional[bool] = None,
**kwargs: Unpack[TransformersKwargs],
) -> ImageClassifierOutput:
outputs = self.softcon(pixel_values=pixel_values, return_dict=True, **kwargs)
logits = self.classifier(outputs.pooler_output)
loss = None
if labels is not None:
loss = self.loss_function(labels, logits, self.config, **kwargs)
if not return_dict:
output = (logits,) + outputs[1:]
return ((loss,) + output) if loss is not None else output
return ImageClassifierOutput(
loss=loss,
logits=logits,
hidden_states=outputs.hidden_states,
attentions=outputs.attentions,
)
__all__ = [
"SoftConConfig",
"SoftConForImageClassification",
"SoftConModel",
"SoftConPreTrainedModel",
]