-
Notifications
You must be signed in to change notification settings - Fork 2.2k
add equality check for index field; allennlp interpret #3073
Conversation
Can you add a test case to this that fails before this change and passes afterward? |
Actually, it looks like there is a test to make sure we don't have this functionality, see here https://github.com/allenai/allennlp/blob/master/allennlp/tests/data/fields/index_field_test.py#L41. Any reason why that is there @joelgrus? |
originally an so we changed that, but I added a test that it wasn't equal to a different index field because that was not desired / expected behavior (at the time, maybe it is now) |
that said, I mostly feel like it's the right behavior (if you look at the original issue, I wasn't a huge fan of the proposed change) and that you probably shouldn't be checking fields for equality this way. What's the argument for allowing it? |
Im using this in an update to the |
Imagine you want to compare two More broadly, though, why should we have some |
In particular, this is the default implementation: allennlp/allennlp/data/fields/field.py Lines 113 to 115 in 5014d02
Seems like classes that override this should only be more permissive, not less. |
I get the use case, I'm just not sure that two Instances should be equal (in a Python object sense) whenever their fields contain the same data. If I have two IndexFields that both contain 3 but have different underlying SequenceFields should they be equal? It's not entirely clear to me how surprising that would be or if it might cause hard-to-debug errors. |
If they have different underlying sequence fields, |
>>> from allennlp.data.fields import Field, IndexField
>>> f = IndexField(3, None)
>>> g = IndexField(3, None)
>>> f == g
False
>>> Field.__eq__(f, g)
True
>>> |
but that's not the proposed change?
or am I missing something? |
I haven't actually looked at the proposed change, other than to ask for a test. I was responding to your arguments. I definitely agree that that change is wrong, it should just fall back to a More tests, just for kicks: >>> from allennlp.data.fields import Field, IndexField, TextField
>>> from allennlp.data import Token
>>> t = TextField([Token('hi')], None)
>>> t2 = TextField([Token('bye')], None)
>>> t3 = TextField([Token('hi')], None)
>>> t == t3
True
>>> t == t2
False
>>> f = IndexField(3, t)
>>> g = IndexField(3, t2)
>>> f == g
False
>>> Field.__eq__(f, g)
False
>>> g = IndexField(3, t3)
>>> f == g
False
>>> Field.__eq__(f, g)
True
>>> |
my arguments were about the proposed change 😢 |
@Eric-Wallace, adding something like the examples I have above would be a good way to test this (and check both cases, that equality fails if the underlying sequence fields don't test equal). |
Sorry =). |
Are you happy with what I'm suggesting? |
Also, to be super clear, I would propose this: def __eq__(self, other) -> bool:
# Allow equality checks to ints that are the sequence index
if isinstance(other, int):
return self.sequence_index == other
return super().__eq__(other) |
yeah, that seems fine |
If the SequenceField's are two different objects but have the same value, then this won't recursively do the equals, right? In my use case I have:
Note the objects are different. Matt's proposed change will say these two index fields are not equal |
Look at the examples I gave. It'll work. |
But I just ran this and it didn't work. I think TextField needs an equals method? |
Is that because you're changing the underlying text field? It clearly works in my example. What's different about what you're doing? |
It's probably best to just make a test case for this that shows your use case. So:
Once you have failing test cases with this implementation, I'll be in a much better situation to help figure out any remaining issues. |
Yeah I have two text fields that have the same everything but are different objects. |
See |
The reason mine is failing was because my |
because we are deepcopying the instances each time I think |
I am comfortable adding a similar |
Seems there already was one, and the problem was in CharacterTokenizer. I just added an equals like the above that checks the |
so this seems like we're creating an enormously complicated solution to a not complicated problem? you want to change the minimal number of inputs so that the outputs change. why not just compare the outputs directly rather than shoving them into existing fields [?] and comparing the fields? |
like, it's a bad code smell for me that suddenly we have to care very deeply about when fields are equal and when token indexers [!] are equal when all we really want to do is compare some model outputs. Am I missing something? |
@@ -156,7 +156,8 @@ def attack_from_json(self, | |||
current_instance_labeled = self.predictor.predictions_to_labeled_instances(current_instance, | |||
outputs)[0] | |||
# if the prediction has changed, then stop | |||
if any(current_instance_labeled[field] != fields_to_compare[field] for field in fields_to_compare): | |||
if any(not current_instance_labeled[field].__eq__(fields_to_compare[field]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't call .__eq__
, just use ==
. I only did that in my example to force using the base class implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you shouldn't need to change this line at all. Why do you need to change this? Changing the other parts should have been sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O I see. I didn't know how equals works in python.
In general, yes I think it is reasonable to expect our data objects to test for equality of state, not equality of id. This is something we already support. We have run into this issue before, which is why both the It's possible that there was a better way to check for equality of model outputs (I'm not certain about that, though, because the code needs to be entirely generic about what model it gets, and it forced a particular design). But, again, what they did worked for everything except |
Ok, this should do it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. @joelgrus, I'm going to merge this soon, unless you're still strongly opposed to this.
* add equality check for index field; allennlp interpret * add test * change hotflip to use equals method * tests per matt * newline * change input reduction to eq also * undo * add newline * fix pylutn
This is needed in AllenNLP interpret to check for equality between two reading comprehension model outputs.