1"""Gaussian Mixture Model.""" 2 3# Author: Wei Xue <xuewei4d@gmail.com> 4# Modified by Thierry Guillemot <thierry.guillemot.work@gmail.com> 5# License: BSD 3 clause 6 7import numpy as np 8 9from scipy import linalg 10 11from ._base import BaseMixture, _check_shape 12from ..utils import check_array 13from ..utils.extmath import row_norms 14 15 16############################################################################### 17# Gaussian mixture shape checkers used by the GaussianMixture class 18 19 20def _check_weights(weights, n_components): 21 """Check the user provided 'weights'. 22 23 Parameters 24 ---------- 25 weights : array-like of shape (n_components,) 26 The proportions of components of each mixture. 27 28 n_components : int 29 Number of components. 30 31 Returns 32 ------- 33 weights : array, shape (n_components,) 34 """ 35 weights = check_array(weights, dtype=[np.float64, np.float32], ensure_2d=False) 36 _check_shape(weights, (n_components,), "weights") 37 38 # check range 39 if any(np.less(weights, 0.0)) or any(np.greater(weights, 1.0)): 40 raise ValueError( 41 "The parameter 'weights' should be in the range " 42 "[0, 1], but got max value %.5f, min value %.5f" 43 % (np.min(weights), np.max(weights)) 44 ) 45 46 # check normalization 47 if not np.allclose(np.abs(1.0 - np.sum(weights)), 0.0): 48 raise ValueError( 49 "The parameter 'weights' should be normalized, but got sum(weights) = %.5f" 50 % np.sum(weights) 51 ) 52 return weights 53 54 55def _check_means(means, n_components, n_features): 56 """Validate the provided 'means'. 57 58 Parameters 59 ---------- 60 means : array-like of shape (n_components, n_features) 61 The centers of the current components. 62 63 n_components : int 64 Number of components. 65 66 n_features : int 67 Number of features. 68 69 Returns 70 ------- 71 means : array, (n_components, n_features) 72 """ 73 means = check_array(means, dtype=[np.float64, np.float32], ensure_2d=False) 74 _check_shape(means, (n_components, n_features), "means") 75 return means 76 77 78def _check_precision_positivity(precision, covariance_type): 79 """Check a precision vector is positive-definite.""" 80 if np.any(np.less_equal(precision, 0.0)): 81 raise ValueError("'%s precision' should be positive" % covariance_type) 82 83 84def _check_precision_matrix(precision, covariance_type): 85 """Check a precision matrix is symmetric and positive-definite.""" 86 if not ( 87 np.allclose(precision, precision.T) and np.all(linalg.eigvalsh(precision) > 0.0) 88 ): 89 raise ValueError( 90 "'%s precision' should be symmetric, positive-definite" % covariance_type 91 ) 92 93 94def _check_precisions_full(precisions, covariance_type): 95 """Check the precision matrices are symmetric and positive-definite.""" 96 for prec in precisions: 97 _check_precision_matrix(prec, covariance_type) 98 99 100def _check_precisions(precisions, covariance_type, n_components, n_features): 101 """Validate user provided precisions. 102 103 Parameters 104 ---------- 105 precisions : array-like 106 'full' : shape of (n_components, n_features, n_features) 107 'tied' : shape of (n_features, n_features) 108 'diag' : shape of (n_components, n_features) 109 'spherical' : shape of (n_components,) 110 111 covariance_type : str 112 113 n_components : int 114 Number of components. 115 116 n_features : int 117 Number of features. 118 119 Returns 120 ------- 121 precisions : array 122 """ 123 precisions = check_array( 124 precisions, 125 dtype=[np.float64, np.float32], 126 ensure_2d=False, 127 allow_nd=covariance_type == "full", 128 ) 129 130 precisions_shape = { 131 "full": (n_components, n_features, n_features), 132 "tied": (n_features, n_features), 133 "diag": (n_components, n_features), 134 "spherical": (n_components,), 135 } 136 _check_shape( 137 precisions, precisions_shape[covariance_type], "%s precision" % covariance_type 138 ) 139 140 _check_precisions = { 141 "full": _check_precisions_full, 142 "tied": _check_precision_matrix, 143 "diag": _check_precision_positivity, 144 "spherical": _check_precision_positivity, 145 } 146 _check_precisions[covariance_type](precisions, covariance_type) 147 return precisions 148 149 150############################################################################### 151# Gaussian mixture parameters estimators (used by the M-Step) 152 153 154def _estimate_gaussian_covariances_full(resp, X, nk, means, reg_covar): 155 """Estimate the full covariance matrices. 156 157 Parameters 158 ---------- 159 resp : array-like of shape (n_samples, n_components) 160 161 X : array-like of shape (n_samples, n_features) 162 163 nk : array-like of shape (n_components,) 164 165 means : array-like of shape (n_components, n_features) 166 167 reg_covar : float 168 169 Returns 170 ------- 171 covariances : array, shape (n_components, n_features, n_features) 172 The covariance matrix of the current components. 173 """ 174 n_components, n_features = means.shape 175 covariances = np.empty((n_components, n_features, n_features)) 176 for k in range(n_components): 177 diff = X - means[k] 178 covariances[k] = np.dot(resp[:, k] * diff.T, diff) / nk[k] 179 covariances[k].flat[:: n_features + 1] += reg_covar 180 return covariances 181 182 183def _estimate_gaussian_covariances_tied(resp, X, nk, means, reg_covar): 184 """Estimate the tied covariance matrix. 185 186 Parameters 187 ---------- 188 resp : array-like of shape (n_samples, n_components) 189 190 X : array-like of shape (n_samples, n_features) 191 192 nk : array-like of shape (n_components,) 193 194 means : array-like of shape (n_components, n_features) 195 196 reg_covar : float 197 198 Returns 199 ------- 200 covariance : array, shape (n_features, n_features) 201 The tied covariance matrix of the components. 202 """ 203 avg_X2 = np.dot(X.T, X) 204 avg_means2 = np.dot(nk * means.T, means) 205 covariance = avg_X2 - avg_means2 206 covariance /= nk.sum() 207 covariance.flat[:: len(covariance) + 1] += reg_covar 208 return covariance 209 210 211def _estimate_gaussian_covariances_diag(resp, X, nk, means, reg_covar): 212 """Estimate the diagonal covariance vectors. 213 214 Parameters 215 ---------- 216 responsibilities : array-like of shape (n_samples, n_components) 217 218 X : array-like of shape (n_samples, n_features) 219 220 nk : array-like of shape (n_components,) 221 222 means : array-like of shape (n_components, n_features) 223 224 reg_covar : float 225 226 Returns 227 ------- 228 covariances : array, shape (n_components, n_features) 229 The covariance vector of the current components. 230 """ 231 avg_X2 = np.dot(resp.T, X * X) / nk[:, np.newaxis] 232 avg_means2 = means ** 2 233 avg_X_means = means * np.dot(resp.T, X) / nk[:, np.newaxis] 234 return avg_X2 - 2 * avg_X_means + avg_means2 + reg_covar 235 236 237def _estimate_gaussian_covariances_spherical(resp, X, nk, means, reg_covar): 238 """Estimate the spherical variance values. 239 240 Parameters 241 ---------- 242 responsibilities : array-like of shape (n_samples, n_components) 243 244 X : array-like of shape (n_samples, n_features) 245 246 nk : array-like of shape (n_components,) 247 248 means : array-like of shape (n_components, n_features) 249 250 reg_covar : float 251 252 Returns 253 ------- 254 variances : array, shape (n_components,) 255 The variance values of each components. 256 """ 257 return _estimate_gaussian_covariances_diag(resp, X, nk, means, reg_covar).mean(1) 258 259 260def _estimate_gaussian_parameters(X, resp, reg_covar, covariance_type): 261 """Estimate the Gaussian distribution parameters. 262 263 Parameters 264 ---------- 265 X : array-like of shape (n_samples, n_features) 266 The input data array. 267 268 resp : array-like of shape (n_samples, n_components) 269 The responsibilities for each data sample in X. 270 271 reg_covar : float 272 The regularization added to the diagonal of the covariance matrices. 273 274 covariance_type : {'full', 'tied', 'diag', 'spherical'} 275 The type of precision matrices. 276 277 Returns 278 ------- 279 nk : array-like of shape (n_components,) 280 The numbers of data samples in the current components. 281 282 means : array-like of shape (n_components, n_features) 283 The centers of the current components. 284 285 covariances : array-like 286 The covariance matrix of the current components. 287 The shape depends of the covariance_type. 288 """ 289 nk = resp.sum(axis=0) + 10 * np.finfo(resp.dtype).eps 290 means = np.dot(resp.T, X) / nk[:, np.newaxis] 291 covariances = { 292 "full": _estimate_gaussian_covariances_full, 293 "tied": _estimate_gaussian_covariances_tied, 294 "diag": _estimate_gaussian_covariances_diag, 295 "spherical": _estimate_gaussian_covariances_spherical, 296 }[covariance_type](resp, X, nk, means, reg_covar) 297 return nk, means, covariances 298 299 300def _compute_precision_cholesky(covariances, covariance_type): 301 """Compute the Cholesky decomposition of the precisions. 302 303 Parameters 304 ---------- 305 covariances : array-like 306 The covariance matrix of the current components. 307 The shape depends of the covariance_type. 308 309 covariance_type : {'full', 'tied', 'diag', 'spherical'} 310 The type of precision matrices. 311 312 Returns 313 ------- 314 precisions_cholesky : array-like 315 The cholesky decomposition of sample precisions of the current 316 components. The shape depends of the covariance_type. 317 """ 318 estimate_precision_error_message = ( 319 "Fitting the mixture model failed because some components have " 320 "ill-defined empirical covariance (for instance caused by singleton " 321 "or collapsed samples). Try to decrease the number of components, " 322 "or increase reg_covar." 323 ) 324 325 if covariance_type == "full": 326 n_components, n_features, _ = covariances.shape 327 precisions_chol = np.empty((n_components, n_features, n_features)) 328 for k, covariance in enumerate(covariances): 329 try: 330 cov_chol = linalg.cholesky(covariance, lower=True) 331 except linalg.LinAlgError: 332 raise ValueError(estimate_precision_error_message) 333 precisions_chol[k] = linalg.solve_triangular( 334 cov_chol, np.eye(n_features), lower=True 335 ).T 336 elif covariance_type == "tied": 337 _, n_features = covariances.shape 338 try: 339 cov_chol = linalg.cholesky(covariances, lower=True) 340 except linalg.LinAlgError: 341 raise ValueError(estimate_precision_error_message) 342 precisions_chol = linalg.solve_triangular( 343 cov_chol, np.eye(n_features), lower=True 344 ).T 345 else: 346 if np.any(np.less_equal(covariances, 0.0)): 347 raise ValueError(estimate_precision_error_message) 348 precisions_chol = 1.0 / np.sqrt(covariances) 349 return precisions_chol 350 351 352############################################################################### 353# Gaussian mixture probability estimators 354def _compute_log_det_cholesky(matrix_chol, covariance_type, n_features): 355 """Compute the log-det of the cholesky decomposition of matrices. 356 357 Parameters 358 ---------- 359 matrix_chol : array-like 360 Cholesky decompositions of the matrices. 361 'full' : shape of (n_components, n_features, n_features) 362 'tied' : shape of (n_features, n_features) 363 'diag' : shape of (n_components, n_features) 364 'spherical' : shape of (n_components,) 365 366 covariance_type : {'full', 'tied', 'diag', 'spherical'} 367 368 n_features : int 369 Number of features. 370 371 Returns 372 ------- 373 log_det_precision_chol : array-like of shape (n_components,) 374 The determinant of the precision matrix for each component. 375 """ 376 if covariance_type == "full": 377 n_components, _, _ = matrix_chol.shape 378 log_det_chol = np.sum( 379 np.log(matrix_chol.reshape(n_components, -1)[:, :: n_features + 1]), 1 380 ) 381 382 elif covariance_type == "tied": 383 log_det_chol = np.sum(np.log(np.diag(matrix_chol))) 384 385 elif covariance_type == "diag": 386 log_det_chol = np.sum(np.log(matrix_chol), axis=1) 387 388 else: 389 log_det_chol = n_features * (np.log(matrix_chol)) 390 391 return log_det_chol 392 393 394def _estimate_log_gaussian_prob(X, means, precisions_chol, covariance_type): 395 """Estimate the log Gaussian probability. 396 397 Parameters 398 ---------- 399 X : array-like of shape (n_samples, n_features) 400 401 means : array-like of shape (n_components, n_features) 402 403 precisions_chol : array-like 404 Cholesky decompositions of the precision matrices. 405 'full' : shape of (n_components, n_features, n_features) 406 'tied' : shape of (n_features, n_features) 407 'diag' : shape of (n_components, n_features) 408 'spherical' : shape of (n_components,) 409 410 covariance_type : {'full', 'tied', 'diag', 'spherical'} 411 412 Returns 413 ------- 414 log_prob : array, shape (n_samples, n_components) 415 """ 416 n_samples, n_features = X.shape 417 n_components, _ = means.shape 418 # The determinant of the precision matrix from the Cholesky decomposition 419 # corresponds to the negative half of the determinant of the full precision 420 # matrix. 421 # In short: det(precision_chol) = - det(precision) / 2 422 log_det = _compute_log_det_cholesky(precisions_chol, covariance_type, n_features) 423 424 if covariance_type == "full": 425 log_prob = np.empty((n_samples, n_components)) 426 for k, (mu, prec_chol) in enumerate(zip(means, precisions_chol)): 427 y = np.dot(X, prec_chol) - np.dot(mu, prec_chol) 428 log_prob[:, k] = np.sum(np.square(y), axis=1) 429 430 elif covariance_type == "tied": 431 log_prob = np.empty((n_samples, n_components)) 432 for k, mu in enumerate(means): 433 y = np.dot(X, precisions_chol) - np.dot(mu, precisions_chol) 434 log_prob[:, k] = np.sum(np.square(y), axis=1) 435 436 elif covariance_type == "diag": 437 precisions = precisions_chol ** 2 438 log_prob = ( 439 np.sum((means ** 2 * precisions), 1) 440 - 2.0 * np.dot(X, (means * precisions).T) 441 + np.dot(X ** 2, precisions.T) 442 ) 443 444 elif covariance_type == "spherical": 445 precisions = precisions_chol ** 2 446 log_prob = ( 447 np.sum(means ** 2, 1) * precisions 448 - 2 * np.dot(X, means.T * precisions) 449 + np.outer(row_norms(X, squared=True), precisions) 450 ) 451 # Since we are using the precision of the Cholesky decomposition, 452 # `- 0.5 * log_det_precision` becomes `+ log_det_precision_chol` 453 return -0.5 * (n_features * np.log(2 * np.pi) + log_prob) + log_det 454 455 456class GaussianMixture(BaseMixture): 457 """Gaussian Mixture. 458 459 Representation of a Gaussian mixture model probability distribution. 460 This class allows to estimate the parameters of a Gaussian mixture 461 distribution. 462 463 Read more in the :ref:`User Guide <gmm>`. 464 465 .. versionadded:: 0.18 466 467 Parameters 468 ---------- 469 n_components : int, default=1 470 The number of mixture components. 471 472 covariance_type : {'full', 'tied', 'diag', 'spherical'}, default='full' 473 String describing the type of covariance parameters to use. 474 Must be one of: 475 476 - 'full': each component has its own general covariance matrix. 477 - 'tied': all components share the same general covariance matrix. 478 - 'diag': each component has its own diagonal covariance matrix. 479 - 'spherical': each component has its own single variance. 480 481 tol : float, default=1e-3 482 The convergence threshold. EM iterations will stop when the 483 lower bound average gain is below this threshold. 484 485 reg_covar : float, default=1e-6 486 Non-negative regularization added to the diagonal of covariance. 487 Allows to assure that the covariance matrices are all positive. 488 489 max_iter : int, default=100 490 The number of EM iterations to perform. 491 492 n_init : int, default=1 493 The number of initializations to perform. The best results are kept. 494 495 init_params : {'kmeans', 'random'}, default='kmeans' 496 The method used to initialize the weights, the means and the 497 precisions. 498 Must be one of:: 499 500 'kmeans' : responsibilities are initialized using kmeans. 501 'random' : responsibilities are initialized randomly. 502 503 weights_init : array-like of shape (n_components, ), default=None 504 The user-provided initial weights. 505 If it is None, weights are initialized using the `init_params` method. 506 507 means_init : array-like of shape (n_components, n_features), default=None 508 The user-provided initial means, 509 If it is None, means are initialized using the `init_params` method. 510 511 precisions_init : array-like, default=None 512 The user-provided initial precisions (inverse of the covariance 513 matrices). 514 If it is None, precisions are initialized using the 'init_params' 515 method. 516 The shape depends on 'covariance_type':: 517 518 (n_components,) if 'spherical', 519 (n_features, n_features) if 'tied', 520 (n_components, n_features) if 'diag', 521 (n_components, n_features, n_features) if 'full' 522 523 random_state : int, RandomState instance or None, default=None 524 Controls the random seed given to the method chosen to initialize the 525 parameters (see `init_params`). 526 In addition, it controls the generation of random samples from the 527 fitted distribution (see the method `sample`). 528 Pass an int for reproducible output across multiple function calls. 529 See :term:`Glossary <random_state>`. 530 531 warm_start : bool, default=False 532 If 'warm_start' is True, the solution of the last fitting is used as 533 initialization for the next call of fit(). This can speed up 534 convergence when fit is called several times on similar problems. 535 In that case, 'n_init' is ignored and only a single initialization 536 occurs upon the first call. 537 See :term:`the Glossary <warm_start>`. 538 539 verbose : int, default=0 540 Enable verbose output. If 1 then it prints the current 541 initialization and each iteration step. If greater than 1 then 542 it prints also the log probability and the time needed 543 for each step. 544 545 verbose_interval : int, default=10 546 Number of iteration done before the next print. 547 548 Attributes 549 ---------- 550 weights_ : array-like of shape (n_components,) 551 The weights of each mixture components. 552 553 means_ : array-like of shape (n_components, n_features) 554 The mean of each mixture component. 555 556 covariances_ : array-like 557 The covariance of each mixture component. 558 The shape depends on `covariance_type`:: 559 560 (n_components,) if 'spherical', 561 (n_features, n_features) if 'tied', 562 (n_components, n_features) if 'diag', 563 (n_components, n_features, n_features) if 'full' 564 565 precisions_ : array-like 566 The precision matrices for each component in the mixture. A precision 567 matrix is the inverse of a covariance matrix. A covariance matrix is 568 symmetric positive definite so the mixture of Gaussian can be 569 equivalently parameterized by the precision matrices. Storing the 570 precision matrices instead of the covariance matrices makes it more 571 efficient to compute the log-likelihood of new samples at test time. 572 The shape depends on `covariance_type`:: 573 574 (n_components,) if 'spherical', 575 (n_features, n_features) if 'tied', 576 (n_components, n_features) if 'diag', 577 (n_components, n_features, n_features) if 'full' 578 579 precisions_cholesky_ : array-like 580 The cholesky decomposition of the precision matrices of each mixture 581 component. A precision matrix is the inverse of a covariance matrix. 582 A covariance matrix is symmetric positive definite so the mixture of 583 Gaussian can be equivalently parameterized by the precision matrices. 584 Storing the precision matrices instead of the covariance matrices makes 585 it more efficient to compute the log-likelihood of new samples at test 586 time. The shape depends on `covariance_type`:: 587 588 (n_components,) if 'spherical', 589 (n_features, n_features) if 'tied', 590 (n_components, n_features) if 'diag', 591 (n_components, n_features, n_features) if 'full' 592 593 converged_ : bool 594 True when convergence was reached in fit(), False otherwise. 595 596 n_iter_ : int 597 Number of step used by the best fit of EM to reach the convergence. 598 599 lower_bound_ : float 600 Lower bound value on the log-likelihood (of the training data with 601 respect to the model) of the best fit of EM. 602 603 n_features_in_ : int 604 Number of features seen during :term:`fit`. 605 606 .. versionadded:: 0.24 607 608 feature_names_in_ : ndarray of shape (`n_features_in_`,) 609 Names of features seen during :term:`fit`. Defined only when `X` 610 has feature names that are all strings. 611 612 .. versionadded:: 1.0 613 614 See Also 615 -------- 616 BayesianGaussianMixture : Gaussian mixture model fit with a variational 617 inference. 618 619 Examples 620 -------- 621 >>> import numpy as np 622 >>> from sklearn.mixture import GaussianMixture 623 >>> X = np.array([[1, 2], [1, 4], [1, 0], [10, 2], [10, 4], [10, 0]]) 624 >>> gm = GaussianMixture(n_components=2, random_state=0).fit(X) 625 >>> gm.means_ 626 array([[10., 2.], 627 [ 1., 2.]]) 628 >>> gm.predict([[0, 0], [12, 3]]) 629 array([1, 0]) 630 """ 631 632 def __init__( 633 self, 634 n_components=1, 635 *, 636 covariance_type="full", 637 tol=1e-3, 638 reg_covar=1e-6, 639 max_iter=100, 640 n_init=1, 641 init_params="kmeans", 642 weights_init=None, 643 means_init=None, 644 precisions_init=None, 645 random_state=None, 646 warm_start=False, 647 verbose=0, 648 verbose_interval=10, 649 ): 650 super().__init__( 651 n_components=n_components, 652 tol=tol, 653 reg_covar=reg_covar, 654 max_iter=max_iter, 655 n_init=n_init, 656 init_params=init_params, 657 random_state=random_state, 658 warm_start=warm_start, 659 verbose=verbose, 660 verbose_interval=verbose_interval, 661 ) 662 663 self.covariance_type = covariance_type 664 self.weights_init = weights_init 665 self.means_init = means_init 666 self.precisions_init = precisions_init 667 668 def _check_parameters(self, X): 669 """Check the Gaussian mixture parameters are well defined.""" 670 _, n_features = X.shape 671 if self.covariance_type not in ["spherical", "tied", "diag", "full"]: 672 raise ValueError( 673 "Invalid value for 'covariance_type': %s " 674 "'covariance_type' should be in " 675 "['spherical', 'tied', 'diag', 'full']" 676 % self.covariance_type 677 ) 678 679 if self.weights_init is not None: 680 self.weights_init = _check_weights(self.weights_init, self.n_components) 681 682 if self.means_init is not None: 683 self.means_init = _check_means( 684 self.means_init, self.n_components, n_features 685 ) 686 687 if self.precisions_init is not None: 688 self.precisions_init = _check_precisions( 689 self.precisions_init, 690 self.covariance_type, 691 self.n_components, 692 n_features, 693 ) 694 695 def _initialize(self, X, resp): 696 """Initialization of the Gaussian mixture parameters. 697 698 Parameters 699 ---------- 700 X : array-like of shape (n_samples, n_features) 701 702 resp : array-like of shape (n_samples, n_components) 703 """ 704 n_samples, _ = X.shape 705 706 weights, means, covariances = _estimate_gaussian_parameters( 707 X, resp, self.reg_covar, self.covariance_type 708 ) 709 weights /= n_samples 710 711 self.weights_ = weights if self.weights_init is None else self.weights_init 712 self.means_ = means if self.means_init is None else self.means_init 713 714 if self.precisions_init is None: 715 self.covariances_ = covariances 716 self.precisions_cholesky_ = _compute_precision_cholesky( 717 covariances, self.covariance_type 718 ) 719 elif self.covariance_type == "full": 720 self.precisions_cholesky_ = np.array( 721 [ 722 linalg.cholesky(prec_init, lower=True) 723 for prec_init in self.precisions_init 724 ] 725 ) 726 elif self.covariance_type == "tied": 727 self.precisions_cholesky_ = linalg.cholesky( 728 self.precisions_init, lower=True 729 ) 730 else: 731 self.precisions_cholesky_ = np.sqrt(self.precisions_init) 732 733 def _m_step(self, X, log_resp): 734 """M step. 735 736 Parameters 737 ---------- 738 X : array-like of shape (n_samples, n_features) 739 740 log_resp : array-like of shape (n_samples, n_components) 741 Logarithm of the posterior probabilities (or responsibilities) of 742 the point of each sample in X. 743 """ 744 n_samples, _ = X.shape 745 self.weights_, self.means_, self.covariances_ = _estimate_gaussian_parameters( 746 X, np.exp(log_resp), self.reg_covar, self.covariance_type 747 ) 748 self.weights_ /= n_samples 749 self.precisions_cholesky_ = _compute_precision_cholesky( 750 self.covariances_, self.covariance_type 751 ) 752 753 def _estimate_log_prob(self, X): 754 return _estimate_log_gaussian_prob( 755 X, self.means_, self.precisions_cholesky_, self.covariance_type 756 ) 757 758 def _estimate_log_weights(self): 759 return np.log(self.weights_) 760 761 def _compute_lower_bound(self, _, log_prob_norm): 762 return log_prob_norm 763 764 def _get_parameters(self): 765 return ( 766 self.weights_, 767 self.means_, 768 self.covariances_, 769 self.precisions_cholesky_, 770 ) 771 772 def _set_parameters(self, params): 773 ( 774 self.weights_, 775 self.means_, 776 self.covariances_, 777 self.precisions_cholesky_, 778 ) = params 779 780 # Attributes computation 781 _, n_features = self.means_.shape 782 783 if self.covariance_type == "full": 784 self.precisions_ = np.empty(self.precisions_cholesky_.shape) 785 for k, prec_chol in enumerate(self.precisions_cholesky_): 786 self.precisions_[k] = np.dot(prec_chol, prec_chol.T) 787 788 elif self.covariance_type == "tied": 789 self.precisions_ = np.dot( 790 self.precisions_cholesky_, self.precisions_cholesky_.T 791 ) 792 else: 793 self.precisions_ = self.precisions_cholesky_ ** 2 794 795 def _n_parameters(self): 796 """Return the number of free parameters in the model.""" 797 _, n_features = self.means_.shape 798 if self.covariance_type == "full": 799 cov_params = self.n_components * n_features * (n_features + 1) / 2.0 800 elif self.covariance_type == "diag": 801 cov_params = self.n_components * n_features 802 elif self.covariance_type == "tied": 803 cov_params = n_features * (n_features + 1) / 2.0 804 elif self.covariance_type == "spherical": 805 cov_params = self.n_components 806 mean_params = n_features * self.n_components 807 return int(cov_params + mean_params + self.n_components - 1) 808 809 def bic(self, X): 810 """Bayesian information criterion for the current model on the input X. 811 812 You can refer to this :ref:`mathematical section <aic_bic>` for more 813 details regarding the formulation of the BIC used. 814 815 Parameters 816 ---------- 817 X : array of shape (n_samples, n_dimensions) 818 The input samples. 819 820 Returns 821 ------- 822 bic : float 823 The lower the better. 824 """ 825 return -2 * self.score(X) * X.shape[0] + self._n_parameters() * np.log( 826 X.shape[0] 827 ) 828 829 def aic(self, X): 830 """Akaike information criterion for the current model on the input X. 831 832 You can refer to this :ref:`mathematical section <aic_bic>` for more 833 details regarding the formulation of the AIC used. 834 835 Parameters 836 ---------- 837 X : array of shape (n_samples, n_dimensions) 838 The input samples. 839 840 Returns 841 ------- 842 aic : float 843 The lower the better. 844 """ 845 return -2 * self.score(X) * X.shape[0] + 2 * self._n_parameters() 846