-
Notifications
You must be signed in to change notification settings - Fork 220
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
Evaluating the posterior #400
Comments
Hi, the joint is proportional to the posterior. You can compute it the following way @model gdemo(x,s,m) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x[1] ~ Normal(m, sqrt(s))
x[2] ~ Normal(m, sqrt(s))
return s, m
end
model = gdemo(x,1.2,0.5)
res = model()
println(res.logp) |
Wonderful, thank you. Just to make sure I understand, does this create a model where x, s, and m are all assumed to be observed, right? So in order to sample from this model and evaluate the posterior, I need to create two separate models? |
Yes
If you just want to sample from the model (by some samplers) and check the posterior, in stead of evaluating the posterior of some given s and m, you only need the first model. @model gdemo(x) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x[1] ~ Normal(m, sqrt(s))
x[2] ~ Normal(m, sqrt(s))
return s, m
end
mf = gdemo([1.5, 2.0])
chain = sample(mf, HMC(100, 0.2, 3)) # sample this model using HMC for 100 sampler with 0.2 leapfrog stepsize for 3 steps in each iteration
print(chain[:s])
print(chain[:m])
print(chain[:lp]) # log posterior |
Thank you again, I'm starting to understand how this all works. My only problem now is that evaluating the joint using @emilemathieu's code is 77,000 times slower than hand-coding it: using Turing
using BenchmarkTools
Turing.set_verbosity(0)
@model gdemo(x,s,m) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x[1] ~ Normal(m, sqrt(s))
x[2] ~ Normal(m, sqrt(s))
return s, m
end
function gdemo_logp(s, m)
x = [1.5, 2.0]
mf = gdemo(x, s, m)
res = Base.invokelatest(mf)
return res.logp[1]
end
function byhand_logp(s, m)
x = [1.5, 2.0]
sprior = InverseGamma(2,3)
mprior = Normal(0, √s)
likelihood = Normal(m, √s)
lp = logpdf(sprior, s) + logpdf(mprior, m) + logpdf(likelihood, x[1]) + logpdf(likelihood, x[2])
return lp
end
; gdemo_logp(1.2, 0.5)
byhand_logp(1.2, 0.5)
@benchmark byhand_logp(1.2, 0.5)
@benchmark gdemo_logp(1.2, 0.5)
Any ideas on how to evaluate the joint in a reasonable amount of time? |
I was able to get it down to “only” 100x slower than hard-coded, by manipulating the VarInfo object directly. It's not pretty, and still quite slow, so I'd welcome any suggestions. import Turing: VarName, sym, setval!, vns, setlogp!
@model gdemox(x) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x[1] ~ Normal(m, sqrt(s))
x[2] ~ Normal(m, sqrt(s))
return s, m
end
mf = gdemox([1.5, 2.0])
vi = mf()
function gdemox_logp(s, m)
names = Turing.vns(vi)
namedict = Dict{Symbol,VarName}()
setlogp!(vi, 0.0)
for n in names
namedict[sym(n)] = n
end
logp_before = vi.logp[1]
setval!(vi, s, namedict[:s])
setval!(vi, m, namedict[:m])
mf(vi)
return vi.logp[1]
end
gdemox_logp(1.2, 0.5)
@benchmark gdemox_logp(1.2, 0.5)
|
This is a good question. They main reason the way you evaluate posterior is slow is because the function There is another way to do the evaluation, which takes the use of This can be done by: @model gdemo2(x) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x[1] ~ Normal(m, sqrt(s))
x[2] ~ Normal(m, sqrt(s))
return s, m
end
mf = gdemo2(x)
# Generate a VarInfo by sampling from pior
vi = Base.invokelatest(mf)
# NOTE: you can try print(vi) to see what's inside...
function gdemo_logp2(s, m)
vi.logp[1] = 0 # set log posterior to 0
vi.vals[1][1] = s # set s as you want
vi.trans[1][1] = false # set the value of s as non-transformed
vi.vals[1][2] = m # set m as you want
# Evalute using vi
res = Base.invokelatest(mf, vi, nothing)
return res.logp[1]
end NOTE: on my machine the mean time for this is 12.998 μs which is still higher than they by_hand one on my machine which is 233.460 ns. However as you can see we don't provide an easy-to-use interface to build vi yet as it is designed to be use internally. Also in the use case we considered, as the posterior is only evaluated internally, the compilation is only done once so it wasn't a big problem. |
Well you just figured it our when I was typing... |
100x seems a big overhead here. @yebai Do you have any idea on why and how to make it faster? Seems quite significant here. |
I found this is interesting. Do you know why? function gdemo_logp(s, m)
vi.logp = 0
vi.logp += logpdf(InverseGamma(2,3), s)
vi.logp += logpdf(Normal(0, √s), m)
vi.logp += logpdf(Normal(m, sqrt(s)), x[1])
vi.logp += logpdf(Normal(m, sqrt(s)), x[2])
vi.logp
end
But function gdemo_logp(s, m)
logp = 0
logp += logpdf(InverseGamma(2,3), s)
logp += logpdf(Normal(0, √s), m)
logp += logpdf(Normal(m, sqrt(s)), x[1])
logp += logpdf(Normal(m, sqrt(s)), x[2])
logp
end
It seems that the slowness is due to the use of |
@ChrisRackauckas Do you know why the performances in my comment above differs a lot? Is using a filed of a composite type very expensive? |
That seems likely to be due to type instability. They’re both type unstable since you’re initialising to an integer (0) rather than a float. Try using the '@code_warntype' to see what the problem might be. |
Well I can change the 2nd one to a generic type but the 2nd actually much faster. |
How is the first one parameterized? If the type is parameterized or set concrete on that field, then |
Thanks Maxime and Chris, the results above are come from type instability issue. |
Closed in favour of #997 |
What is the best way to evaluate the posterior for a set of parameters? For example, with
how do I evaluate the unnormalised posterior, say, at s=1.2 m=0.5?
The text was updated successfully, but these errors were encountered: