• Gaussian Mixture Model


    • 算法特征:
      ①. 高斯分布作为基函数; ②. 多个高斯分布进行凸组合; ③. 极大似然法估计概率密度.
    • 算法推导:
      GMM概率密度形式如下:
      egin{equation}
      p(x) = sum_{k=1}^{K}pi_kN(x|mu_k, Sigma_k)
      label{eq_1}
      end{equation}
      其中, $pi_k$、$mu_k$、$Sigma_k$分别表示第$k$个高斯分布的权重、均值及协方差矩阵, 且$sumlimits_{k=1}^{K}pi_k = 1, forall pi_k geq 0$.
      令样本集合为${x^{(1)}, x^{(2)}, cdots, x^{(n)}}$, 本文拟采用EM(Expectation-Maximization)算法求解上述优化变量${pi_k, mu_k, Sigma_k}_{k=1sim K}$.
      $step1$:
      随机初始化${pi_k, mu_k, Sigma_k}_{k=1sim K}$.
      $step2 sim E step$:
      计算第$i$个样本落在第$k$个高斯的概率:
      egin{equation}
      gamma_k^{(i)} = frac{pi_kN(x^{(i)}|mu_k, Sigma_k)}{sumlimits_{k=1}^{K} pi_k N(x^{(i)}|mu_k, Sigma_k)}
      label{eq_2}
      end{equation}
      $step3 sim M step$:
      计算第$k$个高斯的样本数:
      egin{equation}
      N_k = sum_{i=1}^{n}gamma_k^{(i)}
      label{eq_3}
      end{equation}
      更新第$k$个高斯的权重:
      egin{equation}
      pi_k = frac{N_k}{N}
      label{eq_4}
      end{equation}
      更新第$k$个高斯的均值:
      egin{equation}
      mu_k = frac{sumlimits_{i=1}^{n}gamma_k^{(i)}x^{(i)}}{N_k}
      label{eq_5}
      end{equation}
      更新第$k$个高斯的协方差矩阵:
      egin{equation}
      Sigma_k = frac{sumlimits_{i=1}^{n}gamma_k^{(i)}(x^{(i)} - mu_k)(x^{(i)} - mu_k)^{mathrm{T}}}{N_k}
      label{eq_6}
      end{equation}
      $step4$:
      回到$step2$, 直到优化变量${pi_k, mu_k, Sigma_k}_{k=1sim K}$均收敛.
    • 代码实现:
      Part Ⅰ:
      现以如下数据集为例进行算法实施:
       1 # 生成聚类数据集
       2 
       3 import numpy
       4 from matplotlib import pyplot as plt
       5 
       6 
       7 numpy.random.seed(3)
       8 
       9 
      10 def generate_data(clusterNum):
      11     mu = [0, 0]
      12     sigma = [[0.03, 0], [0, 0.03]]
      13     data = numpy.random.multivariate_normal(mu, sigma, (1000, ))
      14     
      15     for idx in range(clusterNum  - 1):
      16         mu = numpy.random.uniform(-1, 1, (2, ))
      17         arr = numpy.random.uniform(0, 1, (2, 2))
      18         sigma = numpy.matmul(arr.T, arr) / 10
      19         tmpData = numpy.random.multivariate_normal(mu, sigma, (1000, ))
      20         data = numpy.vstack((data, tmpData))
      21     
      22     return data
      23 
      24 
      25 def plot_data(data):
      26     fig = plt.figure(figsize=(5, 3))
      27     ax1 = plt.subplot()
      28     
      29     ax1.scatter(data[:, 0], data[:, 1], s=1)
      30     ax1.set(xlim=[-2, 2], ylim=[-2, 2])
      31     # plt.show()
      32     fig.savefig("plot_data.png", dpi=100)
      33     plt.close()
      34 
      35         
      36 
      37 if __name__ == "__main__":
      38     X = generate_data(5)
      39     plot_data(X)
      View Code

      Part Ⅱ:
      GMM实现如下:
        1 # Gaussian Mixture Model之实现
        2 
        3 import numpy
        4 from matplotlib import pyplot as plt
        5 
        6 
        7 numpy.random.seed(3)
        8 
        9 
       10 
       11 def generate_data(clusterNum):
       12     mu = [0, 0]
       13     sigma = [[0.03, 0], [0, 0.03]]
       14     data = numpy.random.multivariate_normal(mu, sigma, (1000, ))
       15     
       16     for idx in range(clusterNum  - 1):
       17         mu = numpy.random.uniform(-1, 1, (2, ))
       18         arr = numpy.random.uniform(0, 1, (2, 2))
       19         sigma = numpy.matmul(arr.T, arr) / 10
       20         tmpData = numpy.random.multivariate_normal(mu, sigma, (1000, ))
       21         data = numpy.vstack((data, tmpData))
       22     
       23     return data
       24 
       25 
       26 
       27 class GMM(object):
       28     
       29     def __init__(self, X, clusterNum):
       30         self.__X = X                                      # 聚类数据集, 1行代表1个样本
       31         self.__clusterNum = clusterNum                    # 类簇数量
       32         
       33         
       34     def get_clusters(self, pi, mu, sigma):
       35         '''
       36         获取类簇
       37         '''
       38         X = self.__X
       39         clusterNum = self.__clusterNum
       40         gamma = self.__calc_gamma(X, pi, mu, sigma)
       41         maxIdx = numpy.argmax(gamma, axis=0)
       42         
       43         clusters = list(list() for idx in range(clusterNum))
       44         for idx, x in enumerate(X):
       45             clusters[maxIdx[idx]].append(x)
       46         clusters = list(numpy.array(cluster) for cluster in clusters)
       47         return clusters
       48         
       49         
       50     def PDF(self, x, pi, mu, sigma):
       51         '''
       52         GMM概率密度函数
       53         '''
       54         val = sum(list(self.__calc_gaussian(x, mu[k], sigma[k]) * pi[k] for k in range(mu.shape[0])))
       55         return val
       56         
       57         
       58     def optimize(self, maxIter=1000, epsilon=1.e-6):
       59         '''
       60         maxIter: 最大迭代次数
       61         epsilon: 收敛判据, 优化变量趋于稳定则收敛
       62         '''
       63         X = self.__X
       64         clusterNum = self.__clusterNum    # 簇数量
       65         N = X.shape[0]                    # 样本数量
       66         
       67         pi, mu, sigma = self.__init_GMMOptVars(X, clusterNum)
       68         gamma = numpy.zeros((clusterNum, N))
       69         for idx in range(maxIter):
       70             print("iterCnt: {:3d},   pi = {}".format(idx, pi))
       71             gamma = self.__calc_gamma(X, pi, mu, sigma)
       72             
       73             Nk = numpy.sum(gamma, axis=1)
       74             piNew = Nk / N
       75             muNew = self.__calc_mu(X, gamma, Nk)
       76             sigmaNew = self.__calc_sigma(X, gamma, mu, Nk)
       77             
       78             deltaPi = piNew - pi
       79             deltaMu = muNew - mu
       80             deltaSigma = sigmaNew - sigma
       81             if self.__converged(deltaPi, deltaMu, deltaSigma, epsilon):
       82                 return piNew, muNew, sigmaNew, True
       83             pi, mu, sigma = piNew, muNew, sigmaNew
       84             
       85         return pi, mu, sigma, False
       86             
       87             
       88     def __calc_sigma(self, X, gamma, mu, Nk):
       89         sigma = list()
       90         for gamma_k, mu_k, Nk_k in zip(gamma, mu, Nk):
       91             term1 = X - mu_k
       92             term2 = gamma_k.reshape((-1, 1)) * term1
       93             term3 = numpy.matmul(term2.T, term1)
       94             sigma_k = term3 / Nk_k
       95             sigma.append(sigma_k)
       96         return numpy.array(sigma)
       97             
       98             
       99     def __calc_mu(self, X, gamma, Nk):
      100         mu = list()
      101         for gamma_k, Nk_k in zip(gamma, Nk):
      102             term1 = gamma_k.reshape((-1, 1)) * X
      103             term2 = numpy.sum(term1, axis=0)
      104             mu_k = term2 / Nk_k
      105             mu.append(mu_k)
      106         return numpy.array(mu)
      107             
      108             
      109     def __calc_gamma(self, X, pi, mu, sigma):
      110         gamma = numpy.zeros((pi.shape[0], X.shape[0]))
      111         for k in range(gamma.shape[0] - 1):
      112             for i in range(gamma.shape[1]):
      113                 gamma[k, i] = self.__calc_gamma_ik(X[i], k, pi, mu, sigma)
      114         term1 = numpy.sum(gamma[:-1, :], axis=0)
      115         gamma[-1] = 1 - term1
      116         return gamma
      117         
      118             
      119     def __converged(self, deltaPi, deltaMu, deltaSigma, epsilon):
      120         val1 = numpy.linalg.norm(deltaPi)
      121         val2 = numpy.linalg.norm(deltaMu)
      122         val3 = numpy.linalg.norm(deltaSigma)
      123         if val1 <= epsilon and val2 <= epsilon and val3 <= epsilon:
      124             return True
      125         return False
      126         
      127         
      128     def __calc_gamma_ik(self, x_i, k, pi, mu, sigma):
      129         pi_k = pi[k]
      130         mu_k = mu[k]
      131         sigma_k = sigma[k]
      132         
      133         upperVal = pi_k * self.__calc_gaussian(x_i, mu_k, sigma_k)
      134         lowerVal = self.PDF(x_i, pi, mu, sigma)
      135         gamma_ik = upperVal / lowerVal
      136         return gamma_ik
      137     
      138         
      139     def __calc_gaussian(self, x, mu, sigma):
      140         '''
      141         x: 输入x - 1维数组
      142         mu: 均值
      143         sigma: 协方差矩阵
      144         '''
      145         d = mu.shape[0]
      146         term0 = (x - mu).reshape((-1, 1))
      147         term1 = 1 / numpy.math.sqrt((2 * numpy.math.pi) ** d * numpy.linalg.det(sigma))
      148         term2 = numpy.matmul(numpy.matmul(term0.T, numpy.linalg.inv(sigma)), term0)[0, 0]
      149         term3 = numpy.math.exp(-term2 / 2)
      150         val = term1 * term3
      151         return val
      152         
      153         
      154     def __init_GMMOptVars(self, X, clusterNum):
      155         pi = self.__init_pi(clusterNum)                         # GMM权重初始化
      156         mu = self.__init_mu(X, clusterNum)                      # GMM均值初始化
      157         sigma = self.__init_sigma(X.shape[1], clusterNum)       # GMM协方差矩阵初始化 - 采用单位矩阵初始化之
      158         return pi, mu, sigma
      159         
      160         
      161     def __init_sigma(self, n, clusterNum):
      162         sigma = list()
      163         for idx in range(clusterNum):
      164             sigma.append(numpy.identity(n))
      165         return numpy.array(sigma)
      166         
      167         
      168     def __init_mu(self, X, clusterNum, epsilon=1.e-5):
      169         '''
      170         采用K-means方法进行初始化
      171         '''
      172         lowerBound = numpy.min(X, axis=0)
      173         upperBound = numpy.max(X, axis=0)
      174         oriMu = numpy.random.uniform(lowerBound, upperBound, (clusterNum, X.shape[1]))
      175         traMu = self.__updateMuByKMeans(X, oriMu)
      176         while numpy.linalg.norm(traMu - oriMu) > epsilon:
      177             oriMu = traMu
      178             traMu = self.__updateMuByKMeans(X, oriMu)
      179         return traMu    
      180         
      181             
      182     def __updateMuByKMeans(self, X, oriMu):
      183         distList = list()
      184         for mu in oriMu:
      185             distTerm = numpy.linalg.norm(X - mu, axis=1)
      186             distList.append(distTerm)
      187         distArr = numpy.vstack(distList)
      188         distIdx = numpy.argmin(distArr, axis=0)
      189         
      190         clusLst = list(list() for i in range(oriMu.shape[0]))
      191         for xIdx, dIdx in enumerate(distIdx):
      192             clusLst[dIdx].append(X[xIdx])
      193         
      194         traMu = list()
      195         for clusIdx, clus in enumerate(clusLst):
      196             if len(clus) == 0:
      197                 traMu.append(oriMu[clusIdx])
      198             else:
      199                 tmpClus = numpy.array(clus)
      200                 mu = numpy.sum(tmpClus, axis=0) / tmpClus.shape[0]
      201                 traMu.append(mu)
      202         return numpy.array(traMu)
      203             
      204         
      205         
      206     def __init_pi(self, clusterNum):
      207         pi = numpy.ones((clusterNum, )) / clusterNum
      208         return pi
      209         
      210         
      211         
      212 class GMMPlot(object):
      213     
      214     @staticmethod
      215     def profile_plot(X, gmmObj, pi, mu, sigma):
      216         surfX1 = numpy.linspace(-2, 2, 100)
      217         surfX2 = numpy.linspace(-2, 2, 100)
      218         surfX1, surfX2 = numpy.meshgrid(surfX1, surfX2)
      219         
      220         surfY = numpy.zeros((surfX2.shape[0], surfX1.shape[1]))
      221         for rowIdx in range(surfY.shape[0]):
      222             for colIdx in range(surfY.shape[1]):
      223                 surfY[rowIdx, colIdx] = gmmObj.PDF(numpy.array([surfX1[rowIdx, colIdx], surfX2[rowIdx, colIdx]]), pi, mu, sigma)
      224         
      225         clusters = gmmObj.get_clusters(pi, mu, sigma)
      226         
      227         fig = plt.figure(figsize=(10, 3))
      228         ax1 = plt.subplot(1, 2, 1)
      229         ax2 = plt.subplot(1, 2, 2)
      230         
      231         ax1.contour(surfX1, surfX2, surfY, levels=16)
      232         ax1.scatter(X[:, 0], X[:, 1], marker="o", color="tab:blue", s=1)
      233         ax1.set(xlim=[-2, 2], ylim=[-2, 2], xlabel="$x_1$", ylabel="$x_2$")
      234         
      235         for idx, cluster in enumerate(clusters):
      236             ax2.scatter(cluster[:, 0], cluster[:, 1], s=1, label="$cluster {}$".format(idx))
      237         ax2.set(xlim=[-2, 2], ylim=[-2, 2], xlabel="$x_1$", ylabel="$x_2$")
      238         ax2.legend()
      239         
      240         fig.tight_layout()
      241         # plt.show()
      242         fig.savefig("profile_plot.png", dpi=100)
      243         plt.close()
      244         
      245         
      246         
      247 if __name__ == "__main__":
      248     clusterNum = 5
      249     X = generate_data(5)
      250     
      251     gmmObj = GMM(X, clusterNum)
      252     pi, mu, sigma, _ = gmmObj.optimize()
      253     
      254     GMMPlot.profile_plot(X, gmmObj, pi, mu, sigma)
      View Code
    • 结果展示:
      左侧为聚类轮廓, 右侧为簇划分. 很显然, 高斯混合模型适用于高斯型数据分布, 此时聚类效果较为明显.
    • 使用建议:
      ①. 由于EM算法只能保证局部最优, 因此选择一个合适的初值较为重要, 本文以K-means结果作为初值进行优化;
      ②. 根据基函数特征, 高斯混合模型适用于高斯型数据分布.
    • 参考文档:
      https://baike.baidu.com/item/%E9%AB%98%E6%96%AF%E6%B7%B7%E5%90%88%E6%A8%A1%E5%9E%8B/8878468?fr=aladdin
  • 相关阅读:
    Smartforms 设置纸张打印格式
    JVM的垃圾回收算法
    JVM垃圾回收器
    Java类加载过程
    Java内存模型
    计算机模型
    C#----对时间结构DateTime的使用(时间日期的使用)
    C#----我对坐标系的理解和图形转动
    C#----格式化字符串的操作
    其他----
  • 原文地址:https://www.cnblogs.com/xxhbdk/p/13874439.html
Copyright © 2020-2023  润新知