k-means是用原型向量来刻画聚类,高斯混合(Mixture-of-Gaussian)聚类采用概率模型来表达聚类原型。
不一样参数下,高斯分布如下:
对于多元高斯分布, n n 维样本空间 X X 中的随机向量 x x ,概率密度函数为
高斯混合分布为:
下面是一个高斯模型学习过程简图,先有个感性的认识:
那么该怎么求解各个权重系数,常用的方法是EM算法。
先看一个抛硬币问题,如果我们有A
和B
两个不均匀硬币,选择任意一个硬币抛10次(这里我们知道选择是的哪一个硬币),共计选择5次。正面记为H,背面记为T。记录实验结果,求A
和B
再抛正面向上的概率?
使用极大似然估计(Maximum likelihood)来算:
这样一看极大似然估计还是很简单的嘛~
是不是第一感觉这个问题很无解,我都不知道选择是哪个硬币,我怎么求解啊?
事实上是可以求出的。这里使用的时EM算法。
使用期望最大值(Expectation maximization,EM)算法来算:
那么先看下面这个例子:
假如现在有射击运动员甲和乙,甲乙射击靶心的概率为 θ^甲=0.9,θ^乙=0.2 θ ^ 甲 = 0.9 , θ ^ 乙 = 0.2 ,如果现在有一组实验结果为
有了上面的案例,我们再回到抛硬币的问题上,由贝叶斯公式:
有 0.45×10×12≈2.2 0.45 × 10 × 1 2 ≈ 2.2 ,故第一次实验结果平均下来,有 2.2 2.2 个A硬币正面的可能。同理可得到多次实验的平均结果。
最后相加相除得到新的 A,B A , B 抛硬币正面估计值 θ^(1)A≈0.71,θ^(1)B≈0.58 θ ^ A ( 1 ) ≈ 0.71 , θ ^ B ( 1 ) ≈ 0.58 ,这是我们第一次迭代的值(这就是一次学习过程),照着这个流程迭代多次,得到最后的估测值。
上面我们计算出每次实验中是抛 A A 或抛 B B 的概率值就是隐变量.这个过程就是EM算法的简单案例。
未观测变量的学名是“隐变量”(latent variable).令 X X 表示已观测变量集, Z Z 表示隐变量集, Θ Θ 表示模型参数,欲对 Θ Θ 做极大似然估计,则应最大化对数似然
以初始值 Θ0 Θ 0 为起点,对上式迭代执行以下步骤直到收敛:
这就是EM算法的原型。
则EM算法的两个步骤是:
EM算法使用两个步骤交替计算:第一步是期望E步,利用当前估计的参数值来计算对数似然的期望值;第二步是最大化M步,寻找能使E步产生的似然期望最大化的参数值.得到的新值重新用于E步。重复上面的过程,直到收敛得到局部最优解。
定义的高斯混合分布如下:
假设样本训练集 D={x1,x2,...,xm} D = { x 1 , x 2 , . . . , x m } 由上述混合高斯分布产生的,令随机变量 zj∈{1,2,...,k} z j ∈ { 1 , 2 , . . . , k } 表示生成样本 xj x j 的高斯混合成分,其取值未知。显然, zj z j 的先验概率 P(zj=i) P ( z j = i ) 对应于 αi(i=1,2,...,k) α i ( i = 1 , 2 , . . . , k ) .根据贝叶斯定理, zj z j 的后验分布对应于
使用的是《机器学习》(西瓜书)西瓜数据集4.0。
# coding:utf8
# 高斯混合模型 使用EM算法解算
# 数据集:《机器学习》--西瓜数据4.0 :文件watermelon4.txt
import numpy as np
import matplotlib.pyplot as plt
# 预处理数据
def loadData(filename):
dataSet = []
fr = open(filename)
for line in fr.readlines():
curLine = line.strip().split(' ')
fltLine = list(map(float, curLine))
dataSet.append(fltLine)
return dataSet
# 高斯分布的概率密度函数
def prob(x, mu, sigma):
n = np.shape(x)[1]
expOn = float(-0.5 * (x - mu) * (sigma.I) * ((x - mu).T))
divBy = pow(2 * np.pi, n / 2) * pow(np.linalg.det(sigma), 0.5) # np.linalg.det 计算矩阵的行列式
return pow(np.e, expOn) / divBy
# EM算法
def EM(dataMat, maxIter=50):
m, n = np.shape(dataMat)
# 1.初始化各高斯混合成分参数
alpha = [1 / 3, 1 / 3, 1 / 3] # 1.1初始化 alpha1=alpha2=alpha3=1/3
mu = [dataMat[5, :], dataMat[21, :], dataMat[26, :]] # 1.2初始化 mu1=x6,mu2=x22,mu3=x27
sigma = [np.mat([[0.1, 0], [0, 0.1]]) for x in range(3)] # 1.3初始化协方差矩阵
gamma = np.mat(np.zeros((m, 3)))
for i in range(maxIter):
for j in range(m):
sumAlphaMulP = 0
for k in range(3):
gamma[j, k] = alpha[k] * prob(dataMat[j, :], mu[k], sigma[k]) # 4.计算混合成分生成的后验概率,即gamma
sumAlphaMulP += gamma[j, k]
for k in range(3):
gamma[j, k] /= sumAlphaMulP
sumGamma = np.sum(gamma, axis=0)
for k in range(3):
mu[k] = np.mat(np.zeros((1, n)))
sigma[k] = np.mat(np.zeros((n, n)))
for j in range(m):
mu[k] += gamma[j, k] * dataMat[j, :]
mu[k] /= sumGamma[0, k] # 7.计算新均值向量
for j in range(m):
sigma[k] += gamma[j, k] * (dataMat[j, :] - mu[k]).T *(dataMat[j, :] - mu[k])
sigma[k] /= sumGamma[0, k] # 8. 计算新的协方差矩阵
alpha[k] = sumGamma[0, k] / m # 9. 计算新混合系数
# print(mu)
return gamma
# init centroids with random samples
def initCentroids(dataMat, k):
numSamples, dim = dataMat.shape
centroids = np.zeros((k, dim))
for i in range(k):
index = int(np.random.uniform(0, numSamples))
centroids[i, :] = dataMat[index, :]
return centroids
def gaussianCluster(dataMat):
m, n = np.shape(dataMat)
centroids = initCentroids(dataMat, m) ## step 1: init centroids
clusterAssign = np.mat(np.zeros((m, 2)))
gamma = EM(dataMat)
for i in range(m):
# amx返回矩阵最大值,argmax返回矩阵最大值所在下标
clusterAssign[i, :] = np.argmax(gamma[i, :]), np.amax(gamma[i, :]) # 15.确定x的簇标记lambda
## step 4: update centroids
for j in range(m):
pointsInCluster = dataMat[np.nonzero(clusterAssign[:, 0].A == j)[0]]
centroids[j, :] = np.mean(pointsInCluster, axis=0) # 计算出均值向量
return centroids, clusterAssign
def showCluster(dataMat, k, centroids, clusterAssment):
numSamples, dim = dataMat.shape
if dim != 2:
print("Sorry! I can not draw because the dimension of your data is not 2!")
return 1
mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', ', 'pr']
if k > len(mark):
print("Sorry! Your k is too large!")
return 1
# draw all samples
for i in range(numSamples):
markIndex = int(clusterAssment[i, 0])
plt.plot(dataMat[i, 0], dataMat[i, 1], mark[markIndex])
mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', ', 'pb']
# draw the centroids
for i in range(k):
plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize=12)
plt.show()
if __name__=="__main__":
dataMat = np.mat(loadData('watermelon4.txt'))
centroids, clusterAssign = gaussianCluster(dataMat)
print(clusterAssign)
showCluster(dataMat, 3, centroids, clusterAssign)
输出图:
到30次迭代时,模型已经收敛了,故再运行到50轮时,均值向量基本没变。
scikit提供了GaussianMixture类。构造方法为:
def __init__(self, n_components=1, covariance_type='full', tol=1e-3,
reg_covar=1e-6, max_iter=100, n_init=1, init_params='kmeans',
weights_init=None, means_init=None, precisions_init=None,
random_state=None, warm_start=False,
verbose=0, verbose_interval=10):
参数 | description |
---|---|
n_components | int, defaults to 1.: 8 指定混合成分的数量. |
max_iter | int, default: 100 EM算法最大迭代次数 |
n_init | int, default: 1 指定算法运行次数,算法最后会选择最佳的分类簇作为最终结果 |
covariance_type | {‘full’, ‘tied’, ‘diag’, ‘spherical’} defaults to ‘full’. 指定协方差类型 - full: 每个分模型都有自己的协方差矩阵。 - tied:所有的分模型都共享一个协方差矩阵 - diag:每个分模型的协方差矩阵都是对角矩阵 - spherical:每个分模型的协方差矩阵都是标量值 |
reg_covar | float, defaults to 0. 添加到协方差矩阵对角线上元素,确保所有协方差都是正数 |
init_params | {{‘kmeans’, ‘random’}, defaults to ‘kmeans’. 指定初始化权重策略 |
tol | float, default: 1e-3 指定判断算法收敛的阈值 |
weights_init | array-like, shape (n_components, ), optional ;用于指定初始化权重 |
means_init | array-like, shape (n_components, n_features), optional 指定初始化均值 |
precisions_init | array-like, optional. 用户提供的初始协方差矩阵逆矩阵 |
random_state | integer or numpy.RandomState, optional 指定RandomState实例 |
verbose | int, default 0 是否打印日志 |
warm_start | bool, default to False. 上次训练的结果作为本次训练的初始条件 |
属性 | description |
---|---|
weights_ | array-like, shape (n_components,) 每个分模型的权重 |
means_ | array-like, shape (n_components, n_features) 每个分模型的均值 |
covariances_ | float 所有分模型的协方差 |
方法 | description |
---|---|
fit(self, X, y=None) | 训练模型 |
predict(self, X) | 预测样本所属的簇 |
sample([n_samples, random_state]) | 根据模型来随机生成一组样本 |
# -*- coding: utf-8 -*-
# 使用EM算法解算GGM EM算法采用scikit-learn包提供的api
# 数据集:《机器学习》--西瓜数据4.0 :文件watermelon4.txt
from sklearn import mixture
import matplotlib.pyplot as plt
import numpy as np
# 预处理数据
def loadData(filename):
dataSet = []
fr = open(filename)
for line in fr.readlines():
curLine = line.strip().split(' ')
fltLine = list(map(float, curLine))
dataSet.append(fltLine)
return dataSet
def test_GMM(dataMat, components=3,iter = 100,cov_type="full"):
clst = mixture.GaussianMixture(n_components=n_components,max_iter=iter,covariance_type=cov_type)
clst.fit(dataMat)
predicted_labels =clst.predict(dataMat)
return clst.means_,predicted_labels # clst.means_返回均值
def showCluster(dataMat, k, centroids, clusterAssment):
numSamples, dim = dataMat.shape
if dim != 2:
print("Sorry! I can not draw because the dimension of your data is not 2!")
return 1
mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', ', 'pr']
if k > len(mark):
print("Sorry! Your k is too large!")
return 1
# draw all samples
for i in range(numSamples):
markIndex = int(clusterAssment[i])
plt.plot(dataMat[i, 0], dataMat[i, 1], mark[markIndex])
mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', ', 'pb']
# draw the centroids
for i in range(k):
plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize=12)
plt.show()
if __name__=="__main__":
dataMat = np.mat(loadData('watermelon4.txt'))
n_components = 3
iter=100
cov_types = ['spherical', 'tied', 'diag', 'full']
centroids,labels = test_GMM(dataMat,n_components,iter,cov_types[3])
showCluster(dataMat, n_components, centroids, labels) # 这里labels维度改变了,注意修改showCluster方法
不同数目中心点数和不同迭代次数下:
MOOC-数据挖掘:理论与算法