-
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
Running model without data returns error #544
Comments
I’ll have a look. Does it work if x is a scalar? |
Probably not, since we are indexing |
This problem is related to the fact that we do not know the dimensions and type of Running the code as: julia> using Turing, Distributions
julia> @model gdemo(x, y) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x ~ Normal(m, sqrt(s))
y ~ Normal(m, sqrt(s))
return x, y
end
julia> g = gdemo()
gdemo_model (generic function with 4 methods)
julia> g()
(0.685690547873451, -1.1972706455914328) works fine with the current version of Turing. However, idealy the user should be able to do something in the lines of julia> @model gdemo1(N, x) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x ~ [Normal(m, sqrt(s)) for n in 1:N]
return x
end
julia> g = gdemo(2)
julia> g() # currently doesn't work as we need x in assume, see below or alternativelly something like: julia> @model gdemo2(x) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
N = length(x)
x[1] ~ Normal(m, sqrt(s))
x[2] ~ Normal(m, sqrt(s))
return x
end
julia> g = gdemo(Array{Float64}(undef, 2))
julia> g() # currently doesn't work as we don't check if x is defined or not in which the user has to ensure that @yebai I think gdemo1 would be the cleaner approach but is more involved as Turing.jl/src/samplers/sampler.jl Lines 76 to 97 in ce780b8
This is related to the broadcasting issue described in #476 and will only be possible once #476 can be resolved. The second solution should be possible but is a slightly hackish solution to the actual problem. |
Potentially controversial opinion: I think that @yebai 's example should break. As @trappmartin says, nothing about
This would resolve the inconsistent / complicated behaviour in the current design whereby sometimes you can run a model that has arguments without providing any arguments, but not always, and whether you can or not depends on exactly what types you have used in your model -- if a model has arguments, you must provide them (there's no reason not to allow default values in the same way that regular functions do though). Moreover, we would still be able to write a stdlib of "prior" models that make no / few assumption about what RVs might be observed, and compose these together to make larger models. We would also be able to provide "observation" models that are a really thin wrapper around prior models that could provide standard scenarios eg. for mixture models one could specify a prior model that returns all of the random variables, and a separate observation model that requires observations are provided but not mixture components. |
I think I generally agree. This would also simply the current compiler code and would be potentially much cleaner. I’m just not quite sure what would be a good syntax. julia> @model gmodel() = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x ~ Normal(m, sqrt(s))
return x
end
julia> @model gdemo(x) = begin
N = length(x)
x ~ [gmodel() for n in 1:N]
end |
There are two separate things going on here though: the way we handle sampling from the prior vs providing data, and how to specify vectors of random variables. A scalar version of your example could be @model prior() = begin
s ~ InverseGamma(2, 3)
m ~ Normal(0, sqrt(s))
x ~ Normal(m, sqrt(s))
return x
end
@model observation(x) = begin
x ~ prior()
end which I think is completely fine. As regards the specification of vectors of RVs, I think that there are a few ways to go about this. I suspect that we should try to avoid upgrading our
@model observation(x) = begin
for n in eachindex(x)
x[n] ~ prior()
end
end is total fine to my mind, if a little bit verbose and should already work, if I'm not mistaken. x .~ [prior() for n in eachindex(x)] The goal here would be to completely avoid having to customise any broadcasting behaviour, so that this would just naturally work. This is a whole separate kettle of fish though. @model observation(x) = begin
x ~ Product([prior() for _ in eachindex(x)])
end (the precise name of the distribution is quite reasonable to have some discussion over). So my thoughts on what we should actually do are refactor the compiler a bit, and worry about vectors of RVs separately. |
I've put the current sampling methodology in the documentation for now, but I'll update if/when it changes. |
It makes a lot of sense to further push the compiler design towards
For this particular case, I think we can leverage the using Turing
@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
g = gdemo([1.5, 2]) # standard version
g = gdemo([missing, missing]) # treat x as parameter and draw from prior This would allow us the both
I think the burden of ensuring dimentionality match between arguments and actual use should be left on the user, for example, if the user writes using Turing
@model gdemo(x) = begin
s ~ InverseGamma(2,3)
m ~ Normal(0,sqrt(s))
x ~ Normal(m, sqrt(s)) # no broadcasting syntax
return s, m
end
g = gdemo(1.0)
g = gdemo(missing) Then, the user shouldn't be doing |
I do like your missing value proposal @yebai -- I think the ideal would be to support both proposals as they're not in conflict. |
I agree that the broadcasting issue and the issue regarding draws from a prior are two independent issues. At the moment the solution of @yebai is the most tangible from my point of view. For the compositional model proposal, we would need to construct something that behaves like a distribution. I see the appeal for that and I think it's the way to go on the long run. Maybe let us create an additional issue to discuss a possible redesign of the compiler. |
Yeah, we've definitely discussed this in other issues (I'm not sure which ones). I do have a habit of bringing the "models should just behave like distributions" thing up on a weekly basis, so apologies for that everyone. |
* Moving over to Documenter.jl groundword. * More documentation cleaning and prep * Moving over to Documenter.jl groundword. * More documentation cleaning and prep * Travis settings updated * Travis fixes * Changed doc/build format to .md * Move to Documenter.jl native HTML builds * Update logo-build.jl * Minor updates as discussed in pull #512 * Moved from Pkg notation to the new Pkg3 notation. * Getting this to serve from `docs/` directory. * Adding `docs/build` files. * Set theme jekyll-theme-cayman * Revert "Set theme jekyll-theme-cayman" This reverts commit 38f5916. * index.md relocation * Commented out unused/index.md * Set up doc/ directory to hold Documenter.jl generated documentation * Testing a second index.md file * Fixing error in make.jl * Changed _config.yml to ignore src/ directory * Small cleanup. * refactoring of compiler * removed data macro * refactoring of tilde macro and fixed minor bug. Observer test broken. * improved comment * changed IS to work with new model function scheme * changed hmc core to work with new model function, changed style accoring to guidelines * changed model function calls * fixing compiler tests * fixed varinfo test * re-added kwarg checking * fixed missing data insertion problem * minor fix * lot of changes, wip * code now uses MacroTools, with a few exceptions * work in progress * more refactoring, solving prior sampling in #446 * removed strange tabs * minor change in particlecontainer test * added fix for multiple return statements * info -> debug * fixed typo * runmodel -> runmodel! * unifying initilistation for unconstrained distributions * changing vi.logp inplace, added some tests * added fix for #475 by implementing a data(Dict, Vector{Symbol}) function * typo * changed default assume behavior to sample from the prior * added more tests for the compiler * changed back to use initialisation instead of prior draws * changed default value of CHUNKSIZE to 10 * Introduce a special algorithm for initializing HMC. * Updates to meet suggestions in #512 * Bug fixes, latex support. * a little cleanup * remove AD auto_tune function and sym_str macro * move io.jl and util.jl from core to utilities * logsum -> logsumexp * remove some unused stuff * remove some type annotation because VarInfo is defined later * fix tests * make logsumexp more efficient * use StatsFuns for logsumexp * clean test REQUIRE * clean up * minor fix * added MacroTools to REQUIRE * Minor fixes + style from meeting * Tidy up AD interface + remove cleandual! and realpart!. (#515) Lots of commits. Some stuff went really wrong. Sorry. * Moving over to Documenter.jl groundword. * Unify AD interface * Use new gradient + rename stuff in hmc_core@ * Let SGHMC and SGLD use reverse-mode * Remove cleandual * More documentation cleaning and prep * Moving over to Documenter.jl groundword. * More documentation cleaning and prep * Travis settings updated * Travis fixes * Changed doc/build format to .md * Move to Documenter.jl native HTML builds * Prevent gradient from mutating it's argument * Minor changes * Fix bug involving types of traces * Resolve a bunch of formatting issues * Some more tidying up * Misc reformatting / removal of redundant code * Fix bug, remove printing, and remove code * Remove redundant code * Fix typo * Refactor leapfrog. * Copy old documentation over * Remove some other stuff that I shouldn't have added * Resolves conflicts with #523 * Small fix. * Small fix. * Update REQUIRE * Fix to move .html files to the gh-pages branch. * Updated .gitignore * Tidying up * Changes for #512. * changed LineNumberNode initialisation, issue #534 * Updated guides to document scalar prior sampling, #544. * Added a 'quick start' page. #512 * Removed git workflow section, added links to more maintained guides. #535 * Update README.md * Better testing for transforms + code which conforms to the style guide (#543) * Test logpdf_with_trans for univariate distributions * Make sure that MvNormal and LogMvNormal tests pass * Adds broken tests * Various other tests + implementation for LogMvNormal * Update Turing.jl * Minor guide updates. * Moving over to Documenter.jl groundword. * Moving over to Documenter.jl groundword. * More documentation cleaning and prep * More documentation cleaning and prep * Travis settings updated * Travis fixes * Changed doc/build format to .md * Move to Documenter.jl native HTML builds * Update logo-build.jl * Minor updates as discussed in pull #512 * Moved from Pkg notation to the new Pkg3 notation. * Getting this to serve from `docs/` directory. * Set theme jekyll-theme-cayman * Revert "Set theme jekyll-theme-cayman" This reverts commit 38f5916. * index.md relocation * Commented out unused/index.md * Set up doc/ directory to hold Documenter.jl generated documentation * Testing a second index.md file * Fixing error in make.jl * Changed _config.yml to ignore src/ directory * Updates to meet suggestions in #512 * Bug fixes, latex support. * Fix to move .html files to the gh-pages branch. * Updated .gitignore * Tidying up * Changes for #512. * Updated guides to document scalar prior sampling, #544. * Added a 'quick start' page. #512 * Removed git workflow section, added links to more maintained guides. #535 * Minor guide updates.
* Moving over to Documenter.jl groundword. * More documentation cleaning and prep * Moving over to Documenter.jl groundword. * More documentation cleaning and prep * Travis settings updated * Travis fixes * Changed doc/build format to .md * Move to Documenter.jl native HTML builds * Update logo-build.jl * Minor updates as discussed in pull #512 * Moved from Pkg notation to the new Pkg3 notation. * Getting this to serve from `docs/` directory. * Adding `docs/build` files. * Set theme jekyll-theme-cayman * Revert "Set theme jekyll-theme-cayman" This reverts commit 38f5916. * index.md relocation * Commented out unused/index.md * Set up doc/ directory to hold Documenter.jl generated documentation * Testing a second index.md file * Fixing error in make.jl * Changed _config.yml to ignore src/ directory * Small cleanup. * refactoring of compiler * removed data macro * refactoring of tilde macro and fixed minor bug. Observer test broken. * improved comment * changed IS to work with new model function scheme * changed hmc core to work with new model function, changed style accoring to guidelines * changed model function calls * fixing compiler tests * fixed varinfo test * re-added kwarg checking * fixed missing data insertion problem * minor fix * lot of changes, wip * code now uses MacroTools, with a few exceptions * work in progress * more refactoring, solving prior sampling in #446 * removed strange tabs * minor change in particlecontainer test * added fix for multiple return statements * info -> debug * fixed typo * runmodel -> runmodel! * unifying initilistation for unconstrained distributions * changing vi.logp inplace, added some tests * added fix for #475 by implementing a data(Dict, Vector{Symbol}) function * typo * changed default assume behavior to sample from the prior * added more tests for the compiler * changed back to use initialisation instead of prior draws * changed default value of CHUNKSIZE to 10 * Introduce a special algorithm for initializing HMC. * Updates to meet suggestions in #512 * Bug fixes, latex support. * a little cleanup * remove AD auto_tune function and sym_str macro * move io.jl and util.jl from core to utilities * logsum -> logsumexp * remove some unused stuff * remove some type annotation because VarInfo is defined later * fix tests * make logsumexp more efficient * use StatsFuns for logsumexp * clean test REQUIRE * clean up * minor fix * added MacroTools to REQUIRE * Minor fixes + style from meeting * Tidy up AD interface + remove cleandual! and realpart!. (#515) Lots of commits. Some stuff went really wrong. Sorry. * Moving over to Documenter.jl groundword. * Unify AD interface * Use new gradient + rename stuff in hmc_core@ * Let SGHMC and SGLD use reverse-mode * Remove cleandual * More documentation cleaning and prep * Moving over to Documenter.jl groundword. * More documentation cleaning and prep * Travis settings updated * Travis fixes * Changed doc/build format to .md * Move to Documenter.jl native HTML builds * Prevent gradient from mutating it's argument * Minor changes * Fix bug involving types of traces * Resolve a bunch of formatting issues * Some more tidying up * Misc reformatting / removal of redundant code * Fix bug, remove printing, and remove code * Remove redundant code * Fix typo * Refactor leapfrog. * Copy old documentation over * Remove some other stuff that I shouldn't have added * Resolves conflicts with #523 * Small fix. * Small fix. * Update REQUIRE * Fix to move .html files to the gh-pages branch. * Updated .gitignore * Tidying up * Changes for #512. * changed LineNumberNode initialisation, issue #534 * Updated guides to document scalar prior sampling, #544. * Added a 'quick start' page. #512 * Removed git workflow section, added links to more maintained guides. #535 * Update README.md * Better testing for transforms + code which conforms to the style guide (#543) * Test logpdf_with_trans for univariate distributions * Make sure that MvNormal and LogMvNormal tests pass * Adds broken tests * Various other tests + implementation for LogMvNormal * Update Turing.jl * Minor guide updates. * Moving over to Documenter.jl groundword. * Moving over to Documenter.jl groundword. * More documentation cleaning and prep * More documentation cleaning and prep * Travis settings updated * Travis fixes * Changed doc/build format to .md * Move to Documenter.jl native HTML builds * Update logo-build.jl * Minor updates as discussed in pull #512 * Moved from Pkg notation to the new Pkg3 notation. * Getting this to serve from `docs/` directory. * Set theme jekyll-theme-cayman * Revert "Set theme jekyll-theme-cayman" This reverts commit 38f5916. * index.md relocation * Commented out unused/index.md * Set up doc/ directory to hold Documenter.jl generated documentation * Testing a second index.md file * Fixing error in make.jl * Changed _config.yml to ignore src/ directory * Updates to meet suggestions in #512 * Bug fixes, latex support. * Fix to move .html files to the gh-pages branch. * Updated .gitignore * Tidying up * Changes for #512. * Updated guides to document scalar prior sampling, #544. * Added a 'quick start' page. #512 * Removed git workflow section, added links to more maintained guides. #535 * Minor guide updates.
After discussions with @yebai, this issue will be resolved in #613 by allowing the user to specify a default value for the inputs when defining the model for when they are treated as parameters. For example, the following will be equivalent: @model gdemo(x = Vector{Real}(undef, 2)) = 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
g = gdemo()
g() and @model gdemo() = begin
x = Vector{Real}(undef, 2)
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
g = gdemo()
g() In other words, when a data variable is not input, it is treated as a parameter. If a default value is assigned, it is used, otherwise it is assigned to |
So it seems that a few people were unhappy about the above solution for the following reason if I understood correctly. If you want to set a default "data" value for
One proposal that was discussed today was to use At first glance, this seems like a reasonable solution but it will most likely bite us in the neck later when we want to be able to infer the type of @model gdemo(x::Vector{Float64}=[missing, missing])
....
end So using the above syntax, the user can specify if @model gdemo(x=Param{Vector{Float64}, 2}())
....
end
|
If this change turns out to be controversial, I will remove it from the compiler PR and we can discuss it later. |
I'm partial to the Maybe this has already been discussed, but is there a reason we can't just do something basic like this: @model gdemo(x = Param(300, Float64))
. . . .
end Just a minor thought. I am still a fan of the |
I'm generally in favour of using I understand the appeal for something like |
So, if we were to use Perhaps that's a poor example. I don't know why someone would have that kind of data. |
I think that’s where we should be heading. Reasons for missing data can be diverse, in the simplest case think about sensory data where a sensor doesn’t provide data at time t. Using probabilistic modelling in such cases is attractive as it allows to handle missing data. |
@cpfiffer
@trappmartin if we want to allow this fine granularity of missing data, then we will have to check if each variable is observed or assumed at run-time. I am ok with this, if we want to go that way. So basically, the partially data variable can be of type |
Actually, it should not hurt the performance at all for full parameters and full data variables because if use some if statement like this: if x[i] isa Missing
...
else
...
end and |
As said, I’m fine with the syntax you propose and I’m also fine with the more clumsy syntax using missing values. And inserting those if statements would be a great feature in my opinion. But let’s keep @yebai and @willtebbutt in the loop to find a sensible solution everyone agrees on. |
I'm actually fairly opposed to Turing automatically doing something with arrays comprising a mix of
|
I’m not aware of a feature request on missing values. Regarding solutions, in a Bayesian framework the obvious solution is to treat missing values as unknown parameters. Alternativ solutions are for example to exclude observations with missing values. I personally think it would be good to handle missing values such as other PPLs do. Especially as the overhead in the compiler would be minimal. But I’ll not try to push it if there are reasons not to support missing values. |
@trappmartin Could you point me towards an example of this please?
I don't disagree. My point is that there is more than one way to realise this in practice. For example, if as a user I have vector of data and I know that a subset of the data is missing, then it's usually quite straightforward to chop it up into two vectors by hand, one containing missing stuff and the other observed stuff. You can then treat these as two separate vectors in the model, and continue as usual. I would argue that this is a straightforward solution to this problem, which doesn't require any work on our side. It also means that there's one language feature for the user to concern themselves with, and won't usually add a huge amount of work on their part. There are also some non-trivial technical challenges. For example, if x ~ MvNormal(m, C) and we want to provide for n in ...
x[n] ~ Foo
end situation, where my impression is that we have a better idea of how to implement stuff. I'm open to being convinced about this, but the crux of my argument against doing it is basically that
@yebai where do you stand on this? |
Hm, I’m not sure I understand your approach. 😅 Most cases of missing values I have encountered where those in which a sensor would sometimes not provide a measurement. Meaning that you would usually observe measurements from multiple sensors at a time and that some sensor sometimes lacks information due to whatever reason, e.g. out of range values. How would I proceed with your approach? I’m open to everything btw. Regarding other PPLs, Stan and pymc can handle missing values to some extent. |
https://docs.pymc.io/notebooks/getting_started.html See: Case study 2: Coal mining disasters |
Hmm, I raised this issue of replacing @mohamed82008 perhaps let’s focus on the dimension issue in the compiler PR, ie, to replace |
@yebai may you write down some test cases and expected behavior? There are a few ideas thrown around in this thread and some are incompatible with others, so I want to nail down what is actually required. |
I think the 2 main use cases I have in mind are: @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
g = gdemo([missing, missing]) # treats x as a parameter of length 2 and eltype Real and g = gdemo(Union{Missing, Float64}[missing, 1.0]) # what should this be? If we decide to treat @model gdemo(x = default_value) = begin
....
end This default value could be meaningful data or the missing vectors from above signalling that |
Since the specifications of this change that everyone agrees on are still not clear to me, I will remove it from the compiler PR and do it in a separate PR. |
Hi @mohamed82008, sorry for the confusion. Let's only support the following syntax in the compiler PR (for now): @model gdemo(x = default_value) = begin
....
end to provide a way to initialize data variables, i.e.
The following syntax should be forbidden before we explicitly support missing values during inference. g = gdemo([missing, missing]) # returns error at runtime
g = gdemo(Union{Missing, Float64}[missing, 1.0]) # return error at runtime
|
* compiler refactor proof of concept * rename compiler to model_info * improve compiler readibility and make it more compact * invokelatest -> calling model * respond to Martin's comments * fix ambiguity * fix missing data case and make more compact * fix broken compiler.jl/explicit_ret.jl test * fix sampling from prior * make compiler more compact * fix update_vars! * fix tests * fix mh.jl tests * fix Sampler API * uncomment commented tests * fix seed in test/mh.jl/mh_cons.jl * make pvars and dvars type parameters in CallableModel * make data a field in CallableModel * fix #544 - allows default value of data vars when treated as params * add support for passing data as kwargs to outer function * model::Function -> model * CallableModel -> Model * compiler hygiene and organization * docstrings and some renaming * respond to Will's comments * some more style issues * add test for #544 * comment * model -> model::Model
Here is the error message:
@trappmartin
The text was updated successfully, but these errors were encountered: