Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
b85036f
add initial design for uniform processors + align model
molbap Jun 3, 2024
bb8ac70
fix mutable default :eyes:
molbap Jun 3, 2024
cd8c601
add configuration test
molbap Jun 3, 2024
f00c852
handle structured kwargs w defaults + add test
molbap Jun 3, 2024
693036f
protect torch-specific test
molbap Jun 3, 2024
766da3a
fix style
molbap Jun 3, 2024
844394d
fix
molbap Jun 3, 2024
c19bbc6
fix assertEqual
molbap Jun 4, 2024
3c38119
move kwargs merging to processing common
molbap Jun 4, 2024
81ae819
rework kwargs for type hinting
molbap Jun 5, 2024
ce4abcd
just get Unpack from extensions
molbap Jun 7, 2024
3acdf28
run-slow[align]
molbap Jun 7, 2024
404239f
handle kwargs passed as nested dict
molbap Jun 7, 2024
603be40
add from_pretrained test for nested kwargs handling
molbap Jun 7, 2024
71c9d6c
[run-slow]align
molbap Jun 7, 2024
26383c5
update documentation + imports
molbap Jun 7, 2024
4521f4f
update audio inputs
molbap Jun 7, 2024
b96eb64
protect audio types, silly
molbap Jun 7, 2024
9c5c01c
try removing imports
molbap Jun 7, 2024
3ccb505
make things simpler
molbap Jun 7, 2024
142acf3
simplerer
molbap Jun 7, 2024
60a5730
move out kwargs test to common mixin
molbap Jun 10, 2024
be6c141
[run-slow]align
molbap Jun 10, 2024
84135d7
skip tests for old processors
molbap Jun 10, 2024
ce967ac
[run-slow]align, clip
molbap Jun 10, 2024
f78ec52
!$#@!! protect imports, darn it
molbap Jun 10, 2024
52fd5ad
[run-slow]align, clip
molbap Jun 10, 2024
8f21abe
Merge branch 'main' into uniform_processors_1
molbap Jun 10, 2024
d510030
[run-slow]align, clip
molbap Jun 10, 2024
fd43bcd
update doc
molbap Jun 11, 2024
b2cd7c9
improve documentation for default values
molbap Jun 11, 2024
bcbd646
add model_max_length testing
molbap Jun 11, 2024
39c1587
Raise if kwargs are specified in two places
molbap Jun 11, 2024
1f73bdf
fix
molbap Jun 11, 2024
b3f98ba
Merge branch 'main' into uniform_processors_1
molbap Jun 11, 2024
e4d6d12
expand VideoInput
molbap Jun 12, 2024
1e09e4a
fix
molbap Jun 12, 2024
d4232f0
fix style
molbap Jun 12, 2024
162b1a7
remove defaults values
molbap Jun 12, 2024
0da1dc3
add comment to indicate documentation on adding kwargs
molbap Jun 12, 2024
f955510
Merge branch 'main' into uniform_processors_1
molbap Jun 12, 2024
f6f1dac
protect imports
molbap Jun 12, 2024
c4b7e84
[run-slow]align
molbap Jun 12, 2024
3ce3608
fix
molbap Jun 12, 2024
6b83e39
remove set() that breaks ordering
molbap Jun 13, 2024
3818b86
test more
molbap Jun 13, 2024
31b7a60
removed unused func
molbap Jun 13, 2024
4072336
[run-slow]align
molbap Jun 13, 2024
bcce007
Merge branch 'main' into uniform_processors_1
molbap Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 148 additions & 23 deletions src/transformers/models/align/processing_align.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,66 @@
Image/Text processor class for ALIGN
"""

from ...processing_utils import ProcessorMixin
from ...tokenization_utils_base import BatchEncoding
from typing import List, Union

from ...image_utils import ImageInput
from ...processing_utils import (
CommonKwargs,
ImagesKwargs,
ProcessingKwargs,
ProcessorMixin,
TextKwargs,
)
from ...tokenization_utils_base import BatchEncoding, PreTokenizedInput, TextInput
from ...utils import is_torch_available, is_vision_available


# TODO (@molbap) This is a bother, forward references from TypedDict are resolved and need this to work
if is_vision_available():
import PIL # noqa: F401
if is_torch_available():
import torch # noqa: F401
Comment thread
molbap marked this conversation as resolved.
Outdated


class AlignProcessorKwargs(ProcessingKwargs, total=False):
"""
Inherits from `ProcessingKwargs` to provide:
1) Additional keys that this model requires to process inputs.
2) Default values for extra keys.
New keys have to be defined as follows to ensure type hinting is done correctly.

```python
common_kwargs: CommonKwargs = {
**CommonKwargs.__annotations__,
}
text_kwargs: TextKwargs = {
**TextKwargs.__annotations__,
"a_new_text_boolean_key": Optional[bool],
}
images_kwargs: ImagesKwargs = {
**ImagesKwargs.__annotations__,
"a_new_image_processing_key": Optional[int]
}
```

"""

common_kwargs: CommonKwargs = {
**CommonKwargs.__annotations__,
}
text_kwargs: TextKwargs = {
**TextKwargs.__annotations__,
}
images_kwargs: ImagesKwargs = {
**ImagesKwargs.__annotations__,
}

_defaults = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_defaults = {
padding: "max_length"
max_lenght: 64

should work no? Or does it not update the default for type-hints?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it works for sure, this was to have a structured dict for defaults. Can change :)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, now I remember, it actually can't work like that since Typed Dicts don't support default values, they are made to hold the layout. They can have any attributes however, but it won't pass a value as default -like a dataclass would, but in this case we'd lose typing-, hence the manual operation

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok got it thanks! Let's maybe comment about this!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a comment for future code inspectors? I'm assuming here isn't the best place (we don't want it for all models) but didn't find a corresponding one elsewhere on a quick skim

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On that: there's doc in processing_utils.ProcessingKwargs, I added a comment nudging users to check there for documentation!

"text_kwargs": {
"padding": "max_length",
"max_length": 64,
},
}


class AlignProcessor(ProcessorMixin):
Expand All @@ -26,12 +84,39 @@ class AlignProcessor(ProcessorMixin):
[`BertTokenizer`]/[`BertTokenizerFast`] into a single processor that interits both the image processor and
tokenizer functionalities. See the [`~AlignProcessor.__call__`] and [`~OwlViTProcessor.decode`] for more
information.
The preferred way of passing kwargs is as a dictionary per modality, see usage example below.
```python
from transformers import AlignProcessor
from PIL import Image
model_id = "kakaobrain/align-base"
processor = AlignProcessor.from_pretrained(model_id)

# Define the kwargs for each modality
common_kwargs = {"return_tensors": "pt"}
images_kwargs = {"crop_size": {"height": 224, "width": 224}}
text_kwargs = {"padding": "do_not_pad"}
Comment thread
molbap marked this conversation as resolved.
Outdated

# Combine them into a single dictionary

all_kwargs = {
"images_kwargs": images_kwargs,
"text_kwargs": text_kwargs,
"common_kwargs": common_kwargs
Comment thread
molbap marked this conversation as resolved.
Outdated
}

processor(images=your_pil_image, text=["What is that?"], **all_kwargs)

# passing directly any number of kwargs is also supported, but not recommended

processor(images=your_pil_image, text=["What is that?"], padding="do_not_pad)
```

Args:
image_processor ([`EfficientNetImageProcessor`]):
The image processor is a required input.
tokenizer ([`BertTokenizer`, `BertTokenizerFast`]):
The tokenizer is a required input.

"""

attributes = ["image_processor", "tokenizer"]
Expand All @@ -41,11 +126,21 @@ class AlignProcessor(ProcessorMixin):
def __init__(self, image_processor, tokenizer):
super().__init__(image_processor, tokenizer)

