Repository: cornellius-gp/gpytorch

Issue:
[Docs] Need help in loading a Full Bayesian model that uses Pyro for MCMC
I am trying to build a fully Bayesian GP with MCMC sampled parameters using Pyro based on the documentation. When I try to save and then load the model (using the save/load model documentation) later, I get an error about size mismatch: 

RuntimeError: Error(s) in loading state_dict for ExactGPModel:
	size mismatch for likelihood.noise_covar.raw_noise: copying a param with shape torch.Size([100, 1]) from checkpoint, the shape in current model is torch.Size([1]).
	size mismatch for mean_module.constant: copying a param with shape torch.Size([100, 1]) from checkpoint, the shape in current model is torch.Size([1]).
	size mismatch for covar_module.base_kernel.raw_lengthscale: copying a param with shape torch.Size([100, 1, 13]) from checkpoint, the shape in current model is torch.Size([1, 13]).

In this case, my dataset has 13 features, and I have run a NUTS sampler for 100 samples when i originally built the model. 
I think this is because when I instantiate ExactGPModel as indicated in the documentation, it is allocating only one sample for the different hyperparameters. Any thoughts on how to get around this error? It will also be great to include such an example in the saving and loading documentation.


Discussion:
@natarajanmolecule Ah, yeah. This is definitely an oversight on my part -- didn't even think about saving and loading when I implemented the sampling :-).

I'll put up a PR today or tomorrow that fixes this, and add an example saving and loading at the end of the sampling notebook!
That will be awesome @jacobrgardner ! Thank you for the help. I will look forward to the PR.
Hi @jacobrgardner I wanted to follow up with you on this issue. Please keep me posted and thanks so much for your help.
Hi @natarajanmolecule Sorry for the wait -- I'll try to get the PR that fixes this up by the end of the day.
@natarajanmolecule Actually I might need some more info from you. I finally got around to playing with this today, and the following works for me (in the basic fully Bayesian example notebook):
```python
mcmc_run.run(train_x, train_y)
model.pyro_load_from_samples(mcmc_run.get_samples())
model.load_state_dict(model.state_dict())
```
Are you doing anything in particular different from the example notebook?
Yes, this works for me too. But when I save the model using the model.save(), and then in a new session (where I dont have the mcmc_run object) when I instantiate the model, and try to load the dict I get the error.
Got it. It's because `pyro_load_from_samples` correctly updates the `batch_shape` and size of each parameter, but when you reinstantiate the model and try to load the parameters are batched (e.g., num_samples batch) but the current model parameters are not.
I see - what should the solution be then? How shall i instantiate the model with relevant samples beforehand or based on the loaded state dictionary?
@natarajanmolecule This isn't a problem on your end, it's just some slight complication in fixing it on ours. We are targeting to release a new minor version of GPyTorch by the end of the week that will include a fix for this.

If you absolutely need this to work before the end of the week, when you create your GP the second time (when you intend on loading) you could pass in `batch_shape=torch.Size([num_samples])` to your base kernel, your scale kernel and your likelihood.
Hi @jacobrgardner I wanted to follow up on this again. Actually I tried the batch_shape option, but that also resulted in an error. so it will be very helpful if you can help me figure this out.
Finally got around to putting up a PR for this. Sorry for the delay -- been travelling a lot recently.

Once it's merged in, everything should work on master and I believe we are planning to cut a new release soon.

Resolving patch:
diff --git a/gpytorch/module.py b/gpytorch/module.py
--- a/gpytorch/module.py
+++ b/gpytorch/module.py
@@ -1,5 +1,6 @@
 #!/usr/bin/env python3
 
+import itertools
 from collections import OrderedDict
 
 import torch
@@ -19,6 +20,9 @@ def __init__(self):
         self._constraints = OrderedDict()
 
         self._strict_init = True
+        self._load_strict_shapes = True
+
+        self._register_load_state_dict_pre_hook(self._load_state_hook_ignore_shapes)
 
     def __call__(self, *inputs, **kwargs):
         outputs = self.forward(*inputs, **kwargs)
@@ -268,6 +272,24 @@ def constraint_for_parameter_name(self, param_name):
         except AttributeError:  # submodule may not always be a gpytorch module
             return None
 
+    def _load_state_hook_ignore_shapes(
+        self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs
+    ):
+        if not self._load_strict_shapes:
+            local_name_params = itertools.chain(self._parameters.items(), self._buffers.items())
+            local_state = {k: v for k, v in local_name_params if v is not None}
+
+            for name, param in local_state.items():
+                key = prefix + name
+                if key in state_dict:
+                    param.data = state_dict[key].data
+
+    def load_strict_shapes(self, value):
+        def apply_fn(module):
+            module._load_strict_shapes = value
+
+        self.apply(apply_fn)
+
     def named_parameters_and_constraints(self):
         for name, param in self.named_parameters():
             yield name, param, self.constraint_for_parameter_name(name)


Test patch: