Ta odpowiedź da wgląd w to, co się dzieje, co prowadzi do pojedynczej macierzy kowariancji podczas dopasowywania GMM do zbioru danych, dlaczego tak się dzieje, a także co możemy zrobić, aby temu zapobiec.
Dlatego najlepiej zacząć od podsumowania kroków podczas dopasowywania modelu mieszanki Gaussa do zestawu danych.
0. Zdecyduj, ile źródeł / klastrów (c) chcesz dopasować do swoich danych
1. Zainicjuj parametry średnie , kowariancja Σ c i fraction_per_class π c na klaster c
μdoΣdoπdo
mi- St e p---------
- Obliczyć dla każdego punktu danych prawdopodobieństwo r i c, że punkt danych x i należy do klastra c za pomocą:
r i c = π c N ( x i | μ c , Σ c )xjarja cxja
gdzieN(x|μ,Σ)opisuje wielowariantowy gaussowski z:
N(xi,μc,Σc)=1rja c= πdoN.( xja | μ do, Σdo)ΣK.k = 1πkN.( xja | μ k, Σk)
N.( x | μ , Σ )
ricdaje nam dla każdego punktu danychximiarę:ProbabilitythatxibelongstoclasN.( xja, μdo, Σdo) = 1 ( 2 π)n2)| Σdo|12)e x p ( - 12)( xja- μdo)T.Σ- 1do( xja- μdo) )
rja cxja W związku z tym, jeżelixijest bardzo blisko Gaussa C, to uzyskać wysokąRIcwartością dla innych gaussowskich i stosunkowo niskich wartości.
M-Step_
Dla każdej grupy c: Oblicz masę całkowitąmcP.r o b a b i l i t y t h a t x ja b e l o n gs t o c l a s s c P.r o b a b i l i t y o f xja o V e R l l c L e s e s xjarja c
M.- St e p----------
mdo(luźno mówiąc, ułamek punktów przydzielonych do klastra c) i zaktualizuj , μ c i Σ c używając r i c z:
m c = Σ i r i c π c = m cπdoμdoΣdorja c
mdo = Σ jarjado
μc=1πdo = m dom
Σc=1μdo = 1 mdoΣjarja cxja
Pamiętaj, że musisz użyć zaktualizowanych środków w tym ostatnim wzorze.
Iteracyjnie powtarzaj krok E i M, aż funkcja prawdopodobieństwa logarytmicznego naszego modelu zbiega się, gdzie prawdopodobieństwo logarytmu jest obliczane z:
lnp(X|π,μ,Σ)=Σ N i = 1 ln(Σ KΣdo = 1 mdoΣjarja c( xja- μdo)T.( xja- μdo)
l n p ( X | π,μ,Σ)= Σ N.i = 1 l n ( ΣK.k = 1πkN.( xja | μ k, Σk) )
XA X= XA = ja
[ 0000]
ZAXjaΣ- 1do0powyższa macierz kowariancji, jeśli wielowymiarowy Gaussian wpada w jeden punkt podczas iteracji między krokiem E i M. Może się to zdarzyć, jeśli mamy na przykład zestaw danych, do którego chcemy dopasować 3 gaussów, ale który w rzeczywistości składa się tylko z dwóch klas (klastrów) tak, że luźno mówiąc, dwóch z tych trzech gaussów łapie własny klaster, podczas gdy ostatni gaussian nim zarządza złapać jeden punkt, na którym on siedzi. Zobaczymy jak to wygląda poniżej. Ale krok po kroku: Załóżmy, że masz dwuwymiarowy zestaw danych, który składa się z dwóch klastrów, ale nie wiesz tego i chcesz dopasować do niego trzy modele gaussowskie, to znaczy c = 3. Inicjujesz swoje parametry w kroku E i wykreśl gaussowie na górze twoich danych, które wyglądają na coś jak (może widzisz dwa stosunkowo rozproszone skupiska w lewym dolnym rogu i prawym górnym rogu):
μdoπdo
rja cc o vrja c
rja c= πdoN.( xja | μ do, Σdo)ΣK.k = 1πkN.( xja | μ k, Σk)
rja crja cxjaxjaxjarja cxjarja crja cΣdo = Σ jarja c( xja- μdo)T.( xja- μdo)
rja cxja( xja- μdo)μdoxjajotμjotμjot= xnrja c
[ 0000]
00matryca. Odbywa się to poprzez dodanie bardzo małej wartości (w
GaussianMixture sklearn'a wartość ta jest ustawiona na 1e-6) do cyfr cyfr macierzy kowariancji. Istnieją również inne sposoby zapobiegania osobliwości, takie jak zauważanie, gdy gaussian się zapada i ustawianie jego średniej i / lub macierzy kowariancji na nową, arbitralnie wysoką wartość (wartości). Ta normalizacja kowariancji jest również zaimplementowana w poniższym kodzie, za pomocą którego otrzymujesz opisane wyniki. Być może trzeba uruchomić kod kilka razy, aby uzyskać pojedynczą macierz kowariancji od, jak już powiedziano. nie może się to zdarzyć za każdym razem, ale zależy również od początkowej konfiguracji gaussianów.
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()