GMMを用いたクラスタリング(変分ベイズ編)

前回に続いてクラスタリングについてです。 前回は、GMMを用いたクラスタリングを紹介しました。今回も同じモデルを用いてクラスタリングするのですが、前回と違うところはパラメータの最適化に「変分ベイズ」を用いるところです。 本記事では、「変分ベイズ」という手法について焦点を当てて説明したいと思います。

変分ベイズとは?

変分ベイズは、EMアルゴリズムを拡張したような手法です。最適化するパラメータに対して事前分布を仮定し、パラメータの分布を求めます。 しかし、計算量などの点からパラメータの事前分布を厳密に求めることができない場合が多く、近似分布を考えます。近似分布を求める際には、平均場近似という考え方を用いて近似分布を導出します。 詳しい内容は、PRMLなどの本を参照して下さい。(個人的には「しくみがわかるベイズ統計と機械学習」という本がおすすめです)

変分ベイズ(GMM)

では、変分ベイズをGMMに適用してみましょう。 GMMのパラメータは、主に以下の3つです。 ・平均パラメータμ ・分散パラメータΣ(もしくは精度パラメータΛ) ・混合比π EMアルゴリズムでは、これらのパラメータを最適化しますが(パラメータは何かしらの数値になる)、変分ベイズではこれらのパラメータの分布を考えるため、結局パラメータ分布のパラメータを更新し最適化することになります。 具体的に、各パラメータの近似分布は、πはディリクレ分布、μとΛの同時分布はガウス-ウィシャート分布になります(パラメータが1次元の場合はガウス-ガンマ分布)。計算過程など詳しいことは紹介した本などを参考にして下さい。

サンプルプログラム

今回のサンプルコードも前回と同様scikit-learnを使っていきたい思います。サンプルコードは以下です。

In [1]:
import sys
sys.version
Out[1]:
'3.7.4 (default, Aug 13 2019, 15:17:50) \n[Clang 4.0.1 (tags/RELEASE_401/final)]'
In [2]:
import sklearn
print(sklearn.__version__)
0.21.3
In [3]:
import numpy as np
from sklearn.datasets import load_iris
iris = load_iris()
iris.data.shape
Out[3]:
(150, 4)
In [4]:
# 正解ラベル
iris.target
Out[4]:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
In [5]:
import sklearn.mixture
In [6]:
# gmmインスタンスを作成
# gmm = sklearn.mixture.GaussianMixture(n_components=3, covariance_type="full")
# 前回のコードだと上のようになっている
gmm_vi = sklearn.mixture.BayesianGaussianMixture(n_components=3, covariance_type="full")
In [7]:
gmm_vi.fit(iris.data)
Out[7]:
BayesianGaussianMixture(covariance_prior=None, covariance_type='full',
                        degrees_of_freedom_prior=None, init_params='kmeans',
                        max_iter=100, mean_precision_prior=None,
                        mean_prior=None, n_components=3, n_init=1,
                        random_state=None, reg_covar=1e-06, tol=0.001,
                        verbose=0, verbose_interval=10, warm_start=False,
                        weight_concentration_prior=None,
                        weight_concentration_prior_type='dirichlet_process')
In [8]:
estimate_label = gmm_vi.fit_predict(iris.data)
estimate_label
Out[8]:
array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1,
       0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1,
       0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
In [ ]:
 

今回は、インスタンスを2つ作りました。1つは、n_componentsが3の場合。もう1つはn_componentsが10の場合です。n_componentsはクラスタ数を表しています。 前回のGaussianMixtureではクラスタ数は既知としてインスタンスを作成しましたが、今回は3つとわかっているにも関わらず、クラスタ数を10にして実験してみました。結果は、ラベル数が5つとなってしまっていますが、10個よりは減っています。これが変分ベイズの良いところです。 変分ベイズを用いると、あらかじめクラスタ数がわかっていなくてもいらないクラスタはある程度自動で削減してくれます。(今回は失敗していますが…)初期値などパラメータをいじればもっと良い推定ができるかもしれません。 以上が変分ベイズを用いたクラスタリングです!とても面白い手法ですので是非試してみてください。