Classes and functions related to HMM topology and transition modeling
Collaboration diagram for Classes and functions related to HMM topology and transition modeling:

Modules

 Classes and functions for creating FSTs from HMMs
 

Classes

class  HmmTopology
 A class for storing topology information for phones. More...
 
struct  MleTransitionUpdateConfig
 
struct  MapTransitionUpdateConfig
 
class  TransitionModel
 

Functions

bool SplitToPhones (const TransitionModel &trans_model, const std::vector< int32 > &alignment, std::vector< std::vector< int32 > > *split_alignment)
 SplitToPhones splits up the TransitionIds in "alignment" into their individual phones (one vector per instance of a phone). More...
 
bool ConvertAlignment (const TransitionModel &old_trans_model, const TransitionModel &new_trans_model, const ContextDependencyInterface &new_ctx_dep, const std::vector< int32 > &old_alignment, int32 subsample_factor, bool repeat_frames, bool reorder, const std::vector< int32 > *phone_map, std::vector< int32 > *new_alignment)
 ConvertAlignment converts an alignment that was created using one model, to another model. More...
 
bool ConvertPhnxToProns (const std::vector< int32 > &phnx, const std::vector< int32 > &words, int32 word_start_sym, int32 word_end_sym, std::vector< std::vector< int32 > > *prons)
 
void GetRandomAlignmentForPhone (const ContextDependencyInterface &ctx_dep, const TransitionModel &trans_model, const std::vector< int32 > &phone_window, std::vector< int32 > *alignment)
 
void ChangeReorderingOfAlignment (const TransitionModel &trans_model, std::vector< int32 > *alignment)
 
void GetPdfToPhonesMap (const TransitionModel &trans_model, std::vector< std::set< int32 > > *pdf2phones)
 
bool GetPdfsForPhones (const TransitionModel &trans_model, const std::vector< int32 > &phones, std::vector< int32 > *pdfs)
 Works out which pdfs might correspond to the given phones. More...
 
bool GetPhonesForPdfs (const TransitionModel &trans_model, const std::vector< int32 > &pdfs, std::vector< int32 > *phones)
 Works out which phones might correspond to the given pdfs. More...
 

Variables

static const int32 kNoPdf = -1
 A constant used in the HmmTopology class as the pdf-class kNoPdf, which is used when a HMM-state is nonemitting (has no associated PDF). More...
 

Integer mapping functions

int32 TransitionIdToPdf (int32 trans_id) const
 
int32 TransitionIdToPdfFast (int32 trans_id) const
 

Detailed Description

Function Documentation

◆ ChangeReorderingOfAlignment()

void ChangeReorderingOfAlignment ( const TransitionModel trans_model,
std::vector< int32 > *  alignment 
)

Definition at line 1260 of file hmm-utils.cc.

References TransitionModel::IsSelfLoop(), kaldi::swap(), and TransitionModel::TransitionIdToTransitionState().

Referenced by kaldi::ConvertAlignmentForPhone().

1261  {
1262  int32 start_pos = 0, size = alignment->size();
1263  while (start_pos != size) {
1264  int32 start_tid = (*alignment)[start_pos];
1265  int32 cur_tstate = trans_model.TransitionIdToTransitionState(start_tid);
1266  bool start_is_self_loop = trans_model.IsSelfLoop(start_tid) ? 0 : 1;
1267  int32 end_pos = start_pos + 1;
1268  // If the first instance of this transition-state was a self-loop, then eat
1269  // only non-self-loops of this state; if it was a non-self-loop, then eat
1270  // only self-loops of this state. Imposing this condition on self-loops
1271  // would only actually matter in the rare circumstances that phones can
1272  // have length 1.
1273  while (end_pos != size &&
1274  trans_model.TransitionIdToTransitionState((*alignment)[end_pos]) ==
1275  cur_tstate) {
1276  bool this_is_self_loop = trans_model.IsSelfLoop((*alignment)[end_pos]);
1277  if (!this_is_self_loop) {
1278  if (start_is_self_loop) {
1279  break; // stop before including this transition-id.
1280  } else {
1281  end_pos++;
1282  break; // stop after including this transition-id.
1283  }
1284  }
1285  end_pos++;
1286  }
1287  std::swap((*alignment)[start_pos], (*alignment)[end_pos - 1]);
1288  start_pos = end_pos;
1289  }
1290 }
void swap(basic_filebuf< CharT, Traits > &x, basic_filebuf< CharT, Traits > &y)
kaldi::int32 int32

◆ ConvertAlignment()

bool ConvertAlignment ( const TransitionModel old_trans_model,
const TransitionModel new_trans_model,
const ContextDependencyInterface new_ctx_dep,
const std::vector< int32 > &  old_alignment,
int32  subsample_factor,
bool  repeat_frames,
bool  reorder,
const std::vector< int32 > *  phone_map,
std::vector< int32 > *  new_alignment 
)

ConvertAlignment converts an alignment that was created using one model, to another model.

Returns false if it could not be split to phones (e.g. because the alignment was partial), or because some other error happened, such as we couldn't convert the alignment because there were too few frames for the new topology.

Parameters
old_trans_model[in] The transition model that the original alignment used.
new_trans_model[in] The transition model that we want to use for the new alignment.
new_ctx_dep[in] The new tree
old_alignment[in] The alignment we want to convert
subsample_factor[in] The frame subsampling factor... normally 1, but might be > 1 if we're converting to a reduced-frame-rate system.
repeat_frames[in] Only relevant when subsample_factor != 1 If true, repeat frames of alignment by 'subsample_factor' after alignment conversion, to keep the alignment the same length as the input alignment. [note: we actually do this by interpolating 'subsample_factor' separately generated alignments, to keep the phone boundaries the same as the input where possible.]
reorder[in] True if you want the pdf-ids on the new alignment to be 'reordered'. (vs. the way they appear in the HmmTopology object)
phone_map[in] If non-NULL, map from old to new phones.
new_alignment[out] The converted alignment.

Definition at line 1013 of file hmm-utils.cc.

References kaldi::ConvertAlignmentInternal(), rnnlm::i, and KALDI_ASSERT.

Referenced by main(), and kaldi::TestConvertAlignment().

1021  {
1022  if (!repeat_frames || subsample_factor == 1) {
1023  return ConvertAlignmentInternal(old_trans_model,
1024  new_trans_model,
1025  new_ctx_dep,
1026  old_alignment,
1027  subsample_factor - 1,
1028  subsample_factor,
1029  new_is_reordered,
1030  phone_map,
1031  new_alignment);
1032  // The value "subsample_factor - 1" for conversion_shift above ensures the
1033  // alignments have the same length as the output of 'subsample-feats'
1034  } else {
1035  std::vector<std::vector<int32> > shifted_alignments(subsample_factor);
1036  for (int32 conversion_shift = subsample_factor - 1;
1037  conversion_shift >= 0; conversion_shift--) {
1038  if (!ConvertAlignmentInternal(old_trans_model,
1039  new_trans_model,
1040  new_ctx_dep,
1041  old_alignment,
1042  conversion_shift,
1043  subsample_factor,
1044  new_is_reordered,
1045  phone_map,
1046  &shifted_alignments[conversion_shift]))
1047  return false;
1048  }
1049  KALDI_ASSERT(new_alignment != NULL);
1050  new_alignment->clear();
1051  new_alignment->reserve(old_alignment.size());
1052  int32 max_shifted_ali_length = (old_alignment.size() / subsample_factor)
1053  + (old_alignment.size() % subsample_factor);
1054  for (int32 i = 0; i < max_shifted_ali_length; i++)
1055  for (int32 conversion_shift = subsample_factor - 1;
1056  conversion_shift >= 0; conversion_shift--)
1057  if (i < static_cast<int32>(shifted_alignments[conversion_shift].size()))
1058  new_alignment->push_back(shifted_alignments[conversion_shift][i]);
1059  }
1060  KALDI_ASSERT(new_alignment->size() == old_alignment.size());
1061  return true;
1062 }
kaldi::int32 int32
static bool ConvertAlignmentInternal(const TransitionModel &old_trans_model, const TransitionModel &new_trans_model, const ContextDependencyInterface &new_ctx_dep, const std::vector< int32 > &old_alignment, int32 conversion_shift, int32 subsample_factor, bool new_is_reordered, const std::vector< int32 > *phone_map, std::vector< int32 > *new_alignment)
This function is the same as &#39;ConvertAligment&#39;, but instead of the &#39;repeat_frames&#39; option it supports...
Definition: hmm-utils.cc:926
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ ConvertPhnxToProns()

bool ConvertPhnxToProns ( const std::vector< int32 > &  phnx,
const std::vector< int32 > &  words,
int32  word_start_sym,
int32  word_end_sym,
std::vector< std::vector< int32 > > *  prons 
)

Definition at line 1161 of file hmm-utils.cc.

References rnnlm::i, and rnnlm::j.

Referenced by main(), and kaldi::TestConvertPhnxToProns().

1165  {
1166  size_t i = 0, j = 0;
1167 
1168  while (i < phnx.size()) {
1169  if (phnx[i] == 0) return false; // zeros not valid here.
1170  if (phnx[i] == word_start_sym) { // start a word...
1171  std::vector<int32> pron;
1172  if (j >= words.size()) return false; // no word left..
1173  if (words[j] == 0) return false; // zero word disallowed.
1174  pron.push_back(words[j++]);
1175  i++;
1176  while (i < phnx.size()) {
1177  if (phnx[i] == 0) return false;
1178  if (phnx[i] == word_start_sym) return false; // error.
1179  if (phnx[i] == word_end_sym) { i++; break; }
1180  pron.push_back(phnx[i]);
1181  i++;
1182  }
1183  // check we did see the word-end symbol.
1184  if (!(i > 0 && phnx[i-1] == word_end_sym))
1185  return false;
1186  prons->push_back(pron);
1187  } else if (phnx[i] == word_end_sym) {
1188  return false; // error.
1189  } else {
1190  // start a non-word sequence of phones (e.g. opt-sil).
1191  std::vector<int32> pron;
1192  pron.push_back(0); // 0 serves as the word-id.
1193  while (i < phnx.size()) {
1194  if (phnx[i] == 0) return false;
1195  if (phnx[i] == word_start_sym) break;
1196  if (phnx[i] == word_end_sym) return false; // error.
1197  pron.push_back(phnx[i]);
1198  i++;
1199  }
1200  prons->push_back(pron);
1201  }
1202  }
1203  return (j == words.size());
1204 }
int32 words[kMaxOrder]

◆ GetPdfsForPhones()

bool GetPdfsForPhones ( const TransitionModel trans_model,
const std::vector< int32 > &  phones,
std::vector< int32 > *  pdfs 
)

Works out which pdfs might correspond to the given phones.

Will return true if these pdfs correspond *just* to these phones, false if these pdfs are also used by other phones.

Parameters
trans_model[in] Transition-model used to work out this information
phones[in] A sorted, uniq vector that represents a set of phones
pdfs[out] Will be set to a sorted, uniq list of pdf-ids that correspond to one of this set of phones.
Returns
Returns true if all of the pdfs output to "pdfs" correspond to phones from just this set (false if they may be shared with phones outside this set).

Definition at line 854 of file transition-model.cc.

References kaldi::IsSortedAndUniq(), KALDI_ASSERT, TransitionModel::NumTransitionStates(), kaldi::SortAndUniq(), TransitionModel::TransitionStateToForwardPdf(), TransitionModel::TransitionStateToPhone(), and TransitionModel::TransitionStateToSelfLoopPdf().

Referenced by main(), and TransitionModel::TransitionIdToPdfFast().

856  {
857  KALDI_ASSERT(IsSortedAndUniq(phones));
858  KALDI_ASSERT(pdfs != NULL);
859  pdfs->clear();
860  for (int32 tstate = 1; tstate <= trans_model.NumTransitionStates(); tstate++) {
861  if (std::binary_search(phones.begin(), phones.end(),
862  trans_model.TransitionStateToPhone(tstate))) {
863  pdfs->push_back(trans_model.TransitionStateToForwardPdf(tstate));
864  pdfs->push_back(trans_model.TransitionStateToSelfLoopPdf(tstate));
865  }
866  }
867  SortAndUniq(pdfs);
868 
869  for (int32 tstate = 1; tstate <= trans_model.NumTransitionStates(); tstate++)
870  if ((std::binary_search(pdfs->begin(), pdfs->end(),
871  trans_model.TransitionStateToForwardPdf(tstate)) ||
872  std::binary_search(pdfs->begin(), pdfs->end(),
873  trans_model.TransitionStateToSelfLoopPdf(tstate)))
874  && !std::binary_search(phones.begin(), phones.end(),
875  trans_model.TransitionStateToPhone(tstate)))
876  return false;
877  return true;
878 }
kaldi::int32 int32
void SortAndUniq(std::vector< T > *vec)
Sorts and uniq&#39;s (removes duplicates) from a vector.
Definition: stl-utils.h:39
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
bool IsSortedAndUniq(const std::vector< T > &vec)
Returns true if the vector is sorted and contains each element only once.
Definition: stl-utils.h:63

◆ GetPdfToPhonesMap()

void GetPdfToPhonesMap ( const TransitionModel trans_model,
std::vector< std::set< int32 > > *  pdf2phones 
)

Definition at line 1292 of file hmm-utils.cc.

References rnnlm::i, TransitionModel::NumPdfs(), TransitionModel::NumTransitionIds(), TransitionModel::TransitionIdToPdf(), and TransitionModel::TransitionIdToPhone().

Referenced by main().

1293  {
1294  pdf2phones->clear();
1295  pdf2phones->resize(trans_model.NumPdfs());
1296  for (int32 i = 0; i < trans_model.NumTransitionIds(); i++) {
1297  int32 trans_id = i + 1;
1298  int32 pdf_id = trans_model.TransitionIdToPdf(trans_id);
1299  int32 phone = trans_model.TransitionIdToPhone(trans_id);
1300  (*pdf2phones)[pdf_id].insert(phone);
1301  }
1302 }
kaldi::int32 int32

◆ GetPhonesForPdfs()

bool GetPhonesForPdfs ( const TransitionModel trans_model,
const std::vector< int32 > &  pdfs,
std::vector< int32 > *  phones 
)

Works out which phones might correspond to the given pdfs.

Similar to the above GetPdfsForPhones(, ,)

Definition at line 880 of file transition-model.cc.

References kaldi::IsSortedAndUniq(), KALDI_ASSERT, TransitionModel::NumTransitionStates(), kaldi::SortAndUniq(), TransitionModel::TransitionStateToForwardPdf(), TransitionModel::TransitionStateToPhone(), and TransitionModel::TransitionStateToSelfLoopPdf().

Referenced by kaldi::InitAmGmm(), and TransitionModel::TransitionIdToPdfFast().

882  {
884  KALDI_ASSERT(phones != NULL);
885  phones->clear();
886  for (int32 tstate = 1; tstate <= trans_model.NumTransitionStates(); tstate++) {
887  if (std::binary_search(pdfs.begin(), pdfs.end(),
888  trans_model.TransitionStateToForwardPdf(tstate)) ||
889  std::binary_search(pdfs.begin(), pdfs.end(),
890  trans_model.TransitionStateToSelfLoopPdf(tstate)))
891  phones->push_back(trans_model.TransitionStateToPhone(tstate));
892  }
893  SortAndUniq(phones);
894 
895  for (int32 tstate = 1; tstate <= trans_model.NumTransitionStates(); tstate++)
896  if (std::binary_search(phones->begin(), phones->end(),
897  trans_model.TransitionStateToPhone(tstate))
898  && !(std::binary_search(pdfs.begin(), pdfs.end(),
899  trans_model.TransitionStateToForwardPdf(tstate)) &&
900  std::binary_search(pdfs.begin(), pdfs.end(),
901  trans_model.TransitionStateToSelfLoopPdf(tstate))) )
902  return false;
903  return true;
904 }
kaldi::int32 int32
void SortAndUniq(std::vector< T > *vec)
Sorts and uniq&#39;s (removes duplicates) from a vector.
Definition: stl-utils.h:39
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
bool IsSortedAndUniq(const std::vector< T > &vec)
Returns true if the vector is sorted and contains each element only once.
Definition: stl-utils.h:63

