Repository: pytorch/pytorch

Issue:
Can't get DynamicQuantizedLSTM weight and bias
## 🐛 Bug

when I run https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html

can't get DynamicQuantizedLSTM weight , bias , scale, zero_point 

```
>>print(quantized_model.rnn.state_dict())
OrderedDict()
>>print(quantized_model.state_dict())
OrderedDict([('encoder.weight', tensor([[-0.2349,  0.4934, -0.3151,  ...,  0.4456,  0.4912,  0.1553],
        [-0.0710,  0.5101, -0.2940,  ...,  0.1747,  0.5764,  0.3247],
        [-0.0229,  0.0302,  0.0874,  ...,  0.0390,  0.0736, -0.0558],
        ...,
        [-0.0049, -0.0769, -0.0859,  ..., -0.0870,  0.0463,  0.0657],
        [ 0.0656, -0.0467,  0.0178,  ...,  0.0973,  0.0566, -0.0561],
        [ 0.0098,  0.0591, -0.0863,  ..., -0.0715, -0.0329,  0.0221]])), ('decoder.weight', tensor([[-0.2003,  0.2629,  0.3254,  ...,  0.4631, -0.3630, -0.0125],
        [-0.1502,  0.0501,  0.1627,  ...,  0.5132, -0.0626, -0.3380],
        [ 0.0000,  0.0125,  0.0626,  ..., -0.1001,  0.0626, -0.0501],
        ...,
        [-0.0751,  0.0501,  0.0626,  ...,  0.0000,  0.0751, -0.1252],
        [ 0.0501, -0.0876, -0.0501,  ...,  0.0751, -0.0626, -0.0751],
        [ 0.0376,  0.0876,  0.0626,  ..., -0.0250,  0.0876, -0.0626]],
       size=(33278, 256), dtype=torch.qint8,
       quantization_scheme=torch.per_tensor_affine, scale=0.012516804970800877,
       zero_point=0)), ('decoder.scale', tensor(1.)), ('decoder.zero_point', tensor(0)), ('decoder.bias', tensor([ 5.6618,  4.8450,  0.1365,  ..., -0.3657, -0.3338, -0.3476],
       requires_grad=True))])
```



 - PyTorch Version : 1.3.1
 - OS (e.g., Linux): Ubuntu
 - How you installed PyTorch : conda
 - Build command you used (if compiling from source): conda install pytorch torchvision cudatoolkit=10.1 -c pytorch
 - Python version: 3.7.5


cc @jerryzh168 @jianyuh @dzhulgakov @raghuramank100 @jamesr66a


Discussion:
Looking into it. Meanwhile, you can get the parameters by running 

```
for param in quantized_model.rnn._all_weight_values:
  print(torch.ops.quantized.linear_unpack(param.param))
```


After #31540 lands you can see the weight values after running `state_dict()` on it. You can also access all the weights using `quantized_model.rnn.all_weights`

Resolving patch:
diff --git a/torch/nn/quantized/dynamic/modules/rnn.py b/torch/nn/quantized/dynamic/modules/rnn.py
--- a/torch/nn/quantized/dynamic/modules/rnn.py
+++ b/torch/nn/quantized/dynamic/modules/rnn.py
@@ -1,12 +1,14 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
+from collections import OrderedDict
+import numbers
+
 import torch
 import torch.nn as nn
 from torch import Tensor  # noqa: F401
 from torch import _VF
 from torch._jit_internal import Tuple, Optional, List  # noqa: F401
 from torch.nn.utils.rnn import PackedSequence
-import numbers
 
 
 def apply_permutation(tensor, permutation, dim=1):
@@ -27,6 +29,34 @@ def __setstate__(self, state):
         self.param = torch.ops.quantized.linear_prepack(*state[0])
         self.training = state[1]
 
+    def _save_to_state_dict(self, destination, prefix, keep_vars):
+        super(PackedParameter, self)._save_to_state_dict(destination, prefix,
+                                                         keep_vars)
+        (w, b) = self.unpack()
+
+        destination[prefix + 'weight'] = w
+        destination[prefix + 'bias'] = b
+
+    def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict,
+                              missing_keys, unexpected_keys, error_msgs):
+        weight = state_dict[prefix + 'weight']
+        bias = state_dict[prefix + 'bias']
+        self.param = torch.ops.quantized.linear_prepack(weight, bias)
+        state_dict.pop(prefix + 'weight')
+        state_dict.pop(prefix + 'bias')
+
+        super(PackedParameter, self)._load_from_state_dict(state_dict, prefix,
+                                                           local_metadata,
+                                                           False, missing_keys,
+                                                           unexpected_keys,
+                                                           error_msgs)
+
+    def __repr__(self):
+        return repr(self.unpack())
+
+    def unpack(self):
+        return torch.ops.quantized.linear_unpack(self.param)
+
     # This only exists because there's a bug in recursive scripting
     # that arises only in Python 2 where a recursively scripted
     # module does not have a forward(). We can delete this once we
@@ -150,6 +180,36 @@ def extra_repr(self):
             s += ', bidirectional={bidirectional}'
         return s.format(**self.__dict__)
 
+    def __repr__(self):
+        # We don't want to show `ModuleList` children, hence custom
+        # `__repr__`. This is the same as nn.Module.__repr__, except the check
+        # for the `PackedParameter` and `nn.ModuleList`.
+        # You should still override `extra_repr` to add more info.
+        extra_lines = []
+        extra_repr = self.extra_repr()
+        # empty string will be split into list ['']
+        if extra_repr:
+            extra_lines = extra_repr.split('\n')
+        child_lines = []
+        for key, module in self._modules.items():
+            if isinstance(module, (PackedParameter, nn.ModuleList)):
+                continue
+            mod_str = repr(module)
+            mod_str = nn.modules.module._addindent(mod_str, 2)
+            child_lines.append('(' + key + '): ' + mod_str)
+        lines = extra_lines + child_lines
+
+        main_str = self._get_name() + '('
+        if lines:
+            # simple one-liner info, which most builtin Modules will use
+            if len(extra_lines) == 1 and not child_lines:
+                main_str += extra_lines[0]
+            else:
+                main_str += '\n  ' + '\n  '.join(lines) + '\n'
+
+        main_str += ')'
+        return main_str
+
     def check_input(self, input, batch_sizes):
         # type: (Tensor, Optional[Tensor]) -> None
         expected_input_dim = 2 if batch_sizes is not None else 3
@@ -193,6 +253,13 @@ def permute_hidden(self, hx, permutation):
             return hx
         return apply_permutation(hx, permutation)
 
+    @property
+    def all_weights(self):
+        result = OrderedDict()
+        for idx, name in enumerate(self._all_weight_names):
+            result[name] = self._all_weight_values[idx].unpack()
+        return result
+
     @classmethod
     def from_float(cls, mod):
         assert type(mod) == torch.nn.LSTM, 'nn.quantized.dynamic.RNNBase.from_float only works for nn.LSTM'
diff --git a/torch/nn/quantized/modules/linear.py b/torch/nn/quantized/modules/linear.py
--- a/torch/nn/quantized/modules/linear.py
+++ b/torch/nn/quantized/modules/linear.py
@@ -80,6 +80,10 @@ def __setstate__(self, state):
         self.set_weight_bias(state[0], state[1])
         self.training = state[2]
 
+    def __repr__(self):
+        return self._weight_bias().__repr__()
+
+
 class Linear(torch.nn.Module):
     r"""
     A quantized linear module with quantized tensor as inputs and outputs.
@@ -142,6 +146,36 @@ def extra_repr(self):
             self.in_features, self.out_features, self.scale, self.zero_point, self.weight().qscheme()
         )
 
+    def __repr__(self):
+        # We don't want to show `LinearPackedParams` children, hence custom
+        # `__repr__`. This is the same as nn.Module.__repr__, except the check
+        # for the `LinearPackedParams`.
+        # You should still override `extra_repr` to add more info.
+        extra_lines = []
+        extra_repr = self.extra_repr()
+        # empty string will be split into list ['']
+        if extra_repr:
+            extra_lines = extra_repr.split('\n')
+        child_lines = []
+        for key, module in self._modules.items():
+            if isinstance(module, LinearPackedParams):
+                continue
+            mod_str = repr(module)
+            mod_str = _addindent(mod_str, 2)
+            child_lines.append('(' + key + '): ' + mod_str)
+        lines = extra_lines + child_lines
+
+        main_str = self._get_name() + '('
+        if lines:
+            # simple one-liner info, which most builtin Modules will use
+            if len(extra_lines) == 1 and not child_lines:
+                main_str += extra_lines[0]
+            else:
+                main_str += '\n  ' + '\n  '.join(lines) + '\n'
+
+        main_str += ')'
+        return main_str
+
     def forward(self, x):
         return torch.ops.quantized.linear(
             x, self._packed_params._packed_params, self.scale, self.zero_point)


Test patch: