hmm-test-utils.cc
Go to the documentation of this file.
1 // hmm/hmm-test-utils.cc
2 
3 // Copyright 2015 Johns Hopkins University (author: Daniel Povey)
4 
5 // See ../../COPYING for clarification regarding multiple authors
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
15 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
16 // MERCHANTABLITY OR NON-INFRINGEMENT.
17 // See the Apache 2 License for the specific language governing permissions and
18 // limitations under the License.
19 
20 #include <vector>
21 
22 #include "hmm/hmm-test-utils.h"
23 
24 namespace kaldi {
25 
27  std::vector<int32> phones;
28  phones.push_back(1);
29  for (int32 i = 2; i < 20; i++)
30  if (rand() % 2 == 0)
31  phones.push_back(i);
32  int32 N = 2 + rand() % 2, // context-size N is 2 or 3.
33  P = rand() % N; // Central-phone is random on [0, N)
34 
35  std::vector<int32> num_pdf_classes;
36 
37  ContextDependency *ctx_dep =
38  GenRandContextDependencyLarge(phones, N, P,
39  true, &num_pdf_classes);
40 
41  HmmTopology topo = GenRandTopology(phones, num_pdf_classes);
42 
43  TransitionModel *trans_model = new TransitionModel(*ctx_dep, topo);
44 
45  if (ctx_dep_out == NULL) delete ctx_dep;
46  else *ctx_dep_out = ctx_dep;
47  return trans_model;
48 }
49 
50 HmmTopology GetDefaultTopology(const std::vector<int32> &phones_in) {
51  std::vector<int32> phones(phones_in);
52  std::sort(phones.begin(), phones.end());
53  KALDI_ASSERT(IsSortedAndUniq(phones) && !phones.empty());
54 
55  std::ostringstream topo_string;
56  topo_string << "<Topology>\n"
57  "<TopologyEntry>\n"
58  "<ForPhones> ";
59  for (size_t i = 0; i < phones.size(); i++)
60  topo_string << phones[i] << " ";
61 
62  topo_string << "</ForPhones>\n"
63  "<State> 0 <PdfClass> 0\n"
64  "<Transition> 0 0.5\n"
65  "<Transition> 1 0.5\n"
66  "</State> \n"
67  "<State> 1 <PdfClass> 1 \n"
68  "<Transition> 1 0.5\n"
69  "<Transition> 2 0.5\n"
70  "</State> \n"
71  " <State> 2 <PdfClass> 2\n"
72  " <Transition> 2 0.5\n"
73  " <Transition> 3 0.5\n"
74  " </State> \n"
75  " <State> 3 </State>\n"
76  " </TopologyEntry>\n"
77  " </Topology>\n";
78 
79  HmmTopology topo;
80  std::istringstream iss(topo_string.str());
81  topo.Read(iss, false);
82  return topo;
83 
84 }
85 
86 
87 HmmTopology GenRandTopology(const std::vector<int32> &phones_in,
88  const std::vector<int32> &num_pdf_classes) {
89  std::vector<int32> phones(phones_in);
90  std::sort(phones.begin(), phones.end());
91  KALDI_ASSERT(IsSortedAndUniq(phones) && !phones.empty());
92 
93  std::ostringstream topo_string;
94 
95  std::map<int32, std::vector<int32> > num_pdf_classes_to_phones;
96  for (size_t i = 0; i < phones.size(); i++) {
97  int32 p = phones[i];
98  KALDI_ASSERT(static_cast<size_t>(p) < num_pdf_classes.size());
99  int32 n = num_pdf_classes[p];
100  KALDI_ASSERT(n > 0 && "num-pdf-classes cannot be zero.");
101  num_pdf_classes_to_phones[n].push_back(p);
102  }
103 
104  topo_string << "<Topology>\n";
105  std::map<int32, std::vector<int32> >::const_iterator
106  iter = num_pdf_classes_to_phones.begin(),
107  end = num_pdf_classes_to_phones.end();
108  for (; iter != end; ++iter) {
109  topo_string << "<TopologyEntry>\n"
110  "<ForPhones> ";
111  int32 this_num_pdf_classes = iter->first;
112  const std::vector<int32> &phones = iter->second;
113  for (size_t i = 0; i < phones.size(); i++)
114  topo_string << phones[i] << " ";
115  topo_string << "</ForPhones> ";
116  bool ergodic = (RandInt(0, 1) == 0);
117  if (ergodic) {
118  // Note, this type of topology is not something we ever use in practice- it
119  // has an initial nonemitting state (no PdfClass specified). But it's
120  // supported so we're testing it.
121  std::vector<int32> state_to_pdf_class;
122  state_to_pdf_class.push_back(-1); // state zero, nonemitting.
123  for (int32 i = 0; i < this_num_pdf_classes; i++) {
124  int32 num_states = RandInt(1, 2);
125  for (int32 j = 0; j < num_states; j++)
126  state_to_pdf_class.push_back(i);
127  }
128  state_to_pdf_class.push_back(-1); // final non-emitting state.
129  { // state zero is nonemitting. This is not something used in any current
130  // example script.
131  topo_string << "<State> 0\n";
132  BaseFloat prob = 1.0 / (state_to_pdf_class.size() - 2);
133  for (size_t i = 1; i + 1 < state_to_pdf_class.size(); i++) {
134  topo_string << "<Transition> " << i << ' ' << prob << '\n';
135  }
136  topo_string << "</State>\n";
137  }
138  // ergodic part.
139  for (size_t i = 1; i + 1 < state_to_pdf_class.size(); i++) {
140  BaseFloat prob = 1.0 / (state_to_pdf_class.size() - 1);
141  topo_string << "<State> " << i << " <PdfClass> "
142  << state_to_pdf_class[i] << '\n';
143  for (size_t j = 1; j < state_to_pdf_class.size(); j++)
144  topo_string << "<Transition> " << j << ' ' << prob << '\n';
145  topo_string << "</State>\n";
146  }
147  // final, nonemitting state. No pdf-class, no transitions.
148  topo_string << "<State> " << (state_to_pdf_class.size() - 1) << " </State>\n";
149  } else {
150  // feedforward topology.
151  int32 cur_state = 0;
152  for (int32 pdf_class = 0; pdf_class < this_num_pdf_classes; pdf_class++) {
153  int32 this_num_states = RandInt(1, 2);
154  for (int32 s = 0; s < this_num_states; s++) {
155  topo_string << "<State> " << cur_state << " <PdfClass> " << pdf_class
156  << "\n<Transition> " << cur_state << " 0.5\n<Transition> "
157  << (cur_state + 1) << " 0.5\n</State>\n";
158  cur_state++;
159  }
160  }
161  // final, non-emitting state.
162  topo_string << "<State> " << cur_state << " </State>\n";
163  }
164  topo_string << "</TopologyEntry>\n";
165  }
166  topo_string << "</Topology>\n";
167 
168  HmmTopology topo;
169  std::istringstream iss(topo_string.str());
170  topo.Read(iss, false);
171  return topo;
172 }
173 
175  std::vector<int32> phones;
176  phones.push_back(1);
177  for (int32 i = 2; i < 20; i++)
178  if (rand() % 2 == 0)
179  phones.push_back(i);
180  if (RandInt(0, 1) == 0) {
181  return GetDefaultTopology(phones);
182  } else {
183  std::vector<int32> num_pdf_classes(phones.back() + 1, -1);
184  for (int32 i = 0; i < phones.size(); i++)
185  num_pdf_classes[phones[i]] = RandInt(1, 5);
186  return GenRandTopology(phones, num_pdf_classes);
187  }
188 }
189 
190 void GeneratePathThroughHmm(const HmmTopology &topology,
191  bool reorder,
192  int32 phone,
193  std::vector<std::pair<int32, int32> > *path) {
194  path->clear();
195  const HmmTopology::TopologyEntry &this_entry =
196  topology.TopologyForPhone(phone);
197  int32 cur_state = 0; // start-state is always state zero.
198  int32 num_states = this_entry.size(), final_state = num_states - 1;
199  KALDI_ASSERT(num_states > 1); // there has to be a final nonemitting state
200  // that's different from the start state.
201  std::vector<std::pair<int32, int32> > pending_self_loops;
202  while (cur_state != final_state) {
203  const HmmTopology::HmmState &cur_hmm_state = this_entry[cur_state];
204  int32 num_transitions = cur_hmm_state.transitions.size(),
205  transition_index = RandInt(0, num_transitions - 1);
206  if (cur_hmm_state.forward_pdf_class != -1) {
207  std::pair<int32, int32> pr(cur_state, transition_index);
208  if (!reorder) {
209  path->push_back(pr);
210  } else {
211  bool is_self_loop = (cur_state ==
212  cur_hmm_state.transitions[transition_index].first);
213  if (is_self_loop) { // save these up, we'll put them after the forward
214  // transition.
215  pending_self_loops.push_back(pr);
216  } else {
217  // non-self-loop: output it and then flush out any self-loops we
218  // stored up.
219  path->push_back(pr);
220  path->insert(path->end(), pending_self_loops.begin(),
221  pending_self_loops.end());
222  pending_self_loops.clear();
223  }
224  }
225  }
226  cur_state = cur_hmm_state.transitions[transition_index].first;
227  }
228  KALDI_ASSERT(pending_self_loops.empty());
229 }
230 
231 
233  const TransitionModel &trans_model,
234  bool reorder,
235  const std::vector<int32> &phone_sequence,
236  std::vector<int32> *alignment) {
237  int32 context_width = ctx_dep.ContextWidth(),
238  central_position = ctx_dep.CentralPosition(),
239  num_phones = phone_sequence.size();
240  alignment->clear();
241  for (int32 i = 0; i < num_phones; i++) {
242  std::vector<int32> context_window;
243  context_window.reserve(context_width);
244  for (int32 j = i - central_position;
245  j < i - central_position + context_width;
246  j++) {
247  if (j >= 0 && j < num_phones) context_window.push_back(phone_sequence[j]);
248  else context_window.push_back(0); // zero for out-of-window phones
249  }
250  // 'path' is the path through this phone's HMM, represented as
251  // (emitting-HMM-state, transition-index) pairs
252  std::vector<std::pair<int32, int32> > path;
253  int32 phone = phone_sequence[i];
254  GeneratePathThroughHmm(trans_model.GetTopo(), reorder, phone, &path);
255  for (size_t k = 0; k < path.size(); k++) {
256  const HmmTopology::TopologyEntry &entry =
257  trans_model.GetTopo().TopologyForPhone(phone);
258  int32 hmm_state = path[k].first,
259  transition_index = path[k].second,
260  forward_pdf_class = entry[hmm_state].forward_pdf_class,
261  self_loop_pdf_class = entry[hmm_state].self_loop_pdf_class,
262  forward_pdf_id, self_loop_pdf_id;
263  bool ans = ctx_dep.Compute(context_window, forward_pdf_class, &forward_pdf_id);
264  KALDI_ASSERT(ans && "context-dependency computation failed.");
265  ans = ctx_dep.Compute(context_window, self_loop_pdf_class, &self_loop_pdf_id);
266  KALDI_ASSERT(ans && "context-dependency computation failed.");
267  int32 transition_state = trans_model.TupleToTransitionState(
268  phone, hmm_state, forward_pdf_id, self_loop_pdf_id),
269  transition_id = trans_model.PairToTransitionId(transition_state,
270  transition_index);
271  alignment->push_back(transition_id);
272  }
273  }
274 }
275 
276 
277 } // End namespace kaldi
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
HmmTopology GetDefaultTopology(const std::vector< int32 > &phones_in)
This function returns a HmmTopology object giving a normal 3-state topology, covering all phones in t...
A structure defined inside HmmTopology to represent a HMM state.
Definition: hmm-topology.h:96
int32 PairToTransitionId(int32 trans_state, int32 trans_index) const
ContextDependency * GenRandContextDependencyLarge(const std::vector< int32 > &phone_ids, int N, int P, bool ensure_all_covered, std::vector< int32 > *hmm_lengths)
GenRandContextDependencyLarge is like GenRandContextDependency but generates a larger tree with speci...
Definition: context-dep.cc:97
A class for storing topology information for phones.
Definition: hmm-topology.h:93
virtual bool Compute(const std::vector< int32 > &phoneseq, int32 pdf_class, int32 *pdf_id) const =0
The "new" Compute interface.
int32 TupleToTransitionState(int32 phone, int32 hmm_state, int32 pdf, int32 self_loop_pdf) const
kaldi::int32 int32
void Read(std::istream &is, bool binary)
Definition: hmm-topology.cc:39
std::vector< HmmState > TopologyEntry
TopologyEntry is a typedef that represents the topology of a single (prototype) state.
Definition: hmm-topology.h:133
void GeneratePathThroughHmm(const HmmTopology &topology, bool reorder, int32 phone, std::vector< std::pair< int32, int32 > > *path)
This function generates a random path through the HMM for the given phone.
HmmTopology GenRandTopology(const std::vector< int32 > &phones_in, const std::vector< int32 > &num_pdf_classes)
This method of generating an arbitrary HmmTopology object allows you to specify the number of pdf-cla...
virtual int CentralPosition() const =0
Central position P of the phone context, in 0-based numbering, e.g.
struct rnnlm::@11::@12 n
const TopologyEntry & TopologyForPhone(int32 phone) const
Returns the topology entry (i.e.
const HmmTopology & GetTopo() const
return reference to HMM-topology object.
std::vector< std::pair< int32, BaseFloat > > transitions
A list of transitions, indexed by what we call a &#39;transition-index&#39;.
Definition: hmm-topology.h:109
void GenerateRandomAlignment(const ContextDependencyInterface &ctx_dep, const TransitionModel &trans_model, bool reorder, const std::vector< int32 > &phone_sequence, std::vector< int32 > *alignment)
For use in test code, this function generates an alignment (a sequence of transition-ids) correspondi...
context-dep-itf.h provides a link between the tree-building code in ../tree/, and the FST code in ...
virtual int ContextWidth() const =0
ContextWidth() returns the value N (e.g.
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
TransitionModel * GenRandTransitionModel(ContextDependency **ctx_dep_out)
int32 forward_pdf_class
The Pdf-classes forward-pdf-class, typically 0, 1 or 2 (the same as the HMM-state index)...
Definition: hmm-topology.h:100
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
int32 RandInt(int32 min_val, int32 max_val, struct RandomState *state)
Definition: kaldi-math.cc:95