93scikit-learn 机器学习入门实战--高斯混合模型

高斯混合模型

高斯混合模型

高斯混合模型是多个高斯概率密度函数(二维即正态分布曲线)的线性组合。而混合高斯的曲线是由若干个单高斯函数叠加而成的,即任何一个曲线,无论多么复杂,我们都可以用若干个高斯曲线来无限逼近它,这就是高斯混合模型的基本思想。
在最简单的情况下,GMM 可用于以与 K-Means 相同的方式聚类,我们还是使用前几个实验中也用到的简单可聚类的数据集来查看 GMM 效果:

from sklearn.datasets.samples_generator import make_blobs
from sklearn.mixture import GaussianMixture as GMM
import matplotlib.pyplot as plt
%matplotlib inline

# 导入高斯混合估计器
X, y_true = make_blobs(n_samples=400, centers=4,
                       cluster_std=0.60, random_state=0)  # 数据生成

# n_components 表示聚类数
gmm = GMM(n_components=4).fit(X)
labels = gmm.predict(X)  # 得到预测标签
plt.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis')  # 绘制聚类彩色图

GMM作为密度估计

尽管 GMM 通常被归类为聚类算法,但从根本上说它是一种密度估计算法。也就是说,GMM 在某些数据上的结果更适合作为描述数据分布的生成概率模型。
例如,假设我们在特定分布中有一些一维数据:

import numpy as np

plt.style.use('seaborn')  # 样式美化

np.random.seed(2)  # 随机数种子为 2
x = np.concatenate([np.random.normal(0, 2, 2000),
                    np.random.normal(5, 5, 2000),
                    np.random.normal(3, 0.5, 600)])  # 数组拼接
# 绘制直方图
plt.hist(x, 80, density=True)
plt.xlim(-10, 20)

我们用高斯混合模型得到上图的近似密度:

# 导入高斯混合估计器
from sklearn.mixture import GaussianMixture as GMM

X = x[:, np.newaxis]  # 增加一个维度成二维
clf = GMM(4, max_iter=500, random_state=3).fit(X)
# 组件(高斯分布)个数为 4,最大迭代次数 100

xpdf = np.linspace(-10, 20, 1000)  # 生成曲线 x 数据
density = np.array([np.exp(clf.score([[xp]])) for xp in xpdf])  # 得到模型拟合密度

plt.hist(x, 80, density=True, alpha=0.5)
plt.plot(xpdf, density, '-r')
plt.xlim(-10, 20)

上图中的红线即是我们通过 4 个高斯分布混合模拟出的密度。我们将那 4 个高斯分布绘制出来,可以直观上看看是怎么混合的:

from scipy import stats

plt.hist(x, 80, density=True, alpha=0.3)  # 原数据直方图
plt.plot(xpdf, density, '-r')  # 混合高斯模型估计密度

# 绘制 4 个高斯函数
for i in range(clf.n_components):
    pdf = clf.weights_[i] * stats.norm(clf.means_[i, 0],
                                       np.sqrt(clf.covariances_[i, 0])).pdf(xpdf)
    plt.fill(xpdf, pdf, facecolor='gray',
             edgecolor='none', alpha=0.3)  # 填充函数
plt.xlim(-10, 20)

与 K-Means 一样,高斯混合模型使用期望最大化方法拟合这些单独的高斯分布,并使用后验概率来计算加权均值和协方差。另外,该算法可证明收敛于最优值(尽管最优值不一定是全局的)。
GMM 组件数量
在上面的模型中,我们设定的组件值(高斯分布个数)为 4,是通过原数据直方图大概判断的,你可以试试将组件个数参数改得更大一点,试试拟合效果。理论上该参数越大,拟合出的曲线越接近直方图趋势变化,但此参数并不是越大越好,容易过拟合。
给定一个模型,我们可以使用 赤池信息准则(Akaike Information Criterion,AIC)或者 贝叶斯信息准则(Bayesian Information Criterion,BIC)来评估其对数据的拟合程度,scikit-learn 的 GMM 估计器包含计算这两者的内置方法:

print(clf.bic(X))  # 计算 BIC 值
print(clf.aic(X))  # 计算 AIC 值

让我们看看在上面的数据集中,使用 AIC 和 BIC 确定 GMM 组件数量:

# 组件数从 1-9 的模型
n_estimators = np.arange(1, 10)

clfs = [GMM(n, max_iter=1000).fit(X) for n in n_estimators]
bics = [clf.bic(X) for clf in clfs]
aics = [clf.aic(X) for clf in clfs]

plt.plot(n_estimators, bics, label='BIC')
plt.plot(n_estimators, aics, label='AIC')
plt.legend()

从图中看到,理论上 AIC 和 BIC 值越小越好,但是如果值变化不大的情况下,模型复杂度越小的模型越优,所以 GMM 组件数确定在 5-7 是最合适的。

GMM 用于异常值检测

GMM 是所谓的生成模型:它是一个概率模型,可以从中生成数据集。数据生成模型的一个应用是异常值检测:我们可以简单地评估生成模型下每个点的可能性,可能性较低的点(取决于您自己的偏差/方差偏好)可以标记为异常值。
让我们通过定义带有一些异常值的新数据集来了解一下:

np.random.seed(0)
print(len(x))

# 4600 个数中生成 20 个异常值位数并排序
true_outliers = np.sort(np.random.randint(0, len(x), 20))
y = x.copy()  # 得到 x 的浅复制
y[true_outliers] += 50 * np.random.randn(20)
# 将20个原数据加上 0-50 的正态分布的值变成异常值

# 用加上了20个异常值的数据进行模型拟合
clf = GMM(4, max_iter=500, random_state=0).fit(y[:, np.newaxis])
xpdf = np.linspace(-10, 20, 1000)
density_noise = np.array([np.exp(clf.score([[xp]]))
                          for xp in xpdf])  # 得到带有噪声的密度估计值

plt.hist(y, 80, density=True, alpha=0.5)  # 绘制直方图
plt.plot(xpdf, density_noise, '-r')  # 红线绘制噪声密度估计
plt.xlim(-15, 30)

需要注意的是,生成的 true_outliers 是 20 个标为异常值的点在 4600 个数中的位置,是 [0-4600) 的 20 个整数。
现在让我们评估模型下每个点的对数似然,并将它们作为 y 的函数进行绘制:

# 得到对数似然值
log_likelihood = np.array([clf.score_samples([[yy]]) for yy in y])
plt.plot(y, log_likelihood, '.k')  # 注意横坐标是 y 值

上图是我们绘制的带有噪声的 y 值和其对应的对数似然值,可以看到对数似然值 < -8 的点比较稀疏,这里我们将对数似然值 < -9 判断为异常值:

# 得到检测异常值位数
detected_outliers = np.where(log_likelihood < -9)[0]

print("true outliers:")  # 查看真实异常值位数
print(true_outliers)
print("\ndetected outliers:")  # 查看检测异常值位数
print(detected_outliers)

该算法错过了其中的一些点,这是可以预料的,因为异常值 y 值的生成加上了 0-50 的正态分布值,可能其噪声添加接近于 0。
以下是遗漏的异常值位数:

set(true_outliers) - set(detected_outliers)

以下是被虚假标记为异常值的非异常值位数:

set(detected_outliers) - set(true_outliers)

其他密度估计器

还有一些有用的密度估计器是核密度估计,可通过 sklearn.neighbors.KernelDensity 获得。 在某些方面,这可以看作是 GMM 的一般化,其中在每个训练点的位置都放置了一个高斯。

# 导入核密度估计器
from sklearn.neighbors import KernelDensity

# bandwidth = 0.15
kde = KernelDensity(0.15).fit(x[:, None])
density_kde = np.exp(kde.score_samples(xpdf[:, None]))  # 得到密度估计值

plt.hist(x, 80, density=True, alpha=0.5)  # 绘制直方图
plt.plot(xpdf, density, '-b', label='GMM')  # 蓝线绘制 GMM 密度估计曲线
plt.plot(xpdf, density_kde, '-r', label='KDE')  # 红线绘制核密度估计曲线
plt.xlim(-10, 20)
plt.legend()

所有这些密度估计模型都可以看作是数据的生成模型:也就是说,该模型告诉我们如何创建更多适合模型的数据。

你可能感兴趣的:(93scikit-learn 机器学习入门实战--高斯混合模型)