using Pkg; Pkg.activate("../."); Pkg.instantiate(); using IJulia; try IJulia.clear_output(); catch _ end using DataFrames, CSV, LinearAlgebra, PDMats, SpecialFunctions include("scripts/gmm_plot.jl") # Holds plotting function old_faithful = CSV.read("datasets/old_faithful.csv",DataFrame); X = convert(Matrix{Float64}, [old_faithful[!,1] old_faithful[!,2]]');#data matrix N = size(X, 2) #number of observations K = 6 function sufficientStatistics(X,r,k::Int) #function to compute sufficient statistics N_k = sum(r[k,:]) hat_x_k = sum([r[k,n]*X[:,n] for n in 1:N]) ./ N_k S_k = sum([r[k,n]*(X[:,n]-hat_x_k)*(X[:,n]-hat_x_k)' for n in 1:N]) ./ N_k return N_k, hat_x_k, S_k end function updateMeanPrecisionPi(m_0,β_0,W_0,ν_0,α_0,r) #variational maximisation function m = Array{Float64}(undef,2,K) #mean of the clusters β = Array{Float64}(undef,K) #precision scaling for Gausian distribution W = Array{Float64}(undef,2,2,K) #precision prior for Wishart distributions ν = Array{Float64}(undef,K) #degrees of freedom parameter for Wishart distribution α = Array{Float64}(undef,K) #Dirichlet distribution parameter for k=1:K sst = sufficientStatistics(X,r,k) α[k] = α_0[k] + sst[1]; β[k] = β_0[k] + sst[1]; ν[k] = ν_0[k] .+ sst[1] m[:,k] = (1/β[k])*(β_0[k].*m_0[:,k] + sst[1].*sst[2]) W[:,:,k] = inv(inv(W_0[:,:,k])+sst[3]*sst[1] + ((β_0[k]*sst[1])/(β_0[k]+sst[1])).*(sst[2]-m_0[:,k])*(sst[2]-m_0[:,k])') end return m,β,W,ν,α end function updateR(Λ,m,α,ν,β) #variational expectation function r = Array{Float64}(undef,K,N) #responsibilities hat_π = Array{Float64}(undef,K) hat_Λ = Array{Float64}(undef,K) for k=1:K hat_Λ[k] = 1/2*(2*log(2)+logdet(Λ[:,:,k])+digamma(ν[k]/2)+digamma((ν[k]-1)/2)) hat_π[k] = exp(digamma(α[k])-digamma(sum(α))) for n=1:N r[k,n] = hat_π[k]*exp(-hat_Λ[k]-1/β[k] - (ν[k]/2)*(X[:,n]-m[:,k])'*Λ[:,:,k]*(X[:,n]-m[:,k])) end end for n=1:N r[:,n] = r[:,n]./ sum(r[:,n]) #normalize to ensure r represents probabilities end return r end max_iter = 120 #store the inference results in these vectors ν = fill!(Array{Float64}(undef,K,max_iter),3) β = fill!(Array{Float64}(undef,K,max_iter),1.0) α = fill!(Array{Float64}(undef,K,max_iter),0.01) R = Array{Float64}(undef,K,N,max_iter) M = Array{Float64}(undef,2,K,max_iter) Λ = Array{Float64}(undef,2,2,K,max_iter) clusters_vb = Array{Distribution}(undef,K,max_iter) #clusters to be plotted #initialize prior distribution parameters M[:,:,1] = [[3.0; 60.0];[4.0; 70.0];[2.0; 50.0];[2.0; 60.0];[3.0; 100.0];[4.0; 80.0]] for k=1:K Λ[:,:,k,1] = [1.0 0;0 0.01] R[k,:,1] = 1/(K)*ones(N) clusters_vb[k,1] = MvNormal(M[:,k,1],PDMats.PDMat(convert(Matrix,Hermitian(inv(ν[1,1].*Λ[:,:,k,1]))))) end #variational inference for i=1:max_iter-1 #variational expectation R[:,:,i+1] = updateR(Λ[:,:,:,i],M[:,:,i],α[:,i],ν[:,i],β[:,i]) #variational minimisation M[:,:,i+1],β[:,i+1],Λ[:,:,:,i+1],ν[:,i+1],α[:,i+1] = updateMeanPrecisionPi(M[:,:,i],β[:,i],Λ[:,:,:,i],ν[:,i],α[:,i],R[:,:,i+1]) for k=1:K clusters_vb[k,i+1] = MvNormal(M[:,k,i+1],PDMats.PDMat(convert(Matrix,Hermitian(inv(ν[k,i+1].*Λ[:,:,k,i+1]))))) end end include("scripts/gmm_plot.jl") # Holds plotting function plots = [plotGMM(X, clusters_vb[:,1], R[:,:,1], "Initial situation")] for i=LinRange(2, 120, 5) i = round(Int,i) push!(plots, plotGMM(X, clusters_vb[:,i], R[:,:,i], "After $(i) iterations")) end plot(plots..., layout=(2,3), size=(1500, 900)) using DataFrames, CSV, LinearAlgebra include("scripts/gmm_plot.jl") # Holds plotting function old_faithful = CSV.read("datasets/old_faithful.csv", DataFrame); X = Array(Matrix{Float64}(old_faithful)') N = size(X, 2) # Initialize the GMM. We assume 2 clusters. clusters = [MvNormal([4.;60.], [.5 0;0 10^2]); MvNormal([2.;80.], [.5 0;0 10^2])]; π_hat = [0.5; 0.5] # Mixing weights γ = fill!(Matrix{Float64}(undef,2,N), NaN) # Responsibilities (row per cluster) # Define functions for updating the parameters and responsibilities function updateResponsibilities!(X, clusters, π_hat, γ) # Expectation step: update γ norm = [pdf(clusters[1], X) pdf(clusters[2], X)] * π_hat γ[1,:] = (π_hat[1] * pdf(clusters[1],X) ./ norm)' γ[2,:] = 1 .- γ[1,:] end function updateParameters!(X, clusters, π_hat, γ) # Maximization step: update π_hat and clusters using ML estimation m = sum(γ, dims=2) π_hat = m / N μ_hat = (X * γ') ./ m' for k=1:2 Z = (X .- μ_hat[:,k]) Σ_k = Symmetric(((Z .* (γ[k,:])') * Z') / m[k]) clusters[k] = MvNormal(μ_hat[:,k], convert(Matrix, Σ_k)) end end # Execute the algorithm: iteratively update parameters and responsibilities plots = [plotGMM(X, clusters, γ, "Initial situation")] updateResponsibilities!(X, clusters, π_hat, γ) push!(plots, plotGMM(X, clusters, γ, "After first E-step")) updateParameters!(X, clusters, π_hat, γ) push!(plots, plotGMM(X, clusters, γ, "After first M-step")) iter_counter = 1 for i=1:3 for j=1:i+1 updateResponsibilities!(X, clusters, π_hat, γ) updateParameters!(X, clusters, π_hat, γ) iter_counter += 1 end push!(plots, plotGMM(X, clusters, γ, "After $(iter_counter) iterations")) end plot(plots..., layout=(2,3), size=(1500, 900)) open("../../styles/aipstyle.html") do f display("text/html", read(f, String)) end