Repository: pytorch/pytorch

Issue:
[Bug] Parameters turn into Tensors after load
## Issue description

After a model is saved and then loaded using torch.save / load method, an attribute that was a `Parameter` becomes a `Tensor`

## Code example
```

import torch

class M(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.B = torch.nn.Parameter(torch.Tensor())

class A(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.m = M()

a = A()
print(type(a.m.B))
torch.save(a, 'test')
a = torch.load('test')
print(type(a.m.B))
```
The output is
```
<class 'torch.nn.parameter.Parameter'>
<class 'torch.Tensor'>
```

## System Info

PyTorch version: 0.4.0
Is debug build: No
CUDA used to build PyTorch: 8.0.61

OS: Debian GNU/Linux 9.3 (stretch)
GCC version: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
CMake version: version 3.7.2

Python version: 3.5
Is CUDA available: Yes
CUDA runtime version: 9.0.176
GPU models and configuration: 
GPU 0: GeForce GTX 1080
GPU 1: GeForce GTX 1080

Nvidia driver version: 384.98
cuDNN version: Probably one of the following:
/usr/local/cuda-7.5/lib64/libcudnn.so.7.0.64
/usr/local/cuda-7.5/lib64/libcudnn_static.a
/usr/local/cuda-9.0/lib64/libcudnn.so.7.0.3
/usr/local/cuda-9.0/lib64/libcudnn_static.a
/usr/local/lib/python3.5/dist-packages/torch/lib/libcudnn-900fef33.so.7.0.5
/usr/local/matlab90/bin/glnxa64/libcudnn.so.7.0.64

Versions of relevant libraries:
[pip] Could not collect
[conda] Could not collect

- How you installed PyTorch: pip



Discussion:
Using **a.m.B.type()** instead of **type(a.m.B)** should give you the same output before saving and after loading, i.e.: torch.FloatTensor.
I think the former is the recommended way to check a Tensor's type.
this is a legit bug, we should fix this.

Resolving patch:
diff --git a/torch/nn/parameter.py b/torch/nn/parameter.py
--- a/torch/nn/parameter.py
+++ b/torch/nn/parameter.py
@@ -25,3 +25,6 @@ def __new__(cls, data=None, requires_grad=True):
 
     def __repr__(self):
         return 'Parameter containing:\n' + super(Parameter, self).__repr__()
+
+    def __reduce_ex__(self, proto):
+        return Parameter, (super(Parameter, self), self.requires_grad)


Test patch:
diff --git a/test/test_torch.py b/test/test_torch.py
--- a/test/test_torch.py
+++ b/test/test_torch.py
@@ -6153,6 +6153,30 @@ def test_pickle(self):
         b = pickle.loads(serialized)
         self.assertEqual(a, b)
 
+    def test_pickle_parameter(self):
+        if sys.version_info[0] == 2:
+            import cPickle as pickle
+        else:
+            import pickle
+        a = torch.nn.Parameter(torch.randn(5, 5))
+        serialized = pickle.dumps(a)
+        b = pickle.loads(serialized)
+        self.assertTrue(isinstance(b, torch.nn.Parameter))
+        self.assertEqual(a.requires_grad, b.requires_grad)
+        self.assertEqual(a, b)
+
+    def test_pickle_parameter_no_requires_grad(self):
+        if sys.version_info[0] == 2:
+            import cPickle as pickle
+        else:
+            import pickle
+        a = torch.nn.Parameter(torch.randn(5, 5), requires_grad=False)
+        serialized = pickle.dumps(a)
+        b = pickle.loads(serialized)
+        self.assertTrue(isinstance(b, torch.nn.Parameter))
+        self.assertEqual(a.requires_grad, b.requires_grad)
+        self.assertEqual(a, b)
+
     def test_norm_fastpaths(self):
         x = torch.randn(3, 5)