uniformize kwargs for OneFormer#34547
Conversation
cd15539 to
fad4111
Compare
molbap
left a comment
There was a problem hiding this comment.
Thanks for the initiative! Left a couple initial comments
There was a problem hiding this comment.
Cool! But we should also check that the previous signature works as intended as to not break backwards compatibility - in this case the previous behaviour where arguments were passed positionally should still be supported
There was a problem hiding this comment.
I added *args. I had to add it after images, so the signature will only match after another iteration of deprecation. WDYT?
There was a problem hiding this comment.
since this does more than checking the types, I suggest moving the conversion to list out of this function, and rename it explicitly _validate_input_types for instance.
There was a problem hiding this comment.
so this tokenizes which should be done with self.tokenizer(..., output_kwargs['text_kwargs']), and it seems that it puts pad tokens where the attention mask is 0 - this should be covered by the tokenizer already, would be a nice refactor
There was a problem hiding this comment.
I will look into this...
There was a problem hiding this comment.
Atm I don't see how this can be simplified because the function is called with different arguments inside __call__, and it is also called in encode_inputs. Any suggestions?
There was a problem hiding this comment.
Agreed that it would be best to use self.tokenizer directly, and pass in the other text kwargs as well. For max_length, you can do the following:
- if the max_length kwarg is not set, set it to
max_seq_lengthortask_seq_lengthin the two different calls. - if it is set, use its value for both task and seq.
There was a problem hiding this comment.
I tried this (commit 3c7724d), but I don't see yet how it is simpler/more readable :)
Couple of observations:
- the function
_preprocess_textis called both withtask_seq_lengthandmax_seq_length, so if I add"max_length"to thetext_kwargsdict, I would need to add and then replace in__call__. - in order to do something similar to what the attention-mask is doing (padding with 0s), I would need to change
self.tokenizer.pad_token_id(and then change it back to what was before, e.g. 49407 (endoftext), because I can't pass inpad_token_idto the tokenizer. I am most probably missing here something, so suggestions welcome.
There was a problem hiding this comment.
so the way it should be is that in OneFormerProcessorKwargs, these defaults should be passed to the relevant dictionary, which you can then get back. say
_defaults = {"max_seq_length" = 77,
"task_seq_length" = 77}and you can inform the types with a Kwargs typed class
class OneFormerTextKwargs(TextKwargs, total=False):
max_seq_length: Optional[int]
task_seq_length: Optional[int]then your types are correctly informed as well as your defaults and you can use both.
There was a problem hiding this comment.
This was a bigger refactor. Let me know what you think...
|
Hey! @molbap is off as he needs to rest, cc @yonigozlan can you review this? 🤗 |
yonigozlan
left a comment
There was a problem hiding this comment.
Thanks for working on this!
I mainly have comments about the handling of positional kwargs (like in the SAM PR), and about avoiding the wrapping of text processing inside _preprocess_text
There was a problem hiding this comment.
Agreed that it would be best to use self.tokenizer directly, and pass in the other text kwargs as well. For max_length, you can do the following:
- if the max_length kwarg is not set, set it to
max_seq_lengthortask_seq_lengthin the two different calls. - if it is set, use its value for both task and seq.
There was a problem hiding this comment.
With this refactoring, it looks to me like encode_inputs could be made the same as call while preserving backward compatibility (if the args are handled correctly), so maybe we could just use call here?
There was a problem hiding this comment.
Good catch! Done.
I tried this in commit 027003465, took some time until I realized that there is a big difference in the two calls:
encode_inputsis callingOneFormerImageProcessor.encode_inputs__call__is callingOneFormerImageProcessor.__call__, which will eventually call alsoOneFormerImageProcessor.encode_inputs, but only after quite some checks and massaging.
So atm I don't think there is a simple and elegant way to replace and would just leave as is. WDYT?
There was a problem hiding this comment.
Oh I see, never mind then
There was a problem hiding this comment.
These should be added to the optional_call_args attribute (see udop processor)
40cf888 to
cbddaf4
Compare
yonigozlan
left a comment
There was a problem hiding this comment.
There's a breaking change in the init that needs to be fixed. Otherwise it is looking good!
Just like for SAM, you'll also have to add the ProcessorTesterMixin to the processor test class, make sure all the tests pass and override the, if needed, and rebase on main.
There was a problem hiding this comment.
Oh I see, never mind then
There was a problem hiding this comment.
Sorry I hadn't caught that before, but removing max_seq_length and task_seq_length from the init looks like a breaking change. We should add them back, and use self.max_seq_length and self.task_seq_length in the processing when the "max_length" kwarg is not defined. No need to add max_seq_length and task_seq_length to OneFormerTextKwargs as they weren't accepted before.
There was a problem hiding this comment.
I would say the opposite:
| max_length=max_length if max_length is not None else text_kwargs.get("max_length", 77), | |
| max_length=text_kwargs.get("max_length") if text_kwargs.get("max_length") is not None else max_length, |
So that the max_length kwarg overrides the default kwarg when it is specified
There was a problem hiding this comment.
No need for that (see comments after)
There was a problem hiding this comment.
No need to redefine the defaults kwargs here in the get, as if they are not specified, they will necessarily be "max_length" and True respectively, you can just have:
| padding=text_kwargs.get("padding", "max_length"), | |
| truncation=text_kwargs.get("truncation", True), | |
| padding=text_kwargs.get("padding"), | |
| truncation=text_kwargs.get("truncation"), |
There was a problem hiding this comment.
I believe this is needed because encode_inputs does not pass in any dict, so text_kwargs will become {}, without default.
There was a problem hiding this comment.
what is weird for me here is taht we don't use all the textkwargs. Why not just pass **text_kwargs? They can be typed as well (maybe just tokenizer kwargs) and you just update them with the args passed!
There was a problem hiding this comment.
| max_length=output_kwargs["text_kwargs"]["task_seq_length"], | |
| max_length=self.task_seq_length, |
There was a problem hiding this comment.
| max_length=output_kwargs["text_kwargs"]["max_seq_length"], | |
| max_length=self.max_seq_length, |
9ccaa40 to
b579834
Compare
|
@yonigozlan I had to repeat some of the tests, e.g. |
yonigozlan
left a comment
There was a problem hiding this comment.
LGTM after putting the kwargs back in the init! Thanks for iterating on this :)
| image_processor=None, | ||
| tokenizer=None, | ||
| max_seq_length: int = 77, | ||
| task_seq_length: int = 77, |
There was a problem hiding this comment.
Nit removing the **kwargs here would also be a breaking change, even if it's not used anywhere.
Yes I think that's the best way to do it :) |
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
ArthurZucker
left a comment
There was a problem hiding this comment.
Thanks, not sure I get why encode inputs does not pass kwargs, while call does !
There was a problem hiding this comment.
what is weird for me here is taht we don't use all the textkwargs. Why not just pass **text_kwargs? They can be typed as well (maybe just tokenizer kwargs) and you just update them with the args passed!
| text_inputs = [ | ||
| self._preprocess_text( | ||
| texts, | ||
| max_length=self.max_seq_length, | ||
| text_kwargs=output_kwargs["text_kwargs"], | ||
| ).unsqueeze(0) | ||
| for texts in encoded_inputs.text_inputs |
There was a problem hiding this comment.
let's od a for loop it's more redable. Also this is now almost exactly the same as encode_inputs why don't we call that funciton instead / merge both?
There was a problem hiding this comment.
Added the for loop
|
Hi @ArthurZucker,
Regarding
|
| if not all(task in ["semantic", "instance", "panoptic"] for task in task_inputs): | ||
| raise ValueError("task_inputs must be semantic, instance, or panoptic.") |
There was a problem hiding this comment.
typing with choice should allow us not to have to do this
| if not isinstance(task_inputs, List) or not task_inputs: | ||
| raise TypeError("task_inputs should be a string or a list of strings.") |
There was a problem hiding this comment.
typing is enough IMO we should not do this
| if task_inputs is None: | ||
| raise ValueError("You have to specify the task_inputs. Found None.") | ||
| elif images is None: | ||
| raise ValueError("You have to specify the images. Found None.") |
There was a problem hiding this comment.
this would be the only thing left and we can do something like if task_inputs ^ images: you have to specifiy one of task_inputs or images
There was a problem hiding this comment.
Both are needed, so I have put it into one line. wdyt?
| if hasattr(encoded_inputs, "text_inputs"): | ||
| texts_list = encoded_inputs.text_inputs | ||
| task_token_inputs = [] | ||
| for task in task_inputs: |
There was a problem hiding this comment.
if task_inputs is None this will fail
There was a problem hiding this comment.
Yes, would fail. But it is already caught in _validate_input_types before.
|
|
||
| for texts in encoded_inputs.text_inputs: | ||
| text_inputs.append( | ||
| self._preprocess_text( |
There was a problem hiding this comment.
might be worth updating _preprocess_text because the tokenizer supports directly passing batches of text.
Also, task_inputs are written all the time so there is no case when this is not run no?
There was a problem hiding this comment.
Sorry, but I don't understand neither part of the comment here.
This part is only run if text_inputs is part of encoded_inputs - see the if two lines before. Not connected to task_inputs.
Once above is clarified, could you please specify in more detail how to update _preprocess_text? I have the feeling that this will again become simpler once we drop encode_inputs...
There was a problem hiding this comment.
_preprocess_text could only be called once, as the tokenizer.__call__ fonction supports list of list of text
0f7b044 to
77601ed
Compare
|
Rebased |
|
Hey @tibor-reiss, please run |
|
run-slow: oneformer |
|
This comment contains run-slow, running the specified jobs: This comment contains run-slow, running the specified jobs: models: ['models/oneformer'] |
Adds uniformized processors for OneFormer following #31911.
Small changes to simplify code.
Additional check via
test_processor_oneformer.pythat theinputsare the same before and after the changes (with fixed random seeds).@qubvel @molbap