fmllr-diag-gmm-test.cc
Go to the documentation of this file.
1 // transform/fmllr-diag-gmm-test.cc
2 
3 // Copyright 2009-2011 Microsoft Corporation
4 
5 // See ../../COPYING for clarification regarding multiple authors
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
15 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
16 // MERCHANTABLITY OR NON-INFRINGEMENT.
17 // See the Apache 2 License for the specific language governing permissions and
18 // limitations under the License.
19 
20 #include "util/common-utils.h"
21 #include "gmm/diag-gmm.h"
23 
24 namespace kaldi {
25 
26 
27 void InitRandomGmm (DiagGmm *gmm_in) {
28  int32 num_gauss = 5 + rand () % 4;
29  int32 dim = 10 + Rand() % 10;
30  DiagGmm &gmm(*gmm_in);
31  gmm.Resize(num_gauss, dim);
32  Matrix<BaseFloat> inv_vars(num_gauss, dim),
33  means(num_gauss, dim);
34  Vector<BaseFloat> weights(num_gauss);
35  for (int32 i = 0; i < num_gauss; i++) {
36  for (int32 j = 0; j < dim; j++) {
37  inv_vars(i, j) = Exp(RandGauss() * (1.0 / (1 + j)));
38  means(i, j) = RandGauss() * (1.0 / (1 + j));
39  }
40  weights(i) = Exp(RandGauss());
41  }
42  weights.Scale(1.0 / weights.Sum());
43  gmm.SetWeights(weights);
44  gmm.SetInvVarsAndMeans(inv_vars, means);
45  gmm.ComputeGconsts();
46 }
47 
48 // This test is statistical and relies on some identities
49 // related to the Aikake criterion.
51  using namespace kaldi;
52  DiagGmm gmm;
53  InitRandomGmm(&gmm);
54  int32 dim = gmm.Dim();
55  int32 npoints = dim*(dim+1)*5;
56  Matrix<BaseFloat> rand_points(npoints, dim);
57  for (int32 i = 0; i < npoints; i++) {
58  SubVector<BaseFloat> row(rand_points, i);
59  gmm.Generate(&row);
60  }
61  Matrix<BaseFloat> cur_xform(dim, dim+1);
62  cur_xform.SetUnit(); // set diag to unit.
63 
64  int32 niters = 5;
65  BaseFloat objf_change_tot = 0.0, objf_change, count;
66  for (int32 j = 0; j < niters; j++) {
67  FmllrOptions opts;
68  FmllrDiagGmmAccs stats(dim, j % 2 == 0 ? opts : FmllrOptions());
69  for (int32 i = 0; i < npoints; i++) {
70  SubVector<BaseFloat> row(rand_points, i);
71  if (j == 0) { // split this case off to exercise more of the code.
72  stats.AccumulateForGmm(gmm, row, 1.0);
73  } else {
74  Vector<BaseFloat> xformed_row(row);
75  ApplyAffineTransform(cur_xform, &xformed_row);
76  Vector<BaseFloat> posteriors(gmm.NumGauss());
77  gmm.ComponentPosteriors(xformed_row, &posteriors);
78  stats.AccumulateFromPosteriors(gmm, row, posteriors);
79  }
80  }
81  stats.Update(opts, &cur_xform, &objf_change, &count);
82  { // Test for ApplyFeatureTransformToStats:
83  BaseFloat objf_change_tmp, count_tmp;
84  ApplyFeatureTransformToStats(cur_xform, &stats);
85  Matrix<BaseFloat> mat(dim, dim+1);
86  mat.SetUnit();
87  stats.Update(opts, &mat, &objf_change_tmp, &count_tmp);
88  // After we apply this transform to the stats, there should
89  // be nothing to gain from further transforming the data.
90  KALDI_ASSERT(objf_change_tmp/count_tmp < 0.01);
91  }
92  KALDI_LOG << "Objf change on iter " << j << " is " << objf_change;
93  objf_change_tot += objf_change;
94  }
95  KALDI_ASSERT(ApproxEqual(count, npoints));
96  int32 num_params = dim*(dim+1);
97  BaseFloat expected_objf_change = 0.5 * num_params;
98  KALDI_LOG << "Expected objf change is: not much more than " << expected_objf_change
99  <<", seen: " << objf_change_tot;
100  KALDI_ASSERT(objf_change_tot < 2.0 * expected_objf_change); // or way too much.
101  // This test relies on statistical laws and if it fails it does not *necessarily*
102  // mean that something is wrong.
103 }
104 
105 
106 // This is a test for the diagonal update and also of ApplyModelTransformToStats().
108  using namespace kaldi;
109  DiagGmm gmm;
110  InitRandomGmm(&gmm);
111  int32 dim = gmm.Dim();
112  int32 npoints = dim*(dim+1)*5;
113  Matrix<BaseFloat> rand_points(npoints, dim);
114  for (int32 i = 0; i < npoints; i++) {
115  SubVector<BaseFloat> row(rand_points, i);
116  gmm.Generate(&row);
117  }
118  Matrix<BaseFloat> cur_xform(dim, dim+1);
119  cur_xform.SetUnit(); // set diag to unit.
120 
121  int32 niters = 2;
122  BaseFloat objf_change_tot = 0.0, objf_change, count;
123  FmllrOptions opts;
124  opts.update_type = "diag";
125 
126  for (int32 j = 0; j < niters; j++) {
127  FmllrDiagGmmAccs stats(dim, j % 2 == 0 ? opts : FmllrOptions());
128  for (int32 i = 0; i < npoints; i++) {
129  SubVector<BaseFloat> row(rand_points, i);
130  if (j == 0) { // split this case off to exercise more of the code.
131  stats.AccumulateForGmm(gmm, row, 1.0);
132  } else {
133  Vector<BaseFloat> xformed_row(row);
134  ApplyAffineTransform(cur_xform, &xformed_row);
135  Vector<BaseFloat> posteriors(gmm.NumGauss());
136  gmm.ComponentPosteriors(xformed_row, &posteriors);
137  stats.AccumulateFromPosteriors(gmm, row, posteriors);
138  }
139  }
140 
141  stats.Update(opts, &cur_xform, &objf_change, &count);
142  { // Test for ApplyModelTransformToStats:
143  BaseFloat objf_change_tmp, count_tmp;
144  ApplyModelTransformToStats(cur_xform, &stats);
145  Matrix<BaseFloat> mat(dim, dim+1);
146  mat.SetUnit();
147  stats.Update(opts, &mat, &objf_change_tmp, &count_tmp);
148  // After we apply this transform to the stats, there should
149  // be nothing to gain from further transforming the data.
150  KALDI_ASSERT(objf_change_tmp/count_tmp < 0.01);
151  }
152  KALDI_LOG << "Objf change on iter " << j << " is " << objf_change;
153  objf_change_tot += objf_change;
154  }
155  KALDI_ASSERT(ApproxEqual(count, npoints));
156  int32 num_params = dim*2;
157  BaseFloat expected_objf_change = 0.5 * num_params;
158  KALDI_LOG << "Expected objf change is: not much more than " << expected_objf_change
159  <<", seen: " << objf_change_tot;
160  KALDI_ASSERT(objf_change_tot < 2.0 * expected_objf_change); // or way too much.
161  // This test relies on statistical laws and if it fails it does not *necessarily*
162  // mean that something is wrong.
163 }
164 
165 
166 // This is a test for the offset-only update and also of ApplyModelTransformToStats().
168  using namespace kaldi;
169  DiagGmm gmm;
170  InitRandomGmm(&gmm);
171  int32 dim = gmm.Dim();
172  int32 npoints = dim*(dim+1)*5;
173  Matrix<BaseFloat> rand_points(npoints, dim);
174  for (int32 i = 0; i < npoints; i++) {
175  SubVector<BaseFloat> row(rand_points, i);
176  gmm.Generate(&row);
177  }
178  Matrix<BaseFloat> cur_xform(dim, dim+1);
179  cur_xform.SetUnit(); // set diag to unit.
180 
181  int32 niters = 2;
182  BaseFloat objf_change_tot = 0.0, objf_change, count;
183  FmllrOptions opts;
184  opts.update_type = "offset";
185 
186  for (int32 j = 0; j < niters; j++) {
187  FmllrDiagGmmAccs stats(dim, j % 2 == 0 ? opts : FmllrOptions());
188  for (int32 i = 0; i < npoints; i++) {
189  SubVector<BaseFloat> row(rand_points, i);
190  if (j == 0) { // split this case off to exercise more of the code.
191  stats.AccumulateForGmm(gmm, row, 1.0);
192  } else {
193  Vector<BaseFloat> xformed_row(row);
194  ApplyAffineTransform(cur_xform, &xformed_row);
195  Vector<BaseFloat> posteriors(gmm.NumGauss());
196  gmm.ComponentPosteriors(xformed_row, &posteriors);
197  stats.AccumulateFromPosteriors(gmm, row, posteriors);
198  }
199  }
200 
201  stats.Update(opts, &cur_xform, &objf_change, &count);
202  { // Test for ApplyModelTransformToStats:
203  BaseFloat objf_change_tmp, count_tmp;
204  ApplyModelTransformToStats(cur_xform, &stats);
205  Matrix<BaseFloat> mat(dim, dim+1);
206  mat.SetUnit();
207  stats.Update(opts, &mat, &objf_change_tmp, &count_tmp);
208  // After we apply this transform to the stats, there should
209  // be nothing to gain from further transforming the data.
210  KALDI_ASSERT(objf_change_tmp/count_tmp < 0.01);
211  }
212  KALDI_LOG << "Objf change on iter " << j << " is " << objf_change;
213  objf_change_tot += objf_change;
214  }
215  KALDI_ASSERT(ApproxEqual(count, npoints));
216  int32 num_params = dim;
217  BaseFloat expected_objf_change = 0.5 * num_params;
218  KALDI_LOG << "Expected objf change is: not much more than " << expected_objf_change
219  <<", seen: " << objf_change_tot;
220  KALDI_ASSERT(objf_change_tot < 2.0 * expected_objf_change); // or way too much.
221  // This test relies on statistical laws and if it fails it does not *necessarily*
222  // mean that something is wrong.
223 }
224 
225 } // namespace kaldi ends here
226 
227 int main() {
228  for (int i = 0; i < 2; i++) { // did more iterations when first testing...
232  }
233  std::cout << "Test OK.\n";
234 }
235 
void ApplyModelTransformToStats(const MatrixBase< BaseFloat > &xform, AffineXformStats *stats)
ApplyModelTransformToStats takes a transform "xform", which must be diagonal (i.e.
void InitRandomGmm(DiagGmm *gmm_in)
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
double Exp(double x)
Definition: kaldi-math.h:83
int32 Dim() const
Returns the dimensionality of the Gaussian mean vectors.
Definition: diag-gmm.h:74
void UnitTestFmllrDiagGmmDiagonal()
This does not work with multiple feature transforms.
void AccumulateFromPosteriors(const DiagGmm &gmm, const VectorBase< BaseFloat > &data, const VectorBase< BaseFloat > &posteriors)
Accumulate stats for a GMM, given supplied posteriors.
std::string update_type
"full", "diag", "offset", "none"
float RandGauss(struct RandomState *state=NULL)
Definition: kaldi-math.h:155
kaldi::int32 int32
void SetUnit()
Sets to zero, except ones along diagonal [for non-square matrices too].
void ApplyFeatureTransformToStats(const MatrixBase< BaseFloat > &xform, AffineXformStats *stats)
This function applies a feature-level transform to stats (useful for certain techniques based on fMLL...
const size_t count
void UnitTestFmllrDiagGmm()
BaseFloat ComponentPosteriors(const VectorBase< BaseFloat > &data, Vector< BaseFloat > *posteriors) const
Computes the posterior probabilities of all Gaussian components given a data point.
Definition: diag-gmm.cc:601
int32 NumGauss() const
Returns the number of mixture components in the GMM.
Definition: diag-gmm.h:72
int Rand(struct RandomState *state)
Definition: kaldi-math.cc:45
A class representing a vector.
Definition: kaldi-vector.h:406
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
Definition for Gaussian Mixture Model with diagonal covariances.
Definition: diag-gmm.h:42
void Generate(VectorBase< BaseFloat > *output)
Generates a random data-point from this distribution.
Definition: diag-gmm.cc:922
int main()
void ApplyAffineTransform(const MatrixBase< BaseFloat > &xform, VectorBase< BaseFloat > *vec)
Applies the affine transform &#39;xform&#39; to the vector &#39;vec&#39; and overwrites the contents of &#39;vec&#39;...
#define KALDI_LOG
Definition: kaldi-error.h:153
Represents a non-allocating general vector which can be defined as a sub-vector of higher-level vecto...
Definition: kaldi-vector.h:501
static bool ApproxEqual(float a, float b, float relative_tolerance=0.001)
return abs(a - b) <= relative_tolerance * (abs(a)+abs(b)).
Definition: kaldi-math.h:265
void UnitTestFmllrDiagGmmOffset()
BaseFloat AccumulateForGmm(const DiagGmm &gmm, const VectorBase< BaseFloat > &data, BaseFloat weight)
Accumulate stats for a single GMM in the model; returns log likelihood.
void Update(const FmllrOptions &opts, MatrixBase< BaseFloat > *fmllr_mat, BaseFloat *objf_impr, BaseFloat *count)
Update.