Skip to content

Commit 8ff9df7

Browse files
committed
added readme, DPM code and point estimate code.
1 parent b9f40b3 commit 8ff9df7

20 files changed

+1229
-3
lines changed

README.md

+73-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,76 @@
1-
# BayesianNonparametrics
1+
# BayesianNonparametrics.jl
2+
3+
## Examples
4+
### Dirichlet Process Mixture
5+
The following example illustrates the use of BayesianNonparametrics.jl for clustering of continuous observations.
6+
7+
After loading the package:
8+
```julia
9+
using BayesianNonparametrics
10+
```
11+
we can generate a 2D synthetic dataset (or use a multivariate continuous dataset of interest)
12+
```julia
13+
(X, Y) = bloobs(randomize = false)
14+
```
15+
and construct the parameters of our base distribution:
16+
```julia
17+
μ0 = vec(mean(X, 1))
18+
κ0 = 5.0
19+
ν0 = 9.0
20+
Σ0 = cov(X)
21+
22+
H = WishartGaussian(μ0, κ0, ν0, Σ0)
23+
```
24+
After defining the base distribution we can specify the model:
25+
```julia
26+
model = DPM(H)
27+
```
28+
which is in this case a Dirichlet Process Mixture. Each model has to be initialised, one possible initialisation approach for Dirichlet Process Mixtures is a k-Means initialisation:
29+
```julia
30+
modelBuffer = init(X, model, KMeansInitialisation(k = 10))
31+
```
32+
The resulting buffer object can now be used to apply posterior inference on the model given $X$. In the following we apply Gibbs sampling for 500 iterations without burn in or thining:
33+
```julia
34+
models = train(modelBuffer, DPMHyperparam(), Gibbs(maxiter = 500))
35+
```
36+
You shoud see the progress of the sampling process in the command line. After applying Gibbs sampling, it is possible explore the posterior based on their posterior densities,
37+
```julia
38+
densities = Float64[m.energy for m in models]
39+
```
40+
number of active components
41+
```julia
42+
activeComponents = Int[sum(m.weights .> 0) for m in models]
43+
```
44+
or the groupings of the observations:
45+
```julia
46+
assignments = [m.assignments for m in models]
47+
```
48+
The following animation illustrates posterior samples obtained by a Dirichlet Process Mixture:
49+
![alt text](posteriorSamples.gif "Posterior Sample")
50+
51+
Alternatively, one can compute a point estimate based on the posterior similarity matrix:
52+
```julia
53+
A = reduce(hcat, assignments)
54+
(N, D) = size(X)
55+
56+
PSM = ones(N, N)
57+
M = size(A, 2)
58+
for i in 1:N
59+
for j in 1:i-1
60+
PSM[i, j] = sum(A[i,:] .== A[j,:]) / M
61+
PSM[j, i] = PSM[i, j]
62+
end
63+
end
64+
```
65+
and find the optimal partition which minimizes the lower bound of the variation of information:
66+
```julia
67+
mink = minimum([length(m.weights) for m in models])
68+
maxk = maximum([length(m.weights) for m in models])
69+
70+
(peassignments, _) = pointestimate(PSM, method = :average, mink = mink, maxk = maxk)
71+
```
72+
The grouping wich minimizes the lower bound of the variation of information is illustrated in the following image:
73+
![alt text](pointestimate.png "Point Estimate")
274