◆ GetRandomAlignmentForPhone()

void GetRandomAlignmentForPhone ( const ContextDependencyInterface ctx_dep,
const TransitionModel trans_model,
const std::vector< int32 > &  phone_window,
std::vector< int32 > *  alignment 
)

Definition at line 1207 of file hmm-utils.cc.

References ContextDependencyInterface::CentralPosition(), kaldi::GetHmmAsFsaSimple(), fst::GetInputSymbols(), TransitionModel::GetTopo(), rnnlm::i, rnnlm::j, KALDI_ASSERT, KALDI_ERR, and HmmTopology::MinLength().

Referenced by kaldi::ConvertAlignmentForPhone().

1210  {
1211  typedef fst::StdArc Arc;
1212  int32 length = alignment->size();
1213  BaseFloat prob_scale = 0.0;
1214  fst::VectorFst<Arc> *fst = GetHmmAsFsaSimple(phone_window, ctx_dep,
1215  trans_model, prob_scale);
1216  fst::RmEpsilon(fst);
1217 
1218  fst::VectorFst<Arc> length_constraint_fst;
1219  { // set up length_constraint_fst.
1220  std::vector<int32> symbols;
1221  bool include_epsilon = false;
1222  // note: 'fst' is an acceptor so ilabels == olabels.
1223  GetInputSymbols(*fst, include_epsilon, &symbols);
1224  int32 cur_state = length_constraint_fst.AddState();
1225  length_constraint_fst.SetStart(cur_state);
1226  for (int32 i = 0; i < length; i++) {
1227  int32 next_state = length_constraint_fst.AddState();
1228  for (size_t j = 0; j < symbols.size(); j++) {
1229  length_constraint_fst.AddArc(cur_state,
1230  Arc(symbols[j], symbols[j],
1231  fst::TropicalWeight::One(),
1232  next_state));
1233  }
1234  cur_state = next_state;
1235  }
1236  length_constraint_fst.SetFinal(cur_state, fst::TropicalWeight::One());
1237  }
1238  fst::VectorFst<Arc> composed_fst;
1239  fst::Compose(*fst, length_constraint_fst, &composed_fst);
1240  fst::VectorFst<Arc> single_path_fst;
1241  { // randomly generate a single path.
1242  fst::UniformArcSelector<Arc> selector;
1243  fst::RandGenOptions<fst::UniformArcSelector<Arc> > randgen_opts(selector);
1244  fst::RandGen(composed_fst, &single_path_fst, randgen_opts);
1245  }
1246  if (single_path_fst.NumStates() == 0) {
1247  KALDI_ERR << "Error generating random alignment (wrong length?): "
1248  << "requested length is " << length << " versus min-length "
1249  << trans_model.GetTopo().MinLength(
1250  phone_window[ctx_dep.CentralPosition()]);
1251  }
1252  std::vector<int32> symbol_sequence;
1253  bool ans = fst::GetLinearSymbolSequence<Arc, int32>(
1254  single_path_fst, &symbol_sequence, NULL, NULL);
1255  KALDI_ASSERT(ans && symbol_sequence.size() == length);
1256  symbol_sequence.swap(*alignment);
1257  delete fst;
1258 }
For an extended explanation of the framework of which grammar-fsts are a part, please see Support for...
Definition: graph.dox:21
fst::StdArc StdArc
kaldi::int32 int32
void GetInputSymbols(const Fst< Arc > &fst, bool include_eps, std::vector< I > *symbols)
GetInputSymbols gets the list of symbols on the input of fst (including epsilon, if include_eps == tr...
float BaseFloat
Definition: kaldi-types.h:29
#define KALDI_ERR
Definition: kaldi-error.h:147
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
fst::VectorFst< fst::StdArc > * GetHmmAsFsaSimple(std::vector< int32 > phone_window, const ContextDependencyInterface &ctx_dep, const TransitionModel &trans_model, BaseFloat prob_scale)
Included mainly as a form of documentation, not used in any other code currently. ...
Definition: hmm-utils.cc:155

◆ SplitToPhones()

bool SplitToPhones ( const TransitionModel trans_model,
const std::vector< int32 > &  alignment,
std::vector< std::vector< int32 > > *  split_alignment 
)

SplitToPhones splits up the TransitionIds in "alignment" into their individual phones (one vector per instance of a phone).

At output, the sum of the sizes of the vectors in split_alignment will be the same as the corresponding sum for "alignment". The function returns true on success. If the alignment appears to be incomplete, e.g. not ending at the end-state of a phone, it will still break it up into phones but it will return false. For more serious errors it will die or throw an exception. This function works out by itself whether the graph was created with "reordering", and just does the right thing.

Definition at line 723 of file hmm-utils.cc.

References kaldi::IsReordered(), KALDI_ASSERT, and kaldi::SplitToPhonesInternal().

Referenced by kaldi::AccumulateTreeStats(), kaldi::CompactLatticeToWordProns(), kaldi::ConvertAlignmentInternal(), OnlineFasterDecoder::EndOfUtterance(), kaldi::IsPlausibleWord(), main(), kaldi::TestConvertAlignment(), and kaldi::TestSplitToPhones().

725  {
726  KALDI_ASSERT(split_alignment != NULL);
727  split_alignment->clear();
728 
729  bool is_reordered = IsReordered(trans_model, alignment);
730  return SplitToPhonesInternal(trans_model, alignment,
731  is_reordered, split_alignment);
732 }
static bool IsReordered(const TransitionModel &trans_model, const std::vector< int32 > &alignment)
Definition: hmm-utils.cc:625
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
static bool SplitToPhonesInternal(const TransitionModel &trans_model, const std::vector< int32 > &alignment, bool reordered, std::vector< std::vector< int32 > > *split_output)
Definition: hmm-utils.cc:659

◆ TransitionIdToPdf()

◆ TransitionIdToPdfFast()

int32 TransitionIdToPdfFast ( int32  trans_id) const
inline

Definition at line 334 of file transition-model.h.

References kaldi::GetPdfsForPhones(), kaldi::GetPhonesForPdfs(), and KALDI_PARANOID_ASSERT.

Referenced by DecodableAmDiagGmmRegtreeFmllr::LogLikelihood(), DecodableAmSgmm2::LogLikelihood(), DecodableMatrixScaledMapped::LogLikelihood(), DecodableAmNnet::LogLikelihood(), DecodableAmDiagGmmRegtreeMllr::LogLikelihood(), DecodableMatrixMapped::LogLikelihood(), DecodableAmSgmm2Scaled::LogLikelihood(), DecodableAmNnetParallel::LogLikelihood(), DecodableMatrixMappedOffset::LogLikelihood(), DecodableAmNnetSimpleLooped::LogLikelihood(), DecodableAmNnetSimple::LogLikelihood(), and DecodableAmNnetSimpleParallel::LogLikelihood().

334  {
335  // Note: it's a little dangerous to assert this only in paranoid mode.
336  // However, this function is called in the inner loop of decoders and
337  // the assertion likely takes a significant amount of time. We make
338  // sure that past the end of the id2pdf_id_ array there are big
339  // numbers, which will make the calling code more likely to segfault
340  // (rather than silently die) if this is called for out-of-range values.
342  static_cast<size_t>(trans_id) < id2pdf_id_.size() &&
343  "Likely graph/model mismatch (graph built from wrong model?)");
344  return id2pdf_id_[trans_id];
345 }
std::vector< int32 > id2pdf_id_
#define KALDI_PARANOID_ASSERT(cond)
Definition: kaldi-error.h:206

Variable Documentation

◆ kNoPdf

const int32 kNoPdf = -1
static

A constant used in the HmmTopology class as the pdf-class kNoPdf, which is used when a HMM-state is nonemitting (has no associated PDF).

Definition at line 86 of file hmm-topology.h.

Referenced by HmmTopology::Check(), TransitionModel::ComputeTuplesIsHmm(), TransitionModel::ComputeTuplesNotHmm(), kaldi::GetHmmAsFsa(), kaldi::GetHmmAsFsaSimple(), HmmTopology::Read(), kaldi::SplitToPhonesInternal(), and HmmTopology::Write().