JWS C Library
C language utility library
gmm.c
Go to the documentation of this file.
00001 /*
00002  * This work is licensed under a Creative Commons 
00003  * Attribution-Noncommercial-Share Alike 3.0 United States License.
00004  * 
00005  *    http://creativecommons.org/licenses/by-nc-sa/3.0/us/
00006  * 
00007  * You are free:
00008  * 
00009  *    to Share - to copy, distribute, display, and perform the work
00010  *    to Remix - to make derivative works
00011  * 
00012  * Under the following conditions:
00013  * 
00014  *    Attribution. You must attribute the work in the manner specified by the
00015  *    author or licensor (but not in any way that suggests that they endorse you
00016  *    or your use of the work).
00017  * 
00018  *    Noncommercial. You may not use this work for commercial purposes.
00019  * 
00020  *    Share Alike. If you alter, transform, or build upon this work, you may
00021  *    distribute the resulting work only under the same or similar license to
00022  *    this one.
00023  * 
00024  * For any reuse or distribution, you must make clear to others the license
00025  * terms of this work. The best way to do this is by including this header.
00026  * 
00027  * Any of the above conditions can be waived if you get permission from the
00028  * copyright holder.
00029  * 
00030  * Apart from the remix rights granted under this license, nothing in this
00031  * license impairs or restricts the author's moral rights.
00032  */
00033 
00034 
00046 #include <jwsc/config.h>
00047 
00048 #include <stdlib.h>
00049 #include <stdio.h>
00050 #include <assert.h>
00051 #include <math.h>
00052 #include <inttypes.h>
00053 
00054 #include "jwsc/base/limits.h"
00055 #include "jwsc/vector/vector.h"
00056 #include "jwsc/vector/vector_math.h"
00057 #include "jwsc/matrix/matrix.h"
00058 #include "jwsc/matrix/matrix_math.h"
00059 #include "jwsc/matblock/matblock.h"
00060 #include "jwsc/prob/mv_pdf.h"
00061 #include "jwsc/stat/stat.h"
00062 #include "jwsc/stat/kmeans.h"
00063 #include "jwsc/stat/gmm.h"
00064 
00065 
00070 static void ensure_non_singular_covariance_d(Matrix_d** m, double min)
00071 {
00072     uint32_t n, N;
00073 
00074     Matrix_d* v  = NULL;
00075     Vector_d* d  = NULL;
00076     Matrix_d* dd = NULL;
00077     Error*    e;
00078 
00079     N = (*m)->num_rows;
00080 
00081     /*
00082     for (n = 0; n < N; n++)
00083     {
00084         if ((*m)->elts[ n ][ n ] < min)
00085         {
00086             (*m)->elts[ n ][ n ] = min;
00087         }
00088     }
00089     */
00090 
00091     assert((e = solve_real_symmetric_matrix_eigenproblem_d(&v, &d, *m)) == NULL);
00092     create_zero_matrix_d(&dd, N, N);
00093 
00094     for (n = 0; n < N; n++)
00095     {
00096         if (d->elts[ n ] < min)
00097         {
00098             dd->elts[ n ][ n ] = min;
00099         }
00100         else
00101         {
00102             dd->elts[ n ][ n ] = d->elts[ n ];
00103         }
00104     }
00105 
00106     multiply_matrices_d(m, v, dd);
00107     transpose_matrix_d(&v, v);
00108     multiply_matrices_d(m, *m, v);
00109 
00110     free_matrix_d(v);
00111     free_vector_d(d);
00112     free_matrix_d(dd);
00113 }
00114 
00115 
00117 static void gmm_e_step_d
00118 (
00119     const Matrix_d*   mu,
00120     const Matblock_d* sigma,
00121     const Vector_d*   pi,
00122     Matrix_d*         gamma,
00123     const Matrix_d*   x
00124 )
00125 {
00126     uint32_t n, N;
00127     uint32_t k, K;
00128     double   C_n;
00129 
00130     Vector_d* p_k     = 0;
00131     Vector_d* mu_k    = 0;
00132     Matrix_d* sigma_k = 0;
00133     Error*    e;
00134 
00135     N = x->num_rows;
00136     K = pi->num_elts;
00137 
00138     create_zero_matrix_d(&gamma, N, K);
00139 
00140     for (k = 0; k < K; k++)
00141     {
00142         copy_vector_from_matrix_d(&mu_k, mu, MATRIX_ROW_VECTOR, k);
00143         copy_matrix_from_matblock_d(&sigma_k, sigma, MATBLOCK_MAT_MATRIX, k);
00144 
00145         assert((e = set_mv_gaussian_pdf_d(&p_k, mu_k, sigma_k, x)) == 0);
00146 
00147         copy_vector_into_matrix_d(&gamma, gamma, p_k, MATRIX_COL_VECTOR, k);
00148     }
00149 
00150     for (n = 0; n < N; n++)
00151     {
00152         C_n = 0;
00153 
00154         for (k = 0; k < K; k++)
00155         {
00156             gamma->elts[ n ][ k ] *= pi->elts[ k ];
00157 
00158             C_n += gamma->elts[ n ][ k ];
00159         }
00160 
00161         assert(C_n > 0);
00162 
00163         for (k = 0; k < K; k++)
00164         {
00165             gamma->elts[ n ][ k ] /= C_n;
00166         }
00167     }
00168 
00169     free_vector_d(p_k);
00170     free_vector_d(mu_k);
00171     free_matrix_d(sigma_k);
00172 }
00173 
00174 
00176 static void gmm_m_step_d
00177 (
00178     Matrix_d*       mu,
00179     Matblock_d*     sigma,
00180     Vector_d*       pi,
00181     const Matrix_d* gamma,
00182     const Matrix_d* x
00183 )
00184 {
00185     uint32_t k, K;
00186     uint32_t n, N;
00187     uint32_t d, dd, D;
00188     double   s_1, s_2;
00189 
00190     Vector_d* mu_k    = NULL;
00191     Matrix_d* sigma_k = NULL;
00192 
00193     K = pi->num_elts;
00194     N = x->num_rows;
00195     D = x->num_cols;
00196 
00197     create_zero_vector_d(&pi, K);
00198 
00199     // mu
00200     create_matrix_d(&mu, K, D);
00201     for (k = 0; k < K; k++)
00202     {
00203         create_zero_vector_d(&mu_k, D);
00204 
00205         for (n = 0; n < N; n++)
00206         {
00207             for (d = 0; d < D; d++)
00208             {
00209                 mu_k->elts[ d ] += gamma->elts[ n ][ k ]*x->elts[ n ][ d ];
00210             }
00211 
00212             pi->elts[ k ] += gamma->elts[ n ][ k ];
00213         }
00214 
00215         assert(pi->elts[ k ] > 0);
00216 
00217         multiply_vector_by_scalar_d(&mu_k, mu_k, 1.0 / pi->elts[ k ]);
00218         copy_vector_into_matrix_d(&mu, mu, mu_k, MATRIX_ROW_VECTOR, k);
00219     }
00220 
00221     // sigma
00222     create_matblock_d(&sigma, K, D, D);
00223     for (k = 0; k < K; k++)
00224     {
00225         create_zero_matrix_d(&sigma_k, D, D);
00226 
00227         for (n = 0; n < N; n++)
00228         {
00229             for (d = 0; d < D; d++)
00230             {
00231                 s_1 = x->elts[ n ][ d ] - mu->elts[ k ][ d ];
00232 
00233                 for (dd = 0; dd < D; dd++)
00234                 {
00235                     s_2 = x->elts[ n ][ dd ] - mu->elts[ k ][ dd ];
00236 
00237                     sigma_k->elts[ d ][ dd ] += gamma->elts[ n ][ k ] * s_1*s_2;
00238                 }
00239             }
00240         }
00241 
00242         multiply_matrix_by_scalar_d(&sigma_k, sigma_k, 1.0 / pi->elts[ k ]);
00243         ensure_non_singular_covariance_d(&sigma_k, 1.0e-1);
00244         copy_matrix_into_matblock_d(&sigma, sigma, sigma_k, 
00245                 MATBLOCK_MAT_MATRIX, k);
00246     }
00247 
00248     // pi
00249     multiply_vector_by_scalar_d(&pi, pi, 1.0 / N);
00250 
00251     free_vector_d(mu_k);
00252     free_matrix_d(sigma_k);
00253 }
00254 
00255 
00262 double gmm_log_likelihood_d
00263 (
00264     const Matrix_d*   mu,
00265     const Matblock_d* sigma,
00266     const Vector_d*   pi,
00267     const Matrix_d*   x
00268 )
00269 {
00270     uint32_t n, N;
00271     uint32_t k, K;
00272     double   ll;
00273 
00274     Matrix_d* p       = 0;
00275     Vector_d* p_k     = 0;
00276     Vector_d* q       = 0;
00277     Vector_d* mu_k    = 0;
00278     Matrix_d* sigma_k = 0;
00279     Error*    e;
00280 
00281     N = x->num_rows;
00282     K = pi->num_elts;
00283 
00284     create_matrix_d(&p, N, K);
00285 
00286     for (k = 0; k < K; k++)
00287     {
00288         copy_vector_from_matrix_d(&mu_k, mu, MATRIX_ROW_VECTOR, k);
00289         copy_matrix_from_matblock_d(&sigma_k, sigma, MATBLOCK_MAT_MATRIX, k);
00290 
00291         assert((e = set_mv_gaussian_pdf_d(&p_k, mu_k, sigma_k, x)) == 0);
00292 
00293         copy_vector_into_matrix_d(&p, p, p_k, MATRIX_COL_VECTOR, k);
00294     }
00295 
00296     multiply_matrix_and_vector_d(&q, p, pi);
00297     ll = 0;
00298 
00299     for (n = 0; n < N; n++)
00300     {
00301         ll += log((q->elts[ n ] < JWSC_MIN_LOG_ARG) 
00302                 ? JWSC_MIN_LOG_ARG : q->elts[ n ]);
00303     }
00304 
00305 #if defined(JWSC_HAVE_ISNAN) && defined(JWSC_HAVE_ISINF)
00306     assert(!isnan(ll) && !isinf(ll));
00307 #endif
00308 
00309     free_matrix_d(p);
00310     free_vector_d(p_k);
00311     free_vector_d(q);
00312     free_vector_d(mu_k);
00313     free_matrix_d(sigma_k);
00314 
00315     return ll;
00316 }
00317 
00318 
00331 void gmm_kmeans_init_d
00332 (
00333     Matrix_d**      mu_out,
00334     Matblock_d**    sigma_out,
00335     Vector_d**      pi_out,
00336     const Matrix_d* x,
00337     uint32_t         K
00338 )
00339 {
00340     uint32_t d, D;
00341     uint32_t n, nn, N;
00342     uint32_t k;
00343 
00344     Vector_i32* members = NULL;
00345     Vector_u32* counts  = NULL;
00346     Matrix_d*   x_k     = NULL;
00347     Matrix_d*   sigma_k = NULL;
00348 
00349     D = x->num_cols;
00350     N = x->num_rows;
00351 
00352     init_kmeans_randomly_d(mu_out, x, K);
00353     kmeans_d(mu_out, &members, x, K, 2);
00354 
00355     create_matblock_d(sigma_out, K, D, D);
00356     create_zero_vector_u32(&counts, K);
00357 
00358     for (n = 0; n < N; n++)
00359     {
00360         k = members->elts[ n ];
00361         counts->elts[ k ]++;
00362     }
00363     for (k = 0; k < K; k++)
00364     {
00365         create_matrix_d(&x_k, counts->elts[ k ], D);
00366 
00367         nn = 0;
00368         for (n = 0; n < N; n++)
00369         {
00370             if (k == members->elts[ n ])
00371             {
00372                 for (d = 0; d < D; d++)
00373                 {
00374                     x_k->elts[ nn ][ d ] = x->elts[ n ][ d ];
00375                 }
00376                 nn++;
00377             }
00378         }
00379 
00380         mv_sample_covariance_d(&sigma_k, x_k);
00381         ensure_non_singular_covariance_d(&sigma_k, 1.0e-1);
00382         copy_matrix_into_matblock_d(sigma_out, *sigma_out, sigma_k,
00383                 MATBLOCK_MAT_MATRIX, k);
00384     }
00385 
00386     create_vector_d(pi_out, K);
00387     for (k = 0; k < K; k++)
00388     {
00389         (*pi_out)->elts[ k ] = counts->elts[ k ] / (double)N;
00390     }
00391 
00392     free_vector_i32(members);
00393     free_vector_u32(counts);
00394     free_matrix_d(x_k);
00395     free_matrix_d(sigma_k);
00396 }
00397 
00398 
00413 void gmm_stats_init_d
00414 (
00415     Matrix_d**      mu_out,
00416     Matblock_d**    sigma_out,
00417     Vector_d**      pi_out,
00418     const Matrix_d* x,
00419     uint32_t        K
00420 )
00421 {
00422     uint32_t k;
00423     uint32_t D;
00424 
00425     Vector_d* mu_0    = 0;
00426     Vector_d* mu_k    = 0;
00427     Matrix_d* sigma_k = 0;
00428 
00429     D = x->num_cols;
00430 
00431     if (*pi_out == NULL)
00432     {
00433         create_init_vector_d(pi_out, K, 1.0 / (double)K);
00434     }
00435     else
00436     {
00437         assert((*pi_out)->num_elts == K);
00438     }
00439 
00440     if (*sigma_out == NULL)
00441     {
00442         create_matblock_d(sigma_out, K, D, D);
00443         mv_sample_covariance_d(&sigma_k, x);
00444         ensure_non_singular_covariance_d(&sigma_k, 1.0e-1);
00445 
00446         for (k = 0; k < K; k++)
00447         {
00448             copy_matrix_into_matblock_d(sigma_out, *sigma_out, sigma_k,
00449                     MATBLOCK_MAT_MATRIX, k);
00450         }
00451     }
00452     else
00453     {
00454         assert((*sigma_out)->num_mats == K && (*sigma_out)->num_rows == D 
00455                                            && (*sigma_out)->num_cols == D);
00456 
00457         for (k = 0; k < K; k++)
00458         {
00459             copy_matrix_from_matblock_d(&sigma_k, *sigma_out, 
00460                     MATBLOCK_MAT_MATRIX, k);
00461             ensure_non_singular_covariance_d(&sigma_k, 1.0e-1);
00462             copy_matrix_into_matblock_d(sigma_out, *sigma_out, sigma_k,
00463                     MATBLOCK_MAT_MATRIX, k);
00464         }
00465     }
00466 
00467     if (*mu_out == NULL)
00468     {
00469         create_matrix_d(mu_out, K, D);
00470         mv_sample_mean_d(&mu_0, x);
00471         copy_vector_into_matrix_d(mu_out, *mu_out, mu_0, MATRIX_ROW_VECTOR, 0);
00472 
00473         for (k = 1; k < K; k++)
00474         {
00475             sample_mv_gaussian_pdf_d(&mu_k, mu_0, sigma_k);
00476             copy_vector_into_matrix_d(mu_out, *mu_out, mu_k, 
00477                     MATRIX_ROW_VECTOR, k);
00478         }
00479     }
00480     else
00481     {
00482         assert((*mu_out)->num_rows == K && (*mu_out)->num_cols == D);
00483     }
00484 
00485     free_vector_d(mu_0);
00486     free_vector_d(mu_k);
00487     free_matrix_d(sigma_k);
00488 }
00489 
00490 
00505 void train_gmm_d
00506 (
00507     Matrix_d**      mu_out,
00508     Matblock_d**    sigma_out,
00509     Vector_d**      pi_out,
00510     Matrix_d**      gamma_out,
00511     const Matrix_d* x,
00512     uint32_t        K
00513 )
00514 {
00515     double   ll, ll_prev;
00516 
00517     Matrix_d*   mu;
00518     Matblock_d* sigma;
00519     Vector_d*   pi;
00520     Matrix_d*   gamma = 0;
00521 
00522     gmm_kmeans_init_d(mu_out, sigma_out, pi_out, x, K);
00523     mu    = *mu_out;
00524     sigma = *sigma_out;
00525     pi    = *pi_out;
00526 
00527     if (gamma_out)
00528     {
00529         create_matrix_d(gamma_out, x->num_rows, pi->num_elts);
00530         gamma = *gamma_out;
00531     }
00532     else
00533     {
00534         create_matrix_d(&gamma, x->num_rows, pi->num_elts);
00535     }
00536 
00537     ll_prev = 0;
00538     ll      = log(0);
00539 
00540     while (fabs(ll - ll_prev) > 1.0e-10)
00541     {
00542         gmm_e_step_d(mu, sigma, pi, gamma, x);
00543         gmm_m_step_d(mu, sigma, pi, gamma, x);
00544 
00545         ll_prev = ll;
00546         ll = gmm_log_likelihood_d(mu, sigma, pi, x);
00547     }
00548 
00549     if (!gamma_out)
00550     {
00551         free_matrix_d(gamma);
00552     }
00553 }