375
[![Build Status](https://travis-ci.org/trappmartin/BayesianNonparametrics.jl.svg?branch=master)](https://travis-ci.org/trappmartin/BayesianNonparametrics.jl)
476

REQUIRE

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
julia 0.5
2+
Distributions
3+
Combinatorics

demo/DirichletProcessMixture.jl

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using BayesianNonparametrics
2+
3+
(X, Y) = bloobs(randomize = false)
4+
5+
μ0 = vec(mean(X, 1))
6+
κ0 = 5.0
7+
ν0 = 9.0
8+
Σ0 = cov(X)
9+
10+
H = WishartGaussian(μ0, κ0, ν0, Σ0)
11+
12+
model = DPM(H)
13+
initialisation = KMeansInitialisation(k = 10)
14+
15+
modelBuffer = init(X, model, initialisation)
16+
models = train(modelBuffer, DPMHyperparam(), Gibbs(maxiter = 500))
17+
18+
densities = Float64[m.energy for m in models]
19+
activeComponents = Int[sum(m.weights .> 0) for m in models]
20+
assignments = [m.assignments for m in models]
21+
22+
A = reduce(hcat, assignments)
23+
(N, D) = size(X)
24+
25+
PSM = zeros(N, N)
26+
M = size(A, 2)
27+
for i in 1:N
28+
for j in 1:i
29+
PSM[i, j] = sum(A[i,:] .== A[j,:]) / M
30+
PSM[j, i] = sum(A[i,:] .== A[j,:]) / M
31+
end
32+
end
33+
34+
mink = minimum([length(m.weights) for m in models])
35+
maxk = maximum([length(m.weights) for m in models])
36+
37+
(peassignments, _) = pointestimate(PSM, method = :average, mink = mink, maxk = maxk)

pointestimate.png

24 KB
Loading

posteriorSamples.gif

448 KB
Loading

src/BayesianNonparametrics.jl

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
module BayesianNonparametrics
22

3-
# package code goes here
3+
using Distributions, Combinatorics, Clustering, ProgressMeter
4+
5+
include("common.jl")
6+
include("math.jl")
7+
include("utils.jl")
8+
include("datasets.jl")
9+
include("distributions.jl")
10+
include("distfunctions.jl")
11+
include("inits.jl")
12+
include("inference.jl")
13+
include("models.jl")
14+
include("dpmm.jl")
15+
include("vipointestimate.jl")
416

517
end # module

src/common.jl

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
export init, train
2+
3+
"abstract Hyperparameters"
4+
abstract AbstractHyperparam;
5+
6+
"Abstract Model Data Object"
7+
abstract AbstractModelData;
8+
9+
"Abstract Model Buffer Object"
10+
abstract AbstractModelBuffer;
11+
12+
abstract ModelType;
13+
14+
abstract InitialisationType;
15+
16+
abstract PosteriorInference;
17+
18+
function init(X, model::ModelType, init::InitialisationType)
19+
throw(ErrorException("Initialisation $(init) for $(model) is not available."))
20+
end
21+
22+
function extractpointestimate(B::AbstractModelBuffer)
23+
throw(ErrorException("No point estimate available for $(typeof(B))."))
24+
end
25+
26+
function sampleparameters!(B::AbstractModelBuffer, P::AbstractHyperparam)
27+
throw(ErrorException("Parameter sampling for $(typeof(B)) using $(typeof(P)) is not available."))
28+
end
29+
30+
function gibbs!(B::AbstractModelBuffer)
31+
throw(ErrorException("Gibbs sampling for $(typeof(B)) is not available."))
32+
end
33+
34+
function slicesample!(B::AbstractModelBuffer)
35+
throw(ErrorException("Slice sampling for $(typeof(B)) is not available."))
36+
end
37+
38+
function variationalbayes!(B::AbstractModelBuffer)
39+
throw(ErrorException("Variational inference for $(typeof(B)) is not available."))
40+
end
41+
42+
function train(B::AbstractModelBuffer, P::AbstractHyperparam, I::PosteriorInference)
43+
throw(ErrorException("Posterior inference for $(typeof(B)) is not available."))
44+
end

src/datasets.jl

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export bloobs
2+
3+
function bloobs(;centers = 3, samples = 100, randomize = true)
4+
5+
μ = reduce(hcat, [rand(Uniform(-10, 10), centers) for i in 1:2])
6+
Σ = reduce(hcat, [rand(Uniform(0.5, 4), centers) for i in 1:2])
7+
8+
samplespcenter = ones(Int, centers) * round(Int, samples / centers)
9+
10+
for i = 1:(centers % samples)
11+
samplespcenter[i] += 1
12+
end
13+
14+
X = reduce(hcat, [rand(MvNormal(ones(2) .* μ[i], eye(2) .* Σ[i]), samplespcenter[i]) for i in 1:centers])'
15+
Y = reduce(vcat, [ones(Int, samplespcenter[i]) * i for i in 1:centers])
16+
17+
ids = collect(1:size(X, 1))
18+
if randomize
19+
shuffle!(ids)
20+
end
21+
22+
X = X[ids,:]
23+
Y = Y[ids]
24+
25+
return (X, Y)
26+
end

0 commit comments

Comments
 (0)