在 Python 中拟合具有固定协方差的高斯混合
Fit mixture of Gaussians with fixed covariance in Python
我有一些带有簇(停止位置)的二维数据(GPS 数据),我知道这些数据类似于具有特征标准差(与 GPS 样本的固有噪声成比例)的高斯分布。下图可视化了一个样本,我希望它有两个这样的集群。图片宽25米,高13米。
sklearn
模块有一个函数 sklearn.mixture.GaussianMixture
,它允许您将混合高斯分布拟合到数据中。该函数有一个参数 covariance_type
,它使您能够对高斯形状做出不同的假设。例如,您可以使用 'tied'
参数假设它们是统一的。
但是,假设协方差矩阵保持不变似乎并不直接可行。从 sklearn
源代码来看,进行修改以启用此功能似乎微不足道,但使用允许此功能的更新发出拉取请求感觉有点过分(而且我不想不小心在 sklearn
).在每个高斯的协方差矩阵固定的情况下,是否有更好的方法将混合拟合到数据?
我想假设每个分量的 SD 应该保持在 3 米左右,因为这大致是我的 GPS 样本的噪声水平。
我认为最好的选择是 "roll your own" GMM 模型,通过定义一个新的 scikit-learn class 继承自 GaussianMixture
并覆盖方法以获得您想要的行为.这样您就可以自己实现,而不必更改 scikit-learn 代码(并创建拉取请求)。
另一个可能有效的选项是将 Bayesian version of GMM in scikit-learn. You might be able to set the prior for the covariance matrix so that the covariance is fixed. It seems to use the Wishart distribution 视为协方差的先验。但是我对这个发行版还不够熟悉,无法为您提供更多帮助。
首先,您可以使用 spherical
选项,这将为您提供每个组件的单个方差值。这样你就可以检查自己,如果接收到的方差值差异太大,那就是出了问题。
在您想预设方差的情况下,您的问题退化为只为您的组件找到最佳中心。例如,您可以使用 k-means
来完成。如果您不知道组件的数量,您可以扫描所有逻辑值(如 1 到 20)并评估拟合误差的递减量。或者你可以优化你自己的 EM 函数,同时找到中心和组件的数量。
编写自己的 EM algorithm 实现非常简单。它还会让您对该过程有一个很好的直觉。我假设协方差是已知的,并且组件的先验概率是相等的,并且只适合意味着。
class 看起来像这样(在 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)
)。
我有一些带有簇(停止位置)的二维数据(GPS 数据),我知道这些数据类似于具有特征标准差(与 GPS 样本的固有噪声成比例)的高斯分布。下图可视化了一个样本,我希望它有两个这样的集群。图片宽25米,高13米。
sklearn
模块有一个函数 sklearn.mixture.GaussianMixture
,它允许您将混合高斯分布拟合到数据中。该函数有一个参数 covariance_type
,它使您能够对高斯形状做出不同的假设。例如,您可以使用 'tied'
参数假设它们是统一的。
但是,假设协方差矩阵保持不变似乎并不直接可行。从 sklearn
源代码来看,进行修改以启用此功能似乎微不足道,但使用允许此功能的更新发出拉取请求感觉有点过分(而且我不想不小心在 sklearn
).在每个高斯的协方差矩阵固定的情况下,是否有更好的方法将混合拟合到数据?
我想假设每个分量的 SD 应该保持在 3 米左右,因为这大致是我的 GPS 样本的噪声水平。
我认为最好的选择是 "roll your own" GMM 模型,通过定义一个新的 scikit-learn class 继承自 GaussianMixture
并覆盖方法以获得您想要的行为.这样您就可以自己实现,而不必更改 scikit-learn 代码(并创建拉取请求)。
另一个可能有效的选项是将 Bayesian version of GMM in scikit-learn. You might be able to set the prior for the covariance matrix so that the covariance is fixed. It seems to use the Wishart distribution 视为协方差的先验。但是我对这个发行版还不够熟悉,无法为您提供更多帮助。
首先,您可以使用 spherical
选项,这将为您提供每个组件的单个方差值。这样你就可以检查自己,如果接收到的方差值差异太大,那就是出了问题。
在您想预设方差的情况下,您的问题退化为只为您的组件找到最佳中心。例如,您可以使用 k-means
来完成。如果您不知道组件的数量,您可以扫描所有逻辑值(如 1 到 20)并评估拟合误差的递减量。或者你可以优化你自己的 EM 函数,同时找到中心和组件的数量。
编写自己的 EM algorithm 实现非常简单。它还会让您对该过程有一个很好的直觉。我假设协方差是已知的,并且组件的先验概率是相等的,并且只适合意味着。
class 看起来像这样(在 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)
)。