feat(medcat): Allow bulk superivsed training - #598
Open
mart-r wants to merge 39 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR allows bulk supervised training.
The idea is that instead of the previous supervised training method:
we now use a new one:
This refers to the method on the component wishing to be trained in a supervised manner.
The changes
There's a fair few changes that had to be made to make this all work
New protocol for supervised training
The new protocol is the
BatchTrainableComponent.This only defines the new method.
Any future implementation is expected to use this going forward.
The method uses
TrainingExampleobjects.This is a dataclass that keeps track of information relevant to each example.
This includes stuff like the CUI, the entity, the document, whether it's a negative example, how many epochs to do.
But on top of that, it also keeps track of things (empty for now, but these were also empty before) for name description, status, and ontology (which can be used when adding new names).
Supporting both the old and new method
Because this would otherwise be a breaking change, I wanted to support both the old and the new method.
This means that components that follow the old protocol should still work.
The main workhorse for this is the
_LegacyBatchAdapterintype_utils.This adapts the old style to the new one by calling the old method in sequence.
Avoiding changing names for supervised training
There's a part of the vector context model that allows the (artificial) changing of the name of the concept being trained.
At some chance (as per config), the name that actually appears in the text can be replaced with a synonym.
This was meant to include less-common synonyms in self-supervised training.
However, for supervised training, this does not make a lot of sense.
As such, this was disabled.
I would say that this was somewhat of a bug before.
I don't think it should have ever affected supervised training.
Fixed some annotation/entity mismatch issue
Since the PR #374 (in v2.7.0) there has been an issue with annotation / antity mismatch when doing supervised training.
The culprit was the preprocessing of each annotation that meant that some (ones that weren't able to be mapped to an entity - i.e sub-token annotations) were dropped.
And when iterating over the entities and annotations later (in a zip), after the dropped annotation there will have been mismatches there.
This has now been fixed.
Some changes to string ordering
Due to the random nature of string hashing, getting ordered output from sets (e.g when taking a random sample for name changes) is unpredictable even if the underlying random number generator is seeded.
This can be mitigated by setting the
PYTHONHASHSEEDenvironmental variable.But I didn't think the current situation was desirable - if someone seeds the RNG, they expect to get the same output run to run.
So to mitigate this, in some places (only in the name-change part I believe), string sets are sorted before picking a random item from them.
Some extra debug output
While trying to figure out the differences, I added some extra debug output.
Some of this can be removed if we decide to.
But it helped me identify the various issues I faced along the way.
The testing I did to make sure this still works as close to the previous version as possible:
In order to verify that the updated training method did (roughly speaking) the same thing, I ran a number of tests locally.
I ran the tests for two full models (the 2023 model and the 2025 model).
I ran it over a small number of documents from the entity linking challenge.
I ran it over a number of different configurations:
devalue_others=True/Falsetrain_from_false_positives=True/FalseDuring the tests I seeded the
PYTHONHASHSEEDto avoid string-hashing issues.As the output I used:
CDB.get_basic_info)CDB.get_cui2count_train)CDB.get_name2count_train)What I observed was that in all cases the basic information (i.e the first 4 in the above list) were identical.
While the context vectors did change in some scenarios (more on the reasons below), the differences were not huge.
The overall caveats:
There were some differences between the two models were observed in the cases where
devalue_others=Truewas set.The reason for these differences is boils down to the order of operations.
Previously, the devaluation of others (i.e training on negative examples of other concepts that share the same name to push these further from the current context) was done on a per annotation basis directly after a specific annotation was trained.
However, with the bulk training setup, they are done in bulk after the entire batch has been trained.
This can cause issues due to randomness being involved (e.g for negative sampling), but also due to simply training the same concept / name in a different order (i.e if it had a positive annotation and was considered a negative example due to
devalue_othersin another annotation in the same document).Overall I am relatively happy with the small caveat.
But it is still important to note.