brian-j-smith/Mamba.jl

Dirichlet Process

gcgibson opened this issue · 8 comments

Hi All, I really like Julia so far and Mamba is a great addition. I have been trying my hand at a dirichlet process implementation using the stick breaking construction.

#!/Applications/Julia-0.5.app/Contents/Resources/julia/bin/julia
using Mamba


## Data
line = Dict{Symbol, Any}(
  :x => [1, 2, 3, 4, 5],
  :y => [1, 3, 3, 3, 5]
)
line[:xmat] = [ones(5) line[:x]]


function stick_breaking(beta)
	cum_prod = 1 - beta[1]
	weights = ones(5)
	for i = 2:5
		weights[i] = beta[i]*cum_prod
		cum_prod = cum_prod*(1-beta[i])
	end
	weights
end

model = Model(

  y = Stochastic(1,
    (mu) ->  Normal(mean(mu), 1),
    false
  ),

  mu = Logical(5,
  	(beta) -> stick_breaking(beta)
    ,
    true
  ),

  beta = Stochastic(5,
    () -> Beta(1, 2),
    true
  )

)

## Initial Values
inits = [
  Dict{Symbol, Any}(
    :y => line[:y],
    :beta => randn(5)*1
  )
]


## Sampling Scheme
scheme = [NUTS(:beta)]

setsamplers!(model, scheme)


## MCMC Simulations
sim = mcmc(model, line, inits, 10000, burnin=2500, thin=2, chains=1)
describe(sim)


## Posterior Predictive Distribution
ppd = predict(sim, :x)
describe(ppd)

Not sure why the code block formatting isn't working, however I get the following error

ERROR: LoadError: MethodError: Cannot convert an object of type Array{Float64,1} to an object of type Array{Float64,5}
This may have arisen from a call to the constructor Array{Float64,5}(...),
since type constructors fall back to convert methods.

So I assume there is something up with the dimensions of the initial values, but I don't see what is wrong.

The error is because of the way you defined stochastic nodes.

Stochastic(n, ...) defines an n-dimensional node. Your mu and beta are 1-dimensional arrays not 5-dimensional arrays (i.e. matrices are 2-dimensional, etc).


You can do code blocks by using triple back-tick (i.e. ``` to open block and ``` to close). If you put ```julia as open block you will get julia syntax highlighting.

single backtick will give you inline code code . So does double backtick, but I'm not sure if that is any different.

Thanks, that is awesome! Here is my updated code for DP, I feel like I am very close but I'm still missing something, it seems is doesn't like me multiplying the weight by the Normal
ERROR: LoadError: MethodError: no method matching *(::Float64, ::Distributions.Normal{Mamba.ScalarStochastic})

so how can I take a vector of weights and produce a Gaussian mixture likelihood?

#!/Applications/Julia-0.5.app/Contents/Resources/julia/bin/julia
using Mamba


## Data
data = Dict{Symbol, Any}(
  :y => [1, 3, 3, 3, 5]
)

number_of_components = 5


function stick_breaking(beta)
  cum_prod = 1 - beta[1]
  weights = ones(5)
  weights[1] = beta[1]
  for i = 2:number_of_components
    weights[i] = beta[i]*cum_prod
    cum_prod = cum_prod*(1-beta[i])
  end
  weights
end


model = Model(

  y = Stochastic(1,
    (weights,mu,tau) -> UnivariateDistribution[weights[i]*Normal(mu, tau) for i in 1:number_of_components]

  ),
  weights = Logical(1,
    (beta) -> stick_breaking(beta),
    false
  ),

  beta = Stochastic(1,
    () -> Beta(1, 2)
    ),

  mu = Stochastic(
    () -> Normal(0,5)
  ),

  tau = Stochastic(
    () -> Gamma(1,1)
  )

)

## Initial Values
inits = [
  Dict{Symbol, Any}(
    :y => data[:y],
    :beta =>rand(number_of_components)*1,
    :mix_dists => rand(number_of_components),
    :mu => 1,
    :tau => 1
  )
]


## Sampling Scheme
scheme = [NUTS(:beta)]

setsamplers!(model, scheme)


## MCMC Simulations
sim = mcmc(model, data, inits, 10000, burnin=2500, thin=2, chains=1)
describe(sim)

A Stochastic node has to be a take a function that returns a Distribution. In your function you are trying to multiply a distribution by a weight which is not defined i.e.

julia> 5 * Normal()
ERROR: MethodError: no method matching *(::Int64, ::Distributions.Normal{Float64})
Closest candidates are:
  *(::Any, ::Any, ::Any, ::Any...) at operators.jl:424
  *(::Real, ::Complex{Bool}) at complex.jl:254
  *(::Real, ::Complex) at complex.jl:266
  ...

What you want is a mixture distribution which is defined in Distributions.jl ie

julia> mu = collect(1:5);
       sigma = collect(1:5);
       w = rand(Dirichlet(ones(5)));
       MixtureModel( [Normal(mu[i], sigma[i]) for i in 1:5], w)
MixtureModel{Distributions.Normal{Float64}}(K = 5)
components[1] (prior = 0.1456): Distributions.Normal{Float64}=1.0, σ=1.0)
components[2] (prior = 0.2764): Distributions.Normal{Float64}=2.0, σ=2.0)
components[3] (prior = 0.4236): Distributions.Normal{Float64}=3.0, σ=3.0)
components[4] (prior = 0.0463): Distributions.Normal{Float64}=4.0, σ=4.0)
components[5] (prior = 0.1081): Distributions.Normal{Float64}=5.0, σ=5.0)

Thanks!

Also just want to point out that until recently you couldn't use MixtureModel in Mamba because there were some missing functions in Distributions.jl which was fixed recently, so I am not sure if you need to checkout Distributions.jl master or if they have already put out another release. (see #122)

Thanks! when I finish this up should I submit a PR to the examples? Would other people be interested in this?

Feel free to contribute an example, try to match the format of the others.