diag-gmm-test.cc
Go to the documentation of this file.
1 // gmm/diag-gmm-test.cc
2 
3 // Copyright 2009-2011 Microsoft Corporation; Georg Stemmer; Jan Silovsky;
4 // Saarland University
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 "gmm/diag-gmm.h"
22 #include "gmm/mle-diag-gmm.h"
23 #include "util/kaldi-io.h"
24 
25 namespace kaldi {
26 
27 void InitRandomGmm(DiagGmm *gmm_in) {
28  int32 num_gauss = 10 + Rand() % 5;
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.Perturb(0.5 * RandUniform());
46  gmm.ComputeGconsts(); // this is unnecessary; computed in Perturb
47 }
48 
49 
50 
51 // This tests the Generate function and also the HMM-update.
52 // it relies on some statistical ideas related to the Aikake
53 // criterion.
55  DiagGmm gmm;
56  InitRandomGmm(&gmm);
57  int32 dim = gmm.Dim();
58  int32 npoints = 100 * gmm.NumGauss();
59  Matrix<BaseFloat> rand_points(npoints, dim);
60  for (int32 i = 0; i < npoints; i++) {
61  SubVector<BaseFloat> row(rand_points, i);
62  gmm.Generate(&row);
63  }
64  int32 niters = 15;
65  BaseFloat objf_change_tot = 0.0, objf_change, count;
66  for (int32 j = 0; j < niters; j++) {
67  MleDiagGmmOptions opts;
68  AccumDiagGmm stats(gmm, kGmmAll); // all update flags.
69  for (int32 i = 0; i < npoints; i++) {
70  SubVector<BaseFloat> row(rand_points, i);
71  stats.AccumulateFromDiag(gmm, row, 1.0);
72  }
73  MleDiagGmmUpdate(opts, stats, kGmmAll, &gmm, &objf_change, &count);
74  objf_change_tot += objf_change;
75  }
76  AssertEqual(count, npoints, 1e-6);
77  int32 num_params = gmm.NumGauss() * (gmm.Dim()*2 + 1);
78  BaseFloat expected_objf_change = 0.5 * num_params;
79  KALDI_LOG << "Expected objf change is: not much more than "
80  << expected_objf_change <<", seen: " << objf_change_tot;
81  KALDI_ASSERT(objf_change_tot < 2.0 * expected_objf_change); // way too much.
82  // This test relies on statistical laws and if it fails it does not
83  // *necessarily* mean that something is wrong.
84 }
85 
87  // random dimension of the gmm
88  size_t dim = 1 + kaldi::RandInt(0, 5);
89  // random number of mixtures
90  size_t nMix = 1 + kaldi::RandInt(0, 5);
91 
92  std::cout << "Testing NumGauss: " << nMix << ", " << "Dim: " << dim
93  << '\n';
94 
95  // generate random feature vector and
96  // random mean and variance vectors
97  Vector<BaseFloat> feat(dim), weights(nMix), loglikes(nMix);
98  Matrix<BaseFloat> means(nMix, dim), vars(nMix, dim), invvars(nMix, dim);
99 
100  float loglike = 0.0;
101  for (size_t d = 0; d < dim; d++) {
102  feat(d) = kaldi::RandGauss();
103  }
104 
105  float tot_weight = 0.0;
106  for (size_t m = 0; m < nMix; m++) {
107  weights(m) = kaldi::RandUniform();
108  for (size_t d= 0; d < dim; d++) {
109  means(m, d) = kaldi::RandGauss();
110  vars(m, d) = Exp(kaldi::RandGauss()) + 1e-5;
111  }
112  tot_weight += weights(m);
113  }
114 
115  // normalize weights
116  for (size_t m = 0; m < nMix; m++) {
117  weights(m) /= tot_weight;
118  for (size_t d= 0; d < dim; d++) {
119  loglikes(m) += -0.5 * (M_LOG_2PI + Log(vars(m, d)) + (feat(d) -
120  means(m, d)) * (feat(d) - means(m, d)) / vars(m, d));
121  }
122  loglikes(m) += Log(weights(m));
123  }
124 
125  loglike = loglikes.LogSumExp();
126 
127  // new GMM
128  DiagGmm *gmm = new DiagGmm();
129  gmm->Resize(nMix, dim);
130  invvars.CopyFromMat(vars);
131  invvars.InvertElements();
132  gmm->SetWeights(weights);
133  gmm->SetInvVarsAndMeans(invvars, means);
134  gmm->ComputeGconsts();
135 
136  Vector<BaseFloat> posterior1(nMix);
137  float loglike1 = gmm->ComponentPosteriors(feat, &posterior1);
138 
139  std::cout << "LogLike: " << loglike << '\n';
140  std::cout << "LogLike1: " << loglike1 << '\n';
141  AssertEqual(loglike, loglike1, 0.01);
142 
143  AssertEqual(1.0, posterior1.Sum(), 0.01);
144 
145  { // Test various accessors / mutators
146  Vector<BaseFloat> weights_bak(nMix);
147  Matrix<BaseFloat> means_bak(nMix, dim);
148  Matrix<BaseFloat> invvars_bak(nMix, dim);
149 
150  weights_bak.CopyFromVec(gmm->weights());
151  gmm->GetMeans(&means_bak);
152  gmm->GetVars(&invvars_bak); // get vars
153  invvars_bak.InvertElements(); // compute invvars
154 
155  // set all params one-by-one to new model
156  DiagGmm gmm2;
157  gmm2.Resize(gmm->NumGauss(), gmm->Dim());
158  gmm2.SetWeights(weights_bak);
159  gmm2.SetMeans(means_bak);
160  gmm2.SetInvVars(invvars_bak);
161  gmm2.ComputeGconsts();
162  BaseFloat loglike_gmm2 = gmm2.LogLikelihood(feat);
163  AssertEqual(loglike1, loglike_gmm2);
164  {
165  Vector<BaseFloat> loglikes;
166  gmm2.LogLikelihoods(feat, &loglikes);
167  AssertEqual(loglikes.LogSumExp(), loglike_gmm2);
168  }
169  {
170  std::vector<int32> indices;
171  for (int32 i = 0; i < gmm2.NumGauss(); i++)
172  indices.push_back(i);
173  Vector<BaseFloat> loglikes;
174  gmm2.LogLikelihoodsPreselect(feat, indices, &loglikes);
175  AssertEqual(loglikes.LogSumExp(), loglike_gmm2);
176  }
177 
178  // single component mean accessor + mutator
179  DiagGmm gmm3;
180  gmm3.Resize(gmm->NumGauss(), gmm->Dim());
181  gmm3.SetWeights(weights_bak);
182  means_bak.SetZero();
183  for (size_t i = 0; i < nMix; i++) {
184  SubVector<BaseFloat> row(means_bak, i);
185  gmm->GetComponentMean(i, &row);
186  gmm3.SetComponentMean(i, row);
187  }
188  gmm3.SetInvVars(invvars_bak);
189  gmm3.ComputeGconsts();
190  BaseFloat loglike_gmm3 = gmm3.LogLikelihood(feat);
191  AssertEqual(loglike1, loglike_gmm3, 0.01);
192  } // Test various accessors / mutators end
193 
194 
195  // First, non-binary write.
196  gmm->Write(Output("tmpf", false).Stream(), false);
197 
198  delete gmm;
199 
200  {
201  bool binary_in;
202  DiagGmm *gmm2 = new DiagGmm();
203  Input ki("tmpf", &binary_in);
204  gmm2->Read(ki.Stream(), binary_in);
205 
206  float loglike4 = gmm2->ComponentPosteriors(feat, &posterior1);
207  AssertEqual(loglike, loglike4, 0.01);
208 
209  // binary write
210  gmm2->Write(Output("tmpfb", true).Stream(), true);
211  delete gmm2;
212 
213  // binary read
214  DiagGmm *gmm3;
215  gmm3 = new DiagGmm();
216  Input ki2("tmpfb", &binary_in);
217  gmm3->Read(ki2.Stream(), binary_in);
218 
219  float loglike5 = gmm3->ComponentPosteriors(feat, &posterior1);
220  AssertEqual(loglike, loglike5, 0.01);
221  delete gmm3;
222  }
223 
224  { // split and merge test for 1 component GMM (doesn't test the merge crit.)
225  DiagGmm gmm1;
226  Vector<BaseFloat> weights1(1);
227  Matrix<BaseFloat> means1(1, dim), vars1(1, dim), invvars1(1, dim);
228  weights1(0) = 1.0;
229  means1.CopyFromMat(means.Range(0, 1, 0, dim));
230  vars1.CopyFromMat(vars.Range(0, 1, 0, dim));
231  invvars1.CopyFromMat(vars1);
232  invvars1.InvertElements();
233  gmm1.Resize(1, dim);
234  gmm1.SetWeights(weights1);
235  gmm1.SetInvVarsAndMeans(invvars1, means1);
236  gmm1.ComputeGconsts();
237  DiagGmm gmm2;
238  gmm2.CopyFromDiagGmm(gmm1);
239  gmm2.Split(2, 0.001);
240  gmm2.Merge(1);
241  float loglike1 = gmm1.LogLikelihood(feat);
242  float loglike2 = gmm2.LogLikelihood(feat);
243  AssertEqual(loglike1, loglike2, 0.01);
244  }
245 
246 
247  { // split and merge test for 1 component GMM, this time using K-means algorithm.
248  DiagGmm gmm1;
249  Vector<BaseFloat> weights1(1);
250  Matrix<BaseFloat> means1(1, dim), vars1(1, dim), invvars1(1, dim);
251  weights1(0) = 1.0;
252  means1.CopyFromMat(means.Range(0, 1, 0, dim));
253  vars1.CopyFromMat(vars.Range(0, 1, 0, dim));
254  invvars1.CopyFromMat(vars1);
255  invvars1.InvertElements();
256  gmm1.Resize(1, dim);
257  gmm1.SetWeights(weights1);
258  gmm1.SetInvVarsAndMeans(invvars1, means1);
259  gmm1.ComputeGconsts();
260  DiagGmm gmm2;
261  gmm2.CopyFromDiagGmm(gmm1);
262  gmm2.Split(2, 0.001);
263  gmm2.MergeKmeans(1);
264  float loglike1 = gmm1.LogLikelihood(feat);
265  float loglike2 = gmm2.LogLikelihood(feat);
266  AssertEqual(loglike1, loglike2, 0.01);
267  }
268 
269  { // Duplicate Gaussians using initializer that takes a vector, and
270  // check like is unchanged.
271  DiagGmm gmm1;
272  Vector<BaseFloat> weights1(1);
273  Matrix<BaseFloat> means1(1, dim), vars1(1, dim), invvars1(1, dim);
274  weights1(0) = 1.0;
275  means1.CopyFromMat(means.Range(0, 1, 0, dim));
276  vars1.CopyFromMat(vars.Range(0, 1, 0, dim));
277  invvars1.CopyFromMat(vars1);
278  invvars1.InvertElements();
279  gmm1.Resize(1, dim);
280  gmm1.SetWeights(weights1);
281  gmm1.SetInvVarsAndMeans(invvars1, means1);
282  gmm1.ComputeGconsts();
283 
284  std::vector<std::pair<BaseFloat, const DiagGmm*> > vec;
285  vec.push_back(std::make_pair(static_cast<BaseFloat>(0.4), (const DiagGmm*)(&gmm1)));
286  vec.push_back(std::make_pair(static_cast<BaseFloat>(0.6), (const DiagGmm*)(&gmm1)));
287 
288  DiagGmm gmm2(vec);
289 
290  float loglike1 = gmm1.LogLikelihood(feat);
291  float loglike2 = gmm2.LogLikelihood(feat);
292  AssertEqual(loglike1, loglike2, 0.01);
293  }
294 
295  unlink("tmpf");
296  unlink("tmpfb");
297 }
298 
299 } // end namespace kaldi
300 
301 int main() {
302  // repeat the test ten times
303  for (int i = 0; i < 2; i++) {
306  }
307  std::cout << "Test OK.\n";
308 }
309 
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 CopyFromDiagGmm(const DiagGmm &diaggmm)
Copies from given DiagGmm.
Definition: diag-gmm.cc:83
void Perturb(float perturb_factor)
Perturbs the component means with a random vector multiplied by the pertrub factor.
Definition: diag-gmm.cc:215
#define M_LOG_2PI
Definition: kaldi-math.h:60
void SetInvVarsAndMeans(const MatrixBase< Real > &invvars, const MatrixBase< Real > &means)
Use SetInvVarsAndMeans if updating both means and (inverse) variances.
Definition: diag-gmm-inl.h:63
void Write(std::ostream &os, bool binary) const
Definition: diag-gmm.cc:705
void Merge(int32 target_components, std::vector< int32 > *history=NULL)
Merge the components and remember the order in which the components were merged (flat list of pairs) ...
Definition: diag-gmm.cc:295
void LogLikelihoodsPreselect(const VectorBase< BaseFloat > &data, const std::vector< int32 > &indices, Vector< BaseFloat > *loglikes) const
Outputs the per-component log-likelihoods of a subset of mixture components.
Definition: diag-gmm.cc:566
float RandUniform(struct RandomState *state=NULL)
Returns a random number strictly between 0 and 1.
Definition: kaldi-math.h:151
void Split(int32 target_components, float perturb_factor, std::vector< int32 > *history=NULL)
Split the components and remember the order in which the components were split.
Definition: diag-gmm.cc:154
void MleDiagGmmUpdate(const MleDiagGmmOptions &config, const AccumDiagGmm &diag_gmm_acc, GmmFlagsType flags, DiagGmm *gmm, BaseFloat *obj_change_out, BaseFloat *count_out, int32 *floored_elements_out, int32 *floored_gaussians_out, int32 *removed_gaussians_out)
for computing the maximum-likelihood estimates of the parameters of a Gaussian mixture model...
void GetComponentMean(int32 gauss, VectorBase< Real > *out) const
Accessor for single component mean.
Definition: diag-gmm-inl.h:135
void Resize(int32 nMix, int32 dim)
Resizes arrays to this dim. Does not initialize data.
Definition: diag-gmm.cc:66
int32 ComputeGconsts()
Sets the gconsts.
Definition: diag-gmm.cc:114
float RandGauss(struct RandomState *state=NULL)
Definition: kaldi-math.h:155
kaldi::int32 int32
void SetMeans(const MatrixBase< Real > &m)
Use SetMeans to update only the Gaussian means (and not variances)
Definition: diag-gmm-inl.h:43
void CopyFromMat(const MatrixBase< OtherReal > &M, MatrixTransposeType trans=kNoTrans)
Copy given matrix. (no resize is done).
Real LogSumExp(Real prune=-1.0) const
Returns log(sum(exp())) without exp overflow If prune > 0.0, ignores terms less than the max - prune...
void GetVars(Matrix< Real > *v) const
Accessor for covariances.
Definition: diag-gmm-inl.h:115
void CopyFromVec(const VectorBase< Real > &v)
Copy data from another vector (must match own size).
const size_t count
std::istream & Stream()
Definition: kaldi-io.cc:826
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
double Log(double x)
Definition: kaldi-math.h:100
BaseFloat AccumulateFromDiag(const DiagGmm &gmm, const VectorBase< BaseFloat > &data, BaseFloat frame_posterior)
Accumulate for all components given a diagonal-covariance GMM.
void UnitTestDiagGmmGenerate()
BaseFloat LogLikelihood(const VectorBase< BaseFloat > &data) const
Returns the log-likelihood of a data point (vector) given the GMM.
Definition: diag-gmm.cc:517
void GetMeans(Matrix< Real > *m) const
Accessor for means.
Definition: diag-gmm-inl.h:123
int main()
const Vector< BaseFloat > & weights() const
Definition: diag-gmm.h:178
int32 NumGauss() const
Returns the number of mixture components in the GMM.
Definition: diag-gmm.h:72
void SetZero()
Sets matrix to zero.
Configuration variables like variance floor, minimum occupancy, etc.
Definition: mle-diag-gmm.h:38
void Scale(Real alpha)
Multiplies all elements by this constant.
void LogLikelihoods(const VectorBase< BaseFloat > &data, Vector< BaseFloat > *loglikes) const
Outputs the per-component log-likelihoods.
Definition: diag-gmm.cc:528
int Rand(struct RandomState *state)
Definition: kaldi-math.cc:45
Real Sum() const
Returns sum of the elements.
void MergeKmeans(int32 target_components, ClusterKMeansOptions cfg=ClusterKMeansOptions())
Definition: diag-gmm.cc:231
void SetInvVars(const MatrixBase< Real > &v)
Set the (inverse) variances and recompute means_invvars_.
Definition: diag-gmm-inl.h:78
void InvertElements()
Inverts all the elements of the matrix.
void Read(std::istream &in, bool binary)
Definition: diag-gmm.cc:728
A class representing a vector.
Definition: kaldi-vector.h:406
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
static void AssertEqual(float a, float b, float relative_tolerance=0.001)
assert abs(a - b) <= relative_tolerance * (abs(a)+abs(b))
Definition: kaldi-math.h:276
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
void SetWeights(const VectorBase< Real > &w)
Mutators for both float or double.
Definition: diag-gmm-inl.h:28
#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
void UnitTestDiagGmm()
int32 RandInt(int32 min_val, int32 max_val, struct RandomState *state)
Definition: kaldi-math.cc:95