nnet-utils.cc
Go to the documentation of this file.
1 // nnet3/nnet-utils.cc
2 
3 // Copyright 2015 Johns Hopkins University (author: Daniel Povey)
4 // 2016 Daniel Galvez
5 //
6 // See ../../COPYING for clarification regarding multiple authors
7 //
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 //
12 // http://www.apache.org/licenses/LICENSE-2.0
13 //
14 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 // MERCHANTABLITY OR NON-INFRINGEMENT.
18 // See the Apache 2 License for the specific language governing permissions and
19 // limitations under the License.
20 
21 #include <iomanip>
22 #include "nnet3/nnet-utils.h"
23 #include "nnet3/nnet-graph.h"
28 #include "nnet3/nnet-parse.h"
30 #include "nnet3/nnet-diagnostics.h"
31 
32 namespace kaldi {
33 namespace nnet3 {
34 
35 int32 NumOutputNodes(const Nnet &nnet) {
36  int32 ans = 0;
37  for (int32 n = 0; n < nnet.NumNodes(); n++)
38  if (nnet.IsOutputNode(n))
39  ans++;
40  return ans;
41 }
42 
43 int32 NumInputNodes(const Nnet &nnet) {
44  int32 ans = 0;
45  for (int32 n = 0; n < nnet.NumNodes(); n++)
46  if (nnet.IsInputNode(n))
47  ans++;
48  return ans;
49 }
50 
51 
52 bool IsSimpleNnet(const Nnet &nnet) {
53  // check that we have an output node and called "output".
54  if (nnet.GetNodeIndex("output") == -1 ||
55  !nnet.IsOutputNode(nnet.GetNodeIndex("output")))
56  return false;
57  // check that there is an input node named "input".
58  if (nnet.GetNodeIndex("input") == -1 ||
59  !nnet.IsInputNode(nnet.GetNodeIndex("input")))
60  return false;
61  // if there was just one input, then it was named
62  // "input" and everything checks out.
63  if (NumInputNodes(nnet) == 1)
64  return true;
65  // Otherwise, there should be input node with name "input" and one
66  // should be called "ivector".
67  return nnet.GetNodeIndex("ivector") != -1 &&
68  nnet.IsInputNode(nnet.GetNodeIndex("ivector"));
69 }
70 
72  const Nnet &nnet,
73  const ComputationRequest &request,
74  std::vector<std::vector<bool> > *is_computable) {
75  ComputationGraph graph;
76  ComputationGraphBuilder builder(nnet, &graph);
77  builder.Compute(request);
78  builder.GetComputableInfo(is_computable);
79  if (GetVerboseLevel() >= 4) {
80  std::ostringstream graph_pretty;
81  graph.Print(graph_pretty, nnet.GetNodeNames());
82  KALDI_VLOG(4) << "Graph is " << graph_pretty.str();
83  }
84 }
85 
86 // This non-exported function is used in ComputeSimpleNnetContext
87 // to compute the left and right context of the nnet for a particular
88 // window size and shift-length.
89 // It returns false if no outputs were computable, meaning the left and
90 // right context could not be computed. (Normally this means the window
91 // size is too small).
93  const Nnet &nnet,
94  int32 input_start,
95  int32 window_size,
96  int32 *left_context,
97  int32 *right_context) {
98 
99  int32 input_end = input_start + window_size;
100  IoSpecification input;
101  input.name = "input";
102  IoSpecification output;
103  output.name = "output";
104  IoSpecification ivector; // we might or might not use this.
105  ivector.name = "ivector";
106 
107  int32 n = rand() % 10;
108  // in the IoSpecification for now we we will request all the same indexes at
109  // output that we requested at input.
110  for (int32 t = input_start; t < input_end; t++) {
111  input.indexes.push_back(Index(n, t));
112  output.indexes.push_back(Index(n, t));
113  }
114 
115  // most networks will just require the ivector at time t = 0,
116  // but this might not always be the case, and some might use rounding
117  // descriptors with the iVector which might require it at an earlier
118  // frame than the regular input, so we provide the iVector in as wide a range
119  // as it might possibly be needed.
120  for (int32 t = input_start - nnet.Modulus(); t < input_end; t++) {
121  ivector.indexes.push_back(Index(n, t));
122  }
123 
124  ComputationRequest request;
125  request.inputs.push_back(input);
126  request.outputs.push_back(output);
127  if (nnet.GetNodeIndex("ivector") != -1)
128  request.inputs.push_back(ivector);
129  std::vector<std::vector<bool> > computable;
130  EvaluateComputationRequest(nnet, request, &computable);
131 
132  KALDI_ASSERT(computable.size() == 1);
133  std::vector<bool> &output_ok = computable[0];
134  std::vector<bool>::iterator iter =
135  std::find(output_ok.begin(), output_ok.end(), true);
136  int32 first_ok = iter - output_ok.begin();
137  int32 first_not_ok = std::find(iter, output_ok.end(), false) -
138  output_ok.begin();
139  if (first_ok == window_size || first_not_ok <= first_ok)
140  return false;
141  *left_context = first_ok;
142  *right_context = window_size - first_not_ok;
143  return true;
144 }
145 
147  int32 *left_context,
148  int32 *right_context) {
149  KALDI_ASSERT(IsSimpleNnet(nnet));
150  int32 modulus = nnet.Modulus();
151  // modulus >= 1 is a number such that the network ought to be
152  // invariant to time shifts (of both the input and output) that
153  // are a multiple of this number. We need to test all shifts modulo
154  // this number in case the left and right context vary at all within
155  // this range.
156 
157  std::vector<int32> left_contexts(modulus + 1);
158  std::vector<int32> right_contexts(modulus + 1);
159 
160  // window_size is a number which needs to be greater than the total context
161  // of the nnet, else we won't be able to work out the context. Large window
162  // size will make this code slow, so we start off with small window size, and
163  // if it isn't enough, we keep doubling it up to a maximum.
164  int32 window_size = 40, max_window_size = 800;
165 
166  while (window_size < max_window_size) {
167 
168  // by going "<= modulus" instead of "< modulus" we do one more computation
169  // than we really need; it becomes a sanity check.
170  int32 input_start;
171  for (input_start = 0; input_start <= modulus; input_start++) {
172  if (!ComputeSimpleNnetContextForShift(nnet, input_start, window_size,
173  &(left_contexts[input_start]),
174  &(right_contexts[input_start])))
175  break;
176  }
177  if (input_start <= modulus) {
178  // We broke from the loop over 'input_start', which means there was
179  // a failure in ComputeSimpleNnextContextForShift-- we assume at
180  // this point that it was because window_size was too small.
181  window_size *= 2;
182  continue;
183  }
184 
185  KALDI_ASSERT(left_contexts[0] == left_contexts[modulus] &&
186  "nnet does not have the properties we expect.");
187  KALDI_ASSERT(right_contexts[0] == right_contexts[modulus] &&
188  "nnet does not have the properties we expect.");
189  *left_context =
190  *std::max_element(left_contexts.begin(), left_contexts.end());
191  *right_context =
192  *std::max_element(right_contexts.begin(), right_contexts.end());
193  // Success.
194  return;
195  }
196  KALDI_ERR << "Failure in ComputeSimpleNnetContext (perhaps not a simple nnet?)";
197 }
198 
200  Nnet *nnet) {
201  for (int32 c = 0; c < nnet->NumComponents(); c++) {
202  Component *comp = nnet->GetComponent(c);
203  if (comp->Properties() & kUpdatableComponent) {
204  UpdatableComponent *u_comp = dynamic_cast<UpdatableComponent*>(comp);
205  KALDI_ASSERT(u_comp != NULL);
206  u_comp->PerturbParams(stddev);
207  }
208  }
209 }
210 
211 void ComponentDotProducts(const Nnet &nnet1,
212  const Nnet &nnet2,
213  VectorBase<BaseFloat> *dot_prod) {
214  KALDI_ASSERT(nnet1.NumComponents() == nnet2.NumComponents());
215  int32 updatable_c = 0;
216  for (int32 c = 0; c < nnet1.NumComponents(); c++) {
217  const Component *comp1 = nnet1.GetComponent(c),
218  *comp2 = nnet2.GetComponent(c);
219  if (comp1->Properties() & kUpdatableComponent) {
220  const UpdatableComponent
221  *u_comp1 = dynamic_cast<const UpdatableComponent*>(comp1),
222  *u_comp2 = dynamic_cast<const UpdatableComponent*>(comp2);
223  KALDI_ASSERT(u_comp1 != NULL && u_comp2 != NULL);
224  dot_prod->Data()[updatable_c] = u_comp1->DotProduct(*u_comp2);
225  updatable_c++;
226  }
227  }
228  KALDI_ASSERT(updatable_c == dot_prod->Dim());
229 }
230 
231 std::string PrintVectorPerUpdatableComponent(const Nnet &nnet,
232  const VectorBase<BaseFloat> &vec) {
233  std::ostringstream os;
234  os << "[ ";
235  KALDI_ASSERT(NumUpdatableComponents(nnet) == vec.Dim());
236  int32 updatable_c = 0;
237  for (int32 c = 0; c < nnet.NumComponents(); c++) {
238  const Component *comp = nnet.GetComponent(c);
239  if (comp->Properties() & kUpdatableComponent) {
240  const std::string &component_name = nnet.GetComponentName(c);
241  os << component_name << ':' << vec(updatable_c) << ' ';
242  updatable_c++;
243  }
244  }
245  KALDI_ASSERT(updatable_c == vec.Dim());
246  os << ']';
247  return os.str();
248 }
249 
250 BaseFloat DotProduct(const Nnet &nnet1,
251  const Nnet &nnet2) {
252  KALDI_ASSERT(nnet1.NumComponents() == nnet2.NumComponents());
253  BaseFloat ans = 0.0;
254  for (int32 c = 0; c < nnet1.NumComponents(); c++) {
255  const Component *comp1 = nnet1.GetComponent(c),
256  *comp2 = nnet2.GetComponent(c);
257  if (comp1->Properties() & kUpdatableComponent) {
258  const UpdatableComponent
259  *u_comp1 = dynamic_cast<const UpdatableComponent*>(comp1),
260  *u_comp2 = dynamic_cast<const UpdatableComponent*>(comp2);
261  KALDI_ASSERT(u_comp1 != NULL && u_comp2 != NULL);
262  ans += u_comp1->DotProduct(*u_comp2);
263  }
264  }
265  return ans;
266 }
267 
268 
270  for (int32 c = 0; c < nnet->NumComponents(); c++) {
271  Component *comp = nnet->GetComponent(c);
272  comp->ZeroStats(); // for some components, this won't do anything.
273  }
274 }
275 
276 void SetLearningRate(BaseFloat learning_rate,
277  Nnet *nnet) {
278  for (int32 c = 0; c < nnet->NumComponents(); c++) {
279  Component *comp = nnet->GetComponent(c);
280  if (comp->Properties() & kUpdatableComponent) {
281  // For now all updatable components inherit from class UpdatableComponent.
282  // If that changes in future, we will change this code.
283  UpdatableComponent *uc = dynamic_cast<UpdatableComponent*>(comp);
284  if (uc == NULL)
285  KALDI_ERR << "Updatable component does not inherit from class "
286  "UpdatableComponent; change this code.";
287  uc->SetUnderlyingLearningRate(learning_rate);
288  }
289  }
290 }
291 
292 void SetNnetAsGradient(Nnet *nnet) {
293  for (int32 c = 0; c < nnet->NumComponents(); c++) {
294  Component *comp = nnet->GetComponent(c);
295  if (comp->Properties() & kUpdatableComponent) {
296  UpdatableComponent *u_comp = dynamic_cast<UpdatableComponent*>(comp);
297  KALDI_ASSERT(u_comp != NULL);
298  u_comp->SetAsGradient();
299  }
300  }
301 }
302 
303 void SetRequireDirectInput(bool b, Nnet *nnet) {
304  for (int32 c = 0; c < nnet->NumComponents(); c++) {
305  Component *comp = nnet->GetComponent(c);
306  if (dynamic_cast<StatisticsPoolingComponent*>(comp) != NULL)
307  dynamic_cast<StatisticsPoolingComponent*>(comp)->SetRequireDirectInput(b);
308  }
309 }
310 
311 
312 void ScaleNnet(BaseFloat scale, Nnet *nnet) {
313  if (scale == 1.0) return;
314  else {
315  for (int32 c = 0; c < nnet->NumComponents(); c++) {
316  Component *comp = nnet->GetComponent(c);
317  comp->Scale(scale);
318  }
319  }
320 }
321 
322 void AddNnetComponents(const Nnet &src, const Vector<BaseFloat> &alphas,
323  BaseFloat scale, Nnet *dest) {
324  if (src.NumComponents() != dest->NumComponents())
325  KALDI_ERR << "Trying to add incompatible nnets.";
326  int32 i = 0;
327  for (int32 c = 0; c < src.NumComponents(); c++) {
328  const Component *src_comp = src.GetComponent(c);
329  Component *dest_comp = dest->GetComponent(c);
330  if (src_comp->Properties() & kUpdatableComponent) {
331  // For now all updatable components inherit from class UpdatableComponent.
332  // If that changes in future, we will change this code.
333  const UpdatableComponent *src_uc =
334  dynamic_cast<const UpdatableComponent*>(src_comp);
335  UpdatableComponent *dest_uc =
336  dynamic_cast<UpdatableComponent*>(dest_comp);
337  if (src_uc == NULL || dest_uc == NULL)
338  KALDI_ERR << "Updatable component does not inherit from class "
339  "UpdatableComponent; change this code.";
340  KALDI_ASSERT(i < alphas.Dim());
341  dest_uc->Add(alphas(i++), *src_uc);
342  } else { // add stored stats
343  dest_comp->Add(scale, *src_comp);
344  }
345  }
346  KALDI_ASSERT(i == alphas.Dim());
347 }
348 
349 void AddNnet(const Nnet &src, BaseFloat alpha, Nnet *dest) {
350  if (src.NumComponents() != dest->NumComponents())
351  KALDI_ERR << "Trying to add incompatible nnets.";
352  for (int32 c = 0; c < src.NumComponents(); c++) {
353  const Component *src_comp = src.GetComponent(c);
354  Component *dest_comp = dest->GetComponent(c);
355  dest_comp->Add(alpha, *src_comp);
356  }
357 }
358 
359 int32 NumParameters(const Nnet &src) {
360  int32 ans = 0;
361  for (int32 c = 0; c < src.NumComponents(); c++) {
362  const Component *comp = src.GetComponent(c);
363  if (comp->Properties() & kUpdatableComponent) {
364  // For now all updatable components inherit from class UpdatableComponent.
365  // If that changes in future, we will change this code.
366  const UpdatableComponent *uc =
367  dynamic_cast<const UpdatableComponent*>(comp);
368  if (uc == NULL)
369  KALDI_ERR << "Updatable component does not inherit from class "
370  "UpdatableComponent; change this code.";
371  ans += uc->NumParameters();
372  }
373  }
374  return ans;
375 }
376 
377 
378 void VectorizeNnet(const Nnet &src,
379  VectorBase<BaseFloat> *parameters) {
380  KALDI_ASSERT(parameters->Dim() == NumParameters(src));
381  int32 dim_offset = 0;
382  for (int32 c = 0; c < src.NumComponents(); c++) {
383  const Component *comp = src.GetComponent(c);
384  if (comp->Properties() & kUpdatableComponent) {
385  // For now all updatable components inherit from class UpdatableComponent.
386  // If that changes in future, we will change this code.
387  const UpdatableComponent *uc =
388  dynamic_cast<const UpdatableComponent*>(comp);
389  if (uc == NULL)
390  KALDI_ERR << "Updatable component does not inherit from class "
391  "UpdatableComponent; change this code.";
392  int32 this_dim = uc->NumParameters();
393  SubVector<BaseFloat> this_part(*parameters, dim_offset, this_dim);
394  uc->Vectorize(&this_part);
395  dim_offset += this_dim;
396  }
397  }
398 }
399 
400 
401 void UnVectorizeNnet(const VectorBase<BaseFloat> &parameters,
402  Nnet *dest) {
403  KALDI_ASSERT(parameters.Dim() == NumParameters(*dest));
404  int32 dim_offset = 0;
405  for (int32 c = 0; c < dest->NumComponents(); c++) {
406  Component *comp = dest->GetComponent(c);
407  if (comp->Properties() & kUpdatableComponent) {
408  // For now all updatable components inherit from class UpdatableComponent.
409  // If that changes in future, we will change this code.
410  UpdatableComponent *uc = dynamic_cast<UpdatableComponent*>(comp);
411  if (uc == NULL)
412  KALDI_ERR << "Updatable component does not inherit from class "
413  "UpdatableComponent; change this code.";
414  int32 this_dim = uc->NumParameters();
415  const SubVector<BaseFloat> this_part(parameters, dim_offset, this_dim);
416  uc->UnVectorize(this_part);
417  dim_offset += this_dim;
418  }
419  }
420 }
421 
423  int32 ans = 0;
424  for (int32 c = 0; c < dest.NumComponents(); c++) {
425  const Component *comp = dest.GetComponent(c);
426  if (comp->Properties() & kUpdatableComponent)
427  ans++;
428  }
429  return ans;
430 }
431 
432 void FreezeNaturalGradient(bool freeze, Nnet *nnet) {
433  for (int32 c = 0; c < nnet->NumComponents(); c++) {
434  Component *comp = nnet->GetComponent(c);
435  if (comp->Properties() & kUpdatableComponent) {
436  // For now all updatable components inherit from class UpdatableComponent.
437  // If that changes in future, we will change this code.
438  UpdatableComponent *uc = dynamic_cast<UpdatableComponent*>(comp);
439  if (uc == NULL)
440  KALDI_ERR << "Updatable component does not inherit from class "
441  "UpdatableComponent; change this code.";
442  uc->FreezeNaturalGradient(freeze);
443  }
444  }
445 }
446 
448  for(int32 i = 0; i < c_component->NumComponents(); i++) {
449  const Component *c = c_component->GetComponent(i);
450  KALDI_ASSERT(c->Type() != "CompositeComponent" &&
451  "Nesting CompositeComponent within CompositeComponent is not allowed.\n"
452  "(We may change this as more complicated components are introduced.)");
453 
454  if(c->Type() == "RepeatedAffineComponent" ||
455  c->Type() == "NaturalGradientRepeatedAffineComponent") {
456  // N.B.: NaturalGradientRepeatedAffineComponent is a subclass of
457  // RepeatedAffineComponent.
458  const RepeatedAffineComponent *rac =
459  dynamic_cast<const RepeatedAffineComponent*>(c);
460  KALDI_ASSERT(rac != NULL);
462  // following call deletes rac
463  c_component->SetComponent(i, bac);
464  }
465  }
466 }
467 
469  for(int32 i = 0; i < nnet->NumComponents(); i++) {
470  const Component *const_c = nnet->GetComponent(i);
471  if(const_c->Type() == "RepeatedAffineComponent" ||
472  const_c->Type() == "NaturalGradientRepeatedAffineComponent") {
473  // N.B.: NaturalGradientRepeatedAffineComponent is a subclass of
474  // RepeatedAffineComponent.
475  const RepeatedAffineComponent *rac =
476  dynamic_cast<const RepeatedAffineComponent*>(const_c);
477  KALDI_ASSERT(rac != NULL);
479  // following call deletes rac
480  nnet->SetComponent(i, bac);
481  } else if (const_c->Type() == "CompositeComponent") {
482  // We must modify the composite component, so we use the
483  // non-const GetComponent() call here.
484  Component *c = nnet->GetComponent(i);
485  CompositeComponent *cc = dynamic_cast<CompositeComponent*>(c);
486  KALDI_ASSERT(cc != NULL);
488  }
489  }
490 }
491 
492 std::string NnetInfo(const Nnet &nnet) {
493  std::ostringstream ostr;
494  if (IsSimpleNnet(nnet)) {
495  int32 left_context, right_context;
496  // this call will crash if the nnet is not 'simple'.
497  ComputeSimpleNnetContext(nnet, &left_context, &right_context);
498  ostr << "left-context: " << left_context << "\n";
499  ostr << "right-context: " << right_context << "\n";
500  }
501  ostr << "input-dim: " << nnet.InputDim("input") << "\n";
502  ostr << "ivector-dim: " << nnet.InputDim("ivector") << "\n";
503  ostr << "output-dim: " << nnet.OutputDim("output") << "\n";
504  ostr << "# Nnet info follows.\n";
505  ostr << nnet.Info();
506  return ostr.str();
507 }
508 
509 void SetDropoutProportion(BaseFloat dropout_proportion,
510  Nnet *nnet) {
511  for (int32 c = 0; c < nnet->NumComponents(); c++) {
512  Component *comp = nnet->GetComponent(c);
513  DropoutComponent *dc = dynamic_cast<DropoutComponent*>(comp);
514  if (dc != NULL)
515  dc->SetDropoutProportion(dropout_proportion);
517  dynamic_cast<DropoutMaskComponent*>(nnet->GetComponent(c));
518  if (mc != NULL)
519  mc->SetDropoutProportion(dropout_proportion);
521  dynamic_cast<GeneralDropoutComponent*>(nnet->GetComponent(c));
522  if (gdc != NULL)
523  gdc->SetDropoutProportion(dropout_proportion);
524  }
525 }
526 
527 bool HasBatchnorm(const Nnet &nnet) {
528  for (int32 c = 0; c < nnet.NumComponents(); c++) {
529  const Component *comp = nnet.GetComponent(c);
530  if (dynamic_cast<const BatchNormComponent*>(comp) != NULL)
531  return true;
532  }
533  return false;
534 }
535 
536 void ScaleBatchnormStats(BaseFloat batchnorm_stats_scale,
537  Nnet *nnet) {
538  KALDI_ASSERT(batchnorm_stats_scale >= 0.0 && batchnorm_stats_scale <= 1.0);
539  if (batchnorm_stats_scale == 1.0)
540  return;
541  for (int32 c = 0; c < nnet->NumComponents(); c++) {
542  Component *comp = nnet->GetComponent(c);
543  BatchNormComponent *bc = dynamic_cast<BatchNormComponent*>(comp);
544  if (bc != NULL)
545  bc->Scale(batchnorm_stats_scale);
546  }
547 }
548 
549 
550 void RecomputeStats(const std::vector<NnetExample> &egs, Nnet *nnet) {
551  KALDI_LOG << "Recomputing stats on nnet (affects batch-norm)";
552  ZeroComponentStats(nnet);
554  opts.store_component_stats = true;
555  NnetComputeProb prob_computer(opts, nnet);
556  for (size_t i = 0; i < egs.size(); i++)
557  prob_computer.Compute(egs[i]);
558  prob_computer.PrintTotalStats();
559  KALDI_LOG << "Done recomputing stats.";
560 }
561 
562 
563 
564 void SetBatchnormTestMode(bool test_mode, Nnet *nnet) {
565  for (int32 c = 0; c < nnet->NumComponents(); c++) {
566  Component *comp = nnet->GetComponent(c);
567  BatchNormComponent *bc = dynamic_cast<BatchNormComponent*>(comp);
568  if (bc != NULL)
569  bc->SetTestMode(test_mode);
570  }
571 }
572 
573 void SetDropoutTestMode(bool test_mode, Nnet *nnet) {
574  for (int32 c = 0; c < nnet->NumComponents(); c++) {
575  Component *comp = nnet->GetComponent(c);
576  RandomComponent *rc = dynamic_cast<RandomComponent*>(comp);
577  if (rc != NULL)
578  rc->SetTestMode(test_mode);
579  }
580 }
581 
582 void ResetGenerators(Nnet *nnet){
583  for (int32 c = 0; c < nnet->NumComponents(); c++) {
584  Component *comp = nnet->GetComponent(c);
585  RandomComponent *rc = dynamic_cast<RandomComponent*>(comp);
586  if (rc != NULL)
587  rc->ResetGenerator();
588  }
589 }
590 
591 void FindOrphanComponents(const Nnet &nnet, std::vector<int32> *components) {
592  int32 num_components = nnet.NumComponents(), num_nodes = nnet.NumNodes();
593  std::vector<bool> is_used(num_components, false);
594  for (int32 i = 0; i < num_nodes; i++) {
595  if (nnet.IsComponentNode(i)) {
596  int32 c = nnet.GetNode(i).u.component_index;
597  KALDI_ASSERT(c >= 0 && c < num_components);
598  is_used[c] = true;
599  }
600  }
601  components->clear();
602  for (int32 i = 0; i < num_components; i++)
603  if (!is_used[i])
604  components->push_back(i);
605 }
606 
607 void FindOrphanNodes(const Nnet &nnet, std::vector<int32> *nodes) {
608 
609  std::vector<std::vector<int32> > depend_on_graph, dependency_graph;
610  NnetToDirectedGraph(nnet, &depend_on_graph);
611  // depend_on_graph[i] is a list of all the nodes that depend on i.
612  ComputeGraphTranspose(depend_on_graph, &dependency_graph);
613  // dependency_graph[i] is a list of all the nodes that i depends on,
614  // to be computed.
615 
616  // Find all nodes required to produce the outputs.
617  int32 num_nodes = nnet.NumNodes();
618  assert(num_nodes == static_cast<int32>(dependency_graph.size()));
619  std::vector<bool> node_is_required(num_nodes, false);
620  std::vector<int32> queue;
621  for (int32 i = 0; i < num_nodes; i++) {
622  if (nnet.IsOutputNode(i))
623  queue.push_back(i);
624  }
625  while (!queue.empty()) {
626  int32 i = queue.back();
627  queue.pop_back();
628  if (!node_is_required[i]) {
629  node_is_required[i] = true;
630  for (size_t j = 0; j < dependency_graph[i].size(); j++)
631  queue.push_back(dependency_graph[i][j]);
632  }
633  }
634  nodes->clear();
635  for (int32 i = 0; i < num_nodes; i++) {
636  if (!node_is_required[i])
637  nodes->push_back(i);
638  }
639 }
640 
641 
642 // Parameters used in applying SVD:
643 // 1. Energy threshold : For each Affine weights layer in the original baseline nnet3 model,
644 // we perform SVD based factoring of the weights matrix of the layer,
645 // into a singular values (left diagonal) matrix, and two Eigen matrices.
646 //
647 // SVD : Wx = UEV, U,V are Eigen matrices, and E is the singularity matrix)
648 //
649 // We take the center matrix E, and consider only the Singular values which contribute
650 // to (Energy-threshold) times the total Energy of Singularity parameters.
651 // These Singularity parameters are actually sorted in descending order and lower
652 // values are pruned out until the Total energy (Sum of squares) of the pruned set
653 // of parameters is just above (Energy-threshold * Total init energy). The values which
654 // are pruned away are replaced with 0 in the Singularity matrix
655 // and the Weights matrix after SVD is derived with shrinked dimensions.
656 //
657 // 2. Shrinkage-threshold : If the Shrinkage ratio of the SVD refactored Weights matrix
658 // is higher than Shrinkage-threshold for any of the Tdnn layers,
659 // the SVD process is aborted for that particular Affine weights layer.
660 //
661 
662 // this class implements the internals of the edit directive 'apply-svd'.
663 class SvdApplier {
664  public:
665  SvdApplier(const std::string component_name_pattern,
666  int32 bottleneck_dim,
667  BaseFloat energy_threshold,
668  BaseFloat shrinkage_threshold,
669  Nnet *nnet): nnet_(nnet),
670  bottleneck_dim_(bottleneck_dim),
671  energy_threshold_(energy_threshold),
672  shrinkage_threshold_(shrinkage_threshold),
673  component_name_pattern_(component_name_pattern) { }
674  void ApplySvd() {
676  if (!modified_component_info_.empty())
677  ModifyTopology();
678  KALDI_LOG << "Decomposed " << modified_component_info_.size()
679  << " components with SVD dimension " << bottleneck_dim_;
680  }
681 
682  private:
683  // This function finds components to decompose and decomposes them, adding _a and
684  // _b versions of those components to the nnet while not removing the original
685  // ones. Does not affect the graph topology.
687  int32 num_components = nnet_->NumComponents();
688  modification_index_.resize(num_components, -1);
689  for (int32 c = 0; c < num_components; c++) {
690  Component *component = nnet_->GetComponent(c);
691  std::string component_name = nnet_->GetComponentName(c);
692  if (NameMatchesPattern(component_name.c_str(),
693  component_name_pattern_.c_str())) {
694  AffineComponent *affine = dynamic_cast<AffineComponent*>(component);
695  if (affine == NULL) {
696  KALDI_WARN << "Not decomposing component " << component_name
697  << " as it is not an AffineComponent.";
698  continue;
699  }
700  int32 input_dim = affine->InputDim(),
701  output_dim = affine->OutputDim();
702  if (input_dim <= bottleneck_dim_ || output_dim <= bottleneck_dim_) {
703  KALDI_WARN << "Not decomposing component " << component_name
704  << " with SVD to rank " << bottleneck_dim_
705  << " because its dimension is " << input_dim
706  << " -> " << output_dim;
707  continue;
708  }
709  Component *component_a = NULL, *component_b = NULL;
710  if (DecomposeComponent(component_name, *affine, &component_a, &component_b)) {
711  size_t n = modified_component_info_.size();
712  modification_index_[c] = n;
713  modified_component_info_.resize(n + 1);
715  info.component_index = c;
716  info.component_name = component_name;
717  info.component_name_a = component_name + "_a";
718  info.component_name_b = component_name + "_b";
719  if (nnet_->GetComponentIndex(info.component_name_a) >= 0)
720  KALDI_ERR << "Neural network already has a component named "
721  << info.component_name_a;
722  if (nnet_->GetComponentIndex(info.component_name_b) >= 0)
723  KALDI_ERR << "Neural network already has a component named "
724  << info.component_name_b;
726  component_a);
728  component_b);
729  }
730  }
731  }
732  KALDI_LOG << "Converted " << modified_component_info_.size()
733  << " components to FixedAffineComponent.";
734  }
735 
736  // This function finds the minimum index of
737  // the Descending order sorted [input_vector],
738  // over a range of indices from [lower] to [upper] index,
739  // for which the sum of elements upto the found min. index is greater
740  // than [min_val].
741  // We add one to this index to return the reduced dimension value.
742 
744  int32 lower,
745  int32 upper,
746  BaseFloat min_val) {
747  BaseFloat sum = 0;
748  int32 i = 0;
749  for (i = lower; i <= upper; i++) {
750  sum = sum + input_vector(i);
751  if (sum >= min_val) break;
752  }
753  return (i+1);
754  }
755 
756 // Here we perform SVD based refactorig of an input Affine component.
757 // After applying SVD , we sort the Singularity values in descending order,
758 // and take the subset of values which contribute to energy_threshold times
759 // total original sum of squared singular values, and then refactor the Affine
760 // component using only these selected singular values, thus making the bottleneck
761 // dim of the refactored Affine layer equal to the no. of Singular values selected.
762 // This function returs false if the shrinkage ratio of the total no. of parameters,
763 // after the above SVD based refactoring, is greater than shrinkage threshold.
764 //
765  bool DecomposeComponent(const std::string &component_name,
766  const AffineComponent &affine,
767  Component **component_a_out,
768  Component **component_b_out) {
769  int32 input_dim = affine.InputDim(), output_dim = affine.OutputDim();
770  Matrix<BaseFloat> linear_params(affine.LinearParams());
771  Vector<BaseFloat> bias_params(affine.BiasParams());
772  int32 middle_dim = std::min<int32>(input_dim, output_dim);
773 
774  // note: 'linear_params' is of dimension output_dim by input_dim.
775  Vector<BaseFloat> s(middle_dim);
776  Matrix<BaseFloat> A(middle_dim, input_dim),
777  B(output_dim, middle_dim);
778  linear_params.Svd(&s, &B, &A);
779  // make sure the singular values are sorted from greatest to least value.
780  SortSvd(&s, &B, &A);
781  Vector<BaseFloat> s2(s.Dim());
782  s2.AddVec2(1.0, s);
783  BaseFloat s2_sum_orig = s2.Sum();
786  if (energy_threshold_ > 0) {
787  BaseFloat min_singular_sum = energy_threshold_ * s2_sum_orig;
788  bottleneck_dim_ = GetReducedDimension(s2, 0, s2.Dim()-1, min_singular_sum);
789  }
790  SubVector<BaseFloat> this_part(s2, 0, bottleneck_dim_);
791  BaseFloat s2_sum_reduced = this_part.Sum();
792  BaseFloat shrinkage_ratio =
793  static_cast<BaseFloat>(bottleneck_dim_ * (input_dim+output_dim))
794  / static_cast<BaseFloat>(input_dim * output_dim);
795  if (shrinkage_ratio > shrinkage_threshold_) {
796  KALDI_LOG << "Shrinkage ratio " << shrinkage_ratio
797  << " greater than threshold : " << shrinkage_threshold_
798  << " Skipping SVD for this layer.";
799  return false;
800  }
801 
802  s.Resize(bottleneck_dim_, kCopyData);
803  A.Resize(bottleneck_dim_, input_dim, kCopyData);
804  B.Resize(output_dim, bottleneck_dim_, kCopyData);
805  KALDI_LOG << "For component " << component_name
806  << " singular value squared sum changed by "
807  << (s2_sum_orig - s2_sum_reduced)
808  << " (from " << s2_sum_orig << " to " << s2_sum_reduced << ")";
809  KALDI_LOG << "For component " << component_name
810  << " dimension reduced from "
811  << " (" << input_dim << "," << output_dim << ")"
812  << " to [(" << input_dim << "," << bottleneck_dim_
813  << "), (" << bottleneck_dim_ << "," << output_dim <<")]";
814  KALDI_LOG << "shrinkage ratio : " << shrinkage_ratio;
815 
816  // we'll divide the singular values equally between the two
817  // parameter matrices.
818  s.ApplyPow(0.5);
819  A.MulRowsVec(s);
820  B.MulColsVec(s);
821 
822  CuMatrix<BaseFloat> A_cuda(A), B_cuda(B);
823  CuVector<BaseFloat> bias_params_cuda(bias_params);
824 
825  LinearComponent *component_a = new LinearComponent(A_cuda);
826  NaturalGradientAffineComponent *component_b =
827  new NaturalGradientAffineComponent(B_cuda, bias_params_cuda);
828  // set the learning rates, max-change, and so on.
829  component_a->SetUpdatableConfigs(affine);
830  component_b->SetUpdatableConfigs(affine);
831  *component_a_out = component_a;
832  *component_b_out = component_b;
833  return true;
834  }
835 
836  // This function modifies the topology of the neural network, splitting
837  // up the components we're modifying into two parts.
838  // Suppose we have something like:
839  // component-node name=some_node component=some_component input=
840  // nodes_to_modify will be a list of component-node indexes that we
841  // need to split into two. These will be nodes like
842  // component-node name=component_node_name component=component_name input=xxx
843  // where 'component_name' is one of the components that we're splitting.
844  // node_names_modified is nnet_->node_names_ except with, for the nodes that
845  // we are splitting in two, "some_node_name" replaced with
846  // "some_node_name_b" (the second of the two split nodes).
847  void ModifyTopology() {
848  std::set<int32> nodes_to_modify;
849  std::vector<std::string> node_names_orig = nnet_->GetNodeNames(),
850  node_names_modified = node_names_orig;
851 
852  // The following loop sets up 'nodes_to_modify' and 'node_names_modified'.
853  for (int32 n = 0; n < nnet_->NumNodes(); n++) {
854  if (nnet_->IsComponentNode(n)) {
855  NetworkNode &node = nnet_->GetNode(n);
856  int32 component_index = node.u.component_index,
857  modification_index = modification_index_[component_index];
858  if (modification_index >= 0) {
859  // This is a component-node for one of the components that we're
860  // splitting in two.
861  nodes_to_modify.insert(n);
862  std::string node_name = node_names_orig[n],
863  node_name_b = node_name + "_b";
864  node_names_modified[n] = node_name_b;
865  }
866  }
867  }
868 
869 
870  // config_os is a stream to which we are printing lines that we'll later
871  // read using nnet_->ReadConfig().
872  std::ostringstream config_os;
873  // The following loop writes to 'config_os'. The the code is modified from
874  // the private function Nnet::GetAsConfigLine(), and from
875  // Nnet::GetConfigLines().
876  for (int32 n = 0; n < nnet_->NumNodes(); n++) {
878  // component-input descriptor nodes aren't handled separately from their
879  // associated components (we deal with them along with their
880  // component-node); and input-nodes won't be affected so we don't have
881  // to print anything.
882  continue;
883  }
884  const NetworkNode &node = nnet_->GetNode(n);
885  int32 c = node.u.component_index; // 'c' will only be meaningful if the
886  // node is a component-node.
887  std::string node_name = node_names_orig[n];
888  if (node.node_type == kComponent && modification_index_[c] >= 0) {
891  std::string node_name_a = node_name + "_a",
892  node_name_b = node_name + "_b";
893  // we print two component-nodes, the "a" an "b". The original
894  // one will later be removed when we call RemoveOrphanNodes().
895  config_os << "component-node name=" << node_name_a << " component="
896  << info.component_name_a << " input=";
897  nnet_->GetNode(n-1).descriptor.WriteConfig(config_os, node_names_modified);
898  config_os << "\n";
899  config_os << "component-node name=" << node_name_b << " component="
900  << info.component_name_b << " input=" << node_name_a << "\n";
901  } else {
902  // This code is modified from Nnet::GetAsConfigLine(). The key difference
903  // is that we're using node_names_modified, which will replace all the
904  // nodes we're splitting with their "b" versions.
905  switch (node.node_type) {
906  case kDescriptor:
907  // assert that it's an output-descriptor, not one describing the input to
908  // a component-node.
910  config_os << "output-node name=" << node_name << " input=";
911  node.descriptor.WriteConfig(config_os, node_names_modified);
912  config_os << " objective=" << (node.u.objective_type == kLinear ?
913  "linear" : "quadratic");
914  break;
915  case kComponent:
916  config_os << "component-node name=" << node_name << " component="
918  << " input=";
919  nnet_->GetNode(n-1).descriptor.WriteConfig(config_os,
920  node_names_modified);
921  break;
922  case kDimRange:
923  config_os << "dim-range-node name=" << node_name << " input-node="
924  << node_names_modified[node.u.node_index]
925  << " dim-offset=" << node.dim_offset
926  << " dim=" << node.dim;
927  break;
928  default:
929  KALDI_ERR << "Unexpected node type.";
930  }
931  config_os << "\n";
932  }
933  }
934  std::istringstream config_is(config_os.str());
935  nnet_->ReadConfig(config_is);
938  }
939 
940  // modification_index_ is a vector with dimension equal to the number of
941  // components nnet_ had at entry. For each component that we are decomposing,
942  // it contains an index >= 0 into the 'component_info_' vector; for each
943  // component that we are not decomposing, it contains -1.
944  // with SVD.
945  std::vector<int32> modification_index_;
946 
948  int32 component_index; // Index of the component we are modifying.
949  std::string component_name; // The original name of the component,
950  // e.g. "some_component".
951  std::string component_name_a; // The original name of the component, plus "_a"
952  // e.g. "some_component_a".
953  std::string component_name_b; // The original name of the component, plus "_b"
954  // e.g. "some_component_b".
955  int32 component_a_index; // component-index of the left part of the
956  // decomposed component, which will have a name
957  // like "some_component_a".
958  int32 component_b_index; // component-index of the right part of the
959  // decomposed component, which will have a name
960  // like "some_component_b".
961 
962  };
963  std::vector<ModifiedComponentInfo> modified_component_info_;
964 
965 
971 };
972 
973 /*
974  Does an update that moves M closer to being a (matrix with orthonormal rows)
975  times 'scale'. Note: this will diverge if we start off with singular values
976  too far from 'scale'.
977 
978  This function requires 'scale' to be nonzero. If 'scale' is negative, then it
979  will be set internally to the value that ensures the change in M is orthogonal to
980  M (viewed as a vector).
981 */
983  KALDI_ASSERT(scale != 0.0);
984 
985  // We'd like to enforce the rows of M to be orthonormal.
986  // define P = M M^T. If P is unit then M has orthonormal rows.
987  // We actually want P to equal scale^2 * I, so that M's rows are
988  // orthogonal with 2-norms equal to 'scale'.
989  // We (notionally) add to the objective function, the value
990  // -alpha times the sum of squared elements of Q = (P - scale^2 * I).
991  int32 rows = M->NumRows(), cols = M->NumCols();
992  CuMatrix<BaseFloat> M_update(rows, cols);
993  CuMatrix<BaseFloat> P(rows, rows);
994  P.SymAddMat2(1.0, *M, kNoTrans, 0.0);
995  P.CopyLowerToUpper();
996 
997  // The 'update_speed' is a constant that determines how fast we approach a
998  // matrix with the desired properties (larger -> faster). Larger values will
999  // update faster but will be more prone to instability. 0.125 (1/8) is the
1000  // value that gives us the fastest possible convergence when we are already
1001  // close to be a semi-orthogonal matrix (in fact, it will lead to quadratic
1002  // convergence).
1003  // See http://www.danielpovey.com/files/2018_interspeech_tdnnf.pdf
1004  // for more details.
1005  BaseFloat update_speed = 0.125;
1006  bool floating_scale = (scale < 0.0);
1007 
1008 
1009  if (floating_scale) {
1010  // This (letting the scale "float") is described in Sec. 2.3 of
1011  // http://www.danielpovey.com/files/2018_interspeech_tdnnf.pdf,
1012  // where 'scale' here is written 'alpha' in the paper.
1013  //
1014  // We pick the scale that will give us an update to M that is
1015  // orthogonal to M (viewed as a vector): i.e., if we're doing
1016  // an update M := M + X, then we want to have tr(M X^T) == 0.
1017  // The following formula is what gives us that.
1018  // With P = M M^T, our update formula is doing to be:
1019  // M := M + (-4 * alpha * (P - scale^2 I) * M).
1020  // (The math below explains this update formula; for now, it's
1021  // best to view it as an established fact).
1022  // So X (the change in M) is -4 * alpha * (P - scale^2 I) * M,
1023  // where alpha == update_speed / scale^2.
1024  // We want tr(M X^T) == 0. First, forget the -4*alpha, because
1025  // we don't care about constant factors. So we want:
1026  // tr(M * M^T * (P - scale^2 I)) == 0.
1027  // Since M M^T == P, that means:
1028  // tr(P^2 - scale^2 P) == 0,
1029  // or scale^2 = tr(P^2) / tr(P).
1030  // Note: P is symmetric so it doesn't matter whether we use tr(P P) or
1031  // tr(P^T P); we use tr(P^T P) because I believe it's faster to compute.
1032 
1033  BaseFloat trace_P = P.Trace(), trace_P_P = TraceMatMat(P, P, kTrans);
1034 
1035  scale = std::sqrt(trace_P_P / trace_P);
1036 
1037  // The following is a tweak to avoid divergence when the eigenvalues aren't
1038  // close to being the same. trace_P is the sum of eigenvalues of P, and
1039  // trace_P_P is the sum-square of eigenvalues of P. Treat trace_P as a sum
1040  // of positive values, and trace_P_P as their sumsq. Then mean = trace_P /
1041  // dim, and trace_P_P cannot be less than dim * (trace_P / dim)^2,
1042  // i.e. trace_P_P >= trace_P^2 / dim. If ratio = trace_P_P * dim /
1043  // trace_P^2, then ratio >= 1.0, and the excess above 1.0 is a measure of
1044  // how far we are from convergence. If we're far from convergence, we make
1045  // the learning rate slower to reduce the risk of divergence, since the
1046  // update may not be stable for starting points far from equilibrium.
1047  BaseFloat ratio = (trace_P_P * P.NumRows() / (trace_P * trace_P));
1048  KALDI_ASSERT(ratio > 0.99);
1049  if (ratio > 1.02) {
1050  update_speed *= 0.5; // Slow down the update speed to reduce the risk of divergence.
1051  if (ratio > 1.1) update_speed *= 0.5; // Slow it down even more.
1052  }
1053  }
1054 
1055  P.AddToDiag(-1.0 * scale * scale);
1056 
1057  // We may want to un-comment the following code block later on if we have a
1058  // problem with instability in setups with a non-floating orthonormal
1059  // constraint.
1060  /*
1061  if (!floating_scale) {
1062  // This is analogous to the stuff with 'ratio' above, but when we don't have
1063  // a floating scale. It reduces the chances of divergence when we have
1064  // a bad initialization.
1065  BaseFloat error = P.FrobeniusNorm(),
1066  error_proportion = error * error / P.NumRows();
1067  // 'error_proportion' is the sumsq of elements in (P - I) divided by the
1068  // sumsq of elements of I. It should be much less than one (i.e. close to
1069  // zero) if the error is small.
1070  if (error_proportion > 0.02) {
1071  update_speed *= 0.5;
1072  if (error_proportion > 0.1)
1073  update_speed *= 0.5;
1074  }
1075  }
1076  */
1077 
1078  if (GetVerboseLevel() >= 1) {
1079  BaseFloat error = P.FrobeniusNorm();
1080  KALDI_VLOG(2) << "Error in orthogonality is " << error;
1081  }
1082 
1083  // see Sec. 2.2 of http://www.danielpovey.com/files/2018_interspeech_tdnnf.pdf
1084  // for explanation of the 1/(scale*scale) factor, but there is a difference in
1085  // notation; 'scale' here corresponds to 'alpha' in the paper, and
1086  // 'update_speed' corresponds to 'nu' in the paper.
1087  BaseFloat alpha = update_speed / (scale * scale);
1088 
1089  // At this point, the matrix P contains what, in the math, would be Q =
1090  // P-scale^2*I. The derivative of the objective function w.r.t. an element q(i,j)
1091  // of Q is now equal to -2*alpha*q(i,j), i.e. we could write q_deriv(i,j)
1092  // = -2*alpha*q(i,j) This is also the derivative of the objective function
1093  // w.r.t. p(i,j): i.e. p_deriv(i,j) = -2*alpha*q(i,j).
1094  // Suppose we have define this matrix as 'P_deriv'.
1095  // The derivative of the objective w.r.t M equals
1096  // 2 * P_deriv * M, which equals -4*alpha*(P-scale^2*I)*M.
1097  // (Currently the matrix P contains what, in the math, is P-scale^2*I).
1098  M_update.AddMatMat(-4.0 * alpha, P, kNoTrans, *M, kNoTrans, 0.0);
1099  M->AddMat(1.0, M_update);
1100 }
1101 
1109 
1110  for (int32 c = 0; c < nnet->NumComponents(); c++) {
1111  Component *component = nnet->GetComponent(c);
1112  CuMatrixBase<BaseFloat> *params = NULL;
1113  BaseFloat orthonormal_constraint = 0.0;
1114 
1115  LinearComponent *lc = dynamic_cast<LinearComponent*>(component);
1116  if (lc != NULL && lc->OrthonormalConstraint() != 0.0) {
1117  orthonormal_constraint = lc->OrthonormalConstraint();
1118  params = &(lc->Params());
1119  }
1120  AffineComponent *ac = dynamic_cast<AffineComponent*>(component);
1121  if (ac != NULL && ac->OrthonormalConstraint() != 0.0) {
1122  orthonormal_constraint = ac->OrthonormalConstraint();
1123  params = &(ac->LinearParams());
1124  }
1125  TdnnComponent *tc = dynamic_cast<TdnnComponent*>(component);
1126  if (tc != NULL && tc->OrthonormalConstraint() != 0.0) {
1127  orthonormal_constraint = tc->OrthonormalConstraint();
1128  params = &(tc->LinearParams());
1129  }
1130  if (orthonormal_constraint == 0.0 || RandInt(0, 3) != 0) {
1131  // For efficiency, only do this every 4 or so minibatches-- it won't have
1132  // time stray far from the constraint in between.
1133  continue;
1134  }
1135 
1136  int32 rows = params->NumRows(), cols = params->NumCols();
1137  if (rows <= cols) {
1138  ConstrainOrthonormalInternal(orthonormal_constraint, params);
1139  } else {
1140  CuMatrix<BaseFloat> params_trans(*params, kTrans);
1141  ConstrainOrthonormalInternal(orthonormal_constraint, &params_trans);
1142  params->CopyFromMat(params_trans, kTrans);
1143  }
1144  }
1145 }
1146 
1148 #if HAVE_CUDA == 1
1149  if (CuDevice::Instantiate().Enabled()) {
1150  bool print_memory_info = (GetVerboseLevel() >= 1);
1151  if (print_memory_info) {
1152  KALDI_VLOG(1) << "Consolidating memory; will print memory usage before "
1153  "and after consolidating:";
1154  g_cuda_allocator.PrintMemoryUsage();
1155  }
1156  for (int32 c = 0; c < nnet->NumComponents(); c++) {
1157  Component *comp = nnet->GetComponent(c);
1158  comp->ConsolidateMemory();
1159  }
1160  if (print_memory_info) {
1161  g_cuda_allocator.PrintMemoryUsage();
1162  }
1163  }
1164 #endif
1165 }
1166 
1167 
1168 
1169 // This code has been broken out of ReadEditConfig as it's quite long.
1170 // It implements the internals of the edit directive 'reduce-rank'.
1171 // See also the related direcive 'apply-svd'.
1172 void ReduceRankOfComponents(const std::string component_name_pattern,
1173  int32 rank,
1174  Nnet *nnet) {
1175  int32 num_components_changed = 0;
1176  for (int32 c = 0; c < nnet->NumComponents(); c++) {
1177  Component *component = nnet->GetComponent(c);
1178  std::string component_name = nnet->GetComponentName(c);
1179  if (NameMatchesPattern(component_name.c_str(),
1180  component_name_pattern.c_str())) {
1181  AffineComponent *affine = dynamic_cast<AffineComponent*>(component);
1182  if (affine == NULL) {
1183  KALDI_WARN << "Not reducing rank of component " << component_name
1184  << " as it is not an AffineComponent.";
1185  continue;
1186  }
1187  int32 input_dim = affine->InputDim(),
1188  output_dim = affine->OutputDim();
1189  if (input_dim <= rank || output_dim <= rank) {
1190  KALDI_WARN << "Not reducing rank of component " << component_name
1191  << " with SVD to rank " << rank
1192  << " because its dimension is " << input_dim
1193  << " -> " << output_dim;
1194  continue;
1195  }
1196  Matrix<BaseFloat> linear_params(affine->LinearParams());
1197  Vector<BaseFloat> bias_params(affine->BiasParams());
1198 
1199  // note: 'linear_params' is of dimension output_dim by input_dim.
1200  int32 middle_dim = std::min<int32>(input_dim, output_dim);
1201  Vector<BaseFloat> s(middle_dim);
1202  Matrix<BaseFloat> U(output_dim, middle_dim),
1203  Vt(middle_dim, input_dim);
1204  linear_params.Svd(&s, &U, &Vt);
1205  // make sure the singular values are sorted from greatest to least value.
1206  SortSvd(&s, &U, &Vt);
1207  BaseFloat s_sum_orig = s.Sum();
1208  s.Resize(rank, kCopyData);
1209  U.Resize(output_dim, rank, kCopyData);
1210  Vt.Resize(rank, input_dim, kCopyData);
1211  BaseFloat s_sum_reduced = s.Sum();
1212  KALDI_LOG << "For component " << component_name
1213  << " singular value sum changed by reduce-rank command "
1214  << (s_sum_orig - s_sum_reduced)
1215  << " (from " << s_sum_orig << " to " << s_sum_reduced << ")";
1216  U.MulColsVec(s);
1217  Matrix<BaseFloat> linear_params_reduced_rank(output_dim, input_dim);
1218  linear_params_reduced_rank.AddMatMat(1.0, U, kNoTrans, Vt, kNoTrans, 0.0);
1219  CuMatrix<BaseFloat> linear_params_reduced_rank_cuda;
1220  linear_params_reduced_rank_cuda.Swap(&linear_params_reduced_rank);
1221  CuVector<BaseFloat> bias_params_cuda;
1222  bias_params_cuda.Swap(&bias_params);
1223  affine->SetParams(bias_params_cuda, linear_params_reduced_rank_cuda);
1224  num_components_changed++;
1225  }
1226  }
1227  KALDI_LOG << "Reduced rank of parameters of " << num_components_changed
1228  << " components.";
1229 }
1230 
1231 
1232 
1233 
1234 void ReadEditConfig(std::istream &edit_config_is, Nnet *nnet) {
1235  std::vector<std::string> lines;
1236  ReadConfigLines(edit_config_is, &lines);
1237  // we process this as a sequence of lines.
1238  std::vector<ConfigLine> config_lines;
1239  ParseConfigLines(lines, &config_lines);
1240  for (size_t i = 0; i < config_lines.size(); i++) {
1241  ConfigLine &config_line = config_lines[i];
1242  const std::string &directive = config_lines[i].FirstToken();
1243  if (directive == "convert-to-fixed-affine") {
1244  std::string name_pattern = "*";
1245  // name_pattern defaults to '*' if none is given. Note: this pattern
1246  // matches names of components, not nodes.
1247  config_line.GetValue("name", &name_pattern);
1248  int32 num_components_changed = 0;
1249  for (int32 c = 0; c < nnet->NumComponents(); c++) {
1250  Component *component = nnet->GetComponent(c);
1251  AffineComponent *affine = NULL;
1252  if (NameMatchesPattern(nnet->GetComponentName(c).c_str(),
1253  name_pattern.c_str()) &&
1254  (affine = dynamic_cast<AffineComponent*>(component))) {
1255  nnet->SetComponent(c, new FixedAffineComponent(*affine));
1256  num_components_changed++;
1257  }
1258  }
1259  KALDI_LOG << "Converted " << num_components_changed
1260  << " components to FixedAffineComponent.";
1261  } else if (directive == "remove-orphan-nodes") {
1262  bool remove_orphan_inputs = false;
1263  config_line.GetValue("remove-orphan-inputs", &remove_orphan_inputs);
1264  nnet->RemoveOrphanNodes(remove_orphan_inputs);
1265  } else if (directive == "remove-orphan-components") {
1266  nnet->RemoveOrphanComponents();
1267  } else if (directive == "remove-orphans") {
1268  bool remove_orphan_inputs = false;
1269  config_line.GetValue("remove-orphan-inputs", &remove_orphan_inputs);
1270  nnet->RemoveOrphanNodes(remove_orphan_inputs);
1271  nnet->RemoveOrphanComponents();
1272  } else if (directive == "set-learning-rate") {
1273  std::string name_pattern = "*";
1274  // name_pattern defaults to '*' if none is given. This pattern
1275  // matches names of components, not nodes.
1276  config_line.GetValue("name", &name_pattern);
1277  BaseFloat learning_rate = -1;
1278  if (!config_line.GetValue("learning-rate", &learning_rate)) {
1279  KALDI_ERR << "In edits-config, expected learning-rate to be set in line: "
1280  << config_line.WholeLine();
1281  }
1282  // Note: the learning rate you provide will be multiplied by any
1283  // 'learning-rate-factor' that is defined in the component,
1284  // so if you call SetUnderlyingLearningRate(), the actual learning
1285  // rate (learning_rate_) is set to the value you provide times
1286  // learning_rate_factor_.
1287  UpdatableComponent *component = NULL;
1288  int32 num_learning_rates_set = 0;
1289  for (int32 c = 0; c < nnet->NumComponents(); c++) {
1290  if (NameMatchesPattern(nnet->GetComponentName(c).c_str(),
1291  name_pattern.c_str()) &&
1292  (component =
1293  dynamic_cast<UpdatableComponent*>(nnet->GetComponent(c)))) {
1294  component->SetUnderlyingLearningRate(learning_rate);
1295  num_learning_rates_set++;
1296  }
1297  }
1298  KALDI_LOG << "Set learning rates for " << num_learning_rates_set << " components.";
1299  } else if (directive == "set-learning-rate-factor") {
1300  std::string name_pattern = "*";
1301  // name_pattern defaults to '*' if none is given.
1302  config_line.GetValue("name", &name_pattern);
1303  BaseFloat learning_rate_factor = -1;
1304  if (!config_line.GetValue("learning-rate-factor", &learning_rate_factor)) {
1305  KALDI_ERR << "In edits-config, expected learning-rate-factor to be set in line: "
1306  << config_line.WholeLine();
1307  }
1308  // Note: the learning_rate_factor_ defined in the component
1309  // sets to the value you provided, so if you call SetUnderlyingLearningRate(),
1310  // the actual learning rate (learning_rate_) is set to the value you provided
1311  // times learning_rate.
1312  UpdatableComponent *component = NULL;
1313  int32 num_learning_rate_factors_set = 0;
1314  for (int32 c = 0; c < nnet->NumComponents(); c++) {
1315  if (NameMatchesPattern(nnet->GetComponentName(c).c_str(),
1316  name_pattern.c_str()) &&
1317  (component =
1318  dynamic_cast<UpdatableComponent*>(nnet->GetComponent(c)))) {
1319  component->SetLearningRateFactor(learning_rate_factor);
1320  num_learning_rate_factors_set++;
1321  }
1322  }
1323  KALDI_LOG << "Set learning rate factors for " << num_learning_rate_factors_set
1324  << " components.";
1325  } else if (directive == "rename-node") {
1326  // this is a shallow renaming of a node, and it requires that the name used is
1327  // not the name of another node.
1328  std::string old_name, new_name;
1329  if (!config_line.GetValue("old-name", &old_name) ||
1330  !config_line.GetValue("new-name", &new_name) ||
1331  config_line.HasUnusedValues()) {
1332  KALDI_ERR << "In edits-config, could not make sense of this rename-node "
1333  << "directive (expect old-name=xxx new-name=xxx) "
1334  << config_line.WholeLine();
1335  }
1336  if (nnet->GetNodeIndex(old_name) < 0)
1337  KALDI_ERR << "Could not rename node from " << old_name << " to "
1338  << new_name << " because there is no node called "
1339  << old_name;
1340  // further checks will happen inside SetNodeName().
1341  nnet->SetNodeName(nnet->GetNodeIndex(old_name), new_name);
1342  } else if (directive == "remove-output-nodes") {
1343  // note: after remove-output-nodes you probably want to do 'remove-orphans'.
1344  std::string name_pattern;
1345  if (!config_line.GetValue("name", &name_pattern) ||
1346  config_line.HasUnusedValues())
1347  KALDI_ERR << "In edits-config, could not make sense of "
1348  << "remove-output-nodes directive: "
1349  << config_line.WholeLine();
1350  std::vector<int32> nodes_to_remove;
1351  int32 outputs_remaining = 0;
1352  for (int32 n = 0; n < nnet->NumNodes(); n++) {
1353  if (nnet->IsOutputNode(n)) {
1354  if (NameMatchesPattern(nnet->GetNodeName(n).c_str(),
1355  name_pattern.c_str()))
1356  nodes_to_remove.push_back(n);
1357  else
1358  outputs_remaining++;
1359  }
1360  }
1361  KALDI_LOG << "Removing " << nodes_to_remove.size() << " output nodes.";
1362  if (outputs_remaining == 0)
1363  KALDI_ERR << "All outputs were removed.";
1364  nnet->RemoveSomeNodes(nodes_to_remove);
1365  } else if (directive == "set-dropout-proportion") {
1366  std::string name_pattern = "*";
1367  // name_pattern defaults to '*' if none is given. This pattern
1368  // matches names of components, not nodes.
1369  config_line.GetValue("name", &name_pattern);
1370  BaseFloat proportion = -1;
1371  if (!config_line.GetValue("proportion", &proportion)) {
1372  KALDI_ERR << "In edits-config, expected proportion to be set in line: "
1373  << config_line.WholeLine();
1374  }
1375  int32 num_dropout_proportions_set = 0;
1376  for (int32 c = 0; c < nnet->NumComponents(); c++) {
1377  if (NameMatchesPattern(nnet->GetComponentName(c).c_str(),
1378  name_pattern.c_str())) {
1379  DropoutComponent *dropout_component =
1380  dynamic_cast<DropoutComponent*>(nnet->GetComponent(c));
1381  DropoutMaskComponent *mask_component =
1382  dynamic_cast<DropoutMaskComponent*>(nnet->GetComponent(c));
1383  GeneralDropoutComponent *general_dropout_component =
1384  dynamic_cast<GeneralDropoutComponent*>(nnet->GetComponent(c));
1385  if (dropout_component != NULL) {
1386  dropout_component->SetDropoutProportion(proportion);
1387  num_dropout_proportions_set++;
1388  } else if (mask_component != NULL){
1389  mask_component->SetDropoutProportion(proportion);
1390  num_dropout_proportions_set++;
1391  } else if (general_dropout_component != NULL){
1392  general_dropout_component->SetDropoutProportion(proportion);
1393  num_dropout_proportions_set++;
1394  }
1395  }
1396  }
1397  KALDI_LOG << "Set dropout proportions for "
1398  << num_dropout_proportions_set << " components.";
1399  } else if (directive == "apply-svd") {
1400  std::string name_pattern;
1401  int32 bottleneck_dim = -1;
1402  BaseFloat energy_threshold = -1;
1403  BaseFloat shrinkage_threshold = 1.0;
1404  config_line.GetValue("bottleneck-dim", &bottleneck_dim);
1405  config_line.GetValue("energy-threshold", &energy_threshold);
1406  config_line.GetValue("shrinkage-threshold", &shrinkage_threshold);
1407  if (!config_line.GetValue("name", &name_pattern))
1408  KALDI_ERR << "Edit directive apply-svd requires 'name' to be specified.";
1409  if (bottleneck_dim <= 0 && energy_threshold <=0)
1410  KALDI_ERR << "Either Bottleneck-dim or energy-threshold "
1411  "must be set in apply-svd command. "
1412  "Range of possible values is (0 1]";
1413  SvdApplier applier(name_pattern, bottleneck_dim,
1414  energy_threshold,
1415  shrinkage_threshold,
1416  nnet);
1417  applier.ApplySvd();
1418  } else if (directive == "reduce-rank") {
1419  std::string name_pattern;
1420  int32 rank = -1;
1421  if (!config_line.GetValue("name", &name_pattern) ||
1422  !config_line.GetValue("rank", &rank))
1423  KALDI_ERR << "Edit directive reduce-rank requires 'name' and "
1424  "'rank' to be specified.";
1425  if (rank <= 0)
1426  KALDI_ERR << "Rank must be positive in reduce-rank command.";
1427  ReduceRankOfComponents(name_pattern, rank, nnet);
1428  } else {
1429  KALDI_ERR << "Directive '" << directive << "' is not currently "
1430  "supported (reading edit-config).";
1431  }
1432  if (config_line.HasUnusedValues()) {
1433  KALDI_ERR << "Could not interpret '" << config_line.UnusedValues()
1434  << "' in edit config line " << config_line.WholeLine();
1435  }
1436  }
1437 }
1438 
1439 
1441 bool NnetIsRecurrent(const Nnet &nnet) {
1442  std::vector<std::vector<int32> > graph;
1443  NnetToDirectedGraph(nnet, &graph);
1444  return GraphHasCycles(graph);
1445 }
1446 
1448  public:
1450  Nnet *nnet):
1451  config_(config), nnet_(nnet) { }
1452  void Collapse() {
1453  bool changed = true;
1454  int32 num_nodes = nnet_->NumNodes(),
1455  num_iters = 0;
1456  int32 num_components1 = nnet_->NumComponents();
1457  for (; changed; num_iters++) {
1458  changed = false;
1459  for (int32 n = 0; n < num_nodes; n++)
1460  if (OptimizeNode(n))
1461  changed = true;
1462  // we shouldn't iterate more than a couple of times.
1463  if (num_iters >= 10)
1464  KALDI_ERR << "Something went wrong collapsing model.";
1465  }
1466  int32 num_components2 = nnet_->NumComponents();
1469  int32 num_components3 = nnet_->NumComponents();
1470  if (num_components2 != num_components1 ||
1471  num_components3 != num_components2)
1472  KALDI_LOG << "Added " << (num_components2 - num_components1)
1473  << " components, removed "
1474  << (num_components2 - num_components3);
1475  }
1476  private:
1493  int32 CollapseComponents(int32 component_index1,
1494  int32 component_index2) {
1495  int32 ans;
1496  if (config_.collapse_dropout &&
1497  (ans = CollapseComponentsDropout(component_index1,
1498  component_index2)) != -1)
1499  return ans;
1500  if (config_.collapse_batchnorm &&
1501  (ans = CollapseComponentsBatchnorm(component_index1,
1502  component_index2)) != -1)
1503  return ans;
1504  if (config_.collapse_affine &&
1505  (ans = CollapseComponentsAffine(component_index1,
1506  component_index2)) != -1)
1507  return ans;
1508  if (config_.collapse_scale &&
1509  (ans = CollapseComponentsScale(component_index1,
1510  component_index2)) != -1)
1511  return ans;
1512  return -1;
1513  }
1514 
1515 
1516  // If the SumDescriptor has exactly one part that is either a
1517  // SimpleForwardingDescriptor or an OffsetForwardingDescriptor containing a
1518  // SimpleForwardingDescriptor, returns the node-index that the
1519  // SimpleForwardingDescriptor contains. Otherwise returns -1.
1520  //
1521  // E.g. of the SumDescriptor represents something like "foo" it returns
1522  // the index for "foo"; if it represents "Offset(foo, -2)" it returns
1523  // the index for "foo"; if it represents something else like
1524  // "Sum(foo, bar)" or "IfDefined(foo)", then it returns -1.
1526  // I don't much like having to use dynamic_cast here.
1527  const SimpleSumDescriptor *ss = dynamic_cast<const SimpleSumDescriptor*>(
1528  &sum_desc);
1529  if (ss == NULL) return -1;
1530  const ForwardingDescriptor *fd = &(ss->Src());
1531  const OffsetForwardingDescriptor *od =
1532  dynamic_cast<const OffsetForwardingDescriptor*>(fd);
1533  if (od != NULL)
1534  fd = &(od->Src());
1535  const SimpleForwardingDescriptor *sd =
1536  dynamic_cast<const SimpleForwardingDescriptor*>(fd);
1537  if (sd == NULL) return -1;
1538  else {
1539  // the following is a rather roundabout way to get the node-index from a
1540  // SimpleForwardingDescriptor, but it works (it avoids adding other stuff
1541  // to the interface).
1542  std::vector<int32> v;
1543  sd->GetNodeDependencies(&v);
1544  int32 node_index = v[0];
1545  return node_index;
1546  }
1547  }
1548 
1549  // If the Descriptor is a sum over different offsets of a particular node,
1550  // e.g. something of the form "Sum(Offset(foo, -2), Offset(foo, 2))" or in the
1551  // most degenerate case just "foo", then this function returns the index for
1552  // foo; otherwise it returns -1.
1554  int32 ans = SumDescriptorIsCollapsible(desc.Part(0));
1555  for (int32 i = 1; i < desc.NumParts(); i++) {
1556  if (ans != -1) {
1557  int32 node_index = SumDescriptorIsCollapsible(desc.Part(i));
1558  if (node_index != ans)
1559  ans = -1;
1560  }
1561  }
1562  // note: ans is only >= 0 if the answers from all parts of
1563  // the SumDescriptors were >=0 and identical to each other.
1564  // Otherwise it will be -1.
1565  return ans;
1566  }
1567 
1568  // Replaces all the nodes with index 'node_to_replace' in 'src' with the
1569  // descriptor 'expr', and returns the appropriately modified Descriptor. For
1570  // example, if 'src' is 'Append(Offset(foo, -1), Offset(foo, 1))' and 'expr'
1571  // is 'Offset(bar, -1)', this should give you: 'Append(Offset(bar, -2), bar)'.
1573  int32 node_to_replace,
1574  const Descriptor &expr) {
1575  // The way we replace it is at the textual level: we create a "fake" vector
1576  // of node-names where the printed form of 'expr' appears as the
1577  // node name in node_names[node_to_replace]; we print the descriptor
1578  // in 'src' using that faked node-names vector; and we parse it again
1579  // using the real node-names vector.
1580  std::vector<std::string> node_names = nnet_->GetNodeNames();
1581  std::ostringstream expr_os;
1582  expr.WriteConfig(expr_os, node_names);
1583  node_names[node_to_replace] = expr_os.str();
1584  std::ostringstream src_replaced_os;
1585  src.WriteConfig(src_replaced_os, node_names);
1586  std::vector<std::string> tokens;
1587  // now, in the example, src_replaced_os.str() would equal
1588  // Append(Offset(Offset(bar, -1), -1), Offset(Offset(bar, -1), 1)).
1589  bool b = DescriptorTokenize(src_replaced_os.str(),
1590  &tokens);
1591  KALDI_ASSERT(b);
1592  // 'tokens' might now contain something like [ "Append", "(", "Offset", ..., ")" ].
1593  tokens.push_back("end of input");
1594  const std::string *next_token = &(tokens[0]);
1595  Descriptor ans;
1596  // parse using the un-modified node names.
1597  ans.Parse(nnet_->GetNodeNames(), &next_token);
1598  KALDI_ASSERT(*next_token == "end of input");
1599  // Note: normalization of expressions in Descriptors, such as conversion of
1600  // Offset(Offset(bar, -1), -1) to Offset(bar, -2), takes place inside the
1601  // Descriptor parsing code.
1602  return ans;
1603  }
1604 
1605 
1606 
1638  bool OptimizeNode(int32 node_index) {
1639  NetworkNode &descriptor_node = nnet_->GetNode(node_index);
1640  if (descriptor_node.node_type != kDescriptor ||
1641  node_index + 1 >= nnet_->NumNodes())
1642  return false;
1643  NetworkNode &component_node = nnet_->GetNode(node_index + 1);
1644  if (component_node.node_type != kComponent)
1645  return false;
1646  Descriptor &descriptor = descriptor_node.descriptor;
1647  int32 component_index = component_node.u.component_index;
1648 
1649  int32 input_node_index = DescriptorIsCollapsible(descriptor);
1650  if (input_node_index == -1)
1651  return false; // do nothing, the expression in the Descriptor is too
1652  // general for this code to handle.
1653  const NetworkNode &input_node = nnet_->GetNode(input_node_index);
1654  if (input_node.node_type != kComponent)
1655  return false;
1656  int32 input_component_index = input_node.u.component_index;
1657  int32 combined_component_index = CollapseComponents(input_component_index,
1658  component_index);
1659  if (combined_component_index == -1)
1660  return false; // these components were not of types that can be
1661  // collapsed.
1662  component_node.u.component_index = combined_component_index;
1663 
1664  // 'input_descriptor_node' is the input descriptor of the component
1665  // that's the input to the node in "node_index". (e.g. the component for
1666  // the node "foo" in our example above).
1667  const NetworkNode &input_descriptor_node = nnet_->GetNode(input_node_index - 1);
1668  const Descriptor &input_descriptor = input_descriptor_node.descriptor;
1669 
1670  // The next statement replaces the descriptor in the network node with one
1671  // in which the component 'input_component_index' has been replaced with its
1672  // input, thus bypassing the component in 'input_component_index'.
1673  // We'll later remove that component and its node from the network, if
1674  // needed by RemoveOrphanNodes() and RemoveOrphanComponents().
1675  descriptor = ReplaceNodeInDescriptor(descriptor,
1676  input_node_index,
1677  input_descriptor);
1678  return true;
1679  }
1680 
1681 
1694  int32 component_index2) {
1695  const DropoutComponent *dropout_component =
1696  dynamic_cast<const DropoutComponent*>(
1697  nnet_->GetComponent(component_index1));
1698  const GeneralDropoutComponent *general_dropout_component =
1699  dynamic_cast<const GeneralDropoutComponent*>(
1700  nnet_->GetComponent(component_index1));
1701 
1702  if (dropout_component == NULL && general_dropout_component == NULL)
1703  return -1;
1704  BaseFloat scale; // the scale we have to apply to correct for removing
1705  // this dropout comonent.
1706  if (dropout_component != NULL) {
1707  BaseFloat dropout_proportion = dropout_component->DropoutProportion();
1708  scale = 1.0 / (1.0 - dropout_proportion);
1709  } else {
1710  // for GeneralDropoutComponent, it's done in such a way that the expectation
1711  // is always 1. (When it's nonzero, we give it a value 1/(1-dropout_proportion).
1712  // So no scaling is needed.
1713  scale = 1.0;
1714  }
1715  // note: if the 2nd component is not of a type that we can scale, the
1716  // following function call will return -1, which is OK.
1717  return GetScaledComponentIndex(component_index2,
1718  scale);
1719  }
1720 
1721 
1722 
1734  int32 component_index2) {
1735  const BatchNormComponent *batchnorm_component =
1736  dynamic_cast<const BatchNormComponent*>(
1737  nnet_->GetComponent(component_index1));
1738  if (batchnorm_component == NULL)
1739  return -1;
1740 
1741  if (batchnorm_component->Offset().Dim() == 0) {
1742  KALDI_ERR << "Expected batch-norm components to have test-mode set.";
1743  }
1744  std::string batchnorm_component_name = nnet_->GetComponentName(
1745  component_index1);
1746  return GetDiagonallyPreModifiedComponentIndex(batchnorm_component->Offset(),
1747  batchnorm_component->Scale(),
1748  batchnorm_component_name,
1749  component_index2);
1750  }
1751 
1762  int32 component_index2) {
1763 
1764  const FixedAffineComponent *fixed_affine_component1 =
1765  dynamic_cast<const FixedAffineComponent*>(
1766  nnet_->GetComponent(component_index1));
1767  const AffineComponent *affine_component1 =
1768  dynamic_cast<const AffineComponent*>(
1769  nnet_->GetComponent(component_index1)),
1770  *affine_component2 =
1771  dynamic_cast<const AffineComponent*>(
1772  nnet_->GetComponent(component_index2));
1773  if (affine_component2 == NULL ||
1774  (fixed_affine_component1 == NULL && affine_component1 == NULL))
1775  return -1;
1776 
1777  std::ostringstream new_component_name_os;
1778  new_component_name_os << nnet_->GetComponentName(component_index1)
1779  << "." << nnet_->GetComponentName(component_index2);
1780  std::string new_component_name = new_component_name_os.str();
1781  int32 new_component_index = nnet_->GetComponentIndex(new_component_name);
1782  if (new_component_index >= 0)
1783  return new_component_index; // we previously created this.
1784 
1785  const CuMatrix<BaseFloat> *linear_params1;
1786  const CuVector<BaseFloat> *bias_params1;
1787  if (fixed_affine_component1 != NULL) {
1788  if (fixed_affine_component1->InputDim() >
1789  fixed_affine_component1->OutputDim()) {
1790  // first affine component is dimension-reducing, so combining the two
1791  // might be inefficient.
1792  return -1;
1793  }
1794  linear_params1 = &(fixed_affine_component1->LinearParams());
1795  bias_params1 = &(fixed_affine_component1->BiasParams());
1796  } else {
1797  if (affine_component1->InputDim() >
1798  affine_component1->OutputDim()) {
1799  // first affine component is dimension-reducing, so combining the two
1800  // might be inefficient.
1801  return -1;
1802  }
1803  linear_params1 = &(affine_component1->LinearParams());
1804  bias_params1 = &(affine_component1->BiasParams());
1805  }
1806 
1807  int32 input_dim1 = linear_params1->NumCols(),
1808  output_dim1 = linear_params1->NumRows(),
1809  input_dim2 = affine_component2->InputDim(),
1810  output_dim2 = affine_component2->OutputDim();
1811  KALDI_ASSERT(input_dim2 % output_dim1 == 0);
1812  // with typical configurations for TDNNs, like Append(-3, 0, 3) [in xconfigs], a.k.a.
1813  // Append(Offset(foo, -3), foo, Offset(foo, 3)), the first component's output may
1814  // be smaller than the second component's input. We construct a single
1815  // transform with a block-diagonal structure in this case.
1816  int32 multiple = input_dim2 / output_dim1;
1817  CuVector<BaseFloat> bias_params1_full(input_dim2);
1818  CuMatrix<BaseFloat> linear_params1_full(input_dim2,
1819  multiple * input_dim1);
1820  for (int32 i = 0; i < multiple; i++) {
1821  bias_params1_full.Range(i * output_dim1,
1822  output_dim1).CopyFromVec(*bias_params1);
1823  linear_params1_full.Range(i * output_dim1, output_dim1,
1824  i * input_dim1, input_dim1).CopyFromMat(
1825  *linear_params1);
1826  }
1827  const CuVector<BaseFloat> &bias_params2 = affine_component2->BiasParams();
1828  const CuMatrix<BaseFloat> &linear_params2 = affine_component2->LinearParams();
1829 
1830  int32 new_input_dim = multiple * input_dim1,
1831  new_output_dim = output_dim2;
1832  CuMatrix<BaseFloat> new_linear_params(new_output_dim,
1833  new_input_dim);
1834  CuVector<BaseFloat> new_bias_params(bias_params2);
1835  new_bias_params.AddMatVec(1.0, linear_params2, kNoTrans,
1836  bias_params1_full, 1.0);
1837  new_linear_params.AddMatMat(1.0, linear_params2, kNoTrans,
1838  linear_params1_full, kNoTrans, 0.0);
1839 
1840  AffineComponent *new_component = new AffineComponent();
1841  new_component->Init(new_input_dim, new_output_dim, 0.0, 0.0);
1842  new_component->SetParams(new_bias_params, new_linear_params);
1843  return nnet_->AddComponent(new_component_name, new_component);
1844  }
1845 
1846 
1847 
1861  int32 component_index2) {
1862 
1863  const AffineComponent *affine_component1 =
1864  dynamic_cast<const AffineComponent*>(
1865  nnet_->GetComponent(component_index1));
1866  const FixedScaleComponent *fixed_scale_component2 =
1867  dynamic_cast<const FixedScaleComponent*>(
1868  nnet_->GetComponent(component_index2));
1869  if (affine_component1 == NULL ||
1870  fixed_scale_component2 == NULL ||
1871  affine_component1->OutputDim() !=
1872  fixed_scale_component2->InputDim())
1873  return -1;
1874 
1875  std::ostringstream new_component_name_os;
1876  new_component_name_os << nnet_->GetComponentName(component_index1)
1877  << "." << nnet_->GetComponentName(component_index2);
1878  std::string new_component_name = new_component_name_os.str();
1879  int32 new_component_index = nnet_->GetComponentIndex(new_component_name);
1880  if (new_component_index >= 0)
1881  return new_component_index; // we previously created this.
1882 
1883  CuMatrix<BaseFloat> linear_params(affine_component1->LinearParams());
1884  CuVector<BaseFloat> bias_params(affine_component1->BiasParams());
1885  const CuVector<BaseFloat> &scales = fixed_scale_component2->Scales();
1886 
1887  bias_params.MulElements(scales);
1888  linear_params.MulRowsVec(scales);
1889 
1890  AffineComponent *new_affine_component =
1891  dynamic_cast<AffineComponent*>(affine_component1->Copy());
1892  new_affine_component->SetParams(bias_params, linear_params);
1893  return nnet_->AddComponent(new_component_name,
1894  new_affine_component);
1895  }
1896 
1897 
1935  const CuVectorBase<BaseFloat> &offset,
1936  const CuVectorBase<BaseFloat> &scale,
1937  const std::string &src_identifier,
1939  KALDI_ASSERT(offset.Dim() > 0 && offset.Dim() == scale.Dim());
1940  if (offset.Max() == 0.0 && offset.Min() == 0.0 &&
1941  scale.Max() == 1.0 && scale.Min() == 1.0)
1942  return component_index; // identity transform.
1943  std::ostringstream new_component_name_os;
1944  new_component_name_os << src_identifier
1945  << "."
1946  << nnet_->GetComponentName(component_index);
1947  std::string new_component_name = new_component_name_os.str();
1948  int32 new_component_index = nnet_->GetComponentIndex(new_component_name);
1949  if (new_component_index >= 0)
1950  return new_component_index; // we previously created this.
1951 
1952  const Component *component = nnet_->GetComponent(component_index);
1953  const AffineComponent *affine_component =
1954  dynamic_cast<const AffineComponent*>(component);
1955  const LinearComponent *linear_component =
1956  dynamic_cast<const LinearComponent*>(component);
1957  const TdnnComponent *tdnn_component =
1958  dynamic_cast<const TdnnComponent*>(component);
1959 
1960  Component *new_component = NULL;
1961  if (affine_component != NULL) {
1962  new_component = component->Copy();
1963  AffineComponent *new_affine_component =
1964  dynamic_cast<AffineComponent*>(new_component);
1965  PreMultiplyAffineParameters(offset, scale,
1966  &(new_affine_component->BiasParams()),
1967  &(new_affine_component->LinearParams()));
1968  } else if (linear_component != NULL) {
1969  CuVector<BaseFloat> bias_params(linear_component->OutputDim());
1970  AffineComponent *new_affine_component =
1971  new AffineComponent(linear_component->Params(),
1972  bias_params,
1973  linear_component->LearningRate());
1974  PreMultiplyAffineParameters(offset, scale,
1975  &(new_affine_component->BiasParams()),
1976  &(new_affine_component->LinearParams()));
1977  new_component = new_affine_component;
1978  } else if (tdnn_component != NULL) {
1979  new_component = tdnn_component->Copy();
1980  TdnnComponent *new_tdnn_component =
1981  dynamic_cast<TdnnComponent*>(new_component);
1982  if (new_tdnn_component->BiasParams().Dim() == 0) {
1983  // make sure it has a bias even if it had none before.
1984  new_tdnn_component->BiasParams().Resize(
1985  new_tdnn_component->OutputDim());
1986  }
1987  PreMultiplyAffineParameters(offset, scale,
1988  &(new_tdnn_component->BiasParams()),
1989  &(new_tdnn_component->LinearParams()));
1990 
1991  } else {
1992  return -1; // we can't do this: this component isn't of the right type.
1993  }
1994  return nnet_->AddComponent(new_component_name, new_component);
1995  }
1996 
2007  const CuVectorBase<BaseFloat> &offset,
2008  const CuVectorBase<BaseFloat> &scale,
2009  CuVectorBase<BaseFloat> *bias_params,
2010  CuMatrixBase<BaseFloat> *linear_params) {
2011  int32 input_dim = linear_params->NumCols(),
2012  transform_dim = offset.Dim();
2013  KALDI_ASSERT(bias_params->Dim() == linear_params->NumRows() &&
2014  offset.Dim() == scale.Dim() &&
2015  input_dim % transform_dim == 0);
2016  // we may have to repeat 'offset' and scale' several times.
2017  // 'full_offset' and 'full_scale' may be repeated versions of
2018  // 'offset' and 'scale' in case input_dim > transform_dim.
2019  CuVector<BaseFloat> full_offset(input_dim),
2020  full_scale(input_dim);
2021  for (int32 d = 0; d < input_dim; d += transform_dim) {
2022  full_offset.Range(d, transform_dim).CopyFromVec(offset);
2023  full_scale.Range(d, transform_dim).CopyFromVec(scale);
2024  }
2025 
2026  // Image the affine component does y = a x + b, and by applying
2027  // the pre-transform we are replacing x with s x + o
2028  // s for scale and o for offset), so we have:
2029  // y = a s x + (b + a o).
2030  // do: b += a o.
2031  bias_params->AddMatVec(1.0, *linear_params, kNoTrans, full_offset, 1.0);
2032  // do: a = a * s.
2033  linear_params->MulColsVec(full_scale);
2034 
2035 
2036  }
2037 
2038 
2051  BaseFloat scale) {
2052  if (scale == 1.0)
2053  return component_index;
2054  std::ostringstream os;
2055  os << nnet_->GetComponentName(component_index)
2056  << ".scale" << std::setprecision(3) << scale;
2057  std::string new_component_name = os.str(); // e.g. foo.s2.0
2058  int32 ans = nnet_->GetComponentIndex(new_component_name);
2059  if (ans >= 0)
2060  return ans; // one already exists, no need to create it.
2061  const Component *current_component = nnet_->GetComponent(component_index);
2062  const AffineComponent *affine_component =
2063  dynamic_cast<const AffineComponent*>(current_component);
2064  const TimeHeightConvolutionComponent *conv_component =
2065  dynamic_cast<const TimeHeightConvolutionComponent*>(current_component);
2066  const LinearComponent *linear_component =
2067  dynamic_cast<const LinearComponent*>(current_component);
2068  const TdnnComponent *tdnn_component =
2069  dynamic_cast<const TdnnComponent*>(current_component);
2070 
2071  if (affine_component == NULL && conv_component == NULL &&
2072  linear_component == NULL && tdnn_component == NULL) {
2073  // We can't scale this component (at least, not using this code).
2074  return -1;
2075  }
2076 
2077  Component *new_component = current_component->Copy();
2078 
2079  if (affine_component != NULL) {
2080  // AffineComponent or NaturalGradientAffineComponent.
2081  dynamic_cast<AffineComponent*>(new_component)->
2082  LinearParams().Scale(scale);
2083  } else if (conv_component != NULL) {
2084  dynamic_cast<TimeHeightConvolutionComponent*>(new_component)->
2085  ScaleLinearParams(scale);
2086  } else if (linear_component != NULL) {
2087  dynamic_cast<LinearComponent*>(new_component)->Params().Scale(scale);
2088  } else {
2089  KALDI_ASSERT(tdnn_component != NULL);
2090  dynamic_cast<TdnnComponent*>(new_component)->LinearParams().Scale(scale);
2091  }
2092  return nnet_->AddComponent(new_component_name, new_component);
2093  }
2094 
2097 };
2098 
2099 
2101  Nnet *nnet) {
2102  ModelCollapser c(config, nnet);
2103  c.Collapse();
2104 }
2105 
2106 bool UpdateNnetWithMaxChange(const Nnet &delta_nnet,
2107  BaseFloat max_param_change,
2108  BaseFloat max_change_scale,
2109  BaseFloat scale, Nnet *nnet,
2110  std::vector<int32> *
2111  num_max_change_per_component_applied,
2112  int32 *num_max_change_global_applied) {
2113  KALDI_ASSERT(nnet != NULL);
2114  // computes scaling factors for per-component max-change
2115  const int32 num_updatable = NumUpdatableComponents(delta_nnet);
2116  Vector<BaseFloat> scale_factors = Vector<BaseFloat>(num_updatable);
2117  BaseFloat param_delta_squared = 0.0;
2118  int32 num_max_change_per_component_applied_per_minibatch = 0;
2119  BaseFloat min_scale = 1.0;
2120  std::string component_name_with_min_scale;
2121  BaseFloat max_change_with_min_scale;
2122  int32 i = 0;
2123  for (int32 c = 0; c < delta_nnet.NumComponents(); c++) {
2124  const Component *comp = delta_nnet.GetComponent(c);
2125  if (comp->Properties() & kUpdatableComponent) {
2126  const UpdatableComponent *uc =
2127  dynamic_cast<const UpdatableComponent*>(comp);
2128  if (uc == NULL)
2129  KALDI_ERR << "Updatable component does not inherit from class "
2130  << "UpdatableComponent; change this code.";
2131  BaseFloat max_param_change_per_comp = uc->MaxChange();
2132  KALDI_ASSERT(max_param_change_per_comp >= 0.0);
2133  BaseFloat dot_prod = uc->DotProduct(*uc);
2134  if (max_param_change_per_comp != 0.0 &&
2135  std::sqrt(dot_prod) * std::abs(scale) >
2136  max_param_change_per_comp * max_change_scale) {
2137  scale_factors(i) = max_param_change_per_comp * max_change_scale /
2138  (std::sqrt(dot_prod) * std::abs(scale));
2139  (*num_max_change_per_component_applied)[i]++;
2140  num_max_change_per_component_applied_per_minibatch++;
2141  KALDI_VLOG(2) << "Parameters in " << delta_nnet.GetComponentName(c)
2142  << " change too big: " << std::sqrt(dot_prod) << " * "
2143  << scale << " > " << "max-change * max-change-scale="
2144  << max_param_change_per_comp << " * " << max_change_scale
2145  << ", scaling by " << scale_factors(i);
2146  } else {
2147  scale_factors(i) = 1.0;
2148  }
2149  if (i == 0 || scale_factors(i) < min_scale) {
2150  min_scale = scale_factors(i);
2151  component_name_with_min_scale = delta_nnet.GetComponentName(c);
2152  max_change_with_min_scale = max_param_change_per_comp;
2153  }
2154  param_delta_squared += std::pow(scale_factors(i),
2155  static_cast<BaseFloat>(2.0)) * dot_prod;
2156  i++;
2157  }
2158  }
2159  KALDI_ASSERT(i == scale_factors.Dim());
2160  BaseFloat param_delta = std::sqrt(param_delta_squared);
2161  // computes the scale for global max-change
2162  param_delta *= std::abs(scale);
2163  if (max_param_change != 0.0) {
2164  if (param_delta > max_param_change * max_change_scale) {
2165  if (param_delta - param_delta != 0.0) {
2166  KALDI_WARN << "Infinite parameter change, will not apply.";
2167  return false;
2168  } else {
2169  scale *= max_param_change * max_change_scale / param_delta;
2170  (*num_max_change_global_applied)++;
2171  }
2172  }
2173  }
2174  if ((max_param_change != 0.0 &&
2175  param_delta > max_param_change * max_change_scale &&
2176  param_delta - param_delta == 0.0) || min_scale < 1.0) {
2177  std::ostringstream ostr;
2178  if (min_scale < 1.0)
2179  ostr << "Per-component max-change active on "
2180  << num_max_change_per_component_applied_per_minibatch
2181  << " / " << num_updatable << " Updatable Components."
2182  << " (Smallest factor=" << min_scale << " on "
2183  << component_name_with_min_scale
2184  << " with max-change=" << max_change_with_min_scale <<"). ";
2185  if (param_delta > max_param_change * max_change_scale)
2186  ostr << "Global max-change factor was "
2187  << max_param_change * max_change_scale / param_delta
2188  << " with max-change=" << max_param_change << ".";
2189  KALDI_LOG << ostr.str();
2190  }
2191  // applies both of the max-change scalings all at once, component by component
2192  // and updates parameters
2193  scale_factors.Scale(scale);
2194  AddNnetComponents(delta_nnet, scale_factors, scale, nnet);
2195  return true;
2196 }
2197 
2198 int32 GetNumNvalues(const std::vector<NnetIo> &io_vec,
2199  bool exhaustive) {
2200  int32 num_n_values = -1;
2201  for (size_t i = 0; i < io_vec.size(); i++) {
2202  const NnetIo &io = io_vec[i];
2203  int32 this_num_n_values;
2204  const std::vector<Index> &index_vec = io.indexes;
2205  KALDI_ASSERT(!index_vec.empty() &&
2206  "Empty input or output in ComputationRequest?");
2207  if (exhaustive) {
2208  int32 lowest_n_value = std::numeric_limits<int32>::max(),
2209  highest_n_value = std::numeric_limits<int32>::min();
2210  std::vector<Index>::const_iterator
2211  iter = index_vec.begin(), end = index_vec.end();
2212  for (; iter != end; ++iter) {
2213  int32 n = iter->n;
2214  if (n < lowest_n_value) { lowest_n_value = n; }
2215  if (n > highest_n_value) { highest_n_value = n; }
2216  }
2217  this_num_n_values = highest_n_value + 1 - lowest_n_value;
2218  } else {
2219  // we assume that the 'n' values range from zero to N-1,
2220  // where N is the number of distinct 'n' values.
2221  this_num_n_values = index_vec.back().n + 1;
2222  }
2223  if (num_n_values == -1) {
2224  num_n_values = this_num_n_values;
2225  } else {
2226  if (num_n_values != this_num_n_values) {
2227  KALDI_ERR << "Different inputs/outputs of ComputationRequest have "
2228  "different numbers of n values: " << num_n_values
2229  << " vs. " << this_num_n_values;
2230  }
2231  }
2232  }
2233  if (!exhaustive && RandInt(0, 100) == 0) {
2234  int32 num_n_values_check = GetNumNvalues(io_vec, true);
2235  if (num_n_values != num_n_values_check) {
2236  KALDI_ERR << "Exhaustive and quick checks returned different "
2237  "answers: " << num_n_values << " vs. "
2238  << num_n_values_check;
2239  }
2240  }
2241  return num_n_values;
2242 }
2243 
2244 void ApplyL2Regularization(const Nnet &nnet,
2245  BaseFloat l2_regularize_scale,
2246  Nnet *delta_nnet) {
2247  if (l2_regularize_scale == 0.0)
2248  return;
2249  for (int32 c = 0; c < nnet.NumComponents(); c++) {
2250  const Component *src_component_in = nnet.GetComponent(c);
2251  if (src_component_in->Properties() & kUpdatableComponent) {
2252  const UpdatableComponent *src_component =
2253  dynamic_cast<const UpdatableComponent*>(src_component_in);
2254  UpdatableComponent *dest_component =
2255  dynamic_cast<UpdatableComponent*>(delta_nnet->GetComponent(c));
2256  // The following code will segfault if they aren't both updatable, which
2257  // would be a bug in the calling code.
2258  BaseFloat lrate = dest_component->LearningRate(),
2259  l2_regularize = dest_component->L2Regularization();
2260  KALDI_ASSERT(lrate >= 0 && l2_regularize >= 0);
2261  BaseFloat scale = -2.0 * l2_regularize_scale * lrate * l2_regularize;
2262  if (scale != 0.0)
2263  dest_component->Add(scale, *src_component);
2264  }
2265  }
2266 }
2267 
2268 
2269 bool UpdateNnetWithMaxChange(const Nnet &delta_nnet,
2270  BaseFloat max_param_change,
2271  BaseFloat max_change_scale,
2272  BaseFloat scale, Nnet *nnet,
2273  MaxChangeStats *stats) {
2274  bool ans = UpdateNnetWithMaxChange(
2275  delta_nnet, max_param_change, max_change_scale,
2276  scale, nnet,
2278  &(stats->num_max_change_global_applied));
2279  stats->num_minibatches_processed++;
2280  return ans;
2281 }
2282 
2283 
2284 void MaxChangeStats::Print(const Nnet &nnet) const {
2285  int32 i = 0;
2286  for (int32 c = 0; c < nnet.NumComponents(); c++) {
2287  const Component *comp = nnet.GetComponent(c);
2288  if (comp->Properties() & kUpdatableComponent) {
2289  const UpdatableComponent *uc = dynamic_cast<const UpdatableComponent*>(
2290  comp);
2291  if (uc == NULL)
2292  KALDI_ERR << "Updatable component does not inherit from class "
2293  << "UpdatableComponent; change this code.";
2294  if (num_max_change_per_component_applied[i] > 0)
2295  KALDI_LOG << "For " << nnet.GetComponentName(c)
2296  << ", per-component max-change was enforced "
2297  << ((100.0 * num_max_change_per_component_applied[i]) /
2298  num_minibatches_processed)
2299  << " \% of the time.";
2300  i++;
2301  }
2302  }
2303  if (num_max_change_global_applied > 0)
2304  KALDI_LOG << "The global max-change was enforced "
2305  << ((100.0 * num_max_change_global_applied) /
2306  num_minibatches_processed)
2307  << " \% of the time.";
2308 }
2309 
2310 
2311 } // namespace nnet3
2312 } // namespace kaldi
void NnetToDirectedGraph(const Nnet &nnet, std::vector< std::vector< int32 > > *graph)
This function takes an nnet and turns it to a directed graph on nodes.
Definition: nnet-graph.cc:30
void CopyFromMat(const MatrixBase< OtherReal > &src, MatrixTransposeType trans=kNoTrans)
Definition: cu-matrix.cc:344
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
int32 InputDim(const std::string &input_name) const
Definition: nnet-nnet.cc:669
void CollapseModel(const CollapseModelConfig &config, Nnet *nnet)
This function modifies the neural net for efficiency, in a way that suitable to be done in test time...
Definition: nnet-utils.cc:2100
int32 NumNodes() const
Definition: nnet-nnet.h:126
virtual int32 OutputDim() const
Returns output-dimension of this component.
const std::string & FirstToken() const
Definition: text-utils.h:228
void ScaleNnet(BaseFloat scale, Nnet *nnet)
Scales the nnet parameters and stats by this scale.
Definition: nnet-utils.cc:312
const std::string WholeLine()
Definition: text-utils.h:230
void EvaluateComputationRequest(const Nnet &nnet, const ComputationRequest &request, std::vector< std::vector< bool > > *is_computable)
Given an nnet and a computation request, this function works out which requested outputs in the compu...
Definition: nnet-utils.cc:71
void SetDropoutProportion(BaseFloat dropout_proportion, Nnet *nnet)
This function sets the dropout proportion in all dropout components to dropout_proportion value...
Definition: nnet-utils.cc:509
BaseFloat shrinkage_threshold_
Definition: nnet-utils.cc:969
Real Trace(bool check_square=true) const
Return the trace. If check_square = true, will crash if matrix is not square.
Definition: cu-matrix.cc:3075
void SetDropoutProportion(BaseFloat dropout_proportion)
int32 AddComponent(const std::string &name, Component *component)
Adds a new component with the given name, which should not be the same as any existing component name...
Definition: nnet-nnet.cc:161
virtual int32 InputDim() const
Returns input-dimension of this component.
void ReadConfig(std::istream &config_file)
Definition: nnet-nnet.cc:189
void FindOrphanComponents(const Nnet &nnet, std::vector< int32 > *components)
This function finds a list of components that are never used, and outputs the integer comopnent index...
Definition: nnet-utils.cc:591
std::string PrintVectorPerUpdatableComponent(const Nnet &nnet, const VectorBase< BaseFloat > &vec)
This function is for printing, to a string, a vector with one element per updatable component of the ...
Definition: nnet-utils.cc:231
int32 CollapseComponents(int32 component_index1, int32 component_index2)
This function tries to collapse two successive components, where the component &#39;component_index1&#39; app...
Definition: nnet-utils.cc:1493
const std::string & GetNodeName(int32 node_index) const
returns individual node name.
Definition: nnet-nnet.cc:684
void ComponentDotProducts(const Nnet &nnet1, const Nnet &nnet2, VectorBase< BaseFloat > *dot_prod)
Returns dot products between two networks of the same structure (calls the DotProduct functions of th...
Definition: nnet-utils.cc:211
void SetTestMode(bool test_mode)
bool Parse(const std::vector< std::string > &node_names, const std::string **next_token)
const CuVector< BaseFloat > & BiasParams() const
Abstract base-class for neural-net components.
int32 GetDiagonallyPreModifiedComponentIndex(const CuVectorBase< BaseFloat > &offset, const CuVectorBase< BaseFloat > &scale, const std::string &src_identifier, int32 component_index)
This function finds, or creates, a component which is like &#39;component_index&#39; but is combined with a d...
Definition: nnet-utils.cc:1934
virtual int32 InputDim() const
Returns input-dimension of this component.
int32 GetVerboseLevel()
Get verbosity level, usually set via command line &#39;–verbose=&#39; switch.
Definition: kaldi-error.h:60
virtual Component * Copy() const =0
Copies component (deep copy).
int32 DescriptorIsCollapsible(const Descriptor &desc)
Definition: nnet-utils.cc:1553
void FindOrphanNodes(const Nnet &nnet, std::vector< int32 > *nodes)
This function finds a list of nodes that are never used to compute any output, and outputs the intege...
Definition: nnet-utils.cc:607
int32 CollapseComponentsDropout(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1693
void GetComputableInfo(std::vector< std::vector< bool > > *computable) const
virtual void Scale(BaseFloat scale)
This virtual function when called on – an UpdatableComponent scales the parameters by "scale" when c...
TdnnComponent is a more memory-efficient alternative to manually splicing several frames of input and...
SimpleForwardingDescriptor is the base-case of ForwardingDescriptor, consisting of a source node in t...
void SetUpdatableConfigs(const UpdatableComponent &other)
bool OptimizeNode(int32 node_index)
This function modifies the neural network in the case where &#39;node_index&#39; is a component-input node wh...
Definition: nnet-utils.cc:1638
ModelCollapser(const CollapseModelConfig &config, Nnet *nnet)
Definition: nnet-utils.cc:1449
void Compute(const NnetExample &eg)
virtual int32 NumParameters() const
The following new virtual function returns the total dimension of the parameters in this class...
bool DescriptorTokenize(const std::string &input, std::vector< std::string > *tokens)
This function tokenizes input when parsing Descriptor configuration values.
Definition: nnet-parse.cc:30
void ReduceRankOfComponents(const std::string component_name_pattern, int32 rank, Nnet *nnet)
Definition: nnet-utils.cc:1172
CuSubMatrix< Real > Range(const MatrixIndexT row_offset, const MatrixIndexT num_rows, const MatrixIndexT col_offset, const MatrixIndexT num_cols) const
Definition: cu-matrix.h:653
virtual Component * Copy() const
Copies component (deep copy).
virtual void Scale(BaseFloat scale)
This virtual function when called on – an UpdatableComponent scales the parameters by "scale" when c...
void ScaleBatchnormStats(BaseFloat batchnorm_stats_scale, Nnet *nnet)
This function scales the batchorm stats of any batchnorm components (components of type BatchNormComp...
Definition: nnet-utils.cc:536
bool IsInputNode(int32 node) const
Returns true if this is an output node, meaning that it is of type kInput.
Definition: nnet-nnet.cc:120
void SetBatchnormTestMode(bool test_mode, Nnet *nnet)
This function affects only components of type BatchNormComponent.
Definition: nnet-utils.cc:564
std::vector< int32 > num_max_change_per_component_applied
Definition: nnet-utils.h:543
kaldi::int32 int32
void AddToDiag(Real value)
Adds "value" to the diagonal elements of the matrix.
Definition: cu-matrix.cc:604
void AddMat(Real alpha, const CuMatrixBase< Real > &A, MatrixTransposeType trans=kNoTrans)
*this += alpha * A
Definition: cu-matrix.cc:954
std::vector< IoSpecification > inputs
This class is for computing cross-entropy and accuracy values in a neural network, for diagnostics.
void SetComponent(int32 c, Component *component)
Replace the component indexed by c with a new component.
Definition: nnet-nnet.cc:155
std::vector< Index > indexes
"indexes" is a vector the same length as features.NumRows(), explaining the meaning of each row of th...
Definition: nnet-example.h:42
This class represents a matrix that&#39;s stored on the GPU if we have one, and in memory if not...
Definition: matrix-common.h:71
const CuMatrix< BaseFloat > & LinearParams() const
bool IsComponentNode(int32 node) const
Returns true if this is a component node, meaning that it is of type kComponent.
Definition: nnet-nnet.cc:132
std::string component_name_pattern_
Definition: nnet-utils.cc:970
void RemoveOrphanComponents()
Definition: nnet-nnet.cc:844
ObjectiveType objective_type
Definition: nnet-nnet.h:97
virtual void GetNodeDependencies(std::vector< int32 > *node_indexes) const
This function appends to "node_indexes" all the node indexes.
bool NameMatchesPattern(const char *name, const char *pattern)
Definition: nnet-parse.cc:235
CompositeComponent is a component representing a sequence of [simple] components. ...
virtual void Vectorize(VectorBase< BaseFloat > *params) const
Turns the parameters into vector form.
void ReadEditConfig(std::istream &edit_config_is, Nnet *nnet)
ReadEditConfig() reads a file with a similar-looking format to the config file read by Nnet::ReadConf...
Definition: nnet-utils.cc:1234
void SetNodeName(int32 node_index, const std::string &new_name)
This can be used to modify invidual node names.
Definition: nnet-nnet.cc:53
int32 OutputDim(const std::string &output_name) const
Definition: nnet-nnet.cc:677
This file contains declarations of components that in one way or another normalize their input: Norma...
struct Index is intended to represent the various indexes by which we number the rows of the matrices...
Definition: nnet-common.h:44
void VectorizeNnet(const Nnet &src, VectorBase< BaseFloat > *parameters)
Copies the nnet parameters to *params, whose dimension must be equal to NumParameters(src).
Definition: nnet-utils.cc:378
FixedScaleComponent applies a fixed per-element scale; it&#39;s similar to the Rescale component in the n...
This file contains some miscellaneous functions dealing with class Nnet.
void ConvertRepeatedToBlockAffine(CompositeComponent *c_component)
Definition: nnet-utils.cc:447
void SetNnetAsGradient(Nnet *nnet)
Sets nnet as gradient by Setting is_gradient_ to true and learning_rate_ to 1 for each UpdatableCompo...
Definition: nnet-utils.cc:292
This file contains declarations of components that are "simple", meaning they don&#39;t care about the in...
void ComputeGraphTranspose(const std::vector< std::vector< int32 > > &graph, std::vector< std::vector< int32 > > *graph_transpose)
Outputs a graph in which the order of arcs is reversed.
Definition: nnet-graph.cc:63
std::string Info() const
returns some human-readable information about the network, mostly for debugging purposes.
Definition: nnet-nnet.cc:821
int32 Modulus() const
[Relevant for clockwork RNNs and similar].
Definition: nnet-nnet.cc:658
void ConstrainOrthonormal(Nnet *nnet)
This function, to be called after processing every minibatch, is responsible for enforcing the orthog...
Definition: nnet-utils.cc:1108
const CollapseModelConfig & config_
Definition: nnet-utils.cc:2095
void Init(int32 input_dim, int32 output_dim, BaseFloat param_stddev, BaseFloat bias_stddev)
int32 CollapseComponentsBatchnorm(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1733
void AddVec2(const Real alpha, const VectorBase< Real > &v)
Add vector : *this = *this + alpha * rv^2 [element-wise squaring].
void UnVectorizeNnet(const VectorBase< BaseFloat > &parameters, Nnet *dest)
Copies the parameters from params to *dest.
Definition: nnet-utils.cc:401
void FreezeNaturalGradient(bool freeze, Nnet *nnet)
Controls if natural gradient will be updated.
Definition: nnet-utils.cc:432
void SetDropoutTestMode(bool test_mode, Nnet *nnet)
This function affects components of child-classes of RandomComponent.
Definition: nnet-utils.cc:573
int32 GetNumNvalues(const std::vector< NnetIo > &io_vec, bool exhaustive)
This utility function can be used to obtain the number of distinct &#39;n&#39; values in a training example...
Definition: nnet-utils.cc:2198
This is an abstract base-class.
std::string UnusedValues() const
returns e.g.
Definition: text-utils.cc:518
void ResetGenerators(Nnet *nnet)
This function calls &#39;ResetGenerator()&#39; on all components in &#39;nnet&#39; that inherit from class RandomComp...
Definition: nnet-utils.cc:582
bool GraphHasCycles(const std::vector< std::vector< int32 > > &graph)
This function returns &#39;true&#39; if the graph represented in &#39;graph&#39; contains cycles (including cycles wh...
Definition: nnet-graph.cc:300
void ConstrainOrthonormalInternal(BaseFloat scale, CuMatrixBase< BaseFloat > *M)
Definition: nnet-utils.cc:982
const NetworkNode & GetNode(int32 node) const
returns const reference to a particular numbered network node.
Definition: nnet-nnet.h:146
virtual void Scale(BaseFloat scale)
This virtual function when called on – an UpdatableComponent scales the parameters by "scale" when c...
float BaseFloat
Definition: kaldi-types.h:29
int32 NumParameters(const Nnet &src)
Returns the total of the number of parameters in the updatable components of the nnet.
Definition: nnet-utils.cc:359
void AddMatVec(const Real alpha, const CuMatrixBase< Real > &M, MatrixTransposeType trans, const CuVectorBase< Real > &v, const Real beta)
Definition: cu-vector.cc:506
void ParseConfigLines(const std::vector< std::string > &lines, std::vector< ConfigLine > *config_lines)
Definition: nnet-parse.cc:224
CuMatrixBase< BaseFloat > & LinearParams()
virtual void FreezeNaturalGradient(bool freeze)
freezes/unfreezes NaturalGradient updates, if applicable (to be overriden by components that use Natu...
void AddNnetComponents(const Nnet &src, const Vector< BaseFloat > &alphas, BaseFloat scale, Nnet *dest)
Does *dest += alpha * src for updatable components (affects nnet parameters), and *dest += scale * sr...
Definition: nnet-utils.cc:322
virtual int32 InputDim() const
Returns input-dimension of this component.
const ForwardingDescriptor & Src() const
BaseFloat MaxChange() const
Returns the per-component max-change value, which is interpreted as the maximum change (in l2 norm) i...
bool IsOutputNode(int32 node) const
Returns true if this is an output node, meaning that it is of type kDescriptor and is not directly fo...
Definition: nnet-nnet.cc:112
void ApplyL2Regularization(const Nnet &nnet, BaseFloat l2_regularize_scale, Nnet *delta_nnet)
This function is used as part of the regular training workflow, prior to UpdateNnetWithMaxChange().
Definition: nnet-utils.cc:2244
virtual void ZeroStats()
Components that provide an implementation of StoreStats should also provide an implementation of Zero...
void ComputeSimpleNnetContext(const Nnet &nnet, int32 *left_context, int32 *right_context)
ComputeSimpleNnetContext computes the left-context and right-context of a nnet.
Definition: nnet-utils.cc:146
BaseFloat L2Regularization() const
Returns the l2 regularization constant, which may be set in any updatable component (usually from the...
virtual BaseFloat DotProduct(const UpdatableComponent &other) const =0
Computes dot-product between parameters of two instances of a Component.
virtual int32 Properties() const =0
Return bitmask of the component&#39;s properties.
void Swap(Matrix< Real > *mat)
Definition: cu-matrix.cc:123
int32 CollapseComponentsScale(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1860
static void PreMultiplyAffineParameters(const CuVectorBase< BaseFloat > &offset, const CuVectorBase< BaseFloat > &scale, CuVectorBase< BaseFloat > *bias_params, CuMatrixBase< BaseFloat > *linear_params)
This helper function, used GetDiagonallyPreModifiedComponentIndex, modifies the linear and bias param...
Definition: nnet-utils.cc:2006
void RecomputeStats(const std::vector< NnetChainExample > &egs, const chain::ChainTrainingOptions &chain_config_in, const fst::StdVectorFst &den_fst, Nnet *nnet)
This function zeros the stored component-level stats in the nnet using ZeroComponentStats(), then recomputes them with the supplied egs.
virtual Component * Copy() const
Copies component (deep copy).
virtual int32 OutputDim() const
Returns output-dimension of this component.
CuMatrixBase< BaseFloat > & Params()
const SumDescriptor & Part(int32 n) const
returns the n&#39;th part.
struct rnnlm::@11::@12 n
void RemoveOrphanNodes(bool remove_orphan_inputs=false)
Definition: nnet-nnet.cc:932
void SymAddMat2(const Real alpha, const CuMatrixBase< Real > &M, MatrixTransposeType transA, Real beta)
*this = beta * *this + alpha * M M^T, for symmetric matrices.
Definition: cu-matrix.cc:1353
int32 GetComponentIndex(const std::string &node_name) const
returns index associated with this component name, or -1 if no such index.
Definition: nnet-nnet.cc:474
SvdApplier(const std::string component_name_pattern, int32 bottleneck_dim, BaseFloat energy_threshold, BaseFloat shrinkage_threshold, Nnet *nnet)
Definition: nnet-utils.cc:665
void SetLearningRate(BaseFloat learning_rate, Nnet *nnet)
Sets the underlying learning rate for all the components in the nnet to this value.
Definition: nnet-utils.cc:276
bool HasBatchnorm(const Nnet &nnet)
Returns true if nnet has at least one component of type BatchNormComponent.
Definition: nnet-utils.cc:527
void AddMatMat(const Real alpha, const MatrixBase< Real > &A, MatrixTransposeType transA, const MatrixBase< Real > &B, MatrixTransposeType transB, const Real beta)
#define KALDI_ERR
Definition: kaldi-error.h:147
This is the normal base-case of SumDescriptor which just wraps a ForwardingDescriptor.
static bool ComputeSimpleNnetContextForShift(const Nnet &nnet, int32 input_start, int32 window_size, int32 *left_context, int32 *right_context)
Definition: nnet-utils.cc:92
void ZeroComponentStats(Nnet *nnet)
Zeroes the component stats in all nonlinear components in the nnet.
Definition: nnet-utils.cc:269
void Compute(const ComputationRequest &request)
void AddMatMat(Real alpha, const CuMatrixBase< Real > &A, MatrixTransposeType transA, const CuMatrixBase< Real > &B, MatrixTransposeType transB, Real beta)
C = alpha * A(^T)*B(^T) + beta * C.
Definition: cu-matrix.cc:1291
const CuMatrix< BaseFloat > & LinearParams() const
const ForwardingDescriptor & Src() const
#define KALDI_WARN
Definition: kaldi-error.h:150
int32 GetScaledComponentIndex(int32 component_index, BaseFloat scale)
Given a component &#39;component_index&#39;, returns a component which will give the same output as the curre...
Definition: nnet-utils.cc:2050
std::string NnetInfo(const Nnet &nnet)
This function returns various info about the neural net.
Definition: nnet-utils.cc:492
Real TraceMatMat(const MatrixBase< Real > &A, const MatrixBase< Real > &B, MatrixTransposeType trans)
We need to declare this here as it will be a friend function.
const std::string & GetComponentName(int32 component_index) const
returns individual component name.
Definition: nnet-nnet.cc:689
Real * Data()
Returns a pointer to the start of the vector&#39;s data.
Definition: kaldi-vector.h:70
A ForwardingDescriptor describes how we copy data from another NetworkNode, or from multiple other Ne...
void ReadConfigLines(std::istream &is, std::vector< std::string > *lines)
This function reads in a config file and *appends* its contents to a vector of lines; it is responsib...
Definition: text-utils.cc:564
BaseFloat DotProduct(const Nnet &nnet1, const Nnet &nnet2)
Returns dot product between two networks of the same structure (calls the DotProduct functions of the...
Definition: nnet-utils.cc:250
MatrixIndexT Dim() const
Returns the dimension of the vector.
Definition: kaldi-vector.h:64
void Scale(Real alpha)
Multiplies all elements by this constant.
NetworkNode is used to represent, three types of thing: either an input of the network (which pretty ...
Definition: nnet-nnet.h:81
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150
virtual void SetAsGradient()
Sets is_gradient_ to true and sets learning_rate_ to 1, ignoring learning_rate_factor_.
This file contains a few functions that treat the neural net as a graph on nodes: e...
Class UpdatableComponent is a Component which has trainable parameters; it extends the interface of C...
const Component * GetComponent(int32 i) const
Gets the ith component in this component.
void MulColsVec(const VectorBase< Real > &scale)
Equivalent to (*this) = (*this) * diag(scale).
Real Max() const
Returns the maximum value of any element, or -infinity for the empty vector.
Definition: cu-vector.cc:782
virtual void SetLearningRateFactor(BaseFloat lrate_factor)
void MulColsVec(const CuVectorBase< Real > &scale)
scale i&#39;th column by scale[i]
Definition: cu-matrix.cc:765
const CuVector< BaseFloat > & Offset() const
virtual std::string Type() const =0
Returns a string such as "SigmoidComponent", describing the type of the object.
virtual int32 OutputDim() const
Returns output-dimension of this component.
GeneralDropoutComponent implements dropout, including a continuous variant where the thing we multipl...
CuSubVector< Real > Range(const MatrixIndexT o, const MatrixIndexT l)
Definition: cu-vector.h:160
virtual void PerturbParams(BaseFloat stddev)=0
This function is to be used in testing.
Matrix for CUDA computing.
Definition: matrix-common.h:69
std::vector< Index > indexes
void RemoveSomeNodes(const std::vector< int32 > &nodes_to_remove)
Definition: nnet-nnet.cc:881
MatrixIndexT NumCols() const
Definition: cu-matrix.h:216
bool IsSimpleNnet(const Nnet &nnet)
This function returns true if the nnet has the following properties: It has an output called "output"...
Definition: nnet-utils.cc:52
Offsets in &#39;t&#39; and &#39;x&#39; values of other ForwardingDescriptors.
void WriteConfig(std::ostream &os, const std::vector< std::string > &node_names) const
int32 NumComponents() const
Definition: nnet-nnet.h:124
void ConsolidateMemory(Nnet *nnet)
This just calls ConsolidateMemory() on all the components of the nnet.
Definition: nnet-utils.cc:1147
virtual void UnVectorize(const VectorBase< BaseFloat > &params)
Converts the parameters from vector form.
A class representing a vector.
Definition: kaldi-vector.h:406
This class is responsible for parsing input like hi-there xx=yyy a=b c empty= f-oo=Append(bar, sss) ba_z=123 bing=&#39;a b c&#39; baz="a b c d=&#39;a b&#39; e" and giving you access to the fields, in this case.
Definition: text-utils.h:205
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
std::vector< IoSpecification > outputs
void SetRequireDirectInput(bool b, Nnet *nnet)
Calls the corresponding function in any component of type StatisticsPoolingComponent; used as a way t...
Definition: nnet-utils.cc:303
virtual void ConsolidateMemory()
This virtual function relates to memory management, and avoiding fragmentation.
const CuVector< BaseFloat > & BiasParams() const
virtual void SetParams(const CuVectorBase< BaseFloat > &bias, const CuMatrixBase< BaseFloat > &linear)
Descriptor ReplaceNodeInDescriptor(const Descriptor &src, int32 node_to_replace, const Descriptor &expr)
Definition: nnet-utils.cc:1572
int32 NumInputNodes(const Nnet &nnet)
returns the number of input nodes of this nnet.
Definition: nnet-utils.cc:43
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156
std::vector< ModifiedComponentInfo > modified_component_info_
Definition: nnet-utils.cc:963
void PerturbParams(BaseFloat stddev, Nnet *nnet)
Calls PerturbParams (with the given stddev) on all updatable components of the nnet.
Definition: nnet-utils.cc:199
bool HasUnusedValues() const
Definition: text-utils.cc:510
bool GetValue(const std::string &key, std::string *value)
Definition: text-utils.cc:427
void SetComponent(int32 i, Component *component)
Sets the ith component.
Real FrobeniusNorm() const
Definition: cu-matrix.h:226
void Print(const Nnet &nnet) const
Definition: nnet-utils.cc:2284
void Resize(const MatrixIndexT r, const MatrixIndexT c, MatrixResizeType resize_type=kSetZero, MatrixStrideType stride_type=kDefaultStride)
Sets matrix to a specified size (zero is OK as long as both r and c are zero).
int32 NumOutputNodes(const Nnet &nnet)
returns the number of output nodes of this nnet.
Definition: nnet-utils.cc:35
virtual int32 OutputDim() const
Returns output-dimension of this component.
virtual void Scale(BaseFloat scale)
This virtual function when called on – an UpdatableComponent scales the parameters by "scale" when c...
virtual void Scale(BaseFloat scale)
This virtual function when called on – an UpdatableComponent scales the parameters by "scale" when c...
void Swap(CuVector< Real > *vec)
Definition: cu-vector.cc:1019
int32 CollapseComponentsAffine(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1761
union kaldi::nnet3::NetworkNode::@15 u
void Print(std::ostream &os, const std::vector< std::string > &node_names)
This function, useful for debugging/visualization purposes, prints out a summary of the computation g...
int32 GetNodeIndex(const std::string &node_name) const
returns index associated with this node name, or -1 if no such index.
Definition: nnet-nnet.cc:466
int32 NumParts() const
Returns the number of parts that are concatenated over.
MatrixIndexT NumRows() const
Dimensions.
Definition: cu-matrix.h:215
Provides a vector abstraction class.
Definition: kaldi-vector.h:41
bool NnetIsRecurrent(const Nnet &nnet)
Returns true if &#39;nnet&#39; has some kind of recurrency.
Definition: nnet-utils.cc:1441
virtual void SetUnderlyingLearningRate(BaseFloat lrate)
Sets the learning rate of gradient descent- gets multiplied by learning_rate_factor_.
#define KALDI_LOG
Definition: kaldi-error.h:153
bool DecomposeComponent(const std::string &component_name, const AffineComponent &affine, Component **component_a_out, Component **component_b_out)
Definition: nnet-utils.cc:765
virtual void Add(BaseFloat alpha, const Component &other)
This virtual function when called by – an UpdatableComponent adds the parameters of another updatabl...
int32 SumDescriptorIsCollapsible(const SumDescriptor &sum_desc)
Definition: nnet-utils.cc:1525
TimeHeightConvolutionComponent implements 2-dimensional convolution where one of the dimensions of co...
BaseFloat LearningRate() const
Gets the learning rate to be used in gradient descent.
std::vector< int32 > modification_index_
Definition: nnet-utils.cc:945
Represents a non-allocating general vector which can be defined as a sub-vector of higher-level vecto...
Definition: kaldi-vector.h:501
This file contains declarations of components that are not "simple", meaning they care about the inde...
The first step in compilation is to turn the ComputationSpecification into a ComputationGraph, where for each Cindex we have a list of other Cindexes that it depends on.
bool UpdateNnetWithMaxChange(const Nnet &delta_nnet, BaseFloat max_param_change, BaseFloat max_change_scale, BaseFloat scale, Nnet *nnet, std::vector< int32 > *num_max_change_per_component_applied, int32 *num_max_change_global_applied)
This function does the operation &#39;*nnet += scale * delta_nnet&#39;, while respecting any max-parameter-ch...
Definition: nnet-utils.cc:2106
int32 NumUpdatableComponents(const Nnet &dest)
Returns the number of updatable components in the nnet.
Definition: nnet-utils.cc:422
const CuVector< BaseFloat > & Scales() const
const std::vector< std::string > & GetNodeNames() const
returns vector of node names (needed by some parsing code, for instance).
Definition: nnet-nnet.cc:63
Real Min() const
Returns the minimum value of any element, or +infinity for the empty vector.
Definition: cu-vector.cc:744
void AddNnet(const Nnet &src, BaseFloat alpha, Nnet *dest)
Does *dest += alpha * src (affects nnet parameters and stored stats).
Definition: nnet-utils.cc:349
An abstract representation of a set of Cindexes.
void SortSvd(VectorBase< Real > *s, MatrixBase< Real > *U, MatrixBase< Real > *Vt, bool sort_on_absolute_value)
Function to ensure that SVD is sorted.
MatrixIndexT Dim() const
Dimensions.
Definition: cu-vector.h:69
int32 RandInt(int32 min_val, int32 max_val, struct RandomState *state)
Definition: kaldi-math.cc:95
Vector for CUDA computing.
Definition: matrix-common.h:72
bool IsComponentInputNode(int32 node) const
Returns true if this is component-input node, i.e.
Definition: nnet-nnet.cc:172
Config class for the CollapseModel function.
Definition: nnet-utils.h:240
FixedAffineComponent is an affine transform that is supplied at network initialization time and is no...
This class implements an affine transform using a block diagonal matrix e.g., one whose weight matrix...
int32 GetReducedDimension(const Vector< BaseFloat > &input_vector, int32 lower, int32 upper, BaseFloat min_val)
Definition: nnet-utils.cc:743