-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lexiconfree beam search #101
base: master
Are you sure you want to change the base?
Conversation
Two general points:
|
With this PR alone, the search algorithm is not usable yet. I will make PR's for an Flf node and python bindings separately. But I can include the
Yeah, probably. Maybe even into |
src/Search/LexiconfreeTimesyncBeamSearch/LexiconfreeTimesyncBeamSearch.hh
Outdated
Show resolved
Hide resolved
src/Search/LexiconfreeTimesyncBeamSearch/LexiconfreeTimesyncBeamSearch.cc
Outdated
Show resolved
Hide resolved
src/Search/LexiconfreeTimesyncBeamSearch/LexiconfreeTimesyncBeamSearch.cc
Outdated
Show resolved
Hide resolved
log() << extensions.size() << " candidates survived beam pruning"; | ||
} | ||
|
||
std::sort(extensions.begin(), extensions.end()); |
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.
Do they actually have to be sorted if no score pruning is done? If not, I would put that in the if-statement below.
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.
Yes, the recombination also requires the hypotheses to be sorted and we also want beam.front()
to be the single-best in the end.
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.
But you can find the first-best hypotheses faster than in N * log(N). So I would recommend to just compute the minimum for that. Pruning can also be done in linear time. In recombination you don't need order either, you can just check the scores, right?
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.
The order is needed in the recombination because it is assumed that the best hypothesis is first and if there is a hypothesis with the same scoring context later, we know that its score is worse.
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.
I can rewrite the pruning and recombination functions to not depend on sorted hypotheses.
src/Search/LexiconfreeTimesyncBeamSearch/LexiconfreeTimesyncBeamSearch.hh
Outdated
Show resolved
Hide resolved
…lattice from beam
static const Core::ParameterBool paramUseSentenceEnd; | ||
static const Core::ParameterBool paramSentenceEndIndex; |
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.
These two are currently not used. I don't know if you want keep them if at some point you introduce sentence-end handling or if you want to remove them for now.
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.
Global comment: It would be nice if the order of method definitions would match the order of declarations in the class.
SearchAlgorithmV2* Module_::createSearchAlgorithm(const Core::Configuration& config) const { | ||
SearchAlgorithmV2* searchAlgorithm = 0; |
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.
SearchAlgorithmV2* Module_::createSearchAlgorithm(const Core::Configuration& config) const { | |
SearchAlgorithmV2* searchAlgorithm = 0; | |
SearchAlgorithmV2* Module_::createSearchAlgorithmV2(const Core::Configuration& config) const { | |
SearchAlgorithmV2* searchAlgorithm = nullptr; |
src/Search/Module.hh
Outdated
SearchAlgorithm* createRecognizer(SearchType type, const Core::Configuration& config) const; | ||
LatticeHandler* createLatticeHandler(const Core::Configuration& c) const; | ||
SearchAlgorithm* createRecognizer(SearchType type, const Core::Configuration& config) const; | ||
SearchAlgorithmV2* createSearchAlgorithm(const Core::Configuration& config) const; |
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.
SearchAlgorithmV2* createSearchAlgorithm(const Core::Configuration& config) const; | |
SearchAlgorithmV2* createSearchAlgorithmV2(const Core::Configuration& config) const; |
src/Search/Traceback.cc
Outdated
#include "Traceback.hh" | ||
#include <Lattice/LatticeAdaptor.hh> | ||
#include <Speech/Types.hh> | ||
#include <stack> | ||
|
||
#include <stack> |
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.
#include "Traceback.hh" | |
#include <Lattice/LatticeAdaptor.hh> | |
#include <Speech/Types.hh> | |
#include <stack> | |
#include <stack> | |
#include "Traceback.hh" | |
#include <stack> | |
#include <Lattice/LatticeAdaptor.hh> | |
#include <Speech/Types.hh> |
@@ -46,6 +46,7 @@ CHECK_O = $(OBJDIR)/check.o \ | |||
../Mm/libSprintMm.$(a) \ | |||
../Mc/libSprintMc.$(a) \ | |||
../Search/libSprintSearch.$(a) \ |
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.
Better: use LIBS_SEARCH
here and also for all other Makefiles below.
@@ -12,6 +12,7 @@ TARGETS = archiver$(exe) | |||
ARCHIVER_O = $(OBJDIR)/Archiver.o \ | |||
../../Speech/libSprintSpeech.$(a) \ | |||
../../Search/libSprintSearch.$(a) \ | |||
../../Search/LexiconfreeTimesyncBeamSearch/libSprintLexiconfreeTimesyncBeamSearch.$(a) \ |
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.
correct indentation please.
/* | ||
* Collect all possible extensions for all hypotheses in the beam. | ||
*/ | ||
std::vector<ExtensionCandidate> extensions; |
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.
In AdvancedTreeSearch the vector for hypotheses expansion is also part of the class and not reallocated in every step. Maybe you could consider doing the same.
log() << extensions.size() << " candidates survived beam pruning"; | ||
} | ||
|
||
std::sort(extensions.begin(), extensions.end()); |
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.
But you can find the first-best hypotheses faster than in N * log(N). So I would recommend to just compute the minimum for that. Pruning can also be done in linear time. In recombination you don't need order either, you can just check the scores, right?
/* | ||
* Create new beam from surviving extensions. | ||
*/ | ||
std::vector<LabelHypothesis> newBeam; |
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.
See comment above on preallocating vectors.
scorePruning(extensions); | ||
|
||
if (debugLogging_) { | ||
log() << extensions.size() << " candidates survived score pruning"; |
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.
Maybe you could compute the min/avg/max statistics and log them at the end of the segment like in advanced-tree-search.
* Create scoring requests for the label scorer. | ||
* Each extension candidate makes up a request. | ||
*/ | ||
std::vector<Nn::LabelScorer::Request> requests; |
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.
Could we avoid filling that extensions vector above and directly fill out the requests?
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.
We could maybe combine filling the requests and the extensions vector in the same loop? But I don't think we can completely avoid the extensions vector.
Simple time synchronous beam search algorithm based on the new
SearchAlgorithmV2
interface. Does not use (proper) pronunciation lexicon, word-level LM or transition model. Performs special handling of blank if a blank index is set. Main purpose is open vocabulary search with CTC/Neural Transducer (or similar) models.Supports global pruning by max beam-size and by score difference to the best hypothesis. Uses a LabelScorer to context initialization/extension and scoring.
The search requires a lexicon that represents the vocabulary. Each lemma is viewed as a token with its index in the lexicon corresponding to the associated output index of the LabelScorer.
Depends on #103 and #104.