-
Notifications
You must be signed in to change notification settings - Fork 222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Design/improve/unifty sample()
interface
#746
Comments
Does it make sense that we differentiate between initial value and resume sampling? Why not get rid of the resume interface and only provide a clean way to set an initial value? |
Good idea! Then the only question leaves here is how to resume adapted sampler. |
I think we can use function sample(rng::AbstractRNG,
ℓ::ModelType, s::SamplerType,
N::Integer; args...
) where {ModelType, SamplerType, TransitionType}
t = Array{TransitionType, 1}(undef, N)
for i=1:N
t[i] = step(rng::AbstractRNG, ℓ:: ModelType, s::Sampler, N::Integer; args...)
end
# convert `t` into `MCMCChains.Chain`.
end
where
|
Related: #723 |
@cpfiffer Does it make sense moving this |
I don't see why not. The function looks pretty small, and everything's parametric so there's not really any dependencies to speak of. The JuliaStan people may have some opinions though, so allow me to summon the JuliaStan kingpin. @goedman, we're working on something of an overhaul of the Turing internals for our 0.7 release and wanted to know what you think about including a function in MCMCChains like the following: function sample(rng::AbstractRNG,
ℓ:: ModelType, s::SamplerType,
N::Integer; args...
) where {ModelType, SamplerType, TransitionType}
t = Array{TransitionType, 1}(undef, N)
for i=1:N
t[i] = step(rng::AbstractRNG, ℓ::ModelType, s::SamplerType, N::Integer; args...)
end
return Chains(t ...)
end It's designed such that AdvancedHMC and Turing can work independently of one another. I'd view it as basically another MCMCChains constructor. Do you have any issues with including this in MCMCChains? |
That's perfectly ok. |
I've actually started to look at the AHMC sampler would love to spend some time with it next week and try it out! If the performance is reasonably close to DynamicHMC that would be a huge leap forward! |
@yebai Does this interface need some additional components? I've added all the bits to a draft of MCMCChains that includes the addition of I'm not sure on what Additionally, many of the samplers have some initial setup of some kind that isn't caught well by the "iterate through each step" methodology above. I added some "empty functions" that specific samplers can overload if they need to. It would be really cool if this could catch the general cases for all the samplers. For example: function sample(
rng::AbstractRNG,
ℓ::ModelType,
s::SamplerType,
N::Integer;
kwargs...
) where {ModelType<:AbstractModel,
SamplerType<:AbstractSampler,
TransitionType<:AbstractTransition} #Not sure how to get TransitionType into the function
t = Array{TransitionType, 1}(undef, N)
# Perform any necessary setup.
sample_init!(rng, ℓ, s, N; kwargs...)
# Step through the sampler.
for i=1:N
t[i] = step(rng::AbstractRNG, ℓ::ModelType, s::SamplerType, N::Integer; kwargs...)
end
# Wrap up the sampler.
sample_end!(rng, ℓ, s, N; kwargs...)
# Placeholder while I figure out what t looks like.
return Chains(t)
end In summary, this is what I see the sampling interface looking like:
I also have wrapper functions for the above that allow for the sampling of multiple chains, and Comments, pointers, and corrections welcomed. |
Some desired refactoring goals for
Related https://github.com/TuringLang/Turing.jl/issues/634 #602 #582 #599 #689 Here are some relevant HMC, SGLD and SGHMC:
Gibbs
PGibbs
SMC
Stan
|
@cpfiffer This is meant to replace the
I think we can inherit struct Model{pvars, dvars, F, TData, TDefaults} <: Distributions.Sampleable |
I've poked around a bit more and come up with an example of how I might think about modifying one of the samplers to fit this interface style. The changes below would fit fairly well into the common sampler interface function noted above, and give other potential Turing users a better example of how to construct modular sampler/model/general inference tools. Using The
|
function step(model, spl::Sampler{<:SMC}, vi::VarInfo) |
It looks only slightly more complex for the HMC
samplers. In this case we'd need to keep all the values from Sample(vi, spl).vals
separately and then pull out varnames at the end. This would be doable during the sample_end!
phase, or by making a function with the signature MCMCChains.Chains(t::TransitionType, spl::HamiltonianSampler)
. The spl
here is necessary to get the vi
info to the Chains
object.
The Model
type
As noted above, this should have to change too much:
struct Model{pvars, dvars, F, TData, TDefaults} <: Distributions.Sampleable
The Sampler
type
In order to remove Sampler.info
, we could just declare special Sampler
types for each sampler, and store each sampler's necessary fields in the struct itself. The current definition of the SMC
sampler dumps some random stuff into the generic Sampler
struct:
function Sampler(alg::SMC, s::Selector)
info = Dict{Symbol, Any}()
info[:logevidence] = []
return Sampler(alg, info, s)
end
For reference, the Sampler
struct is
mutable struct Sampler{T} <: AbstractSampler
alg :: T
info :: Dict{Symbol, Any} # sampler infomation
selector :: Selector
end
The way I might think about changing this set up is to remove the generic Sampler
type (or maybe replace it with the most bare-bones version possible for interface purposes) and force each sampler type to create it's own type:
mutable struct SMCSampler{T} <: AbstractSampler
alg :: T
logevidence :: Float64
selector :: Selector
particles :: ParticleContainer{Trace} #
end
The gist here would be to just move everything currently stored in any sampler's spl.info
entry and put it directly into the struct. An overload of the sample_init!
and sample_end!
functions would be necessary, as well:
function sample_init!(::AbstractRNG,
model::ModelType,
s::SMCSampler,
::Integer;
kwargs...
) where ModelType<:Distributions.Sampleable
# Instantiate the ParticleContainer.
s.particles = ParticleContainer{Trace}(model)
push!(s.particles, spl.alg.n_particles, spl, VarInfo())
end
* integrate AHMC * make functions safe and add AHMC in REQUIRE * implement experimental interface for ANUTS * fix init_theta masking * update to AHMC's new interface * fix type constraint * replace find_good_eps * replace low-level HMC functions * remove old comments * remove rev and log funcs * remove returned value from gen_grad_func * replace adapation with AHMC code * remove adapation code * remove implict sampler constructors * use integrated sample() if AHMC if possible * delete ahmc.jl * add version to AHMC * make AHMC version master * revert version of AHMC * rename binding AdvancedHMC -> AHMC * add AHMC#master to deps * improve typing of mh_accept * merge hmcda.jl and nuts.jl into hmc.jl * replace conditions with multiple dispatch * not storing adapt_conf in spl.info * relax Sampler to AbstractSampler * use AHMC.transition instead of copied version * tweak comments * mvoe hmc_core.jl into hmc.jl * clean up * clean up hmc.jl * bugfix * Remove some redundant docs. * Some formating fixes - no functionality change. * epsilon ==> ϵ. * tau ==> n_leapfrog. * add type for metricT * remove redudant .logp * fix typo in comment * unroll first and rest iterations for steps * Rename all: epsilon ==> ϵ * Remove global STAN_DEFAULT_ADAPT_CONF and related types. * Disable stan interface temporarily before fixing #746 * Ensure h.metric has the same dim as θ. * Bugfix for using `step` in Gibbs. * Merge SGLD into SGHMC - no functionality change. * Add default metric type to SGLD and SGHMC. * Merge test/SGLD into test/SGHMC - no functionality change.
commit beda95d Author: Kai Xu <[email protected]> Date: Mon May 6 14:09:50 2019 +0100 Replace HMC related codes using AHMC (#739) * integrate AHMC * make functions safe and add AHMC in REQUIRE * implement experimental interface for ANUTS * fix init_theta masking * update to AHMC's new interface * fix type constraint * replace find_good_eps * replace low-level HMC functions * remove old comments * remove rev and log funcs * remove returned value from gen_grad_func * replace adapation with AHMC code * remove adapation code * remove implict sampler constructors * use integrated sample() if AHMC if possible * delete ahmc.jl * add version to AHMC * make AHMC version master * revert version of AHMC * rename binding AdvancedHMC -> AHMC * add AHMC#master to deps * improve typing of mh_accept * merge hmcda.jl and nuts.jl into hmc.jl * replace conditions with multiple dispatch * not storing adapt_conf in spl.info * relax Sampler to AbstractSampler * use AHMC.transition instead of copied version * tweak comments * mvoe hmc_core.jl into hmc.jl * clean up * clean up hmc.jl * bugfix * Remove some redundant docs. * Some formating fixes - no functionality change. * epsilon ==> ϵ. * tau ==> n_leapfrog. * add type for metricT * remove redudant .logp * fix typo in comment * unroll first and rest iterations for steps * Rename all: epsilon ==> ϵ * Remove global STAN_DEFAULT_ADAPT_CONF and related types. * Disable stan interface temporarily before fixing #746 * Ensure h.metric has the same dim as θ. * Bugfix for using `step` in Gibbs. * Merge SGLD into SGHMC - no functionality change. * Add default metric type to SGLD and SGHMC. * Merge test/SGLD into test/SGHMC - no functionality change. commit 502027b Author: Mohamed Tarek <[email protected]> Date: Sun Apr 28 19:39:57 2019 +1000 fix some bug in `eval_num` from #740 (#770)
Thanks, @cpfiffer. The plan looks sensible. To summarise, the new design minimises the generic
It is also worth noting that only some algorithms make use of
So perhaps we can provide a default struct Sampler <: AbstractSampler # Default for all algorithms
get_selector(spl::AbstractSampler) = Selector() # callback interface 1
get_algstr(spl::AbstractSampler) = "AlgorithmName" # callback interface 2
mutable struct SMCSampler{SMC} <: AbstractSampler
log_evidence::Float64
end
get_selector(spl::SMCSampler) = ...
get_algstr(spl::SMCSampler) = ... In general, the Below are all Turing.jl/src/inference/AdvancedSMC.jl Lines 35 to 39 in a4f78b6
Turing.jl/src/inference/AdvancedSMC.jl Lines 114 to 118 in a4f78b6
Turing.jl/src/inference/gibbs.jl Lines 40 to 68 in a4f78b6
Turing.jl/src/inference/hmc.jl Lines 204 to 211 in a4f78b6
Lines 38 to 41 in a4f78b6
Lines 51 to 68 in a4f78b6
Turing.jl/src/inference/AdvancedSMC.jl Lines 310 to 340 in a4f78b6
Turing.jl/src/inference/AdvancedSMC.jl Lines 507 to 526 in a4f78b6
|
One thing I'm a little fuzzy on is how the proposed sampler API works with compositional Gibbs, particularly as it relates to how you declare sample sizes in the components. Currently, you can do this: chn = sample(model, Gibbs(1000, HMC(1, 0.2, 3, :x), PG(20, 1, :y))) The sampler API forces the number of steps to be outside the chn = sample(model, Gibbs(HMC(0.2, 3, :x), PG(20, :y)), 1000) Is this an issue? I presume this would force |
It would be nice to have some support for running
|
Or # Default
chn = sample(model, Gibbs(HMC(0.2, 3, :x), PG(20, :y), (1, 1)), 1000)
# HMC - 2 steps, PG - 2 steps
chn = sample(model, Gibbs(HMC(0.2, 3, :x), PG(20, :y), (2, 2)), 1000) ? |
I like @xukai92's idea better, then there's no need to handle this weird edge case on the interface side, since |
Fixed by #793 |
Questions and my current thoughts
sample(..., init::Array{Union{Missing, Array{Float64,1}},1}=[missing])
init
, and assign those variables which are not missed tovi[spl]
init
is the user's responsibilityAdvancedHMC.Adaptation
sample(..., adapt::Union{Nothing,AbstractAdaptor}=nothing)
sample(..., resume::Union{Nothing,AbstractChain}=nothing)
AdvancedHMC.Adaptation
, the adapted sampler is just the types but not the adapter or any other thing. So if we can make the types in Turing to be 1:1 to those types, we can resume the adapted sampler.Related issue:
The text was updated successfully, but these errors were encountered: