full-gmm-test.cc
Go to the documentation of this file.
1 // gmm/full-gmm-test.cc
2 
3 // Copyright 2009-2011 Jan Silovsky; Saarland University;
4 // Microsoft Corporation
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/full-gmm.h"
22 #include "gmm/diag-gmm.h"
23 #include "gmm/model-test-common.h"
24 #include "util/stl-utils.h"
25 #include "util/kaldi-io.h"
26 #include "gmm/full-gmm-normal.h"
27 #include "gmm/mle-full-gmm.h"
28 
29 using namespace kaldi;
30 
31 void RandPosdefSpMatrix(size_t dim, SpMatrix<BaseFloat> *matrix,
32  TpMatrix<BaseFloat> *matrix_sqrt = NULL,
33  BaseFloat *logdet = NULL) {
34  // generate random (non-singular) matrix
35  Matrix<BaseFloat> tmp(dim, dim);
36  while (1) {
37  tmp.SetRandn();
38  if (tmp.Cond() < 100) break;
39  std::cout << "Condition number of random matrix large "
40  << static_cast<float>(tmp.Cond()) << ", trying again (this is normal)"
41  << '\n';
42  }
43  // tmp * tmp^T will give positive definite matrix
44  matrix->AddMat2(1.0, tmp, kNoTrans, 0.0);
45 
46  if (matrix_sqrt != NULL) matrix_sqrt->Cholesky(*matrix);
47  if (logdet != NULL) *logdet = matrix->LogPosDefDet();
48  if ((matrix_sqrt == NULL) && (logdet == NULL)) {
49  TpMatrix<BaseFloat> sqrt(dim);
50  sqrt.Cholesky(*matrix);
51  }
52 }
53 
55  size_t num_comp = gmm->NumGauss(), dim = gmm->Dim();
56  Vector<BaseFloat> weights(num_comp);
57  Matrix<BaseFloat> means(num_comp, dim), vars(num_comp, dim);
58 
59  BaseFloat tot_weight = 0.0;
60  for (size_t m = 0; m < num_comp; m++) {
61  weights(m) = kaldi::RandUniform();
62  for (size_t d= 0; d < dim; d++) {
63  means(m, d) = kaldi::RandGauss();
64  vars(m, d) = Exp(kaldi::RandGauss()) + 1e-5;
65  }
66  tot_weight += weights(m);
67  }
68 
69  // normalize weights
70  for (size_t m = 0; m < num_comp; m++) {
71  weights(m) /= tot_weight;
72  }
73 
74  vars.InvertElements();
75  gmm->SetWeights(weights);
76  gmm->SetInvVarsAndMeans(vars, means);
77  gmm->Perturb(0.5 * RandUniform());
78  gmm->ComputeGconsts(); // this is unnecassary; computed in Perturb
79 }
80 
82  FullGmm fgmm;
83  int32 dim = 10 + Rand() % 10, num_comp = 1 + Rand() % 10;
84  unittest::InitRandFullGmm(dim, num_comp, &fgmm);
85  int32 num_frames = 5000;
86  Matrix<BaseFloat> feats(num_frames, dim);
87  FullGmmNormal fgmm_normal(fgmm);
88  fgmm_normal.Rand(&feats);
89 
90  AccumFullGmm acc(fgmm, kGmmAll);
91  for (int32 t = 0; t < num_frames; t++)
92  acc.AccumulateFromFull(fgmm, feats.Row(t), 1.0);
93  BaseFloat objf_change, count;
94 
95  MleFullGmmOptions opts;
96 
97  MleFullGmmUpdate(opts, acc, kGmmAll, &fgmm, &objf_change, &count);
98  BaseFloat change = objf_change / count,
99  num_params = (num_comp * (dim + 1 + (dim*(dim+1)/2))),
100  predicted_change = 0.5 * num_params / num_frames; // Was there
101  KALDI_LOG << "Objf change per frame was " << change << " vs. predicted "
102  << predicted_change;
103  KALDI_ASSERT(change < 2.0 * predicted_change && change > 0.0);
104 }
105 
106 
107 void
109  // random dimension of the gmm
110  size_t dim = 1 + kaldi::RandInt(0, 9);
111  // random number of mixtures
112  size_t nMix = 1 + kaldi::RandInt(0, 9);
113 
114  std::cout << "Testing NumGauss: " << nMix << ", " << "Dim: " << dim
115  << '\n';
116 
117  // generate random feature vector and
118  // random mean vectors and covariance matrices
119  Vector<BaseFloat> feat(dim);
120  Vector<BaseFloat> weights(nMix);
121  Vector<BaseFloat> loglikes(nMix);
122  Matrix<BaseFloat> means(nMix, dim);
123  std::vector<SpMatrix<BaseFloat> > invcovars(nMix);
124  for (size_t mix = 0; mix < nMix; mix++) {
125  invcovars[mix].Resize(dim);
126  }
127  Vector<BaseFloat> covars_logdet(nMix);
128 
129  for (size_t d = 0; d < dim; d++) {
130  feat(d) = kaldi::RandGauss();
131  }
132 
133  float tot_weight = 0.0;
134  for (size_t m = 0; m < nMix; m++) {
135  weights(m) = kaldi::RandUniform();
136  for (size_t d = 0; d < dim; d++) {
137  means(m, d) = kaldi::RandGauss();
138  }
139  SpMatrix<BaseFloat> covar(dim);
140  RandPosdefSpMatrix(dim, &covar, NULL, &covars_logdet(m));
141  invcovars[m].CopyFromSp(covar);
142  invcovars[m].InvertDouble();
143  tot_weight += weights(m);
144  }
145 
146  // normalize weights and compute loglike for feature vector
147  for (size_t m = 0; m < nMix; m++) {
148  weights(m) /= tot_weight;
149  }
150 
151  // compute loglike for feature vector
152  float loglike = 0.0;
153  for (size_t m = 0; m < nMix; m++) {
154  loglikes(m) += -0.5 * (M_LOG_2PI * dim
155  + covars_logdet(m)
156  + VecSpVec(means.Row(m), invcovars[m], means.Row(m))
157  + VecSpVec(feat, invcovars[m], feat))
158  + VecSpVec(means.Row(m), invcovars[m], feat);
159  loglikes(m) += Log(weights(m));
160  }
161 
162  loglike = loglikes.LogSumExp();
163 
164 
165  // new GMM
166  FullGmm *gmm = new FullGmm();
167  gmm->Resize(nMix, dim);
168  gmm->SetWeights(weights);
169  gmm->SetInvCovarsAndMeans(invcovars, means);
170  gmm->ComputeGconsts();
171 
172  Vector<BaseFloat> posterior1(nMix);
173  float loglike1 = gmm->ComponentPosteriors(feat, &posterior1);
174 
175  // std::cout << "LogLike: " << loglike << '\n';
176  // std::cout << "LogLike1: " << loglike1 << '\n';
177 
178  AssertEqual(loglike, loglike1, 0.01);
179 
180  KALDI_ASSERT(fabs(1.0 - posterior1.Sum()) < 0.001);
181 
182  { // Test various accessors / mutators
183  Vector<BaseFloat> weights_bak(nMix);
184  Matrix<BaseFloat> means_bak(nMix, dim);
185  std::vector<SpMatrix<BaseFloat> > invcovars_bak(nMix);
186  for (size_t i = 0; i < nMix; i++) {
187  invcovars_bak[i].Resize(dim);
188  }
189 
190  weights_bak.CopyFromVec(gmm->weights());
191  gmm->GetMeans(&means_bak);
192  gmm->GetCovars(&invcovars_bak);
193  for (size_t i = 0; i < nMix; i++) {
194  invcovars_bak[i].InvertDouble();
195  }
196 
197  // set all params one-by-one to new model
198  FullGmm gmm2;
199  gmm2.Resize(gmm->NumGauss(), gmm->Dim());
200  gmm2.SetWeights(weights_bak);
201  gmm2.SetMeans(means_bak);
202  gmm2.SetInvCovars(invcovars_bak);
203  gmm2.ComputeGconsts();
204  BaseFloat loglike_gmm2 = gmm2.LogLikelihood(feat);
205  AssertEqual(loglike1, loglike_gmm2);
206  {
207  Vector<BaseFloat> loglikes;
208  gmm2.LogLikelihoods(feat, &loglikes);
209  AssertEqual(loglikes.LogSumExp(), loglike_gmm2);
210  }
211  {
212  std::vector<int32> indices;
213  for (int32 i = 0; i < gmm2.NumGauss(); i++)
214  indices.push_back(i);
215  Vector<BaseFloat> loglikes;
216  gmm2.LogLikelihoodsPreselect(feat, indices, &loglikes);
217  AssertEqual(loglikes.LogSumExp(), loglike_gmm2);
218  }
219 
220 
221  // single component mean accessor + mutator
222  FullGmm gmm3;
223  gmm3.Resize(gmm->NumGauss(), gmm->Dim());
224  gmm3.SetWeights(weights_bak);
225  means_bak.SetZero();
226  for (size_t i = 0; i < nMix; i++) {
227  SubVector<BaseFloat> tmp = means_bak.Row(i);
228  gmm->GetComponentMean(i, &tmp);
229  }
230  gmm3.SetMeans(means_bak);
231  gmm3.SetInvCovars(invcovars_bak);
232  gmm3.ComputeGconsts();
233  float loglike_gmm3 = gmm3.LogLikelihood(feat);
234  AssertEqual(loglike1, loglike_gmm3, 0.01);
235 
236  // set all params one-by-one to new model
237  FullGmm gmm4;
238  gmm4.Resize(gmm->NumGauss(), gmm->Dim());
239  gmm4.SetWeights(weights_bak);
240  gmm->GetCovarsAndMeans(&invcovars_bak, &means_bak);
241  for (size_t i = 0; i < nMix; i++) {
242  invcovars_bak[i].InvertDouble();
243  }
244  gmm4.SetInvCovarsAndMeans(invcovars_bak, means_bak);
245  gmm4.ComputeGconsts();
246  BaseFloat loglike_gmm4 = gmm4.LogLikelihood(feat);
247  AssertEqual(loglike1, loglike_gmm4, 0.001);
248 
249  } // Test various accessors / mutators end
250 
251  // First, non-binary write
252  gmm->Write(Output("tmpf", false).Stream(), false);
253 
254  { // I/O tests
255  bool binary_in;
256  FullGmm *gmm2 = new FullGmm();
257  Input ki("tmpf", &binary_in);
258  gmm2->Read(ki.Stream(), binary_in);
259 
260  float loglike3 = gmm2->ComponentPosteriors(feat, &posterior1);
261  AssertEqual(loglike, loglike3, 0.01);
262 
263  // binary write
264  gmm2->Write(Output("tmpfb", true).Stream(), true);
265  delete gmm2;
266 
267  // binary read
268  FullGmm *gmm3;
269  gmm3 = new FullGmm();
270 
271  Input ki2("tmpfb", &binary_in);
272  gmm3->Read(ki2.Stream(), binary_in);
273 
274  AssertEqual(loglike, loglike3, 0.01);
275 
276  delete gmm3;
277  }
278 
279  { // CopyFromFullGmm
280  FullGmm gmm4;
281  gmm4.CopyFromFullGmm(*gmm);
282  float loglike5 = gmm4.ComponentPosteriors(feat, &posterior1);
283  AssertEqual(loglike, loglike5, 0.01);
284  }
285 
286  { // test copy from DiagGmm and back to DiagGmm
287  DiagGmm gmm_diag;
288  gmm_diag.Resize(nMix, dim);
289  init_rand_diag_gmm(&gmm_diag);
290  float loglike_diag = gmm_diag.LogLikelihood(feat);
291 
292  FullGmm gmm_full;
293  gmm_full.CopyFromDiagGmm(gmm_diag);
294  float loglike_full = gmm_full.LogLikelihood(feat);
295 
296  DiagGmm gmm_diag2;
297  gmm_diag2.CopyFromFullGmm(gmm_full);
298  float loglike_diag2 = gmm_diag2.LogLikelihood(feat);
299 
300  AssertEqual(loglike_diag, loglike_full, 0.01);
301  AssertEqual(loglike_diag, loglike_diag2, 0.01);
302  }
303 
304  { // split and merge test for 1 component GMM (doesn't test the merge crit.)
305  FullGmm gmm1;
306  Vector<BaseFloat> weights1(1);
307  Matrix<BaseFloat> means1(1, dim);
308  std::vector<SpMatrix<BaseFloat> > invcovars1(1);
309  weights1(0) = 1.0;
310  means1.CopyFromMat(means.Range(0, 1, 0, dim));
311  invcovars1[0].Resize(dim);
312  invcovars1[0].CopyFromSp(invcovars[0]);
313  gmm1.Resize(1, dim);
314  gmm1.SetWeights(weights1);
315  gmm1.SetInvCovarsAndMeans(invcovars1, means1);
316  gmm1.ComputeGconsts();
317  FullGmm gmm2;
318  gmm2.CopyFromFullGmm(gmm1);
319  gmm2.Split(2, 0.001);
320  gmm2.Merge(1);
321  float loglike1 = gmm1.LogLikelihood(feat);
322  float loglike2 = gmm2.LogLikelihood(feat);
323  AssertEqual(loglike1, loglike2, 0.01);
324  }
325 
326  delete gmm;
327 
328  unlink("tmpf");
329  unlink("tmpfb");
330 }
331 
332 int
333 main() {
334  // repeat the test ten times
335  for (int i = 0; i < 2; i++) {
336  UnitTestFullGmm();
338  }
339  std::cout << "Test OK.\n";
340 }
void AddMat2(const Real alpha, const MatrixBase< Real > &M, MatrixTransposeType transM, const Real beta)
rank-N update: if (transM == kNoTrans) (*this) = beta*(*this) + alpha * M * M^T, or (if transM == kTr...
Definition: sp-matrix.cc:1110
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 SetWeights(const Vector< Real > &w)
Mutators for both float or double.
Definition: full-gmm-inl.h:31
Packed symetric matrix class.
Definition: matrix-common.h:62
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: full-gmm.cc:206
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
float RandUniform(struct RandomState *state=NULL)
Returns a random number strictly between 0 and 1.
Definition: kaldi-math.h:151
BaseFloat ComponentPosteriors(const VectorBase< BaseFloat > &data, VectorBase< BaseFloat > *posterior) const
Computes the posterior probabilities of all Gaussian components given a data point.
Definition: full-gmm.cc:719
int32 Dim() const
Returns the dimensionality of the Gaussian mean vectors.
Definition: full-gmm.h:60
Definition for Gaussian Mixture Model with full covariances in normal mode: where the parameters are ...
Real Cond() const
Returns condition number by computing Svd.
Configuration variables like variance floor, minimum occupancy, etc.
Definition: mle-full-gmm.h:38
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: full-gmm.cc:613
int32 ComputeGconsts()
Sets the gconsts.
Definition: full-gmm.cc:92
void Split(int32 target_components, float perturb_factor, std::vector< int32 > *history=NULL)
Merge the components and remember the order in which the components were merged (flat list of pairs) ...
Definition: full-gmm.cc:132
Definition for Gaussian Mixture Model with full covariances.
Definition: full-gmm.h:40
void Resize(int32 nMix, int32 dim)
Resizes arrays to this dim. Does not initialize data.
Definition: diag-gmm.cc:66
void SetInvCovarsAndMeans(const std::vector< SpMatrix< Real > > &invcovars, const Matrix< Real > &means)
Use SetInvCovarsAndMeans if updating both means and (inverse) covariances.
Definition: full-gmm-inl.h:50
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 Write(std::ostream &os, bool binary) const
Definition: full-gmm.cc:758
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 GetCovars(std::vector< SpMatrix< Real > > *v) const
Accessor for covariances.
Definition: full-gmm-inl.h:106
void UnitTestFullGmm()
void CopyFromVec(const VectorBase< Real > &v)
Copy data from another vector (must match own size).
const size_t count
void CopyFromFullGmm(const FullGmm &fullgmm)
Copies from given FullGmm.
Definition: full-gmm.cc:65
std::istream & Stream()
Definition: kaldi-io.cc:826
void Cholesky(const SpMatrix< Real > &orig)
Definition: tp-matrix.cc:88
float BaseFloat
Definition: kaldi-types.h:29
void Resize(int32 nMix, int32 dim)
Resizes arrays to this dim. Does not initialize data.
Definition: full-gmm.cc:41
Real LogPosDefDet() const
Computes log determinant but only for +ve-def matrices (it uses Cholesky).
Definition: sp-matrix.cc:36
const SubVector< Real > Row(MatrixIndexT i) const
Return specific row of matrix [const].
Definition: kaldi-matrix.h:188
double Log(double x)
Definition: kaldi-math.h:100
void UnitTestFullGmmEst()
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 RandPosdefSpMatrix(MatrixIndexT dim, SpMatrix< Real > *matrix)
void MleFullGmmUpdate(const MleFullGmmOptions &config, const AccumFullGmm &fullgmm_acc, GmmFlagsType flags, FullGmm *gmm, BaseFloat *obj_change_out, BaseFloat *count_out)
for computing the maximum-likelihood estimates of the parameters of a Gaussian mixture model...
void Rand(MatrixBase< BaseFloat > *feats)
Generates random features from the model.
void SetRandn()
Sets to random values of a normal distribution.
Class for computing the maximum-likelihood estimates of the parameters of a Gaussian mixture model...
Definition: mle-full-gmm.h:74
Real VecSpVec(const VectorBase< Real > &v1, const SpMatrix< Real > &M, const VectorBase< Real > &v2)
Computes v1^T * M * v2.
Definition: sp-matrix.cc:964
Packed symetric matrix class.
Definition: matrix-common.h:63
int main()
void init_rand_diag_gmm(DiagGmm *gmm)
int32 NumGauss() const
Returns the number of mixture components in the GMM.
Definition: full-gmm.h:58
int32 NumGauss() const
Returns the number of mixture components in the GMM.
Definition: diag-gmm.h:72
void SetZero()
Sets matrix to zero.
const Vector< BaseFloat > & weights() const
Definition: full-gmm.h:144
int Rand(struct RandomState *state)
Definition: kaldi-math.cc:45
Real Sum() const
Returns sum of the elements.
void Read(std::istream &is, bool binary)
Definition: full-gmm.cc:813
void GetMeans(Matrix< Real > *m) const
Accessor for means.
Definition: full-gmm-inl.h:118
void CopyFromFullGmm(const FullGmm &fullgmm)
Copies from given FullGmm.
Definition: diag-gmm.cc:92
void InitRandFullGmm(int32 dim, int32 num_comp, FullGmm *gmm)
void InvertElements()
Inverts all the elements of the matrix.
void SetInvCovars(const std::vector< SpMatrix< Real > > &v)
Set the (inverse) covariances and recompute means_invcovars_.
Definition: full-gmm-inl.h:83
A class representing a vector.
Definition: kaldi-vector.h:406
void LogLikelihoods(const VectorBase< BaseFloat > &data, Vector< BaseFloat > *loglikes) const
Outputs the per-component contributions to the log-likelihood.
Definition: full-gmm.cc:591
void CopyFromDiagGmm(const DiagGmm &diaggmm)
Copies from given DiagGmm.
Definition: full-gmm.cc:77
#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
SubMatrix< Real > Range(const MatrixIndexT row_offset, const MatrixIndexT num_rows, const MatrixIndexT col_offset, const MatrixIndexT num_cols) const
Return a sub-part of matrix.
Definition: kaldi-matrix.h:202
BaseFloat AccumulateFromFull(const FullGmm &gmm, const VectorBase< BaseFloat > &data, BaseFloat frame_posterior)
Accumulate for all components given a full-covariance GMM.
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
BaseFloat LogLikelihood(const VectorBase< BaseFloat > &data) const
Returns the log-likelihood of a data point (vector) given the GMM.
Definition: full-gmm.cc:582
void SetMeans(const Matrix< Real > &m)
Use SetMeans to update only the Gaussian means (and not variances)
Definition: full-gmm-inl.h:38
Represents a non-allocating general vector which can be defined as a sub-vector of higher-level vecto...
Definition: kaldi-vector.h:501
void GetCovarsAndMeans(std::vector< SpMatrix< Real > > *covars, Matrix< Real > *means) const
Accessor for covariances and means.
Definition: full-gmm-inl.h:132
int32 RandInt(int32 min_val, int32 max_val, struct RandomState *state)
Definition: kaldi-math.cc:95
void GetComponentMean(int32 gauss, VectorBase< Real > *out) const
Accessor for component mean.
Definition: full-gmm-inl.h:151