-
Notifications
You must be signed in to change notification settings - Fork 221
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
Wrong prediction results on multivariate params #1352
Comments
I can confirm that I managed to re-produce the issue reported. Anyone knows what this might be related? @TuringLang/turing |
It seems like the following lines is causing the issue: Turing.jl/src/inference/Inference.jl Lines 755 to 757 in 6db5962
If a variable is a vector, then @devmotion @cpfiffer Do any of you have an idea of the "appropriate" functionality to use to ensure that we can only set the values that are present? The following snippet demonstrates the issue: julia> x = randn(2, 100);
julia> y = [1 + 2 * a + 3 * b for (a,b) in eachcol(x)];
julia> m = simple_linear(x, y);
julia> chain = sample(m, NUTS(), 1000);
julia> var_info = Turing.VarInfo(m);
julia> c = chain[1];
julia> v = :coef;
julia> md = var_info.metadata;
julia> vn = first(md[v].vns)
coef
julia> c.name_map.parameters
4-element Array{String,1}:
"coef[1]"
"coef[2]"
"error"
"intercept" |
I think there needs to be a I can't actually find where we've overloaded It needs to be added back in so |
Yeah exactly. I also can't seem to be able to find the previous overload 😕 |
But it seems like the |
It's now fixed on julia> using Turing
[ Info: Precompiling Turing [fce5fe82-541a-59a6-adf8-730c64b5f9a0]
julia> using Turing.Inference: predict
julia> @model function simple_linear(x, y)
intercept ~ Normal(0,1)
## now works
coef ~ MvNormal(2, 1)
## now works
# coef ~ filldist(Normal(0,1), 2)
## but this version works fine
# coef = Vector(undef, 2)
# for i in axes(coef, 1)
# coef[i] ~ Normal(0,1)
# end
## this works too
# coef1 ~ Normal(0,1)
# coef2 ~ Normal(0,1)
# coef = [coef1, coef2]
coef = reshape(coef, 1, size(x,1))
mu = intercept .+ coef * x |> vec
error ~ truncated(Normal(0,1), 0, Inf)
y ~ MvNormal(mu, error)
end;
julia> # simple linear transformation
x = randn(2, 100);
julia> y = [1 + 2 * a + 3 * b for (a,b) in eachcol(x)];
julia> m = simple_linear(x, y);
julia> chain = sample(m, NUTS(), 1000);
┌ Info: Found initial step size
└ ϵ = 0.00625
julia> p = predict(simple_linear(x, missing), chain);
julia> # prediction correctness depends on how multivariate params were constructed
@show y[1]
y[1] = -1.5372850579938522
-1.5372850579938522
julia> @show p["y[1]"].data |> mean # should be close to y[1] above
(p["y[1]"]).data |> mean = -1.537285141344423
-1.537285141344423
julia> @show p["y[1]"].data |> std # sould be close to 0.0
(p["y[1]"]).data |> std = 1.2985395670414417e-6
1.2985395670414417e-6 |
An overload of |
Right, I only left in the |
Hi, so I was running into some issues with predict exactly as described here, then found this thread which was very related. It seems like this approach coef = Vector(undef, 2)
for i in axes(coef, 1)
coef[i] ~ Normal(0,1)
end Does not actually work (anymore). The first post implied it used to work (presumably prior to this change?), which was odd. I can confirm the other approaches do actually work. However, this particular approach (the one that doesn't work) is certainly most convenient in my case. As a side note, to try the model above with the for-loop prior initialization method, I had to make some additional changes too, because when I used the model exactly as given above, but then with the 3rd method uncommented, it gave me errors. So I fixed that error for now by simply not using the coef reshape, and doing: mu = intercept .+ coef[1] * x[1,:] .+ coef[2]*x[2,:] |> vec Does anyone have any idea why this method of constructing priors no longer works (to give the correct results) with the predict method? |
I can confirm that the following now has issues: coef = Vector(undef, 2)
for i in axes(coef, 1)
coef[i] ~ Normal(0,1)
end Thank you @mgmverburg for bringing attention to this! The issue comes down to Turing.jl/src/inference/Inference.jl Lines 618 to 625 in b9db77c
I'm not sure how this got through though. Either I completely forgot to check this implementation and the comment above saying I got a meeting for the next hour, but I'll get this sorted ASAP afterwards 👍 |
EDIT: The below "hotfix" should not be used anymore. This has been fixed in Here's a "hotfix" for the issue: using Turing
import Random
function Turing.Inference.transitions_from_chain(
rng::Random.AbstractRNG,
model::Turing.Model,
chain::MCMCChains.Chains;
sampler = DynamicPPL.SampleFromPrior()
)
vi = Turing.VarInfo(model)
chain_idx = 1
transitions = map(1:length(chain)) do sample_idx
# NEW! Using the "recent" improvement to `setval!` in to do the job + the change in `_setval!` below.
DynamicPPL.setval!(vi, chain, sample_idx, chain_idx)
model(rng, vi, sampler)
# Convert `VarInfo` into `NamedTuple` and save
theta = DynamicPPL.tonamedtuple(vi)
lp = Turing.getlogp(vi)
Turing.Inference.Transition(theta, lp)
end
return transitions
end
function DynamicPPL._setval_kernel!(vi::DynamicPPL.AbstractVarInfo, vn::DynamicPPL.VarName, values, keys)
string_vn = string(vn)
string_vn_indexing = string_vn * "["
indices = findall(keys) do x
string_x = string(x)
return string_x == string_vn || startswith(string_x, string_vn_indexing)
end
if !isempty(indices)
sorted_indices = sort!(indices; by=i -> string(keys[i]), lt=DynamicPPL.NaturalSort.natural)
val = mapreduce(vcat, sorted_indices) do i
values[i]
end
DynamicPPL.setval!(vi, val, vn)
DynamicPPL.settrans!(vi, false, vn)
else
# NEW! If `vn` is not present in `keys`, i.e. no value was given, we assume it should be resampled.
# Alternatively we can whether or not to resample or warn a keyword argument.
DynamicPPL.set_flag!(vi, vn, "del")
end
end This requires one PR to Turing.jl and DynamicPPL.jl, but I'll try to get those up today. |
Currently if one calls `DynamicPPL._setval!(vi, vi.metadata, values, keys)` , then only those values present in `keys` will be set, as expected, but the variables which are _not_ present in `keys` will simply be left as-is. This means that we get the following behavior: ``` julia julia> using Turing julia> @model function demo(x) m ~ Normal(0, 1) for i in eachindex(x) x[i] ~ Normal(m, 1) end end demo (generic function with 1 method) julia> m_missing = demo(fill(missing, 2)); julia> var_info_missing = DynamicPPL.VarInfo(m_missing); julia> var_info_missing.metadata.m.vals 1-element Array{Float64,1}: 0.7251417347423874 julia> var_info_missing.metadata.x.vals 2-element Array{Float64,1}: 1.2576791054418153 0.764913349211408 julia> var_info_missing.metadata.m.vals # ✓ new value 1-element Array{Float64,1}: 0.0 julia> var_info_missing.metadata.x.vals # ✓ still the same value 2-element Array{Float64,1}: 1.2576791054418153 0.764913349211408 julia> m_missing(var_info_missing) # Re-run the model with new value for `m` julia> var_info_missing.metadata.x.vals # × still the same and thus not reflecting the change in `m`! 2-element Array{Float64,1}: 1.2576791054418153 0.764913349211408 ``` _Personally_ I expected `x` to be resampled since now parts of the model has changed and thus the sample `x` is no longer representative of a sample from the model (under the sampler used). This PR "fixes" the above so that you get the following behavior: ``` julia julia> var_info_missing.metadata.x.vals 2-element Array{Float64,1}: 1.2576791054418153 0.764913349211408 julia> DynamicPPL.setval!(var_info_missing, (m = 0.0, )); julia> var_info_missing.metadata.x.vals 2-element Array{Float64,1}: 1.2576791054418153 0.764913349211408 julia> m_missing(var_info_missing) julia> var_info_missing.metadata.x.vals 2-element Array{Float64,1}: -2.0493130638394947 0.3881955730968598 ``` This was discoverd when debugging TuringLang/Turing.jl#1352 as I want to move `Turing.predict` over to using `DynamicPPL.setval!` and it also has consequences for `DynamicPPL.generated_quantities` which uses `DynamicPPL.setval!` under the hood and thus suffer from the same issue. There's an alternative: instead of making this the default-behavior, we could add `kwargs...` to `setval!` which includes `resample_missing::Bool` or something. I'm also completely fine with a solution like that 👍
* predict now uses set_and_resample! introduced in recent DynamicPPL * only attempt to set parameters in predict * added some tests to cover the previous failure cases * removed some redundant namespace specifier * version bump * Apply suggestions from code review Co-authored-by: David Widmann <devmotion@users.noreply.github.com> * bumped version for DPPL in test * changed variable name in predict as per suggestion by @devmotion * version bump * disable failing test Co-authored-by: David Widmann <devmotion@users.noreply.github.com>
Aight, so finally the issue has been resolved. |
The
predict
function outputs wrong results depending on how the multivariate parameters are constructed. I present below a simple linear regression problem which always converge fine, but whosepredict
results depend on how thecoef
parameter is constructed.I'm trying this on Julia 1.4.1 with Turing 0.13.0.
The text was updated successfully, but these errors were encountered: