Esta respuesta dará una idea de lo que está sucediendo que conduce a una matriz de covarianza singular durante el ajuste de un GMM a un conjunto de datos, por qué sucede esto y qué podemos hacer para evitarlo.
Por lo tanto, es mejor comenzar recapitulando los pasos durante el ajuste de un modelo de mezcla gaussiana a un conjunto de datos.
0. Decida cuántas fuentes / grupos (c) desea ajustar a sus datos
1. Inicialice la media de los parámetros , covarianza Σ c , y fracción_por_clase π c por grupo c
μcΣcπc
E−Step–––––––––
- Calcule para cada punto de datos la probabilidad r i c de que el punto de datos x i pertenece al grupo c con:
r i c = π c N ( x i | μ c , Σ c )xiricxi
dondeN(x|μ,Σ)describe la Gaussiana multivariada con:
N(xi,μc,Σc)=1ric=πcN(xi | μc,Σc)ΣKk=1πkN(xi | μk,Σk)
N(x | μ,Σ)
ricnos da para cada punto de datosxila medida de:ProbabilitythatxibelongstoclasN(xi,μc,Σc) = 1(2π)n2|Σc|12exp(−12(xi−μc)TΣ−1c(xi−μc))
ricxi por lo tanto, sixiestá muy cerca de una c gaussiana, obtendrá un altovalor dericpara este gaussiano y valores relativamente bajos de lo contrario.
M-Step_
Para cada grupo c: Calcule el peso totalmcProbability that xi belongs to class cProbability of xi over all classesxiric
M−Step––––––––––
mc(en términos generales, la fracción de puntos asignados al grupo c) y actualice , μ c y Σ c utilizando r i c con:
m c = Σ i r i c π c = m cπcμcΣcric
mc = Σiric
μc=1πc = mcm
Σc=1μc = 1mcΣiricxi
Tenga en cuenta que debe usar los medios actualizados en esta última fórmula.
Repita iterativamente los pasos E y M hasta que la función de probabilidad logarítmica de nuestro modelo converja donde la probabilidad logarítmica se calcula con:
lnp(X|π,μ,Σ)=Σ N i = 1 ln(Σ KΣc = 1mcΣiric(xi−μc)T(xi−μc)
ln p(X | π,μ,Σ) = ΣNi=1 ln(ΣKk=1πkN(xi | μk,Σk))
XAX=XA=I
[0000]
AXIΣ−1c0matriz de covarianza anterior si el gaussiano multivariado cae en un punto durante la iteración entre el paso E y M. Esto podría suceder si tenemos, por ejemplo, un conjunto de datos en el que queremos ajustar 3 gaussianos, pero que en realidad consiste solo en dos clases (grupos) de tal manera que, en términos generales, dos de estos tres gaussianos atrapan su propio grupo mientras que el último gaussiano solo lo maneja para atrapar un solo punto en el que se asienta. Veremos cómo se ve esto a continuación. Pero paso a paso: suponga que tiene un conjunto de datos bidimensional que consta de dos grupos, pero no lo sabe y desea ajustar tres modelos gaussianos, es decir c = 3. Inicializa sus parámetros en el paso E y traza los gaussianos en la parte superior de sus datos que se ve algo. como (tal vez pueda ver los dos grupos relativamente dispersos en la parte inferior izquierda y superior derecha):
μcπc
riccovric
ric=πcN(xi | μc,Σc)ΣKk=1πkN(xi | μk,Σk)
ricricxixixiricxiricricΣc = Σiric(xi−μc)T(xi−μc)
ricxi(xi−μc)μcxijμjμj=xnric
[0000]
00matriz. Esto se hace agregando un valor muy pequeño (en la
Mezcla Gaussiana de sklearn, este valor se establece en 1e-6) a la digonal de la matriz de covarianza. También hay otras formas de prevenir la singularidad, como darse cuenta cuando un gaussiano colapsa y establecer su matriz de media y / o covarianza en un nuevo valor arbitrariamente alto. Esta regularización de covarianza también se implementa en el siguiente código con el que obtiene los resultados descritos. Quizás tenga que ejecutar el código varias veces para obtener una matriz de covarianza singular desde entonces, como se dijo. Esto no debe suceder cada vez, sino que también depende de la configuración inicial de los gaussianos.
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')
from sklearn.datasets.samples_generator import make_blobs
import numpy as np
from scipy.stats import multivariate_normal
# 0. Create dataset
X,Y = make_blobs(cluster_std=2.5,random_state=20,n_samples=500,centers=3)
# Stratch dataset to get ellipsoid data
X = np.dot(X,np.random.RandomState(0).randn(2,2))
class EMM:
def __init__(self,X,number_of_sources,iterations):
self.iterations = iterations
self.number_of_sources = number_of_sources
self.X = X
self.mu = None
self.pi = None
self.cov = None
self.XY = None
# Define a function which runs for i iterations:
def run(self):
self.reg_cov = 1e-6*np.identity(len(self.X[0]))
x,y = np.meshgrid(np.sort(self.X[:,0]),np.sort(self.X[:,1]))
self.XY = np.array([x.flatten(),y.flatten()]).T
# 1. Set the initial mu, covariance and pi values
self.mu = np.random.randint(min(self.X[:,0]),max(self.X[:,0]),size=(self.number_of_sources,len(self.X[0]))) # This is a nxm matrix since we assume n sources (n Gaussians) where each has m dimensions
self.cov = np.zeros((self.number_of_sources,len(X[0]),len(X[0]))) # We need a nxmxm covariance matrix for each source since we have m features --> We create symmetric covariance matrices with ones on the digonal
for dim in range(len(self.cov)):
np.fill_diagonal(self.cov[dim],5)
self.pi = np.ones(self.number_of_sources)/self.number_of_sources # Are "Fractions"
log_likelihoods = [] # In this list we store the log likehoods per iteration and plot them in the end to check if
# if we have converged
# Plot the initial state
fig = plt.figure(figsize=(10,10))
ax0 = fig.add_subplot(111)
ax0.scatter(self.X[:,0],self.X[:,1])
for m,c in zip(self.mu,self.cov):
c += self.reg_cov
multi_normal = multivariate_normal(mean=m,cov=c)
ax0.contour(np.sort(self.X[:,0]),np.sort(self.X[:,1]),multi_normal.pdf(self.XY).reshape(len(self.X),len(self.X)),colors='black',alpha=0.3)
ax0.scatter(m[0],m[1],c='grey',zorder=10,s=100)
mu = []
cov = []
R = []
for i in range(self.iterations):
mu.append(self.mu)
cov.append(self.cov)
# E Step
r_ic = np.zeros((len(self.X),len(self.cov)))
for m,co,p,r in zip(self.mu,self.cov,self.pi,range(len(r_ic[0]))):
co+=self.reg_cov
mn = multivariate_normal(mean=m,cov=co)
r_ic[:,r] = p*mn.pdf(self.X)/np.sum([pi_c*multivariate_normal(mean=mu_c,cov=cov_c).pdf(X) for pi_c,mu_c,cov_c in zip(self.pi,self.mu,self.cov+self.reg_cov)],axis=0)
R.append(r_ic)
# M Step
# Calculate the new mean vector and new covariance matrices, based on the probable membership of the single x_i to classes c --> r_ic
self.mu = []
self.cov = []
self.pi = []
log_likelihood = []
for c in range(len(r_ic[0])):
m_c = np.sum(r_ic[:,c],axis=0)
mu_c = (1/m_c)*np.sum(self.X*r_ic[:,c].reshape(len(self.X),1),axis=0)
self.mu.append(mu_c)
# Calculate the covariance matrix per source based on the new mean
self.cov.append(((1/m_c)*np.dot((np.array(r_ic[:,c]).reshape(len(self.X),1)*(self.X-mu_c)).T,(self.X-mu_c)))+self.reg_cov)
# Calculate pi_new which is the "fraction of points" respectively the fraction of the probability assigned to each source
self.pi.append(m_c/np.sum(r_ic))
# Log likelihood
log_likelihoods.append(np.log(np.sum([k*multivariate_normal(self.mu[i],self.cov[j]).pdf(X) for k,i,j in zip(self.pi,range(len(self.mu)),range(len(self.cov)))])))
fig2 = plt.figure(figsize=(10,10))
ax1 = fig2.add_subplot(111)
ax1.plot(range(0,self.iterations,1),log_likelihoods)
#plt.show()
print(mu[-1])
print(cov[-1])
for r in np.array(R[-1]):
print(r)
print(X)
def predict(self):
# PLot the point onto the fittet gaussians
fig3 = plt.figure(figsize=(10,10))
ax2 = fig3.add_subplot(111)
ax2.scatter(self.X[:,0],self.X[:,1])
for m,c in zip(self.mu,self.cov):
multi_normal = multivariate_normal(mean=m,cov=c)
ax2.contour(np.sort(self.X[:,0]),np.sort(self.X[:,1]),multi_normal.pdf(self.XY).reshape(len(self.X),len(self.X)),colors='black',alpha=0.3)
EMM = EMM(X,3,100)
EMM.run()
EMM.predict()