chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,734 @@
|
||||
// decoder/lattice-incremental-decoder.h
|
||||
|
||||
// Copyright 2019 Zhehuai Chen, Daniel Povey
|
||||
|
||||
// See ../../COPYING for clarification regarding multiple authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
// MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef KALDI_DECODER_LATTICE_INCREMENTAL_DECODER_H_
|
||||
#define KALDI_DECODER_LATTICE_INCREMENTAL_DECODER_H_
|
||||
|
||||
#include "util/stl-utils.h"
|
||||
#include "util/hash-list.h"
|
||||
#include "fst/fstlib.h"
|
||||
#include "itf/decodable-itf.h"
|
||||
#include "fstext/fstext-lib.h"
|
||||
#include "lat/determinize-lattice-pruned.h"
|
||||
#include "lat/kaldi-lattice.h"
|
||||
#include "decoder/grammar-fst.h"
|
||||
#include "decoder/lattice-faster-decoder.h"
|
||||
|
||||
namespace kaldi {
|
||||
/**
|
||||
The normal decoder, lattice-faster-decoder.h, sometimes has an issue when
|
||||
doing real-time applications with long utterances, that each time you get the
|
||||
lattice the lattice determinization can take a considerable amount of time;
|
||||
this introduces latency. This version of the decoder spreads the work of
|
||||
lattice determinization out throughout the decoding process.
|
||||
|
||||
NOTE:
|
||||
|
||||
Please see https://www.danielpovey.com/files/ *TBD* .pdf for a technical
|
||||
explanation of what is going on here.
|
||||
|
||||
GLOSSARY OF TERMS:
|
||||
chunk: We do the determinization on chunks of frames; these
|
||||
may coincide with the chunks on which the user calls
|
||||
AdvanceDecoding(). The basic idea is to extract chunks
|
||||
of the raw lattice and determinize them individually, but
|
||||
it gets much more complicated than that. The chunks
|
||||
should normally be at least as long as a word (let's say,
|
||||
at least 20 frames), or the overhead of this algorithm
|
||||
might become excessive and affect RTF.
|
||||
|
||||
raw lattice chunk: A chunk of raw (i.e. undeterminized) lattice
|
||||
that we will determinize. In the paper this corresponds
|
||||
to the FST B that is described in Section 5.2.
|
||||
|
||||
token_label, state_label / token-label, state-label:
|
||||
|
||||
In the paper these are both referred to as `state labels` (these are
|
||||
special, large integer id's that refer to states in the undeterminized
|
||||
lattice and in the the determinized lattice); but we use two separate
|
||||
terms here, for more clarity, when referring to the undeterminized
|
||||
vs. determinized lattice.
|
||||
|
||||
token_label conceptually refers to states in the
|
||||
raw lattice, but we don't materialize the entire
|
||||
raw lattice as a physical FST and and these tokens
|
||||
are actually tokens (template type Token) held by
|
||||
the decoder
|
||||
|
||||
state_label when used in this code refers specifically
|
||||
to labels that identify states in the determinized
|
||||
lattice (i.e. state indexes in lat_).
|
||||
|
||||
token-final state
|
||||
A state in a raw lattice or in a determinized chunk that has an arc
|
||||
entering it that has a `token-label` on it (as defined above).
|
||||
These states will have nonzero final-probs.
|
||||
|
||||
redeterminized-non-splice-state, aka ns_redet:
|
||||
A redeterminized state which is not also a splice state;
|
||||
refer to the paper for explanation. In the already-determinized
|
||||
part this means a redeterminized state which is not final.
|
||||
|
||||
canonical appended lattice: This is the appended compact lattice
|
||||
that we conceptually have (i.e. what we described in the paper).
|
||||
The difference from the "actual appended lattice" stored
|
||||
in LatticeIncrementalDeterminizer::clat_ is that the
|
||||
actual appended lattice has all its final-arcs replaced with
|
||||
final-probs, and we keep the real final-arcs "on the side" in a
|
||||
separate data structure. The final-probs in clat_ aren't
|
||||
necessarily related to the costs on the final-arcs; instead
|
||||
they can have arbitrary values passed in by the user (e.g.
|
||||
if we want to include final-probs). This means that the
|
||||
clat_ can be returned without modification to the user who wants
|
||||
a partially determinized result.
|
||||
|
||||
final-arc: An arc in the canonical appended CompactLattice which
|
||||
goes to a final-state. These arcs will have `state-labels` as
|
||||
their labels.
|
||||
|
||||
*/
|
||||
struct LatticeIncrementalDecoderConfig {
|
||||
// All the configuration values until det_opts are the same as in
|
||||
// LatticeFasterDecoder. For clarity we repeat them rather than inheriting.
|
||||
BaseFloat beam;
|
||||
int32 max_active;
|
||||
int32 min_active;
|
||||
BaseFloat lattice_beam;
|
||||
int32 prune_interval;
|
||||
BaseFloat beam_delta; // has nothing to do with beam_ratio
|
||||
BaseFloat hash_ratio;
|
||||
BaseFloat prune_scale; // Note: we don't make this configurable on the command line,
|
||||
// it's not a very important parameter. It affects the
|
||||
// algorithm that prunes the tokens as we go.
|
||||
// Most of the options inside det_opts are not actually queried by the
|
||||
// LatticeIncrementalDecoder class itself, but by the code that calls it, for
|
||||
// example in the function DecodeUtteranceLatticeIncremental.
|
||||
fst::DeterminizeLatticePhonePrunedOptions det_opts;
|
||||
|
||||
// The configuration values from this point on are specific to the
|
||||
// incremental determinization. See where they are registered for
|
||||
// explanation.
|
||||
// Caution: these are only inspected in UpdateLatticeDeterminization().
|
||||
// If you call
|
||||
int32 determinize_max_delay;
|
||||
int32 determinize_min_chunk_size;
|
||||
int32 determinize_max_active;
|
||||
|
||||
|
||||
LatticeIncrementalDecoderConfig()
|
||||
: beam(16.0),
|
||||
max_active(std::numeric_limits<int32>::max()),
|
||||
min_active(200),
|
||||
lattice_beam(10.0),
|
||||
prune_interval(25),
|
||||
beam_delta(0.5),
|
||||
hash_ratio(2.0),
|
||||
prune_scale(0.01),
|
||||
determinize_max_delay(60),
|
||||
determinize_min_chunk_size(20),
|
||||
determinize_max_active(200) {
|
||||
det_opts.minimize = false;
|
||||
}
|
||||
void Register(OptionsItf *opts) {
|
||||
det_opts.Register(opts);
|
||||
opts->Register("beam", &beam, "Decoding beam. Larger->slower, more accurate.");
|
||||
opts->Register("max-active", &max_active,
|
||||
"Decoder max active states. Larger->slower; "
|
||||
"more accurate");
|
||||
opts->Register("min-active", &min_active, "Decoder minimum #active states.");
|
||||
opts->Register("lattice-beam", &lattice_beam,
|
||||
"Lattice generation beam. Larger->slower, "
|
||||
"and deeper lattices");
|
||||
opts->Register("prune-interval", &prune_interval,
|
||||
"Interval (in frames) at "
|
||||
"which to prune tokens");
|
||||
opts->Register("beam-delta", &beam_delta,
|
||||
"Increment used in decoding-- this "
|
||||
"parameter is obscure and relates to a speedup in the way the "
|
||||
"max-active constraint is applied. Larger is more accurate.");
|
||||
opts->Register("hash-ratio", &hash_ratio,
|
||||
"Setting used in decoder to "
|
||||
"control hash behavior");
|
||||
opts->Register("determinize-max-delay", &determinize_max_delay,
|
||||
"Maximum frames of delay between decoding a frame and "
|
||||
"determinizing it");
|
||||
opts->Register("determinize-min-chunk-size", &determinize_min_chunk_size,
|
||||
"Minimum chunk size used in determinization");
|
||||
opts->Register("determinize-max-active", &determinize_max_active,
|
||||
"Maximum number of active tokens to update determinization");
|
||||
|
||||
}
|
||||
void Check() const {
|
||||
if (!(beam > 0.0 && max_active > 1 && lattice_beam > 0.0 &&
|
||||
min_active <= max_active && prune_interval > 0 &&
|
||||
beam_delta > 0.0 && hash_ratio >= 1.0 &&
|
||||
prune_scale > 0.0 && prune_scale < 1.0 &&
|
||||
determinize_max_delay > determinize_min_chunk_size &&
|
||||
determinize_min_chunk_size > 0 &&
|
||||
determinize_max_active >= 0))
|
||||
KALDI_ERR << "Invalid options given to decoder";
|
||||
/* Minimization of the chunks is not compatible withour algorithm (or at
|
||||
least, would require additional complexity to implement.) */
|
||||
if (det_opts.minimize || !det_opts.word_determinize)
|
||||
KALDI_ERR << "Invalid determinization options given to decoder.";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This class is used inside LatticeIncrementalDecoderTpl; it handles
|
||||
some of the details of incremental determinization.
|
||||
https://www.danielpovey.com/files/ *TBD*.pdf for the paper.
|
||||
|
||||
*/
|
||||
class LatticeIncrementalDeterminizer {
|
||||
public:
|
||||
using Label = typename LatticeArc::Label; /* Actualy the same labels appear
|
||||
in both lattice and compact
|
||||
lattice, so we don't use the
|
||||
specific type all the time but
|
||||
just say 'Label' */
|
||||
LatticeIncrementalDeterminizer(
|
||||
const TransitionInformation &trans_model,
|
||||
const LatticeIncrementalDecoderConfig &config):
|
||||
trans_model_(trans_model), config_(config) { }
|
||||
|
||||
// Resets the lattice determinization data for new utterance
|
||||
void Init();
|
||||
|
||||
// Returns the current determinized lattice.
|
||||
const CompactLattice &GetDeterminizedLattice() const { return clat_; }
|
||||
|
||||
/**
|
||||
Starts the process of creating a raw lattice chunk. (Search the glossary
|
||||
for "raw lattice chunk"). This just sets up the initial states and
|
||||
redeterminized-states in the chunk. Relates to sec. 5.2 in the paper,
|
||||
specifically the initial-state i and the redeterminized-states.
|
||||
|
||||
After calling this, the caller would add the remaining arcs and states
|
||||
to `olat` and then call AcceptRawLatticeChunk() with the result.
|
||||
|
||||
@param [out] olat The lattice to be (partially) created
|
||||
|
||||
@param [out] token_label2state This function outputs to here
|
||||
a map from `token-label` to the state we created for
|
||||
it in *olat. See glossary for `token-label`.
|
||||
The keys actually correspond to the .nextstate fields
|
||||
in the arcs in final_arcs_; values are states in `olat`.
|
||||
See the last bullet point before Sec. 5.3 in the paper.
|
||||
*/
|
||||
void InitializeRawLatticeChunk(
|
||||
Lattice *olat,
|
||||
unordered_map<Label, LatticeArc::StateId> *token_label2state);
|
||||
|
||||
/**
|
||||
This function accepts the raw FST (state-level lattice) corresponding to a
|
||||
single chunk of the lattice, determinizes it and appends it to this->clat_.
|
||||
Unless this was the
|
||||
|
||||
Note: final-probs in `raw_fst` are treated specially: they are used to
|
||||
guide the pruned determinization, but when you call GetLattice() it will be
|
||||
-- except for pruning effects-- as if all nonzero final-probs in `raw_fst`
|
||||
were: One() if final_costs == NULL; else the value present in `final_costs`.
|
||||
|
||||
@param [in] raw_fst (Consumed destructively). The input
|
||||
raw (state-level) lattice. Would correspond to the
|
||||
FST A in the paper if first_frame == 0, and B
|
||||
otherwise.
|
||||
|
||||
@return returns false if determinization finished earlier than the beam
|
||||
or the determinized lattice was empty; true otherwise.
|
||||
|
||||
NOTE: if this is not the final chunk, you will probably want to call
|
||||
SetFinalCosts() directly after calling this.
|
||||
*/
|
||||
bool AcceptRawLatticeChunk(Lattice *raw_fst);
|
||||
|
||||
/*
|
||||
Sets final-probs in `clat_`. Must only be called if the final chunk
|
||||
has not been processed. (The final chunk is whenever GetLattice() is
|
||||
called with finalize == true).
|
||||
|
||||
The reason this is a separate function from AcceptRawLatticeChunk() is that
|
||||
there may be situations where a user wants to get the latice with
|
||||
final-probs in it, after previously getting it without final-probs; or
|
||||
vice versa. By final-probs, we mean the Final() probabilities in the
|
||||
HCLG (decoding graph; this->fst_).
|
||||
|
||||
@param [in] token_label2final_cost A map from the token-label
|
||||
corresponding to Tokens active on the final frame of the
|
||||
lattice in the object, to the final-cost we want to use for
|
||||
those tokens. If NULL, it means all Tokens should be treated
|
||||
as final with probability One(). If non-NULL, and a particular
|
||||
token-label is not a key of this map, it means that Token
|
||||
corresponded to a state that was not final in HCLG; and
|
||||
such tokens will be treated as non-final. However,
|
||||
if this would result in no states in the lattice being final,
|
||||
we will treat all Tokens as final with probability One(),
|
||||
a warning will be printed (this should not happen.)
|
||||
*/
|
||||
void SetFinalCosts(const unordered_map<Label, BaseFloat> *token_label2final_cost = NULL);
|
||||
|
||||
const CompactLattice &GetLattice() { return clat_; }
|
||||
|
||||
// kStateLabelOffset is what we add to state-ids in clat_ to produce labels
|
||||
// to identify them in the raw lattice chunk
|
||||
// kTokenLabelOffset is where we start allocating labels corresponding to Tokens
|
||||
// (these correspond with raw lattice states);
|
||||
enum { kStateLabelOffset = (int)1e8, kTokenLabelOffset = (int)2e8, kMaxTokenLabel = (int)3e8 };
|
||||
|
||||
private:
|
||||
|
||||
// [called from AcceptRawLatticeChunk()]
|
||||
// Gets the final costs from token-final states in the raw lattice (see
|
||||
// glossary for definition). These final costs will be subtracted after
|
||||
// determinization; in the normal case they are `temporaries` used to guide
|
||||
// pruning. NOTE: the index of the array is not the FST state that is final,
|
||||
// but the label on arcs entering it (these will be `token-labels`). Each
|
||||
// token-final state will have the same label on all arcs entering it.
|
||||
//
|
||||
// `old_final_costs` is assumed to be empty at entry.
|
||||
void GetRawLatticeFinalCosts(const Lattice &raw_fst,
|
||||
std::unordered_map<Label, BaseFloat> *old_final_costs);
|
||||
|
||||
// Sets up non_final_redet_states_. See documentation for that variable.
|
||||
void GetNonFinalRedetStates();
|
||||
|
||||
/** [called from AcceptRawLatticeChunk()] Processes arcs that leave the
|
||||
start-state of `chunk_clat` (if this is not the first chunk); does nothing
|
||||
if this is the first chunk. This includes using the `state-labels` to
|
||||
work out which states in clat_ these states correspond to, and writing
|
||||
that mapping to `state_map`.
|
||||
|
||||
Also modifies forward_costs_, because it has to do a kind of reweighting
|
||||
of the clat states that are the values it puts in `state_map`, to take
|
||||
account of the probabilities on the arcs from the start state of
|
||||
chunk_clat to the states corresponding to those redeterminized-states
|
||||
(i.e. the states in clat corresponding to the values it puts in
|
||||
`*state_map`). It also modifies arcs_in_, mostly because there
|
||||
are rare cases when we end up `merging` sets of those redeterminized-states,
|
||||
because the determinization process mapped them to a single state,
|
||||
and that means we need to reroute the arcs into members of that
|
||||
set into one single member (which will appear as a value in
|
||||
`*state_map`).
|
||||
|
||||
@param [in] chunk_clat The determinized chunk of lattice we are
|
||||
processing
|
||||
@param [out] state_map Mapping from states in chunk_clat to
|
||||
the state in clat_ they correspond to.
|
||||
@return Returns true if this is the first chunk.
|
||||
*/
|
||||
bool ProcessArcsFromChunkStartState(
|
||||
const CompactLattice &chunk_clat,
|
||||
std::unordered_map<CompactLattice::StateId, CompactLattice::StateId> *state_map);
|
||||
|
||||
/**
|
||||
This function, called from AcceptRawLatticeChunk(), transfers arcs from
|
||||
`chunk_clat` to clat_. For those arcs that have `token-labels` on them,
|
||||
they don't get written to clat_ but instead are stored in the arcs_ array.
|
||||
|
||||
@param [in] chunk_clat The determinized lattice for the chunk
|
||||
we are processing; this is the source of the arcs
|
||||
we are moving.
|
||||
@param [in] is_first_chunk True if this is the first chunk in the
|
||||
utterance; it's needed because if it is, we
|
||||
will also transfer arcs from the start state of
|
||||
chunk_clat.
|
||||
@param [in] state_map Map from state-ids in chunk_clat to state-ids
|
||||
in clat_.
|
||||
@param [in] chunk_state_to_token Map from `token-final states`
|
||||
(see glossary) in chunk_clat, to the token-label
|
||||
on arcs entering those states.
|
||||
@param [in] old_final_costs Map from token-label to the
|
||||
final-costs that were on the corresponding
|
||||
token-final states in the undeterminized lattice;
|
||||
these final-costs need to be removed when
|
||||
we record the weights in final_arcs_, because
|
||||
they were just temporary.
|
||||
*/
|
||||
void TransferArcsToClat(
|
||||
const CompactLattice &chunk_clat,
|
||||
bool is_first_chunk,
|
||||
const std::unordered_map<CompactLattice::StateId, CompactLattice::StateId> &state_map,
|
||||
const std::unordered_map<CompactLattice::StateId, Label> &chunk_state_to_token,
|
||||
const std::unordered_map<Label, BaseFloat> &old_final_costs);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Adds one arc to `clat_`. It's like clat_.AddArc(state, arc), except
|
||||
it also modifies arcs_in_ and forward_costs_.
|
||||
*/
|
||||
void AddArcToClat(CompactLattice::StateId state,
|
||||
const CompactLatticeArc &arc);
|
||||
CompactLattice::StateId AddStateToClat();
|
||||
|
||||
|
||||
// Identifies token-final states in `chunk_clat`; see glossary above for
|
||||
// definition of `token-final`. This function outputs a map from such states
|
||||
// in chunk_clat, to the `token-label` on arcs entering them. (It is not
|
||||
// possible that the same state would have multiple arcs entering it with
|
||||
// different token-labels, or some arcs entering with one token-label and some
|
||||
// another, or be both initial and have such arcs; this is true due to how we
|
||||
// construct the raw lattice.)
|
||||
void IdentifyTokenFinalStates(
|
||||
const CompactLattice &chunk_clat,
|
||||
std::unordered_map<CompactLattice::StateId, CompactLatticeArc::Label> *token_map) const;
|
||||
|
||||
// trans_model_ is needed by DeterminizeLatticePhonePrunedWrapper() which this
|
||||
// class calls.
|
||||
const TransitionInformation &trans_model_;
|
||||
// config_ is needed by DeterminizeLatticePhonePrunedWrapper() which this
|
||||
// class calls.
|
||||
const LatticeIncrementalDecoderConfig &config_;
|
||||
|
||||
|
||||
// Contains the set of redeterminized-states which are not final in the
|
||||
// canonical appended lattice. Since the final ones don't physically appear
|
||||
// in clat_, this means the set of redeterminized-states which are physically
|
||||
// in clat_. In code terms, this means set of .first elements in final_arcs,
|
||||
// plus whatever other states in clat_ are reachable from such states.
|
||||
std::unordered_set<CompactLattice::StateId> non_final_redet_states_;
|
||||
|
||||
|
||||
// clat_ is the appended lattice (containing all chunks processed so
|
||||
// far), except its `final-arcs` (i.e. arcs which in the canonical
|
||||
// lattice would go to final-states) are not present (they are stored
|
||||
// separately in final_arcs_) and states which in the canonical lattice
|
||||
// should have final-arcs leaving them will instead have a final-prob.
|
||||
CompactLattice clat_;
|
||||
|
||||
|
||||
// arcs_in_ is indexed by (state-id in clat_), and is a list of
|
||||
// arcs that come into this state, in the form (prev-state,
|
||||
// arc-index). CAUTION: not all these input-arc records will always
|
||||
// be valid (some may be out-of-date, and may refer to an out-of-range
|
||||
// arc or an arc that does not point to this state). But all
|
||||
// input arcs will always be listed.
|
||||
std::vector<std::vector<std::pair<CompactLattice::StateId, int32> > > arcs_in_;
|
||||
|
||||
// final_arcs_ contains arcs which would appear in the canonical appended
|
||||
// lattice but for implementation reasons are not physically present in clat_.
|
||||
// These are arcs to final states in the canonical appended lattice. The
|
||||
// .first elements are the source states in clat_ (these will all be elements
|
||||
// of non_final_redet_states_); the .nextstate elements of the arcs does not
|
||||
// contain a physical state, but contain state-labels allocated by
|
||||
// AllocateNewStateLabel().
|
||||
std::vector<CompactLatticeArc> final_arcs_;
|
||||
|
||||
// forward_costs_, indexed by the state-id in clat_, stores the alpha
|
||||
// (forward) costs, i.e. the minimum cost from the start state to each state
|
||||
// in clat_. This is relevant for pruned determinization. The BaseFloat can
|
||||
// be thought of as the sum of a Value1() + Value2() in a LatticeWeight.
|
||||
std::vector<BaseFloat> forward_costs_;
|
||||
|
||||
// temporary used in a function, kept here to avoid excessive reallocation.
|
||||
std::unordered_set<int32> temp_;
|
||||
|
||||
KALDI_DISALLOW_COPY_AND_ASSIGN(LatticeIncrementalDeterminizer);
|
||||
};
|
||||
|
||||
|
||||
/** This is an extention to the "normal" lattice-generating decoder.
|
||||
See \ref lattices_generation \ref decoders_faster and \ref decoders_simple
|
||||
for more information.
|
||||
|
||||
The main difference is the incremental determinization which will be
|
||||
discussed in the function GetLattice(). This means that the work of determinizatin
|
||||
isn't done all at once at the end of the file, but incrementally while decoding.
|
||||
See the comment at the top of this file for more explanation.
|
||||
|
||||
The decoder is templated on the FST type and the token type. The token type
|
||||
will normally be StdToken, but also may be BackpointerToken which is to support
|
||||
quick lookup of the current best path (see lattice-faster-online-decoder.h)
|
||||
|
||||
The FST you invoke this decoder with is expected to be of type
|
||||
Fst::Fst<fst::StdArc>, a.k.a. StdFst, or GrammarFst. If you invoke it with
|
||||
FST == StdFst and it notices that the actual FST type is
|
||||
fst::VectorFst<fst::StdArc> or fst::ConstFst<fst::StdArc>, the decoder object
|
||||
will internally cast itself to one that is templated on those more specific
|
||||
types; this is an optimization for speed.
|
||||
*/
|
||||
template <typename FST, typename Token = decoder::StdToken>
|
||||
class LatticeIncrementalDecoderTpl {
|
||||
public:
|
||||
using Arc = typename FST::Arc;
|
||||
using Label = typename Arc::Label;
|
||||
using StateId = typename Arc::StateId;
|
||||
using Weight = typename Arc::Weight;
|
||||
using ForwardLinkT = decoder::ForwardLink<Token>;
|
||||
|
||||
// Instantiate this class once for each thing you have to decode.
|
||||
// This version of the constructor does not take ownership of
|
||||
// 'fst'.
|
||||
LatticeIncrementalDecoderTpl(const FST &fst, const TransitionInformation &trans_model,
|
||||
const LatticeIncrementalDecoderConfig &config);
|
||||
|
||||
// This version of the constructor takes ownership of the fst, and will delete
|
||||
// it when this object is destroyed.
|
||||
LatticeIncrementalDecoderTpl(const LatticeIncrementalDecoderConfig &config,
|
||||
FST *fst, const TransitionInformation &trans_model);
|
||||
|
||||
void SetOptions(const LatticeIncrementalDecoderConfig &config) { config_ = config; }
|
||||
|
||||
const LatticeIncrementalDecoderConfig &GetOptions() const { return config_; }
|
||||
|
||||
~LatticeIncrementalDecoderTpl();
|
||||
|
||||
/**
|
||||
CAUTION: it's unlikely that you will ever want to call this function. In a
|
||||
scenario where you have the entire file and just want to decode it, there
|
||||
is no point using this decoder.
|
||||
|
||||
An example of how to do decoding together with incremental
|
||||
determinization. It decodes until there are no more frames left in the
|
||||
"decodable" object.
|
||||
|
||||
In this example, config_.determinize_max_delay, config_.determinize_min_chunk_size
|
||||
and config_.determinize_max_active are used to determine the time to
|
||||
call GetLattice().
|
||||
|
||||
Users will probably want to use appropriate combinations of
|
||||
AdvanceDecoding() and GetLattice() to build their application; this just
|
||||
gives you some idea how.
|
||||
|
||||
The function returns true if any kind of traceback is available (not
|
||||
necessarily from a final state).
|
||||
*/
|
||||
bool Decode(DecodableInterface *decodable);
|
||||
|
||||
/// says whether a final-state was active on the last frame. If it was not,
|
||||
/// the lattice (or traceback) will end with states that are not final-states.
|
||||
bool ReachedFinal() const {
|
||||
return FinalRelativeCost() != std::numeric_limits<BaseFloat>::infinity();
|
||||
}
|
||||
|
||||
/**
|
||||
This decoder has no GetBestPath() function.
|
||||
If you need that functionality you should probably use lattice-incremental-online-decoder.h,
|
||||
which makes it very efficient to obtain the best path. */
|
||||
|
||||
/**
|
||||
This GetLattice() function returns the lattice containing
|
||||
`num_frames_to_decode` frames; this will be all frames decoded so
|
||||
far, if you let num_frames_to_decode == NumFramesDecoded(),
|
||||
but it will generally be better to make it a few frames less than
|
||||
that to avoid the lattice having too many active states at
|
||||
the end.
|
||||
|
||||
@param [in] num_frames_to_include The number of frames that you want
|
||||
to be included in the lattice. Must be >=
|
||||
NumFramesInLattice() and <= NumFramesDecoded().
|
||||
|
||||
@param [in] use_final_probs True if you want the final-probs
|
||||
of HCLG to be included in the output lattice. Must not
|
||||
be set to true if num_frames_to_include !=
|
||||
NumFramesDecoded(). Must be set to true if you have
|
||||
previously called FinalizeDecoding().
|
||||
|
||||
(If no state was final on frame `num_frames_to_include`, the
|
||||
final-probs won't be included regardless of
|
||||
`use_final_probs`; you can test whether this
|
||||
was the case by calling ReachedFinal().
|
||||
|
||||
@return clat The CompactLattice representing what has been decoded
|
||||
up until `num_frames_to_include` (e.g., LatticeStateTimes()
|
||||
on this lattice would return `num_frames_to_include`).
|
||||
|
||||
See also UpdateLatticeDeterminizaton(). Caution: this const ref
|
||||
is only valid until the next time you call AdvanceDecoding() or
|
||||
GetLattice().
|
||||
|
||||
CAUTION: the lattice may contain disconnnected states; you should
|
||||
call Connect() on the output before writing it out.
|
||||
*/
|
||||
const CompactLattice &GetLattice(int32 num_frames_to_include,
|
||||
bool use_final_probs = false);
|
||||
|
||||
/*
|
||||
Returns the number of frames in the currently-determinized part of the
|
||||
lattice which will be a number in [0, NumFramesDecoded()]. It will
|
||||
be the largest number that GetLattice() was called with, but note
|
||||
that GetLattice() may be called from UpdateLatticeDeterminization().
|
||||
|
||||
Made available in case the user wants to give that same number to
|
||||
GetLattice().
|
||||
*/
|
||||
int NumFramesInLattice() const { return num_frames_in_lattice_; }
|
||||
|
||||
/**
|
||||
InitDecoding initializes the decoding, and should only be used if you
|
||||
intend to call AdvanceDecoding(). If you call Decode(), you don't need to
|
||||
call this. You can also call InitDecoding if you have already decoded an
|
||||
utterance and want to start with a new utterance.
|
||||
*/
|
||||
void InitDecoding();
|
||||
|
||||
/**
|
||||
This will decode until there are no more frames ready in the decodable
|
||||
object. You can keep calling it each time more frames become available
|
||||
(this is the normal pattern in a real-time/online decoding scenario).
|
||||
If max_num_frames is specified, it specifies the maximum number of frames
|
||||
the function will decode before returning.
|
||||
*/
|
||||
void AdvanceDecoding(DecodableInterface *decodable, int32 max_num_frames = -1);
|
||||
|
||||
|
||||
/** FinalRelativeCost() serves the same purpose as ReachedFinal(), but gives
|
||||
more information. It returns the difference between the best (final-cost
|
||||
plus cost) of any token on the final frame, and the best cost of any token
|
||||
on the final frame. If it is infinity it means no final-states were
|
||||
present on the final frame. It will usually be nonnegative. If it not
|
||||
too positive (e.g. < 5 is my first guess, but this is not tested) you can
|
||||
take it as a good indication that we reached the final-state with
|
||||
reasonable likelihood. */
|
||||
BaseFloat FinalRelativeCost() const;
|
||||
|
||||
/** Returns the number of frames decoded so far. */
|
||||
inline int32 NumFramesDecoded() const { return active_toks_.size() - 1; }
|
||||
|
||||
/**
|
||||
Finalizes the decoding, doing an extra pruning step on the last frame
|
||||
that uses the final-probs. May be called only once.
|
||||
*/
|
||||
void FinalizeDecoding();
|
||||
|
||||
protected:
|
||||
/* Some protected things are needed in LatticeIncrementalOnlineDecoderTpl. */
|
||||
|
||||
/** NOTE: for parts the internal implementation that are shared with LatticeFasterDecoer,
|
||||
we have removed the comments.*/
|
||||
inline static void DeleteForwardLinks(Token *tok);
|
||||
struct TokenList {
|
||||
Token *toks;
|
||||
bool must_prune_forward_links;
|
||||
bool must_prune_tokens;
|
||||
int32 num_toks; /* Note: you can only trust `num_toks` if must_prune_tokens
|
||||
* == false, because it is only set in
|
||||
* PruneTokensForFrame(). */
|
||||
TokenList()
|
||||
: toks(NULL), must_prune_forward_links(true), must_prune_tokens(true),
|
||||
num_toks(-1) {}
|
||||
};
|
||||
using Elem = typename HashList<StateId, Token *>::Elem;
|
||||
void PossiblyResizeHash(size_t num_toks);
|
||||
inline Token *FindOrAddToken(StateId state, int32 frame_plus_one,
|
||||
BaseFloat tot_cost, Token *backpointer, bool *changed);
|
||||
void PruneForwardLinks(int32 frame_plus_one, bool *extra_costs_changed,
|
||||
bool *links_pruned, BaseFloat delta);
|
||||
void ComputeFinalCosts(unordered_map<Token *, BaseFloat> *final_costs,
|
||||
BaseFloat *final_relative_cost,
|
||||
BaseFloat *final_best_cost) const;
|
||||
void PruneForwardLinksFinal();
|
||||
void PruneTokensForFrame(int32 frame_plus_one);
|
||||
void PruneActiveTokens(BaseFloat delta);
|
||||
BaseFloat GetCutoff(Elem *list_head, size_t *tok_count, BaseFloat *adaptive_beam,
|
||||
Elem **best_elem);
|
||||
BaseFloat ProcessEmitting(DecodableInterface *decodable);
|
||||
void ProcessNonemitting(BaseFloat cost_cutoff);
|
||||
|
||||
HashList<StateId, Token *> toks_;
|
||||
std::vector<TokenList> active_toks_; // indexed by frame.
|
||||
std::vector<StateId> queue_; // temp variable used in ProcessNonemitting,
|
||||
std::vector<BaseFloat> tmp_array_; // used in GetCutoff.
|
||||
const FST *fst_;
|
||||
bool delete_fst_;
|
||||
std::vector<BaseFloat> cost_offsets_;
|
||||
int32 num_toks_;
|
||||
bool warned_;
|
||||
bool decoding_finalized_;
|
||||
|
||||
unordered_map<Token *, BaseFloat> final_costs_;
|
||||
BaseFloat final_relative_cost_;
|
||||
BaseFloat final_best_cost_;
|
||||
|
||||
/***********************
|
||||
Variables below this point relate to the incremental
|
||||
determinization.
|
||||
*********************/
|
||||
LatticeIncrementalDecoderConfig config_;
|
||||
/** Much of the the incremental determinization algorithm is encapsulated in
|
||||
the determinize_ object. */
|
||||
LatticeIncrementalDeterminizer determinizer_;
|
||||
|
||||
|
||||
/* Just a temporary used in a function; stored here to avoid reallocation. */
|
||||
unordered_map<Token*, StateId> temp_token_map_;
|
||||
|
||||
/** num_frames_in_lattice_ is the highest `num_frames_to_include_` argument
|
||||
for any prior call to GetLattice(). */
|
||||
int32 num_frames_in_lattice_;
|
||||
|
||||
// A map from Token to its token_label. Will contain an entry for
|
||||
// each Token in active_toks_[num_frames_in_lattice_].
|
||||
unordered_map<Token*, Label> token2label_map_;
|
||||
|
||||
// A temporary used in a function, kept here to avoid reallocation.
|
||||
unordered_map<Token*, Label> token2label_map_temp_;
|
||||
|
||||
// we allocate a unique id for each Token
|
||||
Label next_token_label_;
|
||||
|
||||
inline Label AllocateNewTokenLabel() { return next_token_label_++; }
|
||||
|
||||
|
||||
// There are various cleanup tasks... the the toks_ structure contains
|
||||
// singly linked lists of Token pointers, where Elem is the list type.
|
||||
// It also indexes them in a hash, indexed by state (this hash is only
|
||||
// maintained for the most recent frame). toks_.Clear()
|
||||
// deletes them from the hash and returns the list of Elems. The
|
||||
// function DeleteElems calls toks_.Delete(elem) for each elem in
|
||||
// the list, which returns ownership of the Elem to the toks_ structure
|
||||
// for reuse, but does not delete the Token pointer. The Token pointers
|
||||
// are reference-counted and are ultimately deleted in PruneTokensForFrame,
|
||||
// but are also linked together on each frame by their own linked-list,
|
||||
// using the "next" pointer. We delete them manually.
|
||||
void DeleteElems(Elem *list);
|
||||
|
||||
void ClearActiveTokens();
|
||||
|
||||
|
||||
// Returns the number of active tokens on frame `frame`. Can be used as part
|
||||
// of a heuristic to decide which frame to determinize until, if you are not
|
||||
// at the end of an utterance.
|
||||
int32 GetNumToksForFrame(int32 frame);
|
||||
|
||||
/**
|
||||
UpdateLatticeDeterminization() ensures the work of determinization is kept
|
||||
up to date so that when you do need the lattice you can get it fast. It
|
||||
uses the configuration values `determinize_max_delay`, `determinize_min_chunk_size`
|
||||
and `determinize_max_active`, to decide whether and when to call
|
||||
GetLattice(). You can safely call this as often as you want (e.g. after
|
||||
each time you call AdvanceDecoding(); it won't do subtantially more work if
|
||||
it is called frequently.
|
||||
*/
|
||||
void UpdateLatticeDeterminization();
|
||||
|
||||
|
||||
KALDI_DISALLOW_COPY_AND_ASSIGN(LatticeIncrementalDecoderTpl);
|
||||
};
|
||||
|
||||
typedef LatticeIncrementalDecoderTpl<fst::StdFst, decoder::StdToken>
|
||||
LatticeIncrementalDecoder;
|
||||
|
||||
|
||||
} // end namespace kaldi.
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user