def __call__(self, text=None, images=None, padding="max_length", max_length=64, return_tensors=None, **kwargs):
def __call__(
self,
text: Union[TextInput, PreTokenizedInput, List[TextInput], List[PreTokenizedInput]] = None,
images: ImageInput = None,
audio=None,
videos=None,
text_kwargs: AlignProcessorKwargs.text_kwargs = None,
images_kwargs: AlignProcessorKwargs.images_kwargs = None,
common_kwargs: AlignProcessorKwargs.common_kwargs = None,
**kwargs: AlignProcessorKwargs,
) -> BatchEncoding:
"""
Main method to prepare text(s) and image(s) to be fed as input to the model. This method forwards the `text`
and `kwargs` arguments to BertTokenizerFast's [`~BertTokenizerFast.__call__`] if `text` is not `None` to encode
the text. To prepare the image(s), this method forwards the `images` and `kwargs` arguments to
arguments to BertTokenizerFast's [`~BertTokenizerFast.__call__`] if `text` is not `None` to encode
the text. To prepare the image(s), this method forwards the `images` arguments to
EfficientNetImageProcessor's [`~EfficientNetImageProcessor.__call__`] if `images` is not `None`. Please refer
to the doctsring of the above two methods for more information.

Expand All @@ -57,20 +152,12 @@ def __call__(self, text=None, images=None, padding="max_length", max_length=64,
images (`PIL.Image.Image`, `np.ndarray`, `torch.Tensor`, `List[PIL.Image.Image]`, `List[np.ndarray]`, `List[torch.Tensor]`):
The image or batch of images to be prepared. Each image can be a PIL image, NumPy array or PyTorch
tensor. Both channels-first and channels-last formats are supported.
padding (`bool`, `str` or [`~utils.PaddingStrategy`], *optional*, defaults to `max_length`):
Activates and controls padding for tokenization of input text. Choose between [`True` or `'longest'`,
`'max_length'`, `False` or `'do_not_pad'`]
max_length (`int`, *optional*, defaults to `max_length`):
Maximum padding value to use to pad the input text during tokenization.

return_tensors (`str` or [`~utils.TensorType`], *optional*):
If set, will return tensors of a particular framework. Acceptable values are:

- `'tf'`: Return TensorFlow `tf.constant` objects.
- `'pt'`: Return PyTorch `torch.Tensor` objects.
- `'np'`: Return NumPy `np.ndarray` objects.
- `'jax'`: Return JAX `jnp.ndarray` objects.

- `'tf'`: Return TensorFlow `tf.constant` objects.
- `'pt'`: Return PyTorch `torch.Tensor` objects.
- `'np'`: Return NumPy `np.ndarray` objects.
- `'jax'`: Return JAX `jnp.ndarray` objects.
Returns:
[`BatchEncoding`]: A [`BatchEncoding`] with the following fields:

Expand All @@ -81,15 +168,53 @@ def __call__(self, text=None, images=None, padding="max_length", max_length=64,
- **pixel_values** -- Pixel values to be fed to a model. Returned when `images` is not `None`.
"""
if text is None and images is None:
raise ValueError("You have to specify either text or images. Both cannot be none.")

raise ValueError("You must specify either text or images.")
# set kwargs as empty dicts to avoid default mutable
if text_kwargs is None:
text_kwargs = {}
if images_kwargs is None:
images_kwargs = {}
if common_kwargs is None:
common_kwargs = {}
# Init with default values if they exist
default_text_kwargs = AlignProcessorKwargs._defaults.get("text_kwargs", {}).copy()

# then override with tokenizer-level arguments passed
default_text_kwargs.update(
{k: v for k, v in self.tokenizer.init_kwargs.items() if k in AlignProcessorKwargs.text_kwargs}
)
# then get passed per-modality dictionaries if they exist
text_kwargs = {**default_text_kwargs, **text_kwargs, **kwargs.pop("text_kwargs", {})}
images_kwargs.update(kwargs.pop("images_kwargs", {}))
common_kwargs.update(kwargs.pop("common_kwargs", {}))

# then merge kwargs by name
for text_key in AlignProcessorKwargs.text_kwargs.keys():
text_kwarg_value = kwargs.pop(text_key, None)
if text_kwarg_value is not None:
text_kwargs[text_key] = text_kwarg_value

for images_key in AlignProcessorKwargs.images_kwargs.keys():
images_kwarg_value = kwargs.pop(images_key, None)
if images_kwarg_value is not None:
images_kwargs[images_key] = images_kwarg_value
# if something remains in kwargs, it belongs to common
common_kwargs.update(kwargs)

# all modality-specific kwargs are updated with common kwargs
text_kwargs.update(common_kwargs)
images_kwargs.update(common_kwargs)

# then, we can pass correct kwargs to each processor
if text is not None:
encoding = self.tokenizer(
text, padding=padding, max_length=max_length, return_tensors=return_tensors, **kwargs
)
encoding = self.tokenizer(text, **text_kwargs)

if images is not None:
image_features = self.image_processor(images, return_tensors=return_tensors, **kwargs)
image_features = self.image_processor(images, **images_kwargs)

# BC for explicit return_tensors
if "return_tensors" in common_kwargs:
return_tensors = common_kwargs.pop("return_tensors", None)

if text is not None and images is not None:
encoding["pixel_values"] = image_features.pixel_values
Expand Down
Loading