在Python中使用具有固定协方差的高斯混合

时间:2018-01-29 13:14:35

标签: python machine-learning scikit-learn gmm

我有一些2D数据(GPS数据),我知道它们与具有特征标准偏差(与GPS样本的固有噪声成比例)的高斯类似的簇(停止位置)。下图显示了我期望有两个这样的聚类的样本。图像宽25米,高13米。

enter image description here

sklearn模块有一个函数sklearn.mixture.GaussianMixture,它允许您将高斯混合物拟合到数据中。该函数有一个参数covariance_type,使您可以假设有关高斯形状的不同内容。例如,您可以使用'tied'参数假设它们是统一的。

然而,似乎不能直接假设协方差矩阵保持不变。从sklearn源代码来看,做一个启用此修改的修改似乎微不足道,但是使用允许此更新的拉取请求感觉有点过分(我也不想在{{中意外添加错误) 1}})。是否有更好的方法将混合拟合到数据中,其中每个高斯的协方差矩阵是固定的?

我想假设每个组件的SD应保持恒定在3米左右,因为这大致是我的GPS样本的噪音水平。

3 个答案:

答案 0 :(得分:3)

我认为最好的选择是"roll your own" GMM模型,它定义了一个新的scikit-learn类,它继承自GaussianMixture并覆盖方法以获得你想要的行为。这样你就可以自己实现一个实现,而不必更改scikit-learn代码(并创建一个pull-request)。

可能有用的另一个选项是查看scikit-learn中的Bayesian version of GMM。您可能能够为协方差矩阵设置先验,以便协方差是固定的。它似乎使用Wishart distribution作为协方差的先验。但是,我对这个发行版不太熟悉,无法帮助你。

答案 1 :(得分:3)

编写自己的EM algorithm实现非常简单。它也会让你对这个过程有一个很好的直觉。我假设协方差是已知的,并且组件的先验概率是相等的,并且仅适合意味着。

该类看起来像这样(在Python 3中):

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal

class FixedCovMixture:
    """ The model to estimate gaussian mixture with fixed covariance matrix. """
    def __init__(self, n_components, cov, max_iter=100, random_state=None, tol=1e-10):
        self.n_components = n_components
        self.cov = cov
        self.random_state = random_state
        self.max_iter = max_iter
        self.tol=tol

    def fit(self, X):
        # initialize the process:
        np.random.seed(self.random_state)
        n_obs, n_features = X.shape
        self.mean_ = X[np.random.choice(n_obs, size=self.n_components)]
        # make EM loop until convergence
        i = 0
        for i in range(self.max_iter):
            new_centers = self.updated_centers(X)
            if np.sum(np.abs(new_centers-self.mean_)) < self.tol:
                break
            else:
                self.mean_ = new_centers
        self.n_iter_ = i

    def updated_centers(self, X):
        """ A single iteration """
        # E-step: estimate probability of each cluster given cluster centers
        cluster_posterior = self.predict_proba(X)
        # M-step: update cluster centers as weighted average of observations
        weights = (cluster_posterior.T / cluster_posterior.sum(axis=1)).T
        new_centers = np.dot(weights, X)
        return new_centers


    def predict_proba(self, X):
        likelihood = np.stack([multivariate_normal.pdf(X, mean=center, cov=self.cov) 
                               for center in self.mean_])
        cluster_posterior = (likelihood / likelihood.sum(axis=0))
        return cluster_posterior

    def predict(self, X):
        return np.argmax(self.predict_proba(X), axis=0)

在像你这样的数据上,模型会快速收敛:

np.random.seed(1)
X = np.random.normal(size=(100,2), scale=3)
X[50:] += (10, 5)

model = FixedCovMixture(2, cov=[[3,0],[0,3]], random_state=1)
model.fit(X)
print(model.n_iter_, 'iterations')
print(model.mean_)

plt.scatter(X[:,0], X[:,1], s=10, c=model.predict(X))
plt.scatter(model.mean_[:,0], model.mean_[:,1], s=100, c='k')
plt.axis('equal')
plt.show();

和输出

11 iterations
[[9.92301067 4.62282807]
 [0.09413883 0.03527411]]

您可以看到估算的中心((9.9, 4.6)(0.09, 0.03))靠近真正的中心((10, 5)(0, 0))。

enter image description here

答案 2 :(得分:1)

首先,您可以使用spherical选项,它会为每个组件提供单个方差值。通过这种方式,您可以检查自己,如果收到的差异值太大,那么就会出现问题。

如果您想要预设方差,则问题会退化为只找到组件的最佳中心。例如,您可以使用k-means来完成此操作。如果您不知道组件的数量,则可以扫描所有逻辑值(例如1到20)并评估拟合错误的减量。或者,您可以优化自己的EM功能,同时查找中心和组件数量。